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.4 Constant Memory

Stone et al. describe a method of Direct Coulomb Summation (DCS) that uses shared memory to hold potential map lattice points for a molecular modeling application, so it must use constant memory to hold body descriptions. Listing 14-5 shows a CUDA kernel that uses the same method for our gravitational simulation. Since only 64K of constant memory is available to developers for a given kernel, each kernel invocation can only process about 4,000 16-byte body descriptions. The constant g_bodiesPerPass specifies the number of bodies that can be considered by the innermost loop.

Since every thread in the innermost loop is reading the same body description, constant memory works well since it is optimized to broadcast reads to all threads in a warp.

const int g_bodiesPerPass = 4000;__constant__ __device__ float4 g_constantBodies[g_bodiesPerPass]; template<typename T>__global__ voidComputeNBodyGravitation_GPU_AOS_const(     T *force,     T *posMass,     T softeningSquared,    size_t n,     size_t N ){    for ( int i = blockIdx.x*blockDim.x + threadIdx.x;              i < N;              i += blockDim.x*gridDim.x )    {        T acc[3] = {0};        float4 me = ((float4 *) posMass)[i];        T myX = me.x;        T myY = me.y;        T myZ = me.z;        for ( int j = 0; j < n; j++ ) {            float4 body = g_constantBodies[j];            float fx, fy, fz;            bodyBodyInteraction(                 &fx, &fy, &fz,                 myX, myY, myZ,                 body.x, body.y, body.z, body.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-5. N-body (constant memory) (source on GitHub)

As shown in Listing 14-6, the host code must loop over the bodies, calling cudaMemcpyToSymbolAsync() to load the constant memory before each kernel invocation.

floatComputeNBodyGravitation_GPU_AOS_const(    float *force,     float *posMass,    float softeningSquared,    size_t N){    cudaError_t status_cudart;    cudaEvent_t evStart = 0, evStop = 0;    float ms = 0.0;    size_t bodiesLeft = N;     void *p;    cuda(GetSymbolAddress( &p, g_constantBodies ) );     cuda(EventCreate( &evStart ) );    cuda(EventCreate( &evStop ) );    cuda(EventRecord( evStart, NULL ) );    for ( size_t i = 0; i < N; i += g_bodiesPerPass ) {        // bodiesThisPass = max(bodiesLeft, g_bodiesPerPass);        size_t bodiesThisPass = bodiesLeft;        if ( bodiesThisPass > g_bodiesPerPass ) {            bodiesThisPass = g_bodiesPerPass;        }        cuda(MemcpyToSymbolAsync(             g_constantBodies,            ((float4 *) posMass)+i,            bodiesThisPass*sizeof(float4),            0,            cudaMemcpyDeviceToDevice,            NULL ) );        ComputeNBodyGravitation_GPU_AOS_const<float> <<<300,256>>>(             force, posMass, softeningSquared, bodiesThisPass, N );        bodiesLeft -= bodiesThisPass;    }    cuda(EventRecord( evStop, NULL ) );    cuda(DeviceSynchronize() );    cuda(EventElapsedTime( &ms, evStart, evStop ) );Error_cudart:    cudaEventDestroy( evStop );    cudaEventDestroy( evStart );    return ms;}
Listing 14-6. Host code (constant memory N-body) (source on GitHub)