CUDA Cheatsheet

Compiling with nvcc

Use this CUDA reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

nvcc Basics

nvcc is NVIDIA's CUDA compiler driver. It separates host and device code, compiles device code to PTX or cubin, and passes host code to the system C++ compiler (GCC, Clang, MSVC).

nvcc myfile.cu -o myapp               # basic compile + link
nvcc myfile.cu -o myapp -O2           # with optimization
nvcc myfile.cu -o myapp -arch=sm_89   # target Ada Lovelace (RTX 4090)

Compute Capability Targets

Flag formMeaning
-arch=sm_XYCompile for SM X.Y; PTX + cubin for that SM only
-code=sm_XYGenerate cubin for SM X.Y (used with -gencode)
-code=compute_XYEmbed PTX for SM X.Y (JIT-compiled at runtime)
-gencode arch=compute_XY,code=sm_XYFull "real" compilation for one SM
-gencode arch=compute_XY,code=compute_XYEmbed PTX only (forward compat)

Multi-arch fat binary (recommended for distribution)

nvcc myfile.cu -o myapp \
  -gencode arch=compute_80,code=sm_80 \    # Ampere
  -gencode arch=compute_89,code=sm_89 \    # Ada
  -gencode arch=compute_90,code=sm_90 \    # Hopper
  -gencode arch=compute_100,code=sm_100 \  # Blackwell data center (CUDA ≥ 12.8)
  -gencode arch=compute_120,code=sm_120 \  # Blackwell RTX 50 (CUDA ≥ 12.8)
  -gencode arch=compute_120,code=compute_120  # PTX (JIT for future GPUs)

Shorthand -arch

-arch=sm_80 is a shortcut for -gencode arch=compute_80,code=sm_80 plus embedding PTX for forward compatibility. Equivalent long form:

-gencode arch=compute_80,code=sm_80 -gencode arch=compute_80,code=compute_80

Common Compiler Flags

FlagEffect
-O0 / -O1 / -O2 / -O3Host optimization level
-gHost debug info
-GDevice debug info (disables optimizations)
-lineinfoDevice line info without disabling opts (for profilers)
-use_fast_mathEnable approximate math (__sinf, FMA, etc.)
-ftz=trueFlush denormals to zero
-prec-div=falseFast (less precise) division
-prec-sqrt=falseFast (less precise) sqrt
--maxrregcount=NCap registers per thread to N
-Xptxas -vShow register/shared mem usage per kernel
-Xptxas -O3PTX assembler optimization level
-rdc=trueRelocatable device code (for dynamic parallelism, separate compilation)
-keepKeep intermediate .ptx, .cubin, .cudafe files
-dryrunPrint subcommands without executing
-vVerbose — show all subcommands
--ptxOutput PTX only (no binary)
--cubinOutput cubin only
-fatbinOutput fat binary
--expt-relaxed-constexprAllow constexpr in device code
--expt-extended-lambdaAllow __device__ lambdas
-std=c++17C++ standard for host code
-Xcompiler -fopenmpPass flag to host compiler
-Xlinker -rpath,/usr/local/cuda/lib64Pass flag to linker

Separate Compilation and Linking

For large projects with multiple .cu files:

# Compile each file to a relocatable device object
nvcc -rdc=true -c fileA.cu -o fileA.o
nvcc -rdc=true -c fileB.cu -o fileB.o

# Link (device link step + host link)
nvcc -rdc=true fileA.o fileB.o -o myapp

# Or explicitly device-link then host-link:
nvcc -rdc=true -dlink fileA.o fileB.o -o device_link.o
g++ fileA.o fileB.o device_link.o -lcudart -o myapp

CMake Integration

cmake_minimum_required(VERSION 3.18)
project(MyCUDAProject LANGUAGES CXX CUDA)

set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_ARCHITECTURES "80;89;90;120")  # or "native" for local GPU

add_executable(myapp main.cu kernels.cu)

target_compile_options(myapp PRIVATE
    $<$<COMPILE_LANGUAGE:CUDA>:--use_fast_math -lineinfo>
)

# Separate compilation (for dynamic parallelism)
set_target_properties(myapp PROPERTIES CUDA_SEPARABLE_COMPILATION ON)

PTX (Parallel Thread Execution)

PTX is NVIDIA's virtual ISA — an intermediate representation that the driver JIT-compiles to machine code.

