Repository navigation
cuda: stage synchronous downloads through a thread's pinned buffer - #2103
Conversation
|
Thanks @shuuul, and sorry for the wait. A careful write-up of a fault that's hard to pin. Your three questions: stage only where the device is integrated: the engine already reads |
5ec11e9 to
b76aa63
Compare
|
All four are in: staging is gated on |
|
@shuuul, one thing CI found on
Those fakes need |
|
Thanks. I'll set |
|
Pushed 0d5cd20. Every fake driver table that reaches |
|
Thanks @shuuul. That's every fake table, and answering discrete keeps each test on its old direct copy. One small thing: |
|
Done in 2d87813: both download tests now |
|
Thanks @shuuul, that's the one line. It's queued for after the 1.2.3 cut, with our GB10 run. |
|
Our GB10 run, main against this PR, Qwen3.8-27B: verdict GREEN on the head before your one-line |
2d87813 to
73008e5
Compare
On GB10 (driver 580.178.04, ATS) a synchronous cuMemcpyDtoH_v2 straight into pageable host memory can return success while some pages keep their old contents and the bytes land elsewhere; zspark's rank-round test met it 10 times in 60 runs, corrupting its allocator's metadata. Pin a bounce buffer per thread (at most 4 MiB a piece) and copy on from it with the CPU. The cost on an idle GB10 (2,000 copies, direct against staged): 4 B 5.90 vs 6.51 us, 32 KiB 5.33 vs 6.91, a 303,104 B logits row 10.59 vs 20.97, 4 MiB 84.74 vs 212.36, 64 MiB 1,354.59 vs 4,095.36. The smoke test now downloads 6 MiB through two pieces.
The fake writes only pinned destinations; the test asserts the copy's destination is the thread's pinned buffer and the pageable caller gets the bytes, so a regression to the direct copy fails without needing the fault.
Discrete cards keep their direct DtoH copies and their speed. The bounce's pinned bytes now count in memory.usage and are released with it. Comments are one line each; tests cover both the integrated and discrete paths.
download now asks cuCtxGetDevice and cuDeviceGetAttribute whether the device is integrated. The fake driver tables that only set the entries their test used left those two undefined, so the probe jumped into garbage. Each now answers device 0 and attribute 0, which keeps the direct copy path every host test expects. zig build test passes.
The integrated test cached its reading by the fake driver's address; a later test whose fake lands there would reuse it and call cuMemHostAlloc on a table that does not set it. Both tests clear it.
73008e5 to
5e0abe0
Compare
|
Thank you @shuuul. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
Relates to #2102.
What
DeviceBuffer.downloadnow stages through a per-thread pinned bounce only on an integrated device; discrete cards keep their direct DtoH copy and their speed. The engine'sCU_DEVICE_ATTRIBUTE_INTEGRATEDreading gates it (cached per thread and driver). A newcuda/download_stage.zigowns the bounce, which counts inmemory.usageand is released with the thread's buffer.downloadAsyncis unchanged and still requires pinned memory, as its doc says.The smoke test downloads 6 MiB through two pieces. A fake-driver test covers both paths: integrated stages into pinned memory and the pageable caller gets the bytes; discrete calls
{{SECRET_kr7wjfjl}}with the caller's slice and allocates nothing.Why
On GB10 (driver 580.178.04, ATS) a synchronous DtoH copy straight into pageable host memory can return success while some pages keep their old contents and the bytes land elsewhere. The downstream zspark fork hit it 10 times in 60 rank-round runs, corrupting an allocator's metadata; pinning the compare buffers gave 0/60, staging gave 0/100. Standalone loops and 200 unmodified
tf-cuda-test smokeruns here never miss, so the issue has no small public reproducer; #2102 records what we have.This is the defensive fix, not a claim that TensorFold's pointers are wrong.
Cost (idle GB10, 2,000 copies for the small sizes, direct against staged)
These are the integrated-device numbers; a discrete card takes the direct column unchanged. The 303,104-byte row is a logits row; the large rows are checks/test paths, not decoding.
Verified
zig build test-runtime-host: 282/282 tests pass, including both fake-driver paths.zig build -Dnvcc=/usr/local/cuda/bin/nvcc compile-cuda-tests, thentf-cuda-test smokeon a GB10: all pass, including the 6 MiB case.python3 tools/zig/lean_check.py: 425 problems, the same as main; the new comments are one line each.smokeruns pass.Review resolution
memory.usageand released bydrop.For the A/B on a GB10: replies should be byte-equal, decode unchanged, and the logits-row copy is the 303,104-byte row above.