Prefer to read without ads? Become a member — from $10/month — and support the work. Already a member? Log in to read ad-free on this device.

14.5 Warp Shuffle

The warp shuffle instructions (described in Section 8.6.1) enable threads to exchange data without writing the data to shared memory. The __shfl() intrinsic can be used to broadcast one thread’s register value to all other threads in the warp; as shown in Listing 14-7, instead of using tiles sized to the threadblock and using shared memory, we can use tiles of size 32 (corresponding to the warp size) and broadcast the body description read by each thread to the other threads within the warp.

Interestingly, this strategy has 25% lower performance than the shared memory implementation (34 billion as opposed to 45.2 billion interactions per second); the gap persists on modern hardware, where a GeForce RTX 3060 runs the shuffle kernel at about 199 billion versus 291 billion for the shared-memory kernel. The warp shuffle instruction takes about as long as a read from shared memory, and the computation is tiled at the warp size (32 threads) rather than a thread block size. So it seems the benefits of warp shuffle are best realized when replacing both a write and a read to shared memory, not just a read; and warp shuffle should only be used if the kernel needs shared memory for other purposes.

__global__ voidComputeNBodyGravitation_Shuffle(     float *force,     float *posMass,     float softeningSquared,     size_t N ){    const int laneid = threadIdx.x & 31;    for ( int i = blockIdx.x*blockDim.x + threadIdx.x;              i < N;              i += blockDim.x*gridDim.x )    {        float acc[3] = {0};        float4 myPosMass = ((float4 *) posMass)[i];         for ( int j = 0; j < N; j += 32 ) {            float4 shufSrcPosMass = ((float4 *) posMass)[j+laneid];#pragma unroll 32            for ( int k = 0; k < 32; k++ ) {                float fx, fy, fz;                float4 shufDstPosMass;                 shufDstPosMass.x = __shfl( shufSrcPosMass.x, k );                shufDstPosMass.y = __shfl( shufSrcPosMass.y, k );                shufDstPosMass.z = __shfl( shufSrcPosMass.z, k );                shufDstPosMass.w = __shfl( shufSrcPosMass.w, k );                 bodyBodyInteraction(                    &fx, &fy, &fz,                     myPosMass.x, myPosMass.y, myPosMass.z,                     shufDstPosMass.x,                     shufDstPosMass.y,                     shufDstPosMass.z,                     shufDstPosMass.w,                     softeningSquared);                acc[0] += fx;                acc[1] += fy;                acc[2] += fz;            }        }         force[3*i+0] = acc[0];        force[3*i+1] = acc[1];        force[3*i+2] = acc[2];    }}
Listing 14-7. ComputeNBodyGravitation_Shuffle (source on GitHub)