CUDA Cheatsheet
Streams and Events
Use this CUDA reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Streams Overview
A stream is an ordered queue of CUDA operations (kernels, memcpies, events) that execute in sequence on the GPU. Operations in different streams may overlap.
Stream 0 (default): |--copy H2D--|--kernel--|--copy D2H--| Stream 1: |--copy H2D--|--kernel--|--copy D2H--| ^^^ overlap
Creating and Destroying Streams
cudaStream_t stream; cudaStreamCreate(&stream); // ... use stream ... cudaStreamDestroy(stream); // With flags cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking); // cudaStreamDefault (0) — blocks on the default stream // cudaStreamNonBlocking — does not block on default stream operations // With priority (lower number = higher priority) int loPri, hiPri; cudaDeviceGetStreamPriorityRange(&loPri, &hiPri); cudaStreamCreateWithPriority(&stream, cudaStreamNonBlocking, hiPri);
Using Streams
// Enqueue to a stream cudaMemcpyAsync(d_in, h_in, bytes, cudaMemcpyHostToDevice, stream); myKernel<<<grid, block, 0, stream>>>(d_in, d_out, n); cudaMemcpyAsync(h_out, d_out, bytes, cudaMemcpyDeviceToHost, stream); // Synchronize cudaStreamSynchronize(stream); // block CPU until stream empty cudaDeviceSynchronize(); // block CPU until ALL streams empty // Non-blocking query cudaError_t s = cudaStreamQuery(stream); // cudaSuccess → complete; cudaErrorNotReady → still running
Default Stream Behavior
| Stream value | Behavior |
|---|---|
0 or cudaStreamLegacy | Legacy default stream — serializes with all per-thread default streams |
cudaStreamPerThread | Per-thread default stream (enable with --default-stream per-thread) |
cudaStreamNonBlocking flag | Stream does NOT synchronize with legacy default stream |
// Enable per-thread default stream globally: // nvcc --default-stream per-thread myfile.cu // Or per translation unit: #define CUDA_API_PER_THREAD_DEFAULT_STREAM #include <cuda_runtime.h>
Events
Events mark points in a stream. Used for timing and cross-stream dependencies.
cudaEvent_t start, stop; cudaEventCreate(&start); cudaEventCreate(&stop); // Record events into stream cudaEventRecord(start, stream); myKernel<<<grid, block, 0, stream>>>(args); cudaEventRecord(stop, stream); // Wait on CPU for stop to be reached cudaEventSynchronize(stop); float ms; cudaEventElapsedTime(&ms, start, stop); // milliseconds printf("Kernel time: %.3f ms\n", ms); cudaEventDestroy(start); cudaEventDestroy(stop);
Event flags
cudaEventCreateWithFlags(&ev, cudaEventDefault); cudaEventCreateWithFlags(&ev, cudaEventBlockingSync); // CPU sleeps instead of polls cudaEventCreateWithFlags(&ev, cudaEventDisableTiming); // no timing — lowest overhead cudaEventCreateWithFlags(&ev, cudaEventInterprocess); // for IPC
Cross-Stream Synchronization with Events
Force stream B to wait until an event in stream A is reached, without blocking the CPU.
cudaEvent_t event; cudaEventCreateWithFlags(&event, cudaEventDisableTiming); // Stream A: record event after kernel finishes kernelA<<<g, b, 0, streamA>>>(d_a); cudaEventRecord(event, streamA); // Stream B: wait for that event before starting cudaStreamWaitEvent(streamB, event, 0); // 0 = no flags kernelB<<<g, b, 0, streamB>>>(d_b);
Overlap Pattern: Double Buffering
Classic pattern for hiding PCIe transfer latency:
const int CHUNKS = 4; size_t chunkBytes = totalBytes / CHUNKS; cudaStream_t streams[2]; cudaStreamCreate(&streams[0]); cudaStreamCreate(&streams[1]); float *d_in[2], *d_out[2]; cudaMalloc(&d_in[0], chunkBytes); cudaMalloc(&d_out[0], chunkBytes); cudaMalloc(&d_in[1], chunkBytes); cudaMalloc(&d_out[1], chunkBytes); float *h_in, *h_out; cudaMallocHost(&h_in, totalBytes); // must be pinned for async cudaMallocHost(&h_out, totalBytes); for (int i = 0; i < CHUNKS; i++) { int s = i & 1; // ping-pong between 0 and 1 cudaMemcpyAsync(d_in[s], h_in + i * chunkBytes / sizeof(float), chunkBytes, cudaMemcpyHostToDevice, streams[s]); process<<<grid, block, 0, streams[s]>>>(d_in[s], d_out[s], chunkBytes / sizeof(float)); cudaMemcpyAsync(h_out + i * chunkBytes / sizeof(float), d_out[s], chunkBytes, cudaMemcpyDeviceToHost, streams[s]); } cudaDeviceSynchronize(); cudaFreeHost(h_in); cudaFreeHost(h_out); for (int s = 0; s < 2; s++) { cudaFree(d_in[s]); cudaFree(d_out[s]); cudaStreamDestroy(streams[s]); }
CUDA Graphs
Capture a sequence of operations into a reusable graph to eliminate CPU launch overhead.
Stream capture
cudaGraph_t graph; cudaGraphExec_t instance; // Begin capture cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); // Record operations (enqueued but NOT yet executed) cudaMemcpyAsync(d_in, h_in, bytes, cudaMemcpyHostToDevice, stream); myKernel<<<grid, block, 0, stream>>>(d_in, d_out, n); cudaMemcpyAsync(h_out, d_out, bytes, cudaMemcpyDeviceToHost, stream); // End capture → graph cudaStreamEndCapture(stream, &graph); // Instantiate (compile) and launch cudaGraphInstantiate(&instance, graph, nullptr, nullptr, 0); for (int iter = 0; iter < 1000; iter++) cudaGraphLaunch(instance, stream); // very low CPU overhead cudaStreamSynchronize(stream); cudaGraphExecDestroy(instance); cudaGraphDestroy(graph);
Capture mode flags
| Flag | Description |
|---|---|
cudaStreamCaptureModeGlobal | Any stream may be captured simultaneously |
cudaStreamCaptureModeThreadLocal | Only the calling thread captures |
cudaStreamCaptureModeRelaxed | Relaxed constraints; unsafe cross-thread ops |
Updating a graph (avoid re-instantiation)
cudaGraphExecKernelNodeSetParams(instance, node, &newParams); cudaGraphExecMemcpyNodeSetParams(instance, node, &newMemcpyParams); // After updating nodes, optionally call: cudaGraphExecUpdate(instance, graph, nullptr, nullptr);
Stream Callbacks (deprecated)
cudaStreamAddCallback schedules a CPU function to run when a stream reaches that point — but it is deprecated; use cudaLaunchHostFunc (next section) in new code.
void CUDART_CB myCallback(cudaStream_t stream, cudaError_t status, void* data) { printf("Stream done: %p\n", data); } cudaStreamAddCallback(stream, myCallback, myData, 0); // 0 = no flags (currently the only option)
Restriction: Callbacks cannot call any CUDA API functions — use for pure CPU work (logging, signaling a condition variable, etc.).
Host Functions in Streams (CUDA 10+)
Host functions scheduled into the stream (unlike callbacks, these CAN call CUDA API in some contexts).
auto hostFn = [](void* data) { int* val = (int*)data; *val = 42; // runs on CPU in stream order }; cudaLaunchHostFunc(stream, hostFn, myDataPtr);
IPC Event and Memory Handles
Share GPU memory or events between processes.
// Process A — export cudaIpcMemHandle_t memHandle; cudaIpcGetMemHandle(&memHandle, d_ptr); // Send memHandle via IPC mechanism (pipe, shared mem, etc.) cudaIpcEventHandle_t evHandle; cudaIpcGetEventHandle(&evHandle, event); // Send evHandle // Process B — import float* d_shared; cudaIpcOpenMemHandle((void**)&d_shared, memHandle, cudaIpcMemLazyEnablePeerAccess); cudaEvent_t sharedEvent; cudaIpcOpenEventHandle(&sharedEvent, evHandle); cudaStreamWaitEvent(myStream, sharedEvent, 0); useKernel<<<g, b, 0, myStream>>>(d_shared); cudaIpcCloseMemHandle(d_shared);
Event and Stream API Summary
| Function | Description |
|---|---|
cudaStreamCreate | Create stream |
cudaStreamCreateWithFlags | Create with NonBlocking or Default |
cudaStreamCreateWithPriority | Create with numeric priority |
cudaStreamDestroy | Destroy stream |
cudaStreamSynchronize | Block CPU until stream empty |
cudaStreamQuery | Poll stream status |
cudaStreamWaitEvent | Insert cross-stream dependency |
cudaStreamAddCallback | Schedule CPU callback (deprecated — use cudaLaunchHostFunc) |
cudaLaunchHostFunc | Schedule host function |
cudaStreamBeginCapture | Begin graph capture |
cudaStreamEndCapture | End capture → cudaGraph_t |
cudaEventCreate | Create event |
cudaEventCreateWithFlags | Create with timing/blocking flags |
cudaEventRecord | Record event into stream |
cudaEventSynchronize | Block CPU until event reached |
cudaEventElapsedTime | Milliseconds between two events |
cudaEventQuery | Poll event status |
cudaEventDestroy | Destroy event |
cudaGraphInstantiate | Compile graph into executable |
cudaGraphLaunch | Execute graph instance |
cudaGraphExecDestroy | Destroy graph instance |
cudaGraphDestroy | Destroy graph definition |