| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "nikitina_v_hoar_sort_batcher/stl/include/ops_stl.hpp" | ||
| 2 | |||
| 3 | #include <algorithm> | ||
| 4 | #include <limits> | ||
| 5 | #include <thread> | ||
| 6 | #include <utility> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "nikitina_v_hoar_sort_batcher/common/include/common.hpp" | ||
| 10 | |||
| 11 | namespace nikitina_v_hoar_sort_batcher { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | 96 | void QuickSortHoare(std::vector<int> &arr, int low, int high) { | |
| 16 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
|
96 | if (low >= high) { |
| 17 | ✗ | return; | |
| 18 | } | ||
| 19 | 96 | std::vector<std::pair<int, int>> stack; | |
| 20 |
1/2✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
|
96 | stack.emplace_back(low, high); |
| 21 | |||
| 22 |
2/2✓ Branch 0 taken 480 times.
✓ Branch 1 taken 96 times.
|
576 | while (!stack.empty()) { |
| 23 | auto [l, h] = stack.back(); | ||
| 24 | stack.pop_back(); | ||
| 25 | |||
| 26 |
2/2✓ Branch 0 taken 288 times.
✓ Branch 1 taken 192 times.
|
480 | if (l >= h) { |
| 27 | 288 | continue; | |
| 28 | } | ||
| 29 | |||
| 30 | 192 | int pivot = arr[l + ((h - l) / 2)]; | |
| 31 | 192 | int i = l - 1; | |
| 32 | 192 | int j = h + 1; | |
| 33 | |||
| 34 | while (true) { | ||
| 35 | 312 | i++; | |
| 36 |
2/2✓ Branch 0 taken 104 times.
✓ Branch 1 taken 312 times.
|
416 | while (arr[i] < pivot) { |
| 37 | 104 | i++; | |
| 38 | } | ||
| 39 | |||
| 40 | 312 | j--; | |
| 41 |
2/2✓ Branch 0 taken 104 times.
✓ Branch 1 taken 312 times.
|
416 | while (arr[j] > pivot) { |
| 42 | 104 | j--; | |
| 43 | } | ||
| 44 | |||
| 45 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 192 times.
|
312 | if (i >= j) { |
| 46 | break; | ||
| 47 | } | ||
| 48 | std::swap(arr[i], arr[j]); | ||
| 49 | } | ||
| 50 | |||
| 51 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | stack.emplace_back(l, j); |
| 52 |
1/4✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
192 | stack.emplace_back(j + 1, h); |
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | 120 | void CompareSplit(std::vector<int> &arr, int start1, int len1, int start2, int len2) { | |
| 57 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
|
120 | if (len1 == 0 || len2 == 0) { |
| 58 | ✗ | return; | |
| 59 | } | ||
| 60 | |||
| 61 |
1/2✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
|
120 | std::vector<int> left_block(arr.begin() + start1, arr.begin() + start1 + len1); |
| 62 |
1/4✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
120 | std::vector<int> right_block(arr.begin() + start2, arr.begin() + start2 + len2); |
| 63 | |||
| 64 | int p1 = 0; | ||
| 65 | int p2 = 0; | ||
| 66 | int write1 = start1; | ||
| 67 | int write2 = start2; | ||
| 68 | |||
| 69 |
2/2✓ Branch 0 taken 720 times.
✓ Branch 1 taken 120 times.
|
840 | for (int i = 0; i < len1 + len2; ++i) { |
| 70 | int val = 0; | ||
| 71 |
6/6✓ Branch 0 taken 560 times.
✓ Branch 1 taken 160 times.
✓ Branch 2 taken 496 times.
✓ Branch 3 taken 64 times.
✓ Branch 4 taken 296 times.
✓ Branch 5 taken 200 times.
|
720 | if (p1 < len1 && (p2 == len2 || left_block[p1] <= right_block[p2])) { |
| 72 | 360 | val = left_block[p1++]; | |
| 73 | } else { | ||
| 74 | 360 | val = right_block[p2++]; | |
| 75 | } | ||
| 76 | |||
| 77 |
2/2✓ Branch 0 taken 360 times.
✓ Branch 1 taken 360 times.
|
720 | if (i < len1) { |
| 78 | 360 | arr[write1++] = val; | |
| 79 | } else { | ||
| 80 | 360 | arr[write2++] = val; | |
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | 72 | void BuildPairs(std::vector<std::pair<int, int>> &pairs, int num_threads, int step_p, int step_k) { | |
| 86 |
2/2✓ Branch 0 taken 96 times.
✓ Branch 1 taken 72 times.
|
168 | for (int idx_j = step_k % step_p; idx_j + step_k < num_threads; idx_j += (step_k * 2)) { |
| 87 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 96 times.
|
216 | for (int idx_i = 0; idx_i < std::min(step_k, num_threads - idx_j - step_k); idx_i++) { |
| 88 |
1/2✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
|
120 | if ((idx_j + idx_i) / (step_p * 2) == (idx_j + idx_i + step_k) / (step_p * 2)) { |
| 89 | 120 | pairs.emplace_back(idx_j + idx_i, idx_j + idx_i + step_k); | |
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | 72 | } | |
| 94 | |||
| 95 | // Вспомогательная функция для снижения когнитивной сложности | ||
| 96 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | void ExecuteMergeStep(std::vector<int> &output, const std::vector<int> &offsets, |
| 97 | const std::vector<std::pair<int, int>> &pairs, int actual_threads) { | ||
| 98 | 72 | int num_pairs = static_cast<int>(pairs.size()); | |
| 99 | 72 | int chunk_size = (num_pairs + actual_threads - 1) / actual_threads; | |
| 100 | 72 | std::vector<std::thread> threads; | |
| 101 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | threads.reserve(actual_threads); |
| 102 | |||
| 103 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 72 times.
|
192 | for (int thread_idx = 0; thread_idx < actual_threads; ++thread_idx) { |
| 104 | 120 | int start = thread_idx * chunk_size; | |
| 105 |
1/2✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
|
120 | int end = std::min(start + chunk_size, num_pairs); |
| 106 | |||
| 107 |
1/2✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
|
120 | if (start < end) { |
| 108 |
1/2✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
|
120 | threads.emplace_back([start, end, &output, &offsets, &pairs]() { |
| 109 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 120 times.
|
240 | for (int idx = start; idx < end; ++idx) { |
| 110 | 120 | int block_a = pairs[idx].first; | |
| 111 | 120 | int block_b = pairs[idx].second; | |
| 112 | 120 | CompareSplit(output, offsets[block_a], offsets[block_a + 1] - offsets[block_a], offsets[block_b], | |
| 113 | 120 | offsets[block_b + 1] - offsets[block_b]); | |
| 114 | } | ||
| 115 | 120 | }); | |
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 72 times.
|
192 | for (auto &th : threads) { |
| 120 |
1/2✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
|
120 | th.join(); |
| 121 | } | ||
| 122 | 72 | } | |
| 123 | |||
| 124 | 24 | void BatcherMergePhase(std::vector<int> &output, const std::vector<int> &offsets, int num_threads, int hw_threads) { | |
| 125 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
|
72 | for (int step_p = 1; step_p < num_threads; step_p *= 2) { |
| 126 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 48 times.
|
120 | for (int step_k = step_p; step_k > 0; step_k /= 2) { |
| 127 | 72 | std::vector<std::pair<int, int>> pairs; | |
| 128 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | BuildPairs(pairs, num_threads, step_p, step_k); |
| 129 | |||
| 130 | 72 | int num_pairs = static_cast<int>(pairs.size()); | |
| 131 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
72 | if (num_pairs == 0) { |
| 132 | continue; | ||
| 133 | } | ||
| 134 | |||
| 135 | int actual_threads = std::min(hw_threads, num_pairs); | ||
| 136 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | ExecuteMergeStep(output, offsets, pairs, actual_threads); |
| 137 | } | ||
| 138 | } | ||
| 139 | 24 | } | |
| 140 | |||
| 141 | } // namespace | ||
| 142 | |||
| 143 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | HoareSortBatcherSTL::HoareSortBatcherSTL(const InType &in) { |
| 144 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 145 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | GetInput() = in; |
| 146 | 32 | } | |
| 147 | |||
| 148 | 32 | bool HoareSortBatcherSTL::ValidationImpl() { | |
| 149 | 32 | return true; | |
| 150 | } | ||
| 151 | |||
| 152 | 32 | bool HoareSortBatcherSTL::PreProcessingImpl() { | |
| 153 | 32 | GetOutput() = GetInput(); | |
| 154 | 32 | return true; | |
| 155 | } | ||
| 156 | |||
| 157 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 | bool HoareSortBatcherSTL::RunImpl() { |
| 158 | auto &output = GetOutput(); | ||
| 159 | |||
| 160 | 32 | int orig_n = static_cast<int>(output.size()); | |
| 161 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 | if (orig_n <= 1) { |
| 162 | return true; | ||
| 163 | } | ||
| 164 | |||
| 165 | // Получаем количество доступных аппаратных потоков | ||
| 166 | 24 | int hw_threads = static_cast<int>(std::thread::hardware_concurrency()); | |
| 167 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (hw_threads == 0) { |
| 168 | hw_threads = 4; // Fallback, если hardware_concurrency() вернул 0 | ||
| 169 | } | ||
| 170 | |||
| 171 | int t = 1; | ||
| 172 |
3/4✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
|
72 | while (t * 2 <= hw_threads && t * 2 <= orig_n) { |
| 173 | t *= 2; | ||
| 174 | } | ||
| 175 | |||
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (t == 1) { |
| 177 | ✗ | QuickSortHoare(output, 0, orig_n - 1); | |
| 178 | ✗ | return true; | |
| 179 | } | ||
| 180 | |||
| 181 | 24 | int pad = (t - (orig_n % t)) % t; | |
| 182 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
|
72 | for (int i = 0; i < pad; ++i) { |
| 183 | 48 | output.push_back(std::numeric_limits<int>::max()); | |
| 184 | } | ||
| 185 | |||
| 186 | 24 | int n = orig_n + pad; | |
| 187 | 24 | std::vector<int> offsets(t + 1, 0); | |
| 188 | 24 | int chunk = n / t; | |
| 189 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 24 times.
|
144 | for (int i = 0; i <= t; ++i) { |
| 190 | 120 | offsets[i] = i * chunk; | |
| 191 | } | ||
| 192 | |||
| 193 | // Параллельный запуск сортировок локальных блоков | ||
| 194 | 24 | std::vector<std::thread> sort_threads; | |
| 195 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | sort_threads.reserve(t); // Предварительное выделение памяти для векторов |
| 196 | |||
| 197 |
2/2✓ Branch 0 taken 96 times.
✓ Branch 1 taken 24 times.
|
120 | for (int i = 0; i < t; ++i) { |
| 198 |
1/2✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
|
192 | sort_threads.emplace_back([&output, &offsets, i]() { QuickSortHoare(output, offsets[i], offsets[i + 1] - 1); }); |
| 199 | } | ||
| 200 | |||
| 201 |
2/2✓ Branch 0 taken 96 times.
✓ Branch 1 taken 24 times.
|
120 | for (auto &th : sort_threads) { |
| 202 |
1/2✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
|
96 | th.join(); |
| 203 | } | ||
| 204 | |||
| 205 | // Запуск фазы слияния | ||
| 206 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | BatcherMergePhase(output, offsets, t, hw_threads); |
| 207 | |||
| 208 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | output.resize(orig_n); |
| 209 | |||
| 210 | return true; | ||
| 211 | 24 | } | |
| 212 | |||
| 213 | 32 | bool HoareSortBatcherSTL::PostProcessingImpl() { | |
| 214 | 32 | return true; | |
| 215 | } | ||
| 216 | |||
| 217 | } // namespace nikitina_v_hoar_sort_batcher | ||
| 218 |