feat(v4/cuda): the CUDA tier wired into the DeepSeek V4 engine — generic kernels for any sm_80+, Windows runtime DLL loader, Linux CUDA=1 - #1055
Conversation
|
Two of the things your stack was carrying have landed in
So #1055, #1053 and #1056 no longer need to carry either of them. A rebase on current One thing worth flagging before you rebase: I have deliberately not merged the smaller PRs that touch On the overlap with #1031: your numbers (3324-token prefill 271 s → 90 s, decode at 3k context 0.6 → 1.6–1.76 tok/s) are a different order of magnitude from what that PR measures (+4–5% on a 10.7 GB card), and you offered to reconcile with whichever lands first. That's the right spirit and it's noted — the decision on which carries the tier is the maintainer's, and it'll be made on the rebased diff rather than the stacked one. Also: #1056 vendors third-party headers and adds |
…efill segments, cancellable prefill, batched indexer selection
- Prefix checkpoints (V4_PREFIX_CKPT, on): snapshot the attention
transaction (window KV + compressed slots + compressor/indexer state) at a
shared boundary and restore it for any later request whose prompt starts
with the same bytes. Capture rules: the gateway's prefix hint (prefix_bytes
in the session options), the longest common prefix of two successive fresh
prompts, and a prompt-end capture after every prefill (agent clients
re-render the assistant reply). 4 LRU slots (V4_PREFIX_CKPT_SLOTS),
prompt-end evicted first.
- Disk persistence (V4_PREFIX_CKPT_DISK, on): <model>/.coli_ckpt/, config
fingerprinted, temp+rename, lazily loaded after a restart. New
coli_v4_{attention,compressor,indexer}_snapshot_write/read; restore grows
the live buffers and prepares the per-layer window attention state.
- Segmented prefill: atomic 4096-token segments (V4_PREFILL_SEGMENT), a
should_abort poll between them (ColiV4SessionAbortFn in the session
options), completed segments recorded so an identical retry resumes;
progress lines per segment.
- 128-token chunks (V4_PREFILL_CHUNK clamp 64 -> 128; sums[128] in the fp8
batch reference).
- Batched indexer selection in prefill (V4_IDX_BATCH, on): advance per
token, score/select once per chunk past index_topk, per-token numerics
unchanged; V4_IDX_IDENTITY (off) documents the identity short-circuit.
- MTP: clamp the noise token to the vocabulary.
CPU parity vs dev: 826-token prompt, 48 greedy tokens, COLI_V4_ROWS16=0
COLI_V4_AUTOPIN=0 COLI_V4_SAVE_USAGE=0 V4_PREFIX_CKPT=0 -> byte-identical
text (md5 9cf553633292 on both).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e acks a CANCEL - Emit the byte offset of the first user/assistant turn marker as an optional 8th SUBMIT header field (older engines ignore it) so the very first request seeds the shared-prefix checkpoint. - Do not release the scheduler admission before the engine confirms a client cancel: releasing early let the next request SUBMIT into a pipe the busy engine was not reading (every later request hung). Wait for the ERROR CANCELLED / DONE frame; raise ClientCancelled on a DONE that arrives after a CANCEL. Wire client_disconnected as the abort poll. - Exit cleanly on Ctrl-C. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…U-path output identity means Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts.M) coli plan counted zero routed experts for V4 checkpoints because the expert regex only knew model.layers.N.mlp.experts.M. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cee7149 to
056b920
Compare
|
Rebased all three on current
Re-verified after the rebase: #1053 CPU-only build; #1055 Windows engine + both DLLs via the make targets, 826-token prompt / 48 greedy tokens → same text as before the rebase (md5 d4e3f23d3d68), generic DLL forced also OK; #1056 |
…eneric sm_80+ + opt-in DeepGEMM, Windows DLL export/loader On top of JustVugg#772's rescoped backend_cuda_dsv4.cu (vLLM/FlashInfer paths gone, DeepGEMM an external opt-in): the kernels the engine wiring in the next commit calls, all validated against the CPU reference on greedy text. - Batched prefill attention on a persistent device KV ring + compressed buffer (dsv4_cuda_sparse_attn_batch, _cached, _cached_idx for selections past index_topk), compressor/indexer bf16 projections, whole-chunk mHC (pre-norm / post), grouped wo tail, batched dense fp8 GEMM. - Route-aware transient expert bank: dsv4_cuda_route_top6_batch on device, dsv4_cuda_expert_bank_upload (page-locked host slabs via cudaHostRegister/VirtualQuery on Windows, pageable elsewhere), dsv4_cuda_route_moe_ids_batch over host routes; expert-grouped GEMM rows. - Decode: fused routed+shared MoE (dsv4_cuda_moe), fp4 expert mirrors with in-place refill (dsv4_cuda_tensor_refill_fp4), device-KV sparse attention, indexer scoring kernel (__fadd_rn/__fmul_rn: bitwise the CPU loop — MinGW GCC does not contract FMA), fp8 reference-matmul replica incl. the rows8-packed layout, dsv4_cuda_mem_free_mb. - Backend identity: dsv4_cuda_backend_arch_ok / dsv4_cuda_backend_name so a host can pick a DLL flavour per device. - Windows: dsv4.def exports and backend_loader_dsv4.c (MinGW engine, MSVC/nvcc DLL): tries coli_cuda_dsv4_dg.dll then coli_cuda_dsv4.dll, COLI_DSV4_DLL forces one, resolves every mandatory symbol up front. - -DCOLI_DSV4_NO_TC compiles out the opt-in cuBLASLt MXFP8 path for toolkits older than 12.8 (verified: CUDA 12.6 fails on exactly the four block-scaling symbols without it). - Makefile: cuda-dsv4-dll (generic; CUDA_ARCH=portable for a fat binary) and cuda-dsv4-dg-dll (DEEPGEMM_HOME external, sm_120a); DeepGEMM needs the community sm120 port + patches-deepgemm-sm120-msvc.patch (MSVC ignores alignas(64) on by-value TMA descriptors; the patch pads them). - Kernel tests: tests/test_dsv4_sparse_attn_batch_cuda.c (max diff 1e-3 = one bf16 ulp vs the CPU reference), test_dsv4_decode_cuda.c through the loader ABI, test_dsv4_gpu_grouped.c. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ical with per-stage fallback
COLI_V4_UNIT_GPU + the #ifdef COLI_V4_GPU_TIER hooks: the engine keeps all
state in host memory and lets the tier accelerate stage by stage, falling
back to the CPU reference for anything it declines. Every stage was accepted
only when greedy text matched the CPU reference.
- Dense/attention mirrors in VRAM (6.3 GB) uploaded per resident layer;
GPU dense matvecs (fp8, grouped wo) in decode; ColiTensorView.gpu /
ColiExpertView .gpu handles (tensor.h, expert_store.h).
- Decode: fp4 expert mirror cache (VRAM-sized: DSV4_CUDA_EXPERT_MIRRORS is an
upper bound, growth stops when DSV4_CUDA_VRAM_RESERVE_MB is left; probe
throttled), in-place refill, fused routed+shared MoE, sparse attention on
the device KV, indexer via advance + select_batch(1); DSV4_DECODE_PROF.
- Prefill (COLI_CUDA_ATTN_BATCH=1): whole-chunk attention block on the GPU
— compressor/indexer projections, sparse attention on the persistent
device KV ring (indexed kernel past index_topk), wo, mHC — with the ring
appended after attention (chunk rows passed separately) and shared
coli_v4_gpu_kv_cache_{sync,advance,poison,invalidate_all}; batched
indexer scoring on the GPU (bitwise the CPU loop) with the fp8 reference
matmul replica for the query projection; cached scratch arena.
- Prefill MoE (COLI_CUDA_MOE_BATCH=1): route on device, refill the transient
per-layer VRAM expert bank route-aware in pin-slot-sized groups
(V4_MOE_REFILL_GROUP), lookups of group g+1 overlapped with the uploads of
group g, page-locked slabs; expert-grouped GEMM rows; a lookup that loses
the pin-slot race is retried sequentially instead of failing the layer to
the CPU union; V4_MOE_BANK_FULL opt-in whole-layer prefetch (measured
worse, documented).
- Linux: Makefile.deepseek-v4 CUDA=1 [CUDA_ARCH=native|portable|sm_XX]
[DEEPGEMM=1 DEEPGEMM_HOME=...] [NO_TC=1] links the tier directly (libcuda
from the toolkit stubs + rpath); coli_v4_gpu_engine_open consults
dsv4_cuda_backend_arch_ok and stays CPU-only on a device the binary
cannot run on. Verified on Ubuntu 22.04/WSL2, RTX 5080, CUDA 13.3 + 12.6.
- CLI: --gpu/--vram for the V4 engine (coli), V4_MTP_GPU opt-in, doctor
detects the DLL next to the engine, tiny fixture test covers the GPU MTP
draft.
Numbers (RTX 5080 16 GB, 2x NVMe mirror, 32 GB RAM): 3324-token prefill
271 s -> 90 s (generic kernels 225 s, text identical), 8.3k-token agent first
turn ~4 min once, later sessions/turns 6-9 s (with the prefix checkpoints of
the previous PR), decode 0.6 -> 1.6-1.76 tok/s at 3k context. 826-token
prompt, 48 greedy tokens: text identical to the previous engine build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… VRAM budget + per-card settings, performance, GPU coverage, environment reference, validation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
056b920 to
5eb9b32
Compare
|
Merged into What decided it, since I did not read all 7,161 lines and won't pretend otherwise:
Also worth recording: your design decision to keep every stage CPU-canonical with a per-stage fallback, and to accept a GPU stage only when its greedy text matched the CPU reference on the same prompt, is the reason this could be merged on a compile flag rather than after a month of review. The Validation section in the docs — being explicit about what "identical" can and cannot mean across kernels and cache states — is better than most projects manage. Three follow-ups, none blocking:
Thanks for splitting this the way you did. Landing the kernels with their tests first, then the wiring, made both reviewable — that sequencing was your suggestion and it was the right one. |
Stacked on #772 (@ZacharyZcR's kernels-as-a-tier, rescoped head), on #988 and on #1053 — until those merge, the diff against
devincludes them. The three commits that are this PR:dc54ce3(kernels/loader),c259de0(engine wiring),cee7149(docs).This is the engine side #772 deliberately left out, plus the kernels the wiring needed. It follows the direction set in #772's review: our own kernels, small consumer cards first, every stage CPU-canonical with a per-stage CPU fallback. The vLLM/FlashInfer files are gone (the same removal as #772's rescope), DeepGEMM stays an opt-in build (
DEEPGEMM_HOME; a small follow-up PR offers to vendor the sm120 headers, separable).What it is
COLI_V4_UNIT_GPU,#ifdef COLI_V4_GPU_TIER): all state stays in host memory; the tier accelerates stage by stage and falls back to the CPU reference for anything it declines. Dense/attention mirrors in VRAM (6.3 GB) with GPU dense matvecs in decode; a VRAM-sized fp4 expert mirror cache for decode (DSV4_CUDA_EXPERT_MIRRORSis an upper bound, growth stops whenDSV4_CUDA_VRAM_RESERVE_MBis left; in-place refill); fused routed+shared MoE, sparse attention on the device KV, GPU indexer.COLI_CUDA_ATTN_BATCH=1): the whole-chunk attention block on the GPU — compressor/indexer projections, sparse attention on a persistent device KV ring (indexed kernel pastindex_topk), wo, mHC — plus batched indexer scoring (bitwise the CPU loop:__fadd_rn/__fmul_rn, MinGW GCC does not contract FMA) with a bitwise fp8 reference-matmul replica for the query projection.COLI_CUDA_MOE_BATCH=1): route on device, refill a transient per-layer VRAM expert bank route-aware in pin-slot-sized groups (V4_MOE_REFILL_GROUP), lookups of group g+1 overlapped with the uploads of group g, page-locked host slabs; expert-grouped GEMM rows; a lookup that loses the pin-slot race is retried sequentially instead of failing the layer to the CPU union.dsv4_cuda_backend_arch_ok:coli_cuda_dsv4.dll(generic kernels,CUDA_ARCH=portable= sm_80/86/89/90/120 + PTX, any RTX 30/40/50, A/H) andcoli_cuda_dsv4_dg.dll(DeepGEMM sm_120a, opt-in).backend_loader_dsv4.cresolves every mandatory symbol up front;COLI_DSV4_DLLforces one; banner[DSV4 CUDA] backend=... (generic|deepgemm-sm120).make -f Makefile.deepseek-v4 deepseek-v4 CUDA=1 [CUDA_ARCH=native|portable|sm_XX] [DEEPGEMM=1 DEEPGEMM_HOME=...] [NO_TC=1]links the tier directly (libcuda from the toolkit stubs + rpath);NO_TC=1compiles out the opt-in cuBLASLt MXFP8 path for toolkits older than 12.8 (Jetson). Verified on Ubuntu 22.04/WSL2, RTX 5080, CUDA 13.3.1 + 12.6.85 — CPU-only, generic, DeepGEMM builds; the 12.6 compile fails on exactly the four cuBLASLt 12.8 symbols without the gate.--gpu/--vramfor the V4 engine,coli doctorsees the DLL,V4_MTP_GPUopt-in; kernel testsc/tests/test_dsv4_*_cuda.c(batched sparse attention: max diff 1e-3 = one bf16 ulp vs the CPU reference; decode kernels through the loader ABI).Numbers (RTX 5080 16 GB, 2× NVMe mirror, 32 GB RAM)
Per-card settings for 6/8/10–12/16/24/32 GB are in
docs/deepseek-v4.md(VRAM budget: 6.3 GB dense mirrors, 2.2 GB prefill bank, ~8 MB per decode mirror; free VRAM sizes the cache at run time). Every GPU stage was accepted only when its greedy text matched the CPU reference on the same prompt; what "identical" can and cannot mean across kernels/cache states is written up in the docs' Validation section.Overlap
#1031 (@rafpigna) touches the same area with a different design (single DLL, per-call safetensors reads for dense-on-GPU,
.coli_kvsingle-session persistence). Happy to reconcile with whichever lands first; the numbers above are the comparison point.🤖 Generated with Claude Code