<<<CUDA C++ Coursegrid · block · warp · lane
Cheat Sheet

Cheat sheet

The things you look up while writing a kernel, on one page. For why they work that way, go back to the matching lesson.

Builtins and qualifiers

FormMeaning
threadIdx.x/y/zThread coordinates in the block
blockIdx.x/y/zBlock coordinates in the grid
blockDim.x/y/zThreads per block
gridDim.x/y/zNumber of blocks in the grid
warpSizeAlways 32
__global__Kernel entry: GPU executes, host launches, returns void
__device__GPU executes, GPU calls
__host__ __device__Compiled once for each side
__shared__On-chip memory shared within the block
__constant__Read-only constant memory, 64 KB, broadcast-optimized
__restrict__No-alias guarantee; with const it can use the read-only cache
__launch_bounds__(T, B)Constrains the compiler's register budget

Index arithmetic

CUDA C++
// 1Dint i = blockIdx.x * blockDim.x + threadIdx.x;if (i < n) { ... } // 2D (x is the column, the contiguous memory direction)int col = blockIdx.x * blockDim.x + threadIdx.x;int row = blockIdx.y * blockDim.y + threadIdx.y;if (row < rows && col < cols) m[row * cols + col] = ...; // grid-stride loop: grid size is decoupled from data sizefor (int i = blockIdx.x * blockDim.x + threadIdx.x;     i < n; i += gridDim.x * blockDim.x) { ... } // Linear tid in the block (warps slice this into groups of 32)int tid = threadIdx.x        + threadIdx.y * blockDim.x        + threadIdx.z * blockDim.x * blockDim.y; // Ceiling division for the grid sizeint blocks = (n + threads - 1) / threads;

Runtime API

CUDA C++
cudaMalloc(&d_p, bytes);                 // note the &d_pcudaMallocManaged(&p, bytes);            // unified memorycudaHostAlloc(&h_p, bytes, cudaHostAllocDefault);   // pinned; required for async copiescudaMemcpy(dst, src, bytes, cudaMemcpyHostToDevice);cudaMemcpyAsync(dst, src, bytes, kind, stream);cudaMemset(d_p, 0, bytes);cudaFree(d_p);  cudaFreeHost(h_p); cudaStreamCreate(&s);cudaStreamSynchronize(s);                // wait on this stream onlycudaStreamWaitEvent(s2, evt, 0);         // cross-stream dependency; CPU does not blockcudaDeviceSynchronize();                 // wait for everything; debug and shutdown only cudaEventCreate(&e);  cudaEventRecord(e, s);cudaEventSynchronize(e);cudaEventElapsedTime(&ms, start, stop);  // the only correct way to time a kernel cudaGetLastError();                      // synchronous error from the launchcudaGetErrorString(err);cudaOccupancyMaxPotentialBlockSize(&minGrid, &block, kernel, 0, 0);cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, 96*1024);

Synchronization and warp primitives

PrimitiveScopeNotes
__syncthreads()blockBarrier. Never place it in a divergent branch
__syncwarp(mask)warpAfter Volta, exchanging data inside a warp requires an explicit sync
__threadfence()deviceMemory ordering, not a barrier
__shfl_sync(m, v, src)warpBroadcast
__shfl_down_sync(m, v, d)warpReduction; the result lands in lane 0
__shfl_xor_sync(m, v, k)warpButterfly; every lane receives the result
__ballot_sync(m, pred)warpVote bitmap; pair with __popc for stream compaction
__activemask()warpBuild a mask on a divergent path
Warp reduction template
__inline__ __device__ float warpReduceSum(float v) {    for (int off = 16; off > 0; off >>= 1)        v += __shfl_down_sync(0xffffffff, v, off);    return v;}

Compiler flags

FlagEffect
-arch=sm_80Specifies both the virtual and the real architecture
-gencode arch=compute_80,code=sm_80Precise control of a single target
-gencode arch=compute_90,code=compute_90Keeps PTX so newer cards can JIT. Always ship this in a release
-Xptxas -vPrints register usage and spills
-lineinfoAdds line numbers for profiler and sanitizer; does not affect optimization
-GDevice-side debug. Disables all optimization. Use with care
--use_fast_mathFast math: trades precision for speed
-maxrregcount=NGlobal register cap; too coarse. Prefer __launch_bounds__
--default-stream per-threadStops the default stream from implicitly synchronizing with other streams
ArchitectureCode nameTypical cards
sm_70VoltaV100
sm_75TuringT4, RTX 20 series
sm_80AmpereA100
sm_86AmpereRTX 30 series, A10
sm_89AdaRTX 40 series, L4
sm_90HopperH100

Profiling commands

shell
# Step 1: inspect the overall timeline and find the real bottleneck kernelnsys profile -o report --stats=true ./app # Step 2: drill into a single kernelncu --set full -o profile ./appncu --kernel-name myKernel --launch-count 3 ./app # Key metrics only (much faster)ncu --metrics \  sm__throughput.avg.pct_of_peak_sustained_elapsed,\  gpu__dram_throughput.avg.pct_of_peak_sustained_elapsed,\  sm__warps_active.avg.pct_of_peak_sustained_active \  ./app # Check coalescing: sectors/requests ideal is 4; near 32 means no coalescing at allncu --metrics \  l1tex__t_sectors_pipe_lsu_mem_global_op_ld.sum,\  l1tex__t_requests_pipe_lsu_mem_global_op_ld.sum ./app # Check bank conflictncu --metrics l1tex__data_bank_conflicts_pipe_lsu_mem_shared.sum ./app # Correctnesscompute-sanitizer --tool memcheck ./appcompute-sanitizer --tool racecheck ./app # Inspect the compiler outputcuobjdump -sass ./app | lessnvcc -ptx -arch=sm_80 -o k.ptx k.cu

Numbers worth remembering

These magnitudes help you decide whether an idea is worth trying

ItemMagnitude
Register latency~1 cycle
Shared memory latency~20-30 cycles
L2 latency~200 cycles
Global memory latency~400-800 cycles
Sector granularity32 bytes
Shared memory banks32 banks, 4 bytes wide each
A100 peak bandwidth1.55 TB/s
A100 FP32 throughput19.5 TFLOPS
A100 BF16 Tensor Core312 TFLOPS (about 16x)
A100 arithmetic intensity knee≈ 12.5 FLOP/Byte
Kernel launch overhead3-10 microseconds
Max threads per block1024

Symptom to cause

SymptomMost likely cause
printf in a kernel prints nothingMissing a sync point; the process already exited
Results are all zerosForgot to cudaMemcpy back, or the launch failed and errors were not checked
Results are occasionally wrongMissing __syncthreads(), or a PyTorch extension used the default stream
invalid configuration argumentblockDim exceeds 1024, or shared memory is over the limit
no kernel image is availableCompiled -arch does not match the GPU, and no PTX was kept
Copy and compute do not overlapHost memory is not pinned, or the default stream was used
A one-line change tanks performanceRegister count crossed a cliff and occupancy dropped; check -Xptxas -v
Performance far below the bandwidth ceilingAccesses are not coalesced; check the sectors/requests ratio
Shared-memory kernel is slowBank conflict; try row width +1

Not started yet? Begin with your first kernel , or jump to the occupancy calculator and see what is capping your kernel.