cuda: copy model into VRAM on single-GPU via --gpu-resident - #622
Open
pvaccarello wants to merge 1 commit into
Open
cuda: copy model into VRAM on single-GPU via --gpu-resident#622pvaccarello wants to merge 1 commit into
pvaccarello wants to merge 1 commit into
Conversation
On a single discrete CUDA GPU whose VRAM fits the whole model, ds4 never copied the weights to device memory: it kept the model host-mapped (zero-copy) and read routed-expert weights over PCIe on every token. On an RTX PRO 6000 Blackwell (96 GB) with DeepSeek-V4-Flash-IQ2XXS (~80.76 GiB) this pinned decode at ~1 tok/s with VRAM stuck at ~18 GB, even with --gpu-vram auto. Root cause: ds4_gpu_set_model_map_range() registered the no-copy host mapping first (setting g_model_registered = 1), then called cuda_model_copy_chunked(), whose first guard is `if (g_model_device_owned || g_model_registered) return 1;`. So the copy was always skipped and DS4_CUDA_COPY_MODEL_CHUNKED was a no-op on this path. Fix: attempt the chunked device copy before the no-copy registration, and fall back to no-copy if it fails (OOM or an existing escape-hatch env var). Weight reads already resolve through g_model_device_base, so kernels read from the VRAM image with no further changes. Expose it as a --gpu-resident flag (maps to DS4_CUDA_COPY_MODEL_CHUNKED via setenv) in ds4-server, ds4, ds4-agent and ds4-bench, with shared help text. Measured on the setup above: VRAM ~18 -> ~94 GB, decode ~1 -> ~55 tok/s (~50x), coherent output, GPU compute-bound instead of PCIe-bound. Opt-in only; default behaviour and the multi-GPU / SSD-streaming / Metal / CPU paths are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Can confirm this on identical hardware RTX PRO 6000 Blackwell 96GB, same IQ2XXS Flash 0731. On current main (54b36ed) I was getting ~0.7 tok/s with VRAM stuck around 13GB, and DS4_CUDA_COPY_MODEL_CHUNKED did nothing. Traced it to the same early-return in cuda_model_copy_chunked that this PR describes: g_model_registered is already 1 by the time it runs, so the copy is dead code on the single-GPU path. After patching just that guard locally, the chunk copy runs (80.76 GiB in ~7.7s from warm page cache), the model goes fully to VRAM and decode jumps to 61.5 tok/s at 128k context. So the diagnois here is definitely right, and the numbers match what's reported. Would be great to see this merged. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On a single discrete CUDA GPU whose VRAM is large enough to hold the whole model,
ds4never copies the weights into device memory. It keeps the modelhost-mapped (zero-copy) and reads every routed-expert weight across PCIe on each
token. On an RTX PRO 6000 Blackwell (96 GB) running
DeepSeek-V4-Flash-IQ2XXS(~80.76 GiB) this pins decode at ~1 tok/s with VRAM stuck at ~18 GB, even with
--gpu-vram auto(which reports a 90 GB budget).This PR:
--gpu-resident(maps to the existingDS4_CUDA_COPY_MODEL_CHUNKEDenv var).Result on the hardware above: ~55 tok/s (a ~50× speedup), model fully
resident in VRAM.
Root cause
The single-GPU, no-slice startup path calls
ds4_gpu_set_model_map_range():ds4_gpu_register_model_map_no_copy()doescudaHostRegister(...)and setsg_model_registered = 1.cuda_model_copy_chunked()opens withif (g_model_device_owned || g_model_registered) return 1;— so, because (1)already set
g_model_registered = 1, the copy is always skipped and everyweight access goes over PCIe.
DS4_CUDA_COPY_MODEL_CHUNKEDis effectively ano-op on this path.
Fix
ds4_cuda.cu— inds4_gpu_set_model_map_range(), attempt the chunkeddevice copy before the no-copy host registration, so the guard no longer
short-circuits it. On failure (OOM, or the existing
DS4_CUDA_NO_MODEL_COPY/DS4_CUDA_DIRECT_MODELescape hatches) we restore state and fall back to theoriginal no-copy path. Default behavior is unchanged unless the copy is
explicitly requested.
Execution already routes weight reads through the resolver
if (model_map == g_model_host_base && g_model_device_base) return g_model_device_base + offset;,so once the copy sets
g_model_device_baseto the VRAM image, kernels readfrom VRAM with no further changes.
ds4_server.c,ds4_cli.c,ds4_agent.c,ds4_bench.c— new--gpu-residentflag setsDS4_CUDA_COPY_MODEL_CHUNKEDviasetenv()beforeengine creation, in each binary's arg parser. (Kept as a thin mapping over the
env var so no cross-backend symbol/plumbing is added; can be promoted to a
proper
ds4_engine_optionsfield if you prefer.)ds4_help.c— shared help text for the new flag (shown by all binaries).Benchmark (RTX PRO 6000 Blackwell, 96 GB, sm_120, CUDA 13.0)
Model
DeepSeek-V4-Flash-IQ2XXS(~80.76 GiB),make ds4-server CUDA_ARCH=sm_120.--gpu-resident)Both the flag and the raw env var were tested and produce identical behavior.
Run:
Notes for the maintainer
--gpu-resident(or the env var), behavior isbyte-for-byte the previous no-copy streaming path.
engine_install_per_device_caches()(multi-tier)deliberately uses the no-copy variant; untouched here.
cuda_model_prefetch_range()fallback is dropped: it only ran inthe "env set but copy skipped" case, which no longer exists. Can be re-added in
the copy-failure branch as a host-page warm hint if desired.
96 GB card lands at ~94–95 GB (
--ctx 64000…100000). If it doesn't fit,cudaMallocfails and we fall back to no-copy.--gpu-residentis wired into all four CUDA-capable binaries:ds4-server,ds4(CLI),ds4-agent,ds4-bench.Test plan
make ds4-server ds4 ds4-agent ds4-bench CUDA_ARCH=sm_120.--gpu-residentonds4-server(no env var): model copied to VRAM,~55 tok/s, coherent output.
--gpu-residentonds4CLI: triggers the VRAM copy end-to-end.--help.DS4_CUDA_COPY_MODEL_CHUNKED=1(no flag): same behavior.CPU paths (all untouched).
PS: ci vediamo a Campobello di Licata!!!