A GPU-accelerated chess perft calculator using CUDA.
This is a heavily optimized rewrite of the original perft_gpu project, stripped down to single-GPU mode with a host-driven BFS approach. The focus is on raw move generation throughput, with optional transposition tables for deep perft.
perft_gpu <fen> <depth> [-nott] [-dtt <MB>] [-htt <MB>] [-ld <N>] [-cpu]
| Flag | Description |
|---|---|
<fen> |
FEN string for the position to analyze |
<depth> |
Maximum perft depth |
-nott |
Disable transposition tables (raw move generation throughput) |
-dtt <MB> |
Override device TT memory budget in MB (default: auto, 95% of free VRAM) |
-htt <MB> |
Override host TT memory budget in MB (default: auto, 90% of system RAM) |
-ld <N> |
Manual BFS/CPU switchover depth (auto-detected if omitted) |
-cpu |
Pure CPU mode (no GPU, useful for debugging/comparison) |
Transposition tables are enabled by default. They provide massive speedups at deeper depths by caching previously computed subtree counts.
# Starting position, depth 10, with transposition tables (default)
perft_gpu "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 10
# Kiwipete position, depth 7, without transposition tables
perft_gpu "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 7 -nott
# With explicit launch depth and custom TT budgets
perft_gpu "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 12 -ld 8 -dtt 8192 -htt 32768
# Pure CPU mode (no GPU)
perft_gpu "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 7 -cpuThe program prints cumulative perft results for depth 1 through the specified maximum depth.
Without transposition tables:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 9 | 2,439,530,234,167 | 1.867s | ~1,306 billion nps |
| Position 2 (Kiwipete) | 7 | 374,190,009,323 | 0.177s | ~2,110 billion nps |
With transposition tables enabled and lossless host TTs:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 9 | 2,439,530,234,167 | 0.174s | ~14,004 billion nps |
| Starting position | 10 | 69,352,859,712,417 | 2.040s | ~33,989 billion nps |
| Starting position | 11 | 2,097,651,003,696,806 | 24.33s | ~86,226 billion nps |
| Starting position | 12 | 62,854,969,236,701,747 | 373.6s | ~168,224 billion nps |
| Starting position | 13 | 1,981,066,775,000,396,239 | 5,664.9s | ~349,708 billion nps |
| Starting position | 14 | 61,885,021,521,585,529,237 | 99,956.6s | ~619,119 billion nps |
Without transposition tables:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 9 | 2,439,530,234,167 | 2.90s | ~841 billion nps |
| Starting position | 10 | 69,352,859,712,417 | 88.25s | ~786 billion nps |
| Position 2 (Kiwipete) | 7 | 374,190,009,323 | 0.286s | ~1,308 billion nps |
| Position 3 | 10 | 860,322,381,070 | 1.55s | ~555 billion nps |
With transposition tables enabled, lossless host TTs, launch depth 8:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 9 | 2,439,530,234,167 | 0.302s | ~8,066 billion nps |
| Starting position | 10 | 69,352,859,712,417 | 3.91s | ~17,733 billion nps |
| Starting position | 11 | 2,097,651,003,696,806 | 51.36s | ~40,840 billion nps |
The DGX Spark data below was measured with a slightly older build, before some of the latest optimizations.
Without transposition tables:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 8 | 84,998,978,956 | 0.356s | ~239 billion nps |
| Starting position | 9 | 2,439,530,234,167 | 9.38s | ~260 billion nps |
| Position 2 (Kiwipete) | 7 | 374,190,009,323 | 0.86s | ~435 billion nps |
With transposition tables enabled, lossless host TTs, launch depth 8:
| Position | Depth | Nodes | Time | Speed |
|---|---|---|---|---|
| Starting position | 8 | 84,998,978,956 | 0.112s | ~759 billion nps |
| Starting position | 9 | 2,439,530,234,167 | 1.38s | ~1,763 billion nps |
| Starting position | 10 | 69,352,859,712,417 | 16.71s | ~4,151 billion nps |
| Starting position | 11 | 2,097,651,003,696,806 | 214.88s | ~9,762 billion nps |
The perft tree is explored using breadth-first search driven from the host. At each BFS level, CUDA kernels expand all positions at the current depth in parallel:
- Make moves + count children - Apply each move and count the resulting legal moves
- Prefix scan (CUB InclusiveSum) - Compute output offsets for the next level
- Merge-path interval expand - Load-balanced mapping from child indices back to parent indices, ensuring all CTAs do equal work regardless of how many moves each position has
- Generate moves - Write out child moves to the next level's buffer using shared memory staging for coalesced writes
The last two levels of the tree are handled by a fused 2-level leaf kernel: each thread makes a move, generates all child moves locally, then for each child makes the move and counts grandchildren. This eliminates the most memory-intensive BFS expansion and enables deeper perft without running out of GPU memory.
- Quad-bitboard representation - Each position is stored as 4 x uint64 (32 bytes) encoding piece type + color per square, with game state in a separate 1-byte packed struct. This gives power-of-2 struct size for GPU coalescing.
- Host-driven BFS with one kernel launch per phase per level (no CUDA Dynamic Parallelism)
- Fused 2-level leaf kernel - Handles the last 2 tree levels per-thread, eliminating massive memory allocations for the deepest BFS level and improving cache locality
- Merge-path interval expand (inspired by moderngpu) for perfect load balancing across CTAs
- Bump allocator for GPU memory - a single pre-allocated buffer avoids per-level
cudaMalloc/cudaFreeoverhead - Dynamic launch depth - Automatically estimates optimal CPU/GPU split based on branching factor and available memory
- Bitboard move generation using magic bitboards for sliding pieces
Requirements:
- CUDA Toolkit (tested with CUDA 13.1)
- CMake 3.18+
- GPU with compute capability 8.0+ (e.g., RTX 3090, RTX 4090, RTX 6000 Blackwell)
cmake -B build
cmake --build build --config ReleaseTo target a different GPU architecture, edit CMAKE_CUDA_ARCHITECTURES in CMakeLists.txt.
| File | Description |
|---|---|
perft.cu |
Entry point, CLI parsing, flag handling |
launcher.cu / launcher.h |
GPU init, launch depth estimation, GPU+CPU perft launchers, TT allocation, OOM fallback |
perft_kernels.cu |
GPU kernels, host-driven BFS, upsweep, move generator init |
MoveGeneratorBitboard.h |
Bitboard-based legal move generation (~1950 lines) |
chess.h |
Core data structures (QuadBitBoard, GameState, CMove, magic entries) |
switches.h |
Compile-time flags (BLOCK_SIZE, MIN_BLOCKS_PER_MP, TT budgets, etc.) |
tt.h |
Transposition table structures (TTEntry, TTTable, LosslessTT, probe/store) |
zobrist.h / zobrist.cpp |
128-bit Zobrist hashing |
uint128.h |
128-bit unsigned integer for deep perft accumulation |
utils.h / util.cpp |
FEN parsing, board display, Timer class |
GlobalVars.cpp |
Magic tables, attack tables |
Magics.cpp |
Magic number generation for sliding piece attacks |
randoms.cpp |
Pre-generated random numbers for Zobrist hashing |
MIT