Warp-level primitives#
Lanes 0..31 of a warp are consecutive linear thread ids (tid % 32 == laneId). Shuffle/vote/reduce are collective: every participating lane deposits a value, rendezvous at a warp-wide barrier (the scheduler releases the warp only once all its live lanes have arrived), then reads peers from the snapshot. Values travel as an 8-byte blob so any <=8-byte type (int, unsigned, float, long long, double) shuffles correctly.
Defined in code/simulator.h.
This page documents 15 symbols: 1 constants, 14 functions.
Constants#
Signature |
Description |
Availability |
Location |
Example |
|---|---|---|---|---|
|
Lanes 0..31 of a warp are consecutive linear thread ids (tid % 32 == laneId). |
sim only |
simulator.h:1007 |
Functions#
Signature |
Description |
Availability |
Location |
Example |
|---|---|---|---|---|
|
__activemask does NOT synchronise; approximate the converged set as every real lane of the warp (good enough for the common ballot/shuffle feeding uses). |
sim only |
simulator.h:1103 |
|
|
Non-zero iff the predicate holds for EVERY participating lane of the warp. |
sim only |
simulator.h:1093 |
|
|
Non-zero iff the predicate holds for ANY participating lane of the warp. |
sim only |
simulator.h:1084 |
|
|
Vote: ballot returns the bitmask of participating lanes whose predicate holds. |
sim only |
simulator.h:1074 |
|
|
Warp reductions. NOTE: the real intrinsics need sm_80 (Ampere) — they do NOT exist on this repo’s sm_75 target, so a portable kernel can’t use them on a T4. Provided here for sim completeness / … |
sim only |
simulator.h:1114 |
|
|
sim only |
simulator.h:1134 |
||
|
sim only |
simulator.h:1123 |
||
|
Shift down: read lane (self+delta); lanes past the subsection end keep their own. |
sim only |
simulator.h:1039 |
|
|
Indexed broadcast: read lane |
sim only |
simulator.h:1018 |
|
|
Shift up: read lane (self-delta); lanes that would read before the subsection base keep their own value. |
sim only |
simulator.h:1029 |
|
|
Butterfly: read lane (self ^ laneMask) within the subsection. |
sim only |
simulator.h:1050 |
|
|
__syncwarp: warp-wide barrier with no data exchange. |
sim only |
simulator.h:1062 |
|
|
sim only |
simulator.h:1013 |
||
|
Pack/unpack a shuffle payload into the 8-byte exchange slot (any <=8-byte type). |
sim only |
simulator.h:1010 |
Examples#
Code from the repository’s example corpus (code\*.cu); the highlighted line is the call site. These blocks are what the View links above open.
__all_sync (test_warp.cu:74)
64 for (int off = 16; off > 0; off >>= 1) v += __shfl_xor_sync(FULL, v, off);
65 out[gid] = v; // every lane ends with the full warp sum
66}
67
68__global__ void kBallot(const float* in, unsigned* outWarp, int* outAny, int* outAll) {
69 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
70 const unsigned lane = threadIdx.x % 32u;
71 const int pred = in[gid] > 6.0f;
72 const unsigned b = __ballot_sync(FULL, pred);
73 const int any = __any_sync(FULL, pred);
74 const int all = __all_sync(FULL, pred);
75 if (lane == 0) { outWarp[gid / 32u] = b; outAny[gid / 32u] = any; outAll[gid / 32u] = all; }
76}
77
78// Partial-warp ballot: a single block of `n` threads (n not a multiple of 32).
79__global__ void kBallotPartial(unsigned* outWarp) {
80 const unsigned tid = threadIdx.x;
81 const unsigned lane = tid % 32u;
82 const unsigned b = __ballot_sync(FULL, 1); // predicate always true
83 if (lane == 0) outWarp[tid / 32u] = b; // -> mask of ACTIVE lanes only
84}
__any_sync (test_warp.cu:73)
63 float v = in[gid];
64 for (int off = 16; off > 0; off >>= 1) v += __shfl_xor_sync(FULL, v, off);
65 out[gid] = v; // every lane ends with the full warp sum
66}
67
68__global__ void kBallot(const float* in, unsigned* outWarp, int* outAny, int* outAll) {
69 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
70 const unsigned lane = threadIdx.x % 32u;
71 const int pred = in[gid] > 6.0f;
72 const unsigned b = __ballot_sync(FULL, pred);
73 const int any = __any_sync(FULL, pred);
74 const int all = __all_sync(FULL, pred);
75 if (lane == 0) { outWarp[gid / 32u] = b; outAny[gid / 32u] = any; outAll[gid / 32u] = all; }
76}
77
78// Partial-warp ballot: a single block of `n` threads (n not a multiple of 32).
79__global__ void kBallotPartial(unsigned* outWarp) {
80 const unsigned tid = threadIdx.x;
81 const unsigned lane = tid % 32u;
82 const unsigned b = __ballot_sync(FULL, 1); // predicate always true
83 if (lane == 0) outWarp[tid / 32u] = b; // -> mask of ACTIVE lanes only
84}
__ballot_sync (test_warp.cu:72)
62 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
63 float v = in[gid];
64 for (int off = 16; off > 0; off >>= 1) v += __shfl_xor_sync(FULL, v, off);
65 out[gid] = v; // every lane ends with the full warp sum
66}
67
68__global__ void kBallot(const float* in, unsigned* outWarp, int* outAny, int* outAll) {
69 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
70 const unsigned lane = threadIdx.x % 32u;
71 const int pred = in[gid] > 6.0f;
72 const unsigned b = __ballot_sync(FULL, pred);
73 const int any = __any_sync(FULL, pred);
74 const int all = __all_sync(FULL, pred);
75 if (lane == 0) { outWarp[gid / 32u] = b; outAny[gid / 32u] = any; outAll[gid / 32u] = all; }
76}
77
78// Partial-warp ballot: a single block of `n` threads (n not a multiple of 32).
79__global__ void kBallotPartial(unsigned* outWarp) {
80 const unsigned tid = threadIdx.x;
81 const unsigned lane = tid % 32u;
82 const unsigned b = __ballot_sync(FULL, 1); // predicate always true
83 if (lane == 0) outWarp[tid / 32u] = b; // -> mask of ACTIVE lanes only
__shfl_down_sync (test_warp.cu:41)
31constexpr int N = 256; // 8 warps
32constexpr int BLOCK = 128; // 4 warps/block, multiple of 32
33constexpr int WARPS = N / 32;
34
35// ---- Kernels ---------------------------------------------------------------
36
37__global__ void kReduceSum(const float* in, float* outWarp) {
38 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
39 const unsigned lane = threadIdx.x % 32u;
40 float v = in[gid];
41 for (int off = 16; off > 0; off >>= 1) v += __shfl_down_sync(FULL, v, off);
42 if (lane == 0) outWarp[gid / 32u] = v; // lane 0 holds the warp sum
43}
44
45__global__ void kBroadcast(const float* in, float* out) {
46 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
47 out[gid] = __shfl_sync(FULL, in[gid], 0); // every lane <- lane 0's value
48}
49
50__global__ void kInclusiveScan(const float* in, float* out) {
51 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
52 const unsigned lane = threadIdx.x % 32u;
__shfl_sync (test_warp.cu:47)
37__global__ void kReduceSum(const float* in, float* outWarp) {
38 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
39 const unsigned lane = threadIdx.x % 32u;
40 float v = in[gid];
41 for (int off = 16; off > 0; off >>= 1) v += __shfl_down_sync(FULL, v, off);
42 if (lane == 0) outWarp[gid / 32u] = v; // lane 0 holds the warp sum
43}
44
45__global__ void kBroadcast(const float* in, float* out) {
46 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
47 out[gid] = __shfl_sync(FULL, in[gid], 0); // every lane <- lane 0's value
48}
49
50__global__ void kInclusiveScan(const float* in, float* out) {
51 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
52 const unsigned lane = threadIdx.x % 32u;
53 float v = in[gid];
54 for (int off = 1; off < 32; off <<= 1) {
55 float n = __shfl_up_sync(FULL, v, off);
56 if (lane >= static_cast<unsigned>(off)) v += n;
57 }
58 out[gid] = v;
__shfl_up_sync (test_warp.cu:55)
45__global__ void kBroadcast(const float* in, float* out) {
46 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
47 out[gid] = __shfl_sync(FULL, in[gid], 0); // every lane <- lane 0's value
48}
49
50__global__ void kInclusiveScan(const float* in, float* out) {
51 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
52 const unsigned lane = threadIdx.x % 32u;
53 float v = in[gid];
54 for (int off = 1; off < 32; off <<= 1) {
55 float n = __shfl_up_sync(FULL, v, off);
56 if (lane >= static_cast<unsigned>(off)) v += n;
57 }
58 out[gid] = v;
59}
60
61__global__ void kXorSum(const float* in, float* out) {
62 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
63 float v = in[gid];
64 for (int off = 16; off > 0; off >>= 1) v += __shfl_xor_sync(FULL, v, off);
65 out[gid] = v; // every lane ends with the full warp sum
66}
__shfl_xor_sync (test_warp.cu:64)
54 for (int off = 1; off < 32; off <<= 1) {
55 float n = __shfl_up_sync(FULL, v, off);
56 if (lane >= static_cast<unsigned>(off)) v += n;
57 }
58 out[gid] = v;
59}
60
61__global__ void kXorSum(const float* in, float* out) {
62 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
63 float v = in[gid];
64 for (int off = 16; off > 0; off >>= 1) v += __shfl_xor_sync(FULL, v, off);
65 out[gid] = v; // every lane ends with the full warp sum
66}
67
68__global__ void kBallot(const float* in, unsigned* outWarp, int* outAny, int* outAll) {
69 const unsigned gid = blockIdx.x * blockDim.x + threadIdx.x;
70 const unsigned lane = threadIdx.x % 32u;
71 const int pred = in[gid] > 6.0f;
72 const unsigned b = __ballot_sync(FULL, pred);
73 const int any = __any_sync(FULL, pred);
74 const int all = __all_sync(FULL, pred);
75 if (lane == 0) { outWarp[gid / 32u] = b; outAny[gid / 32u] = any; outAll[gid / 32u] = all; }
__syncwarp (test_web_bf_warp_cg.cu:71)
61 }
62 __syncthreads();
63 }
64
65 for (int offset = 16; offset > 0; offset >>= 1)
66 {
67 if (tid < offset)
68 {
69 s_y[tid] += s_y[tid + offset];
70 }
71 __syncwarp();
72 }
73
74 if (tid == 0)
75 {
76 atomicAdd(d_y, s_y[0]);
77 }
78}
79
80// verbatim device kernel — __shfl_down_sync for the last warp
81void __global__ reduce_shfl(const real *d_x, real *d_y, const int N)
82{