fix(qwen35): trim legacy pools during long prefills

Merged
NE
1mo ago
+149-31

Summary

  • add an opt-in interval for trimming cached allocations from legacy CUDA/HIP pools between completed Qwen3.5 prefill chunks
  • reuse the synchronized trim hook introduced by #668, which retires captured graphs and the Q8 memo before returning cached blocks
  • document DFLASH_PREFILL_POOL_TRIM_TOKENS

Problem

#668 trims legacy pools when request scratch is released. A single long, shape-changing prefill can still exhaust a non-VMM HIP device before reaching that request boundary. On gfx1201, a 60,363-token prefill OOMed in the stock runtime even though many retained pool blocks were no longer live.

This change trims only after a chunk has fully completed: target compute and argmax readback are done, the cache position is committed, and draft-side features have been synchronized. It is disabled unless the environment variable is set. VMM backends remain unaffected because ggml_backend_cuda_has_legacy_pool() returns false.

Validation

Hardware and runtime:

  • Radeon AI PRO R9700 (gfx1201, 31.859 GiB VRAM)
  • ROCm 7.2.4
  • Qwen3.8-27B Q4_K_M target with DFlash2 block 16
  • Q8 K/V, --max-ctx 262144, concurrency 1
  • DFLASH_PREFILL_POOL_TRIM_TOKENS=8192
  • fresh server for every measurement
WorkloadStockPatched
60,363 input tokensOOMPASS, 125.6 s
60,363 peak VRAMOOM near card limit29.789 GiB
32,781 input prefill54.0 s53.9 s
32,781 peak VRAM30.961 GiB29.637 GiB
32,781 outputbaselinebyte-identical content SHA-256

Long-context patched probes:

Input tokensPrefillAverage prefillPeak VRAMFree at peak
60,363125.6 s480.6 tok/s29.789 GiB2.070 GiB
100,013270.9 s369.2 tok/s29.898 GiB1.961 GiB
131,013420.3 s311.7 tok/s30.319 GiB1.541 GiB
200,013861.7 s232.1 tok/s30.273 GiB1.586 GiB
258,0131351.1 s191.0 tok/s30.366 GiB1.494 GiB

During the 258K run, process GTT remained about 8 MiB, swap was disabled, and pswpin/pswpout remained zero. The capacity result therefore did not rely on VRAM-to-RAM paging.

The 60K run logged seven trims at 8,192-token intervals, releasing 238-760 MiB per trim. The direct- and meta-backend allocator shutdown/trim tests passed 2/2 on gfx1201. The patched dflash_server also builds against current main (d227a505) with only pre-existing warnings, and git diff --check passes.

Scope and tradeoffs

This was validated on the single-sequence Qwen3.5 path using a gfx1201 HIP legacy pool. The pool API's meta-backend traversal was exercised through a one-rank meta backend; CUDA, VMM, gfx1100, multi-rank tensor parallelism, and concurrent-serving paths were not runtime-tested. The feature is opt-in because each trim synchronizes the target backend and requires graph recapture; the 32K A/B showed no measurable prefill regression, but very short intervals are not recommended.

The configured 262,144-token window was nearly reached: 258,013 input tokens plus one generated token completed successfully. This does not claim a full 4K-token completion at that prompt length.