nvcc -ptx myfile.cu -o myfile.ptx    # emit PTX
nvcc --ptxas-options=-v myfile.cu    # show register/smem usage

Inline PTX

__global__ void inlinePtxDemo(unsigned* out) {
    unsigned tid;
    asm("mov.u32 %0, %%tid.x;" : "=r"(tid));    // read threadIdx.x via PTX
    *out = tid;

    // Atomic add with relaxed ordering
    asm volatile("atom.relaxed.global.add.u32 [%0], 1;" :: "l"(out) : "memory");
}

Linking CUDA Libraries

LibraryLink flagHeader
CUDA Runtime-lcudart<cuda_runtime.h>
CUDA Driver-lcuda<cuda.h>
cuBLAS-lcublas<cublas_v2.h>
cuSPARSE-lcusparse<cusparse.h>
cuFFT-lcufft<cufft.h>
cuRAND-lcurand<curand.h>
cuDNN-lcudnn<cudnn.h>
cuSOLVER-lcusolver<cusolverDn.h>
NCCL-lnccl<nccl.h>
Thrust(header-only)<thrust/...>
CUB(header-only)<cub/cub.cuh>
NVTX-lnvToolsExt<nvtx3/nvToolsExt.h>
NVRTC (runtime compile)-lnvrtc<nvrtc.h>
Device runtime-lcudadevrtneeded for -rdc=true
nvcc myfile.cu -o myapp -lcublas -lcufft -lcurand

Environment Variables

VariableEffect
CUDA_VISIBLE_DEVICESComma-list of device IDs visible to process (or NoDevFiles)
CUDA_DEVICE_ORDERFASTEST_FIRST (default) or PCI_BUS_ID
CUDA_LAUNCH_BLOCKING1 → make all kernel launches synchronous (debug)
CUDA_CACHE_DISABLE1 → disable JIT cubin cache
CUDA_CACHE_PATHOverride JIT cache directory
CUDA_FORCE_PTX_JIT1 → force JIT from PTX even if cubin present
NVCC_PREPEND_FLAGSPrepend these flags to all nvcc invocations
NVCC_APPEND_FLAGSAppend these flags
CUDA_VISIBLE_DEVICES=0,1 ./myapp    # restrict to GPUs 0 and 1
CUDA_LAUNCH_BLOCKING=1 ./myapp      # synchronize after every kernel (debugging)

Runtime Compilation (NVRTC)

Compile CUDA source code at runtime (useful for JIT kernels in libraries).

#include <nvrtc.h>
#include <cuda.h>

const char* src = R"(
    extern "C" __global__ void myKernel(float* data, int n) {
        int i = blockIdx.x * blockDim.x + threadIdx.x;
        if (i < n) data[i] *= 2.0f;
    }
)";

nvrtcProgram prog;
nvrtcCreateProgram(&prog, src, "myKernel.cu", 0, nullptr, nullptr);

const char* opts[] = {"--gpu-architecture=compute_89"};
nvrtcCompileProgram(prog, 1, opts);

size_t ptxSize;
nvrtcGetPTXSize(prog, &ptxSize);
char* ptx = new char[ptxSize];
nvrtcGetPTX(prog, ptx);

// Load and launch via Driver API
CUmodule module; CUfunction kernel;
cuModuleLoadDataEx(&module, ptx, 0, nullptr, nullptr);
cuModuleGetFunction(&kernel, module, "myKernel");

void* args[] = { &d_data, &n };
cuLaunchKernel(kernel, gridDim, 1, 1, blockDim, 1, 1, 0, 0, args, nullptr);

nvrtcDestroyProgram(&prog);
delete[] ptx;
cuModuleUnload(module);

Useful nvcc Output and Inspection

# Show PTX for a single function
nvcc -ptx -arch=compute_89 myfile.cu && grep -A 50 "myKernel" myfile.ptx

# Show SASS (native assembly)
cuobjdump -sass myapp | grep -A 30 "myKernel"

# Show embedded cubins / PTX in a binary
cuobjdump -all myapp
cuobjdump -ptx  myapp    # extract PTX sections
cuobjdump -elf  myapp    # extract ELF sections

# Register / shared mem usage per kernel
nvcc --ptxas-options=-v myfile.cu 2>&1 | grep "Used"
# Output: ptxas info: Used 32 registers, 4096 bytes smem, 352 bytes cmem[0]