改动 / #3022
#3022 native_dense: STRATA_HC_Q8 uploads only the stage's own Q8_0 rows on a trimmed layer split
open · draft · @havok2-htwo · 0 评论 · 去 GitHub 看
BenchmarksSetup & installMulti-GPUAMD / HIPNVIDIA / CUDAModels & quants
说明
## Title Issue: none found (searched "HC_Q8"). Related: #2427, a different leak on a split, in other lines. ## Summary On a layer split with `--trim-stage-weights` (or `STRATA_STAGE_TRIM=1`) and `STRATA_HC_Q8` on, the loop in `src/core/native_dense.cpp` that attaches the GGUF's Q8_0 hyper-connection rows does not skip other stages' layers. A trimmed stage still has their table entries (metadata only), so every trimmed card uploads the Q8_0 rows of all 48 layers and reads only its own: 0.63 GiB per card on the rig below, 0.15-0.18 GiB with this change. ## What changed `src/core/native_dense.cpp`, +2 lines: the loop skips a tensor when `outside(name) || !in_range(name)`, the filter the upload loop above it uses. `in_range` does the work here (stage trim sets it through `set_layer_range`; the bf16-pack quantize loop below already checks it). No value changes: each stage reads the same rows as before. It applies only where all three hold: `STRATA_HC_Q8` is on (setup writes `STRATA_HC_Q8=1` for CUDA UD-* installs; HIP gfx12 turns it on by default), stage trim is on, and the `--native` GGUF itself stores Q8_0 hyper-connection rows (the UD-* files). A bf16 pack's rows come from the quantize loop, which already filters. CUDA0 is trimmed only with an explicit `--layer-split`; the later stages are also trimmed under `auto`, because their weights load after the split search. Without stage trim, `set_layer_range` never runs and `in_range` passes every layer, so nothing changes: the code path is the same, and with the fix and no trim each card still loads 0.63 GiB (I did not run 0.1.45 without trim, and the no-trim tokens are not compared). ## Extra Notes Measured on 4x RTX 3090 24 GB (sm_86), CUDA 13.0, setup.py release flags, v0.1.45 (`477ac13c`) with and without this commit. Qwen3.8-Flash-Next UD-IQ4_XS (`--native` GGUF + pack), `--layer-split 12,25,37 --resident-experts --expert-cache auto --expert-profile <profile> --prefill 512 --spec 4 --spec-min-p 0.5 --mtp <draft> --max-context 262144 --kv int8 --kv-resident 28672 --ple-io ram --batch 16`; env `STRATA_HC_Q8=1 STRATA_SPLIT_MTP_BATCH=1 STRATA_KV_PREFETCH=1 STRATA_NAN_GUARD=0` (the last one turns off a default). All experts are resident in the two trimmed arms; without trim 87 %. | | trim, 0.1.45 | trim, + fix | no trim, + fix | |---|---:|---:|---:| | `STRATA_HC_Q8=1: ... GiB` (engine line), CUDA0/1/2/3 | 0.63 / 0.63 / 0.63 / 0.63 | 0.16 / 0.18 / 0.16 / 0.15 | 0.63 / 0.63 / 0.63 / 0.63 | | free before the expert cache, GiB, CUDA0/1/2/3 | 16.01 / 16.27 / 16.43 / 15.25 | 16.60 / 16.85 / 17.02 / 15.86 | 12.86 / 13.13 / 13.19 / 11.92 | | expert cache slots, CUDA0/1/2/3 | 6144 / 6656 / 6144 / 5632 | 6144 / 6656 / 6144 / 5632 | 5240 / 5691 / 5557 / 4835 | | free with everything loaded, MiB, CUDA0/1/2/3 | 1507 / 1601 / 1707 / 1805 | 1325 / 1403 / 2319 / 1639 | 411 / 507 / 507 / 285 | Free VRAM before the expert cache goes up by 0.58-0.61 GiB per card, a little more than the 0.45-0.48 GiB drop in the engine line. That line counts payload bytes only; I did not look into the difference. On this rig the expert cache already holds every profiled pair, so the slot counts stay the same. The freed VRAM changes what the prompt path borrows instead: on 0.1.45, CUDA0/1/3 lend 324-348 cache slots each to the prompt path and 2.27 GiB of RAM is pinned for them; with the fix every card keeps its own prompt buffers (0.75 GiB each) and nothing is pinned. Those own buffers are new allocations, while the loan reused cache slots, so CUDA0/1/3 end 0.16-0.19 GiB lower with everything loaded, and CUDA2, which had no loan, ends 0.6 GiB higher. Tokens: `tools/batch_test.py --batch 16 --n 16 --max-new 128 --keys temperature=0 --extra "--pcie-frac 0 --adapt-every 1000000"`, with the same expert cache on every card in both trimmed arms. 0.1.45 vs fix: solo 16/16 and batch 16/16 identical, and in both arms every slot matches its own solo run. Batch throughput is the same: 236.3 vs 236.4 tok/s; I took no other speed measurement. The no-trim arm has a smaller cache, so its tokens are not compared. #2427 drops the bf16 copies on a split; this change drops other stages' Q8_0 copies. They change different lines. I did not run them together. **Not tested:** only CUDA sm_86 was built and run. I have no ROCm, SYCL or Metal toolchain here, so the HIP and Metal builds of this file were not compiled. The two lines only call `outside` and `in_range`, which the same function already calls, so they add no code a backend could fail on. That still leaves the AGENTS.md build rule open, so this stays a draft until someone builds it for HIP. On HIP gfx12 with a UD-* GGUF and stage trim, this should lower VRAM in the same way under the default; the per-card `STRATA_HC_Q8=1: ... GiB` line on an R9700 split would confirm it. The SYCL copy of this loop (`sycl/src/core/native_dense.cpp`) gets the same filter in #2810, so this PR leaves SYCL alone. Also not run: long prompts (the tokens check is 16 short prompts, 128 new tokens, and the prompt path's buffers changed), 2- or 3-GPU splits, `--layer-split auto`, `--keep-canonical`, `STRATA_HC_Q8_INJECT=1`, bf16/GSQ packs, other models. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
本站相关内容
相关页面的快捷入口。