Interstellar12312

Modified blockQuicksort with three element rule vs Standard blockQuicksort

Aug 31st, 2026
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.74 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <chrono>
  5. #include <random>
  6. #include <iomanip>
  7. #include <cmath>
  8.  
  9. // Modified blockQuicksort with three element rule vs Standard blockQuicksort
  10. // Contact Email [email protected]
  11. // Created by I.A
  12.  
  13. void apply_three_element_rule(std::vector<int>& arr, int low, int high) {
  14.     if (high - low == 2) {
  15.         int pivot = arr[low];
  16.         int third_num = arr[high];
  17.  
  18.         if (third_num <= pivot) {
  19.             int temp_mid = arr[low + 1];
  20.             arr[high] = temp_mid;
  21.             arr[low + 1] = pivot;
  22.             arr[low] = third_num;
  23.         } else {
  24.             int temp_mid = arr[low + 1];
  25.             arr[low] = temp_mid;
  26.             arr[low + 1] = pivot;
  27.             arr[high] = third_num;
  28.         }
  29.     }
  30. }
  31.  
  32.  
  33. // Pure BlockQuicksort
  34.  
  35. void blockQuicksortUnmodified(std::vector<int>& arr, int low, int high) {
  36.     if (low >= high) return;
  37.  
  38.     if (high - low == 1) {
  39.         if (arr[high] < arr[low]) std::swap(arr[low], arr[high]);
  40.         return;
  41.     }
  42.  
  43.     int mid = low + (high - low) / 2;
  44.     if (arr[mid] < arr[low])  std::swap(arr[low], arr[mid]);
  45.     if (arr[high] < arr[low]) std::swap(arr[low], arr[high]);
  46.     if (arr[high] < arr[mid]) std::swap(arr[mid], arr[high]);
  47.  
  48.     int pivot = arr[mid];
  49.     std::swap(arr[mid], arr[high]);
  50.  
  51.     constexpr size_t BLOCK_SIZE = 64;
  52.     int left_offsets[BLOCK_SIZE];
  53.     int right_offsets[BLOCK_SIZE];
  54.  
  55.     int i = low;
  56.     int j = high - 1;
  57.  
  58.     int num_left = 0, num_right = 0;
  59.     int idx_l = 0, idx_r = 0;
  60.     int start_l = 0, start_r = 0;
  61.  
  62.     while (true) {
  63.         if (num_left == 0) {
  64.             if (i > j) break;
  65.             start_l = i;
  66.             int block = std::min<int>(BLOCK_SIZE, j - i + 1);
  67.             for (int k = 0; k < block; ++k) {
  68.                 left_offsets[num_left] = k;
  69.                 num_left += (arr[i + k] >= pivot);
  70.             }
  71.             i += block;
  72.             idx_l = 0;
  73.         }
  74.  
  75.         if (num_right == 0) {
  76.             if (i > j) break;
  77.             int block = std::min<int>(BLOCK_SIZE, j - i + 1);
  78.             start_r = j - block + 1;
  79.             for (int k = block - 1; k >= 0; --k) {
  80.                 right_offsets[num_right] = k;
  81.                 num_right += (arr[start_r + k] < pivot);
  82.             }
  83.             j -= block;
  84.             idx_r = 0;
  85.         }
  86.  
  87.         int min_swaps = std::min(num_left, num_right);
  88.         for (int k = 0; k < min_swaps; ++k) {
  89.             std::swap(arr[start_l + left_offsets[idx_l + k]],
  90.                       arr[start_r + right_offsets[idx_r + k]]);
  91.         }
  92.  
  93.         num_left -= min_swaps;
  94.         num_right -= min_swaps;
  95.         idx_l += min_swaps;
  96.         idx_r += min_swaps;
  97.     }
  98.  
  99.     int split;
  100.     if (num_left > 0) {
  101.         int last_less = i - 1;
  102.         for (int k = num_left - 1; k >= 0; --k) {
  103.             std::swap(arr[start_l + left_offsets[idx_l + k]], arr[last_less]);
  104.             last_less--;
  105.         }
  106.         split = last_less + 1;
  107.     } else if (num_right > 0) {
  108.         int first_greater = j + 1;
  109.         for (int k = num_right - 1; k >= 0; --k) {
  110.             std::swap(arr[start_r + right_offsets[idx_r + k]], arr[first_greater]);
  111.             first_greater++;
  112.         }
  113.         split = first_greater;
  114.     } else {
  115.         split = i;
  116.     }
  117.  
  118.     std::swap(arr[split], arr[high]);
  119.  
  120.     blockQuicksortUnmodified(arr, low, split - 1);
  121.     blockQuicksortUnmodified(arr, split + 1, high);
  122. }
  123.  
  124.  
  125. // 2. Modified BlockQuicksort with Custom Rule
  126.  
  127. void blockQuicksortModified(std::vector<int>& arr, int low, int high) {
  128.     if (low >= high) return;
  129.  
  130.     if (high - low == 1) {
  131.         if (arr[high] < arr[low]) std::swap(arr[low], arr[high]);
  132.         return;
  133.     }
  134.  
  135.     if (high - low == 2) {
  136.         apply_three_element_rule(arr, low, high);
  137.         return;
  138.     }
  139.  
  140.     int mid = low + (high - low) / 2;
  141.     if (arr[mid] < arr[low])  std::swap(arr[low], arr[mid]);
  142.     if (arr[high] < arr[low]) std::swap(arr[low], arr[high]);
  143.     if (arr[high] < arr[mid]) std::swap(arr[mid], arr[high]);
  144.  
  145.     int pivot = arr[mid];
  146.     std::swap(arr[mid], arr[high]);
  147.  
  148.     constexpr size_t BLOCK_SIZE = 64;
  149.     int left_offsets[BLOCK_SIZE];
  150.     int right_offsets[BLOCK_SIZE];
  151.  
  152.     int i = low;
  153.     int j = high - 1;
  154.  
  155.     int num_left = 0, num_right = 0;
  156.     int idx_l = 0, idx_r = 0;
  157.     int start_l = 0, start_r = 0;
  158.  
  159.     while (true) {
  160.         if (num_left == 0) {
  161.             if (i > j) break;
  162.             start_l = i;
  163.             int block = std::min<int>(BLOCK_SIZE, j - i + 1);
  164.             for (int k = 0; k < block; ++k) {
  165.                 left_offsets[num_left] = k;
  166.                 num_left += (arr[i + k] >= pivot);
  167.             }
  168.             i += block;
  169.             idx_l = 0;
  170.         }
  171.  
  172.         if (num_right == 0) {
  173.             if (i > j) break;
  174.             int block = std::min<int>(BLOCK_SIZE, j - i + 1);
  175.             start_r = j - block + 1;
  176.             for (int k = block - 1; k >= 0; --k) {
  177.                 right_offsets[num_right] = k;
  178.                 num_right += (arr[start_r + k] < pivot);
  179.             }
  180.             j -= block;
  181.             idx_r = 0;
  182.         }
  183.  
  184.         int min_swaps = std::min(num_left, num_right);
  185.         for (int k = 0; k < min_swaps; ++k) {
  186.             std::swap(arr[start_l + left_offsets[idx_l + k]],
  187.                       arr[start_r + right_offsets[idx_r + k]]);
  188.         }
  189.  
  190.         num_left -= min_swaps;
  191.         num_right -= min_swaps;
  192.         idx_l += min_swaps;
  193.         idx_r += min_swaps;
  194.     }
  195.  
  196.     int split;
  197.     if (num_left > 0) {
  198.         int last_less = i - 1;
  199.         for (int k = num_left - 1; k >= 0; --k) {
  200.             std::swap(arr[start_l + left_offsets[idx_l + k]], arr[last_less]);
  201.             last_less--;
  202.         }
  203.         split = last_less + 1;
  204.     } else if (num_right > 0) {
  205.         int first_greater = j + 1;
  206.         for (int k = num_right - 1; k >= 0; --k) {
  207.             std::swap(arr[start_r + right_offsets[idx_r + k]], arr[first_greater]);
  208.             first_greater++;
  209.         }
  210.         split = first_greater;
  211.     } else {
  212.         split = i;
  213.     }
  214.  
  215.     std::swap(arr[split], arr[high]);
  216.  
  217.     blockQuicksortModified(arr, low, split - 1);
  218.     blockQuicksortModified(arr, split + 1, high);
  219. }
  220.  
  221.  
  222. // Benchmark Runner
  223.  
  224. int main() {
  225.     const size_t ARRAY_SIZE = 1000000;
  226.     const size_t NUM_TRIALS = 100;
  227.  
  228.     std::mt19937 rng(1337);
  229.     std::uniform_int_distribution<int> dist(1, 1000000);
  230.  
  231.     double total_time_unmodified = 0.0;
  232.     double total_time_modified = 0.0;
  233.  
  234.     std::cout << "Starting benchmark...\n";
  235.     std::cout << "Array Size: " << ARRAY_SIZE << "\n";
  236.     std::cout << "Trials:     " << NUM_TRIALS << "\n\n";
  237.  
  238.     for (size_t trial = 1; trial <= NUM_TRIALS; ++trial) {
  239.         std::vector<int> original(ARRAY_SIZE);
  240.         for (size_t i = 0; i < ARRAY_SIZE; ++i) {
  241.             original[i] = dist(rng);
  242.         }
  243.  
  244.         std::vector<int> vec1 = original;
  245.         std::vector<int> vec2 = original;
  246.  
  247.         double current_unmod_time = 0.0;
  248.         double current_mod_time = 0.0;
  249.  
  250.         // Alternate execution order to mitigate CPU cache effects
  251.         if (trial % 2 != 0) {
  252.             // Run Modified First
  253.             auto start2 = std::chrono::steady_clock::now();
  254.             blockQuicksortModified(vec2, 0, vec2.size() - 1);
  255.             auto end2 = std::chrono::steady_clock::now();
  256.             current_mod_time = std::chrono::duration<double, std::milli>(end2 - start2).count();
  257.  
  258.             // Run Unmodified Second
  259.             auto start1 = std::chrono::steady_clock::now();
  260.             blockQuicksortUnmodified(vec1, 0, vec1.size() - 1);
  261.             auto end1 = std::chrono::steady_clock::now();
  262.             current_unmod_time = std::chrono::duration<double, std::milli>(end1 - start1).count();
  263.         } else {
  264.             // Run Unmodified First
  265.             auto start1 = std::chrono::steady_clock::now();
  266.             blockQuicksortUnmodified(vec1, 0, vec1.size() - 1);
  267.             auto end1 = std::chrono::steady_clock::now();
  268.             current_unmod_time = std::chrono::duration<double, std::milli>(end1 - start1).count();
  269.  
  270.             // Run Modified Second
  271.             auto start2 = std::chrono::steady_clock::now();
  272.             blockQuicksortModified(vec2, 0, vec2.size() - 1);
  273.             auto end2 = std::chrono::steady_clock::now();
  274.             current_mod_time = std::chrono::duration<double, std::milli>(end2 - start2).count();
  275.         }
  276.  
  277.         total_time_unmodified += current_unmod_time;
  278.         total_time_modified += current_mod_time;
  279.  
  280.         std::cout << "Trial " << std::setw(2) << trial
  281.                   << " - Unmodified: " << std::fixed << std::setprecision(2) << current_unmod_time << " ms"
  282.                   << " | Modified: " << current_mod_time << " ms"
  283.                   << " (Order: " << (trial % 2 != 0 ? "Mod First" : "Unmod First") << ")\n";
  284.     }
  285.  
  286.     double avg_unmod = total_time_unmodified / NUM_TRIALS;
  287.     double avg_mod = total_time_modified / NUM_TRIALS;
  288.  
  289.     // Calculate percentage difference relative to the unmodified version
  290.     double percent_diff = ((avg_unmod - avg_mod) / avg_unmod) * 100.0;
  291.  
  292.     std::cout << "\n=== FINAL RESULTS ===\n";
  293.     std::cout << "Average Time (Unmodified): " << std::fixed << std::setprecision(2) << avg_unmod << " ms\n";
  294.     std::cout << "Average Time (Modified):   " << avg_mod << " ms\n";
  295.  
  296.     std::cout << "Performance Difference:    ";
  297.     if (percent_diff > 0) {
  298.         std::cout << "Modified is " << percent_diff << "% FASTER than Unmodified\n";
  299.     } else if (percent_diff < 0) {
  300.         std::cout << "Modified is " << std::abs(percent_diff) << "% SLOWER than Unmodified\n";
  301.     } else {
  302.         std::cout << "Both versions performed exactly the same\n";
  303.     }
  304.  
  305.     return 0;
  306. }
Advertisement
Add Comment
Please, Sign In to add comment