File indexing completed on 2026-07-26 08:22:10
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "traccc/definitions/math.hpp"
0010 #include "traccc/utils/pair.hpp"
0011
0012
0013 #include "../../utils/barrier.hpp"
0014 #include "../../utils/global_index.hpp"
0015 #include "remove_tracks.cuh"
0016
0017
0018 #include <vecmem/containers/device_vector.hpp>
0019 #include <vecmem/containers/jagged_device_vector.hpp>
0020
0021
0022 #include <thrust/binary_search.h>
0023 #include <thrust/count.h>
0024 #include <thrust/execution_policy.h>
0025 #include <thrust/find.h>
0026
0027 namespace traccc::cuda::kernels {
0028
0029 __device__ __forceinline__ uint64_t pack_key(uint32_t meas, uint32_t thr) {
0030 return (uint64_t(meas) << 32) | uint64_t(thr);
0031 }
0032 __device__ __forceinline__ void unpack_key(uint64_t k, uint32_t& meas,
0033 uint32_t& thr) {
0034 meas = uint32_t(k >> 32);
0035 thr = uint32_t(k & 0xFFFFFFFFu);
0036 }
0037
0038 __device__ void count_tracks(int tid, int* sh_n_meas, int n_tracks,
0039 unsigned int& bound, unsigned int& count) {
0040 unsigned int add = 0;
0041
0042
0043
0044 const int lane = threadIdx.x & 31;
0045 const unsigned int full_mask = 0xFFFFFFFFu;
0046
0047
0048 int v = (tid < n_tracks) ? sh_n_meas[tid] : 0;
0049
0050
0051 const unsigned int mask = __ballot_sync(full_mask, tid < n_tracks);
0052
0053 const int max_stride = min(n_tracks, 32);
0054
0055 for (int stride = 1; stride < max_stride; stride <<= 1) {
0056
0057 unsigned int other = __shfl_down_sync(mask, v, stride);
0058 if (lane + stride < 32 && (tid + stride) < n_tracks) {
0059 v += other;
0060 }
0061
0062
0063 if (tid == 0) {
0064 if (v < bound) {
0065 add = stride << 1;
0066 }
0067 }
0068 }
0069
0070
0071 if (tid < n_tracks) {
0072 sh_n_meas[tid] = static_cast<int>(v);
0073 }
0074 __syncthreads();
0075
0076
0077
0078 for (int stride = 32; stride < n_tracks; stride <<= 1) {
0079 if ((tid + stride) < n_tracks) {
0080 sh_n_meas[tid] += sh_n_meas[tid + stride];
0081 }
0082 __syncthreads();
0083
0084 if (tid == 0 && sh_n_meas[0] < bound) {
0085 add = stride << 1;
0086 }
0087 __syncthreads();
0088 }
0089
0090
0091 if (tid == 0) {
0092 count += add;
0093 }
0094 __syncthreads();
0095 }
0096
0097 __launch_bounds__(512) __global__
0098 void remove_tracks(device::remove_tracks_payload payload) {
0099 if (threadIdx.x == 0) {
0100 if (*(payload.max_shared) == 0) {
0101 *(payload.terminate) = 1;
0102 }
0103
0104 *(payload.max_shared) = 0;
0105 *(payload.n_updated_tracks) = 0;
0106 }
0107
0108 __syncthreads();
0109
0110 if (*(payload.terminate) == 1) {
0111 return;
0112 }
0113
0114 __shared__ int sh_buffer[512];
0115 __shared__ measurement_id_type sh_meas_ids[512];
0116 __shared__ unsigned int sh_threads[512];
0117 __shared__ uint64_t sh_keys[512];
0118 __shared__ unsigned int n_meas_total;
0119 __shared__ unsigned int bound;
0120 __shared__ unsigned int n_tracks_to_iterate;
0121 __shared__ unsigned int min_thread;
0122 __shared__ unsigned int N;
0123 __shared__ unsigned int n_updating_threads;
0124
0125 auto threadIndex = threadIdx.x;
0126
0127 int gid = static_cast<int>(*payload.n_accepted) - 1 - threadIndex;
0128 sh_buffer[threadIndex] = 0;
0129 sh_meas_ids[threadIndex] = std::numeric_limits<measurement_id_type>::max();
0130 sh_threads[threadIndex] = std::numeric_limits<unsigned int>::max();
0131
0132 vecmem::device_vector<const unsigned int> sorted_ids(payload.sorted_ids_view);
0133 vecmem::jagged_device_vector<const measurement_id_type> meas_ids(
0134 payload.meas_ids_view);
0135 vecmem::device_vector<const unsigned int> n_meas(payload.n_meas_view);
0136 vecmem::device_vector<const unsigned int> meas_id_to_unique_id(
0137 payload.meas_id_to_unique_id_view);
0138 vecmem::jagged_device_vector<const unsigned int> tracks_per_measurement(
0139 payload.tracks_per_measurement_view);
0140 vecmem::jagged_device_vector<int> track_status_per_measurement(
0141 payload.track_status_per_measurement_view);
0142 vecmem::device_vector<unsigned int> n_accepted_tracks_per_measurement(
0143 payload.n_accepted_tracks_per_measurement_view);
0144 vecmem::device_vector<unsigned int> n_shared(payload.n_shared_view);
0145 vecmem::device_vector<traccc::scalar> rel_shared(payload.rel_shared_view);
0146 vecmem::device_vector<unsigned int> updated_tracks(
0147 payload.updated_tracks_view);
0148 vecmem::device_vector<int> is_updated(payload.is_updated_view);
0149 vecmem::device_vector<int> track_count(payload.track_count_view);
0150
0151 if (threadIndex == 0) {
0152 *(payload.n_removable_tracks) = 0;
0153 *(payload.n_meas_to_remove) = 0;
0154 *(payload.n_valid_threads) = 0;
0155 n_meas_total = 0;
0156 bound = 512;
0157 N = 1;
0158 n_tracks_to_iterate = 0;
0159 min_thread = std::numeric_limits<unsigned int>::max();
0160 }
0161
0162 __syncthreads();
0163
0164 unsigned int trk_id = 0;
0165 unsigned int n_m = 0;
0166 if (gid >= 0) {
0167 trk_id = sorted_ids[gid];
0168 n_m = n_meas[trk_id];
0169
0170
0171 sh_buffer[threadIndex] = n_m;
0172 }
0173
0174 __syncthreads();
0175
0176 auto n_tracks_total = min(bound, *payload.n_accepted);
0177
0178
0179
0180
0181
0182 count_tracks(threadIdx.x, sh_buffer, n_tracks_total, bound,
0183 n_tracks_to_iterate);
0184
0185 if (threadIndex == 0 && n_tracks_to_iterate == 0) {
0186 n_tracks_to_iterate = 1;
0187 }
0188
0189
0190 if (threadIndex < n_tracks_to_iterate && gid >= 0) {
0191 const unsigned int pos = atomicAdd(&n_meas_total, n_m);
0192
0193 const auto& mids = meas_ids[trk_id];
0194 for (int i = 0; i < n_m; i++) {
0195 sh_meas_ids[pos + i] = mids[i];
0196 sh_threads[pos + i] = threadIndex;
0197 }
0198 }
0199
0200 __syncthreads();
0201
0202
0203 if (threadIndex == 0) {
0204 N = (n_meas_total == 0) ? 1 : 1 << (32 - __clz(n_meas_total - 1));
0205 }
0206 __syncthreads();
0207
0208 const auto tid = threadIndex;
0209
0210
0211 uint64_t key = (tid < N) ? pack_key(sh_meas_ids[tid], sh_threads[tid])
0212 : 0xFFFFFFFFFFFFFFFFull;
0213
0214
0215 for (int k = 2; k <= N; k <<= 1) {
0216
0217 for (int j = (k >> 1); j >= warpSize; j >>= 1) {
0218 sh_keys[tid] = key;
0219 __syncthreads();
0220
0221 const int ixj = tid ^ j;
0222
0223 uint64_t other = (ixj < N) ? sh_keys[ixj] : key;
0224
0225 const bool dir = ((tid & k) == 0);
0226 const bool lower = ((tid & j) == 0);
0227
0228 const uint64_t mn = (key < other) ? key : other;
0229 const uint64_t mx = (key < other) ? other : key;
0230
0231 key = dir ? (lower ? mn : mx) : (lower ? mx : mn);
0232
0233 __syncthreads();
0234 }
0235
0236
0237 for (int j = min(k >> 1, warpSize >> 1); j > 0; j >>= 1) {
0238 const unsigned mask = 0xFFFFFFFFu;
0239 uint64_t other = __shfl_xor_sync(mask, key, j);
0240
0241 const bool dir = ((tid & k) == 0);
0242 const bool lower = ((tid & j) == 0);
0243
0244 const uint64_t mn = (key < other) ? key : other;
0245 const uint64_t mx = (key < other) ? other : key;
0246
0247 key = dir ? (lower ? mn : mx) : (lower ? mx : mn);
0248 }
0249
0250
0251 sh_keys[tid] = key;
0252 __syncthreads();
0253 }
0254
0255
0256 if (tid < N) {
0257 uint32_t meas, thr;
0258 unpack_key(key, meas, thr);
0259 sh_meas_ids[tid] = meas;
0260 sh_threads[tid] = thr;
0261 }
0262
0263
0264 if (threadIndex < n_meas_total) {
0265 auto mid = sh_meas_ids[threadIndex];
0266 bool is_start = (threadIndex == 0) || (sh_meas_ids[threadIndex - 1] != mid);
0267 const auto unique_meas_idx = meas_id_to_unique_id.at(mid);
0268 const auto its_accepted_tracks =
0269 n_accepted_tracks_per_measurement.at(unique_meas_idx);
0270
0271 if (is_start) {
0272 int i = threadIndex + 1;
0273 int n_sharing_tracks = 1;
0274
0275 while (i < n_meas_total && sh_meas_ids[i] == mid) {
0276 if (sh_threads[i] != sh_threads[i - 1]) {
0277 n_sharing_tracks++;
0278
0279 if (n_sharing_tracks == its_accepted_tracks) {
0280 atomicMin(&min_thread, sh_threads[i - 1]);
0281 break;
0282 }
0283 }
0284 i++;
0285 }
0286 }
0287 }
0288
0289 __syncthreads();
0290
0291 if (threadIndex == 0) {
0292 if (min_thread == 0) {
0293 *(payload.n_removable_tracks) = 1;
0294 } else if (min_thread == std::numeric_limits<unsigned int>::max()) {
0295 *(payload.n_removable_tracks) = n_tracks_to_iterate;
0296 } else {
0297 *(payload.n_removable_tracks) = min_thread;
0298 }
0299 }
0300
0301 __syncthreads();
0302
0303 auto meas_to_remove_temp = sh_meas_ids[threadIndex];
0304 auto threads_temp = sh_threads[threadIndex];
0305
0306 __syncthreads();
0307
0308 if (threadIndex == 0) {
0309 *(payload.n_meas_to_remove) = n_meas_total;
0310 }
0311
0312 __syncthreads();
0313
0314 int is_valid = (threads_temp < *(payload.n_removable_tracks)) ? 1 : 0;
0315
0316
0317 if (is_valid) {
0318 atomicAdd(payload.n_valid_threads, 1);
0319 }
0320
0321 __syncthreads();
0322
0323
0324
0325
0326 sh_buffer[threadIndex] = is_valid;
0327 __syncthreads();
0328
0329 for (int offset = 1; offset < *(payload.n_meas_to_remove); offset <<= 1) {
0330 int val = 0;
0331 if (threadIndex >= offset) {
0332 val = sh_buffer[threadIndex - offset];
0333 }
0334 __syncthreads();
0335 sh_buffer[threadIndex] += val;
0336 __syncthreads();
0337 }
0338
0339 if (is_valid) {
0340 sh_buffer[threadIndex] -= 1;
0341 sh_meas_ids[sh_buffer[threadIndex]] = meas_to_remove_temp;
0342 sh_threads[sh_buffer[threadIndex]] = threads_temp;
0343 }
0344
0345 __syncthreads();
0346
0347 meas_to_remove_temp = sh_meas_ids[threadIndex];
0348 threads_temp = sh_threads[threadIndex];
0349
0350
0351
0352
0353
0354 __syncthreads();
0355
0356 bool is_valid_thread = false;
0357 bool is_duplicate = true;
0358
0359 const unsigned n_accepted_prev = *(payload.n_accepted);
0360
0361 __syncthreads();
0362
0363 if (threadIndex == 0) {
0364 (*payload.n_accepted) -= *(payload.n_removable_tracks);
0365 n_updating_threads = 0;
0366 }
0367
0368 if (threadIndex < *(payload.n_valid_threads)) {
0369 sh_meas_ids[threadIndex] = meas_to_remove_temp;
0370 sh_threads[threadIndex] = threads_temp;
0371 is_valid_thread = true;
0372 }
0373
0374 __syncthreads();
0375
0376 if (is_valid_thread) {
0377 const auto id = sh_meas_ids[threadIndex];
0378 is_duplicate = (threadIndex > 0 && sh_meas_ids[threadIndex - 1] == id);
0379 }
0380
0381
0382 sh_buffer[threadIndex] = std::numeric_limits<int>::max();
0383
0384 bool active = false;
0385 unsigned int pos1;
0386 int alive_trk_id = 0;
0387
0388 if (!is_duplicate && is_valid_thread) {
0389 const auto id = sh_meas_ids[threadIndex];
0390 const auto unique_meas_idx = meas_id_to_unique_id.at(id);
0391
0392
0393
0394 const auto& tracks = tracks_per_measurement[unique_meas_idx];
0395 auto track_status = track_status_per_measurement[unique_meas_idx];
0396
0397 auto trk_id = sorted_ids.at(n_accepted_prev - 1 - sh_threads[threadIndex]);
0398
0399 unsigned int worst_idx =
0400 thrust::lower_bound(thrust::seq, tracks.begin(), tracks.end(), trk_id) -
0401 tracks.begin();
0402
0403 track_status[worst_idx] = 0;
0404
0405 int n_sharing_tracks = 1;
0406 for (unsigned int i = threadIndex + 1; i < *(payload.n_valid_threads);
0407 ++i) {
0408 if (sh_meas_ids[i] == id && sh_threads[i] != sh_threads[i - 1]) {
0409 n_sharing_tracks++;
0410
0411 trk_id = sorted_ids[n_accepted_prev - 1 - sh_threads[i]];
0412
0413 worst_idx = thrust::lower_bound(thrust::seq, tracks.begin(),
0414 tracks.end(), trk_id) -
0415 tracks.begin();
0416
0417 track_status[worst_idx] = 0;
0418
0419 } else if (sh_meas_ids[i] != id) {
0420 break;
0421 }
0422 }
0423
0424 vecmem::device_atomic_ref<unsigned int> n_accepted_per_meas(
0425 n_accepted_tracks_per_measurement.at(
0426 static_cast<unsigned int>(unique_meas_idx)));
0427 const unsigned int N_A = n_accepted_per_meas.fetch_sub(n_sharing_tracks);
0428
0429 if (N_A == 1 + n_sharing_tracks) {
0430 active = true;
0431 const unsigned int alive_idx =
0432 thrust::find(thrust::seq, track_status.begin(), track_status.end(),
0433 1) -
0434 track_status.begin();
0435
0436 pos1 = atomicAdd(&n_updating_threads, 1);
0437 alive_trk_id = static_cast<int>(tracks[alive_idx]);
0438
0439 sh_buffer[pos1] = alive_trk_id;
0440 atomicAdd(&track_count[alive_trk_id], 1);
0441
0442 const auto m_count = static_cast<unsigned int>(
0443 thrust::count(thrust::seq, meas_ids[alive_trk_id].begin(),
0444 meas_ids[alive_trk_id].end(), id));
0445
0446 const unsigned int N_S =
0447 vecmem::device_atomic_ref<unsigned int>(n_shared.at(alive_trk_id))
0448 .fetch_sub(m_count);
0449 }
0450 }
0451
0452 __syncthreads();
0453
0454 if (active) {
0455 auto count = atomicAdd(&track_count[alive_trk_id], -1);
0456 if (count == 1) {
0457
0458 vecmem::device_atomic_ref<unsigned int> num_updated_tracks(
0459 *(payload.n_updated_tracks));
0460
0461 const unsigned int pos2 = num_updated_tracks.fetch_add(1);
0462
0463 updated_tracks[pos2] = alive_trk_id;
0464 is_updated[alive_trk_id] = 1;
0465
0466 rel_shared.at(alive_trk_id) = math::div_ieee754(
0467 static_cast<traccc::scalar>(n_shared.at(alive_trk_id)),
0468 static_cast<traccc::scalar>(n_meas.at(alive_trk_id)));
0469 }
0470 }
0471 }
0472
0473 }