CUDA CPU Simulator

An NVIDIA GeForce 7900 GS graphics card, the kind of GPU whose CUDA kernels this simulator runs on an ordinary CPU.
Photo by Evan-Amos, public domain.

The CUDA CPU Simulator is a single-header, dual-target implementation of the CUDA execution model that runs GPU kernels on an ordinary CPU — no NVIDIA hardware, no driver, and no vendor toolchain required. One source file compiles two ways: under nvcc it is a thin pass-through to the real CUDA runtime and the kernel runs on the GPU unchanged; under a plain host compiler the same header becomes the device, and the kernel runs as ordinary C++.

Its purpose is to debug massively parallel code with the determinism, tooling, and observability of single-threaded host development — and above all, the simulator is designed to be driven by an AI agent that debugs autonomously: every control is a plain API call, every observation is text, and every run is exactly reproducible.

The guarantee is functional: results and ordering semantics, not wall-clock behaviour. Stream ordering is modelled — asynchronous work is queued per stream and replayed in a randomised, dependency-respecting order, so a missing synchronisation becomes a reproducible failure — but true hardware concurrency, memory timing, and performance are not; profiling remains the province of real hardware. Kernels validated here are then compiled with nvcc and run unmodified on a GPU.

A complete simulation of the CUDA programming model on the host: device memory distinct from host memory, full 3-D grid and block indexing, static and dynamic shared memory, barriers, the full atomic set, warp primitives, cooperative groups and cooperative launch, streams, events, graphs, textures and surfaces, dynamic parallelism, and CPU subsets of cuBLAS, Thrust, cuRAND and the half/bfloat16 types. GPU-CUDA solutions are validated end to end using a CPU-only solution.

The GPU's thousands of concurrent threads become cooperative fibers on a single CPU thread: blocks run one at a time, each thread running until it yields at a barrier that releases only once all participants arrive. Block and thread order can be pinned, permuted, reversed, or randomised at run time, and a step hook can re-order threads at any barrier. Sweeping seeds surfaces races a real GPU hides behind a lucky ordering; pinning one permutation reproduces the failure on demand.

Nothing here needs a human at a breakpoint. An agent can compile, run, form a hypothesis, express it directly, instrument the kernel, re-run, debug, and iterate to a fix — with no interactive debugger and no GPU in the loop. A built-in sanitizer reports out-of-bounds and misaligned accesses, uninitialised reads, races, and barrier divergence.

The same controls serve manual debugging. Kernels run on the host: tracing works anywhere in device code, in the chosen visit order; an ordinary CPU debugger steps through it; and the step hook gives break-inspect-continue at any barrier.