Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
batcherOddEvenSort.cpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
16
20#include <algorithm>
21#include <stdexcept>
22#include <utility>
23#include <vector>
24
26
32 template<class Tkey, class Tval>
34
36 inline static void compare_exchange(
37 Tkey *__restrict__ keys, Tval *__restrict__ vals, u32 a, u32 b) {
38
39 Tkey key_a = keys[a];
40 Tkey key_b = keys[b];
41 Tval val_a = vals[a];
42 Tval val_b = vals[b];
43
44 bool swap = key_b < key_a;
45
46 keys[a] = (swap) ? key_b : key_a;
47 keys[b] = (swap) ? key_a : key_b;
48 vals[a] = (swap) ? val_b : val_a;
49 vals[b] = (swap) ? val_a : val_b;
50 }
51
75 inline static void merge_step(
76 Tkey *__restrict__ keys,
77 Tval *__restrict__ vals,
78 u64 len,
79 u64 k,
80 u64 j0,
81 u32 log_k,
82 u32 log_2p,
83 u64 t) {
84
85 u64 x = j0 + ((t >> log_k) << (log_k + 1)) + (t & (k - 1));
86
87 if (x + k >= len) {
88 return; // comparator truncated away by the end of the array
89 }
90 if ((x >> log_2p) != ((x + k) >> log_2p)) {
91 return; // not part of this odd-even merge
92 }
93
94 compare_exchange(keys, vals, u32(x), u32(x + k));
95 }
96 };
97
98 template<class Tkey, class Tval>
100 const sham::DeviceScheduler_ptr &sched,
102 sham::DeviceBuffer<Tval> &buf_values,
103 u32 len) {
104
105 if (len < 2) {
106 return; // nothing to do, and the network below is empty anyway
107 }
108
110
111 u64 n = len;
112
113 // each thread carries at most one comparator, and the low indices of the comparators
114 // of a stage are spread over the array with a period of two
115 u64 n_threads = (n + 1) / 2;
116
117 // the loop counters are carried as exponents so that no shift can overflow when len
118 // is close to the largest u32
119 for (u32 log_p = 0; (u64(1) << log_p) < n; log_p++) {
120 for (i32 log_k = i32(log_p); log_k >= 0; log_k--) {
121
122 u64 k = u64(1) << log_k;
123 u64 j0 = (log_k == i32(log_p)) ? 0 : k; // k mod p
124 u32 log_2p = log_p + 1;
125 u32 lk = u32(log_k);
126
128 sched->get_queue(),
130 sham::MultiRef{buf_key, buf_values},
131 n_threads,
132 [=](u64 gid, Tkey *keys, Tval *vals) {
133 B::merge_step(keys, vals, n, k, j0, lk, log_2p, gid);
134 });
135 }
136 }
137 }
138
139 template<class Tkey, class Tval>
141 std::vector<Tkey> &keys, std::vector<Tval> &values) {
142
143 if (keys.size() != values.size()) {
145 "the keys and the values must have the same length");
146 }
147
148 // Batcher's odd-even merge network, kept as the plain four loops on purpose, this is
149 // the readable statement of what the device kernel computes.
150 //
151 // for p = 1,2,4,... while p<n
152 // for k = p,p/2,...,1
153 // for j = k mod p to n-1-k step 2k
154 // for i = 0 to min(k-1, n-j-k-1)
155 // if floor((i+j)/2p) == floor((i+j+k)/2p):
156 // compare_exchange(a[i+j], a[i+j+k])
157
158 i32 n = static_cast<i32>(keys.size());
159 for (i32 p = 1; p < n; p <<= 1) {
160 for (i32 k = p; k >= 1; k >>= 1) {
161 for (i32 j = k % p; j <= n - 1 - k; j += 2 * k) {
162 i32 imax = std::min(k - 1, n - j - k - 1);
163 for (i32 i = 0; i <= imax; ++i) {
164 i32 idx1 = i + j;
165 i32 idx2 = i + j + k;
166 if ((idx1 / (2 * p)) == (idx2 / (2 * p))) {
167 if (keys[idx2] < keys[idx1]) {
168 std::swap(keys[idx1], keys[idx2]);
169 std::swap(values[idx1], values[idx2]);
170 }
171 }
172 }
173 }
174 }
175 }
176 }
177
178 template void sort_by_key_batcher_odd_even<u32, u32>(
179 const sham::DeviceScheduler_ptr &sched,
181 sham::DeviceBuffer<u32> &buf_values,
182 u32 len);
183
184 template void sort_by_key_batcher_odd_even<u64, u32>(
185 const sham::DeviceScheduler_ptr &sched,
187 sham::DeviceBuffer<u32> &buf_values,
188 u32 len);
189
190 template void sort_by_key_batcher_odd_even<f32, f32>(
191 const sham::DeviceScheduler_ptr &sched,
193 sham::DeviceBuffer<f32> &buf_values,
194 u32 len);
195
196 template void sort_by_key_batcher_odd_even<f64, f64>(
197 const sham::DeviceScheduler_ptr &sched,
199 sham::DeviceBuffer<f64> &buf_values,
200 u32 len);
201
202 template void sort_by_key_batcher_odd_even<f32, u32>(
203 const sham::DeviceScheduler_ptr &sched,
205 sham::DeviceBuffer<u32> &buf_values,
206 u32 len);
207
208 template void sort_by_key_batcher_odd_even<f64, u32>(
209 const sham::DeviceScheduler_ptr &sched,
211 sham::DeviceBuffer<u32> &buf_values,
212 u32 len);
213
214 template void sort_by_key_batcher_odd_even_host_reference<u32, u32>(
215 std::vector<u32> &keys, std::vector<u32> &values);
216
217 template void sort_by_key_batcher_odd_even_host_reference<u64, u32>(
218 std::vector<u64> &keys, std::vector<u32> &values);
219
220 template void sort_by_key_batcher_odd_even_host_reference<f32, f32>(
221 std::vector<f32> &keys, std::vector<f32> &values);
222
223 template void sort_by_key_batcher_odd_even_host_reference<f32, u32>(
224 std::vector<f32> &keys, std::vector<u32> &values);
225
226 template void sort_by_key_batcher_odd_even_host_reference<f64, u32>(
227 std::vector<f64> &keys, std::vector<u32> &values);
228
229 template void sort_by_key_batcher_odd_even_host_reference<f64, f64>(
230 std::vector<f64> &keys, std::vector<f64> &values);
231
232} // namespace shamalgs::algorithm::details
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
std::int32_t i32
32 bit integer
Batcher odd-even mergesort, native for any length.
A buffer allocated in USM (Unified Shared Memory).
This header file contains utility functions related to exception handling in the code.
void kernel_call_u64(sham::DeviceQueue &q, RefIn in, RefOut in_out, u64 n, Functor &&func, SourceLocation &&callsite=SourceLocation{})
u64 indexed variant of kernel_call
namespace to store algorithms implemented by shamalgs
void sort_by_key_batcher_odd_even_host_reference(std::vector< Tkey > &keys, std::vector< Tval > &values)
Host reference of sort_by_key_batcher_odd_even.
void sort_by_key_batcher_odd_even(const sham::DeviceScheduler_ptr &sched, sham::DeviceBuffer< Tkey > &buf_key, sham::DeviceBuffer< Tval > &buf_values, u32 len)
Sort key-value pairs of any length using a Batcher odd-even merge network.
void throw_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Throw an exception and append the source location to it.
A class that references multiple buffers or similar objects.
Definition MultiRef.hpp:33
Device side primitives of the odd-even merge network.
static void compare_exchange(Tkey *__restrict__ keys, Tval *__restrict__ vals, u32 a, u32 b)
Ascending branchless compare-exchange of the pair (a, b), with a < b.
static void merge_step(Tkey *__restrict__ keys, Tval *__restrict__ vals, u64 len, u64 k, u64 j0, u32 log_k, u32 log_2p, u64 t)
Work of a single thread within the (p, k) stage of the network.