cuda: zero-copy expert views on pageable-shared memory (GB10) - #936
cuda: zero-copy expert views on pageable-shared memory (GB10)#936Nanetnounou wants to merge 1 commit into
Conversation
On integrated devices with pageableMemoryAccess=1 (GB10 / DGX Spark
class: one LPDDR5X pool shared by CPU and GPU), uploading an expert
copies bytes the GPU can already read in place. Every streamed expert
pays a redundant copy, and with CUDA_RELEASE_HOST each device copy
also holds a mirror host page: the same physical bytes are paid twice.
This teaches the CUDA backend non-owning device VIEWS onto host slabs:
- coli_cuda_tensor_wrap builds a view (weights_owned=0) instead of
a device copy when the device is pageable-shared; freeing a view
never touches the borrowed host bytes;
- streamed experts and PILOT-prefetched experts (demand=0) get
views, so 100% of routed experts compute on the GPU. Without the
demand=0 case, PILOT resurrected 914 expert rows on the CPU per
decode window and gave back its I/O gain. Measured after: routed
CPU rows during decode = 0;
- pinned experts are left without device copies after placement (a
view suffices; the pass runs after the RAM-tier load loop and
after placement, not during load: both other orderings were
measured wrong -- 8 views out of 3807 candidates when too early,
non-finite logits when the host slab is released under the view);
- the io_uring loader wires the same view path (it set the format
but not the view, so uring loads silently fell back to copies);
- fix in coli_cuda_tensor_upload: a VIEW must never satisfy a
request for a COPY. The cached-copy shortcut matches on
fmt/I/O/device, so it would accept a view and report success
while the slot holds a borrowed host pointer; and because a view
declares weight_bytes=0, expert-tier accounting silently
collapses (measured: tier fell from 3795 to 128 experts). Latent
on dev today -- nothing creates views yet -- but armed the moment
anything does.
Together with the E8 warp kernels, decode p50 on GLM-5.2 E8-IQ3 on
GB10 went from 1376.6 to 640.9 ms/token (2.15x).
Known limitation, deliberate: COLI_GROUP_ASYNC=1 must stay off while
views are in use. The ESlot LRU has no reference count, so the async
group path can recycle a slot while a GPU read is still in flight
(observed: non-finite router logits at a varying layer, then
"pread qs: Bad address"). The sync path costs 3.5% of orchestration
time. A per-slot refcount is the real fix and is out of scope here;
reported separately.
New test: c/tests/test_zerocopy.c.
Developed with AI assistance (Anthropic Claude), human-reviewed; all
figures above were measured on the hardware described.
|
On the trilogy (#934, #935, #936, plus the #933 analysis behind it): this is serious work and it's in the review queue — but deliberately after the imminent release. The release carries the v1.5.0 regression fix and is soaking now; GPU/kernel changes land in the next cycle where they get proper time, and #936 in particular needs its lifetime story reviewed against your own #933 refcount analysis before it can be on by default. Two things that would speed review: confirm #936 contains (or depends on) the per-slot refcount from #933 rather than preceding it, and — since we have no GB10-class hardware — a second measurement from anyone with unified-memory NVIDIA would help. The NEON path (#934) is the least entangled of the three and will likely go first. |
|
Authored by Fable 5 in Claude Code, analysis in partnership with @monotophic. Three things — a state update that affects the review question, a technical 1. The #933 dependency question is now a different question. Since the 2. A question from #1037's cycle, registered before we measure: do the 3. The unified-memory NVIDIA measurement: we're running it. My GB10 pair |
|
Authored by Fable 5 in Claude Code, analysis in partnership with @monotophic Here is the second unified-memory measurement asked for above, from my GB10 box (DGX Spark Correctness. View and copy outputs are bitwise identical at S=1 and S=4 (memcmp over Throughput. Kernel window (
(two runs shown as a/b). Views = copies within noise at S≤8, ~1% slower at S=32. Measured On the refcount question. #936 as submitted contains no per-slot refcount and does not Alignment. Nothing in CUDA dispatch keys on 16/32-byte pointer alignment (no The shipped test needs three small fixes: (1) |
What
On integrated devices with
pageableMemoryAccess=1(GB10 / DGX Spark class: one LPDDR5X pool shared by CPU and GPU), uploading an expert copies bytes the GPU can already read in place — and withCUDA_RELEASE_HOSTeach device copy also keeps a mirror host page, so the same physical bytes are paid twice. Worse: experts that are not resident on the device tier fall back to CPU compute, a rational choice on a discrete GPU and a pure loss on unified memory.This PR teaches the CUDA backend non-owning device views onto host slabs (
coli_cuda_tensor_wrap, using theweights_ownedfield already present in dev):demand=0) compute on the GPU through views — without thedemand=0case, PILOT resurrected 914 expert rows on the CPU per decode window and gave back its I/O gain; measured after: routed CPU rows during decode = 0;coli_cuda_tensor_upload: a view must never satisfy a request for a copy — the cached-copy shortcut matches on fmt/I/O/device, would accept a view, and the view'sweight_bytes=0then collapses expert-tier accounting (measured: tier fell from 3795 to 128 experts). Latent on dev today, armed the moment anything creates views.Together with the E8 warp kernels (separate PR), decode p50 on GLM-5.2 E8-IQ3 on GB10 went 1376.6 → 640.9 ms/token (2.15x).
Known limitation (deliberate)
COLI_GROUP_ASYNC=1must stay off while views are in use: theESlotLRU has no reference count, so the async group path can recycle a slot while a GPU read is in flight. Reported with full analysis in #933; the sync path costs ~3.5% of orchestration time. A per-slot refcount is the real fix and is out of scope here.Dropped during rebase
An earlier version also changed GPU-tier capacity planning to use routed-expert width; dev's
expert_bytes_row(#856) already solves that better, so it was dropped.Suggestion (no code in this PR)
On devices with
pageableMemoryAccess=1, the unified behavior could become the default: views on, GPU prefill pipeline on, async group path guarded off. Today that requires a combination of env vars that only someone who has done this debugging would know. Happy to send a follow-up PR if there is interest.Tests
New
c/tests/test_zerocopy.c.make check: 415 tests OK. Same note as the kernels PR:cuda-testexits 1 on GB10 already on a pristine dev checkout (v1.5.0-based builds pass on the same machine/driver) — separate issue to follow.Disclosure
Developed with AI assistance (Anthropic Claude), human-reviewed; all figures were measured on the hardware described.