VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
cpuRadixSorter.h
Go to the documentation of this file.
1/*--------------------------------*- VM2D -*-----------------*---------------*\
2| ## ## ## ## #### ##### | | Version 1.14 |
3| ## ## ### ### ## ## ## ## | VM2D: Vortex Method | 2026/03/06 |
4| ## ## ## # ## ## ## ## | for 2D Flow Simulation *----------------*
5| #### ## ## ## ## ## | Open Source Code |
6| ## ## ## ###### ##### | https://www.github.com/vortexmethods/VM2D |
7| |
8| Copyright (C) 2017-2026 I. Marchevsky, K. Sokol, E. Ryatina, A. Kolganova |
9*-----------------------------------------------------------------------------*
10| File name: cpuRadixSorter.h |
11| Info: Source code of VM2D |
12| |
13| This file is part of VM2D. |
14| VM2D is free software: you can redistribute it and/or modify it |
15| under the terms of the GNU General Public License as published by |
16| the Free Software Foundation, either version 3 of the License, or |
17| (at your option) any later version. |
18| |
19| VM2D is distributed in the hope that it will be useful, but WITHOUT |
20| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
21| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
22| for more details. |
23| |
24| You should have received a copy of the GNU General Public License |
25| along with VM2D. If not, see <http://www.gnu.org/licenses/>. |
26\*---------------------------------------------------------------------------*/
27
39#ifndef CPURADIXSORT_H
40#define CPURADIXSORT_H
41
42#include <iostream>
43#include <vector>
44#include <algorithm>
45#include <type_traits>
46#include <omp.h>
47#include <string>
48
49namespace VM2D
50{
51
52 // SFINAE для проверки тривиальной копируемости (оптимизация)
53 template<typename T>
54 using enable_if_trivially_copyable = typename std::enable_if<std::is_trivially_copyable<T>::value>::type;
55
56 template<typename T>
57 using enable_if_not_trivially_copyable = typename std::enable_if<!std::is_trivially_copyable<T>::value>::type;
58
59 // Основной шаблонный класс
60 template<typename ValueType>
62 private:
63 // Переиспользуемые буферы
64 std::vector<int> keyBuffer;
65 std::vector<ValueType> valueBuffer;
66
67 std::vector<int> globalHist;
68 std::vector<std::vector<int>> histThreads;
69 std::vector<std::vector<int>> localStarts;
70 std::vector<std::vector<int>> localCounters;
71
75
76 constexpr static int RADIX = 256;
77 constexpr static int MASK = 0xFF;
78 constexpr static int PASSES = 4; // для 32-bit int
79
80 void initializeThreadData(int threads) {
81 numThreads = threads;
82
83 // Выделяем память для гистограмм потоков
84 histThreads.resize(numThreads);
85 for (int t = 0; t < numThreads; ++t) {
86 histThreads[t].assign(RADIX, 0);
87 }
88
89 // Выделяем память для локальных стартовых позиций
90 localStarts.resize(numThreads);
92 for (int t = 0; t < numThreads; ++t) {
93 localStarts[t].assign(RADIX, 0);
94 localCounters[t].assign(RADIX, 0);
95 }
96
97 // Глобальная гистограмма
98 globalHist.assign(RADIX, 0);
99
100 initialized = true;
101 }
102
103 void ensureCapacity(size_t n) {
104 if (n > currentCapacity) {
105 // Увеличиваем буферы с запасом 10% для будущих сортировок
106 keyBuffer.resize(n + n / 10);
107 valueBuffer.resize(n + n / 10);
108 currentCapacity = keyBuffer.size();
109 }
110 }
111
113 // Быстрый сброс гистограмм (переиспользуем память)
114 for (int t = 0; t < numThreads; ++t) {
115 std::fill(histThreads[t].begin(), histThreads[t].end(), 0);
116 }
117 std::fill(globalHist.begin(), globalHist.end(), 0);
118 }
119
120 // Оптимизированное копирование для тривиально копируемых типов
121 template<typename T = ValueType>
122 void copyValues(T* dest, const T* src, size_t n, enable_if_trivially_copyable<T>* = nullptr) {
123 std::memcpy(dest, src, n * sizeof(T));
124 }
125
126 // Копирование для нетривиально копируемых типов
127 template<typename T = ValueType>
128 void copyValues(T* dest, const T* src, size_t n, enable_if_not_trivially_copyable<T>* = nullptr) {
129 for (size_t i = 0; i < n; ++i) {
130 dest[i] = src[i];
131 }
132 }
133
134 public:
136#pragma omp parallel
137 {
138#pragma omp single
139 numThreads = omp_get_num_threads();
140 }
141 if (numThreads == 0) numThreads = 1;
142
144 }
145
146 // Конструктор с предварительным выделением памяти
147 explicit OptimizedRadixSorter(size_t maxSize) : numThreads(0), currentCapacity(0), initialized(false) {
148#pragma omp parallel
149 {
150#pragma omp single
151 numThreads = omp_get_num_threads();
152 }
153 if (numThreads == 0) numThreads = 1;
154
156 ensureCapacity(maxSize);
157 }
158
159 // Основной метод сортировки
160 void sort(int* keys, ValueType* values, size_t n) {
161 if (n < 2) return;
162
165
166 for (int pass = 0; pass < PASSES; ++pass) {
167 int shift = pass * 8;
168
169 // 1. Параллельный подсчёт гистограмм
170#pragma omp parallel for schedule(static)
171 for (int i = 0; i < n; ++i) {
172 int tid = omp_get_thread_num();
173 int bucket = (keys[i] >> shift) & MASK;
174 histThreads[tid][bucket]++;
175 }
176
177 // 2. Объединение гистограмм
178 for (int t = 0; t < numThreads; ++t) {
179 const auto& hist = histThreads[t];
180 auto& global = globalHist;
181 for (int b = 0; b < RADIX; ++b) {
182 global[b] += hist[b];
183 }
184 }
185
186 // 3. Вычисление префиксных сумм
187 std::vector<int> prefix(RADIX);
188 int sum = 0;
189 for (int b = 0; b < RADIX; ++b) {
190 prefix[b] = sum;
191 sum += globalHist[b];
192 }
193
194 // 4. Расчёт стартовых позиций для каждого потока
195 std::copy(prefix.begin(), prefix.end(), localStarts[0].begin());
196
197 for (int t = 1; t < numThreads; ++t) {
198 auto& start = localStarts[t];
199 const auto& prevStart = localStarts[t - 1];
200 const auto& prevHist = histThreads[t - 1];
201
202 for (int b = 0; b < RADIX; ++b) {
203 start[b] = prevStart[b] + prevHist[b];
204 }
205 }
206
207 // 5. Копируем стартовые позиции в счётчики
208 for (int t = 0; t < numThreads; ++t) {
209 std::copy(localStarts[t].begin(), localStarts[t].end(),
210 localCounters[t].begin());
211 }
212
213 // 6. Параллельная разноска элементов
214#pragma omp parallel for schedule(static)
215 for (int i = 0; i < n; ++i) {
216 int tid = omp_get_thread_num();
217 int bucket = (keys[i] >> shift) & MASK;
218 int pos = localCounters[tid][bucket]++;
219
220 keyBuffer[pos] = keys[i];
221 valueBuffer[pos] = values[i];
222 }
223
224 // 7. Копирование обратно в исходные массивы
225#pragma omp parallel for schedule(static)
226 for (int i = 0; i < n; ++i) {
227 keys[i] = keyBuffer[i];
228 values[i] = valueBuffer[i];
229 }
230
231 // 8. Сброс гистограмм
232 for (int t = 0; t < numThreads; ++t) {
233 std::fill(histThreads[t].begin(), histThreads[t].end(), 0);
234 }
235 std::fill(globalHist.begin(), globalHist.end(), 0);
236 }
237 }
238
239 // Перегруженный метод для работы с std::vector
240 void sort(std::vector<int>& keys, std::vector<ValueType>& values) {
241 if (keys.size() != values.size()) {
242 throw std::runtime_error("Key and value arrays must have same size");
243 }
244 sort(keys.data(), values.data(), keys.size());
245 }
246
247 // Метод для сортировки с указанием диапазона
248 void sort(int* keys, ValueType* values, size_t start, size_t end) {
249 if (start >= end) return;
250 sort(keys + start, values + start, end - start);
251 }
252
254 std::vector<int>().swap(keyBuffer);
255 std::vector<ValueType>().swap(valueBuffer);
256 std::vector<int>().swap(globalHist);
257 std::vector<std::vector<int>>().swap(histThreads);
258 std::vector<std::vector<int>>().swap(localStarts);
259 std::vector<std::vector<int>>().swap(localCounters);
260 currentCapacity = 0;
261 initialized = false;
262 }
263
264 size_t getMemoryUsage() const {
265 size_t total = keyBuffer.capacity() * sizeof(int);
266 total += valueBuffer.capacity() * sizeof(ValueType);
267 total += globalHist.capacity() * sizeof(int);
268 for (const auto& hist : histThreads) {
269 total += hist.capacity() * sizeof(int);
270 }
271 for (const auto& start : localStarts) {
272 total += start.capacity() * sizeof(int);
273 }
274 for (const auto& counter : localCounters) {
275 total += counter.capacity() * sizeof(int);
276 }
277 return total;
278 }
279
280 // Получить число потоков
281 int getNumThreads() const { return numThreads; }
282
283 // Установить число потоков (должно быть вызвано ДО первой сортировки)
284 void setNumThreads(int threads) {
285 if (!initialized) {
286 numThreads = threads;
288 }
289 }
290 };
291
292 // Частичная специализация для указателей (если нужно хранить указатели)
293 template<typename ValueType>
294 class OptimizedRadixSorter<ValueType*> {
295 private:
296 std::vector<int> keyBuffer;
297 std::vector<ValueType*> valueBuffer;
298 // ... остальная реализация аналогична
299 };
300
301 // Примеры использования
302 struct ComplexData {
303 double x;
304 double y;
305 int id;
306
307 ComplexData() : x(0), y(0), id(0) {}
308 ComplexData(double _x, double _y, int _id) : x(_x), y(_y), id(_id) {}
309
310 bool operator==(const ComplexData& other) const {
311 return x == other.x && y == other.y && id == other.id;
312 }
313 };
314
315}//VM2D
316
317#endif
void sort(int *keys, ValueType *values, size_t start, size_t end)
void setNumThreads(int threads)
static constexpr int MASK
void copyValues(T *dest, const T *src, size_t n, enable_if_not_trivially_copyable< T > *=nullptr)
OptimizedRadixSorter(size_t maxSize)
static constexpr int PASSES
void sort(int *keys, ValueType *values, size_t n)
std::vector< int > globalHist
std::vector< ValueType > valueBuffer
std::vector< std::vector< int > > localStarts
std::vector< int > keyBuffer
void initializeThreadData(int threads)
std::vector< std::vector< int > > localCounters
void sort(std::vector< int > &keys, std::vector< ValueType > &values)
static constexpr int RADIX
void copyValues(T *dest, const T *src, size_t n, enable_if_trivially_copyable< T > *=nullptr)
std::vector< std::vector< int > > histThreads
typename std::enable_if< std::is_trivially_copyable< T >::value >::type enable_if_trivially_copyable
typename std::enable_if<!std::is_trivially_copyable< T >::value >::type enable_if_not_trivially_copyable
ComplexData(double _x, double _y, int _id)
bool operator==(const ComplexData &other) const