Skip to content

Eval bug: CUDA decode+prefill collapse at deep KV position on qwen35 hybrid (~5.5x tg, ~21x pp at d154855) #29172

Open

Description

@hot-YUser

System

  • OS: Windows 11 (x64)
  • GPU: NVIDIA GeForce RTX 5060 Ti 16GB (16283 MiB, compute capability 12.0 Blackwell, sm_120)
  • Driver: 616.92, CUDA 13.4 runtime
  • CPU: AMD Ryzen 5 7500F (6C/12T)
  • RAM: 16GB DDR5-5600 single stick
  • Build: prebuilt llama-b11047-bin-win-cuda-13.4-x64.zip + matching cudart (upstream release, not self-built)
  • Version string: version: 0.3.0-dev (build 11047) — exact string from --version below

Configuration

llama-bench.exe -m Ternary-Bonsai-2-27B-PTQ1_0.gguf -ngl 99 -fa on -p 512 -n 32 -r 1 -o md -d <depth>

Model: prism-ml/Ternary-Bonsai-2-27B-gguf, file Ternary-Bonsai-2-27B-PTQ1_0.gguf (5.95 GB, PTQ1_0 ternary, arch=qwen35, 64 blocks, hybrid attention ~75% linear, SHA256 53107f53…fe33ee3 verified)
Note: this is a ternary-weight model, but the symptom below is in the backend decode path (identical shape to dense-model reports #27623/#27734), not in weight dequantization.

The Issue

Decode (and at depth, also prefill) throughput collapses once the KV position passed in via -d exceeds roughly 150K tokens, on an otherwise healthy fully-GPU-resident run. Short-context numbers on the same binary/model/GPU are normal.

llama-bench -p 512 -n 32 -r 1 (engine-internal timings, not wall clock):

test t/s
pp512 (d0) 464.81
tg32 (d0) 44.68
pp512 @ d154855 21.87
tg32 @ d154855 8.10

That is a ~21x prefill collapse and ~5.5x decode collapse at depth, with exit code 0 (no error, no OOM, no CPU fallback lines). The -d 154855 run took ~1h20m wall, all of it in prompt processing of the synthetic depth prefix.

Same-model CLI spot checks agree: -c 154855 short prompt gives pp ~153 / tg ~42, while -c 262144 gives pp ~44 / tg ~9.

Related control: llama-bench -fa off -d 154855 fails in ~7s at context creation (non-FA path must materialize the full attention matrix); -fa off -d 32768 works (pp 314 / tg 29); -fa off at d0 matches -fa on (pp 462 / tg 43.9). So FA tiled kernels are required at depth, and the collapse happens within the FA path.

Suspected Area

Decode-path attention over long KV for hybrid linear/full-attention models (qwen35-family here: 64 blocks, ~75% linear). Possibly a fallback/slow kernel engaged past some KV-extent threshold, or a memory-latency-bound matvec regime (per #26581's ~21-25ns/KV/attention-layer constant, which is backend-invariant and also points at decode-attention kernels rather than bandwidth).

Two candidate mechanisms I have already ruled out on this machine, with evidence:

  1. Vulkan-style suballocation fragmentation (Vulkan: ~78% decode throughput cliff at 131072 context — suballocation fragmentation; fixed via GGML_VK_SUBALLOCATION_BLOCK_SIZE=4 GiB #27734): does not apply to CUDA. The CUDA backend uses ggml_cuda_pool_vmm (contiguous 32GB virtual reservation via cuMemAddressReserve, on-demand physical mapping, LIFO free) on this card (VMM: yes in init log). There is no suballocation block splitting to tune, and no GGML_CUDA_* pool-size knob exists in the binary (all 14 GGML_CUDA_* env strings enumerated — none pool-related).
  2. FA VEC→MMA kernel switch at K>=8192 (fattn.cu:617, gqa_ratio > 4 && K->ne[1] >= 8192): exists in source, but is 19x below the observed 155K cliff and cannot explain it directly.

What I have not done: Nsight/ncu profiling of the deep-position decode kernels (no CUDA toolkit profiler on this box), so I cannot say whether the time goes to a specific fallback kernel, TLB/page-table effects, or latency-bound matvecs. Happy to run any diagnostic build/flags a maintainer suggests — the model file and repro are cheap (modulo the ~80min deep bench run).

Relationship to Existing Issues

Steps to Reproduce

  1. Download Ternary-Bonsai-2-27B-PTQ1_0.gguf from prism-ml/Ternary-Bonsai-2-27B-gguf.
  2. Official CUDA build is enough (no special flags):
    llama-bench.exe -m Ternary-Bonsai-2-27B-PTQ1_0.gguf -ngl 99 -fa on -p 512 -n 32 -r 1 -o md
    llama-bench.exe -m Ternary-Bonsai-2-27B-PTQ1_0.gguf -ngl 99 -fa on -p 512 -n 32 -r 1 -o md -d 154855
  3. Compare the tg32 rows (44.68 vs 8.10). Expect ~80min wall for the deep run (all prefill).

Expected: modest depth decay (cf. 2×3090 non-repro in #27623: 40.92 → 28.68 from d0 to d131072).
Actual: ~5.5x decode and ~21x prefill collapse at d154855.

Logs

Logs
$ llama-bench.exe -m Ternary-Bonsai-2-27B-PTQ1_0.gguf -ngl 99 -fa on -p 512 -n 32 -r 1 -o md
| qwen35 27B PTQ1_0 - 1.75 bpw ternary (group 128) |   5.53 GiB |    26.90 B | CUDA       |  99 |   1 |           pp512 |        464.81 ± 0.00 |
| qwen35 27B PTQ1_0 - 1.75 bpw ternary (group 128) |   5.53 GiB |    26.90 B | CUDA       |  99 |   1 |            tg32 |         44.68 ± 0.00 |
build: d8f26eec7 (10683)

$ llama-bench.exe [same] -d 154855
| qwen35 27B PTQ1_0 - 1.75 bpw ternary (group 128) |   5.53 GiB |    26.90 B | CUDA       |  99 |   1 | pp512 @ d154855 |        21.87 ± 0.00 |
| qwen35 27B PTQ1_0 - 1.75 bpw ternary (group 128) |   5.53 GiB |    26.90 B | CUDA       |  99 |   1 |  tg32 @ d154855 |          8.10 ± 0.00 |
build: d8f26eec7 (10683)

Note: binary reports build 10683 (PrismML fork, required for ternary weights — stock builds reject PQ2_0/PTQ1_0 files). The fork is at a 9/9 upstream base; the decode path exercised here is upstream code. Happy to re-run on a stock build with a dense qwen35-family model if a maintainer considers the ternary aspect relevant — but the symptom shape matches dense-model reports #27623/#27734 too closely for the weights to be the driver.

dmon snapshot during deep decode (sm 1-2%, mem ~40%, pclk ~400MHz both sides — compute, bandwidth and clocks are not the differentiator; time is spent elsewhere).


Disclosure: this report was drafted with AI assistance (Muse Spark 1.3). Every number above is a measured value from the listed hardware (the deep bench run took ~80min, exit code 0); no AI-generated code is included. The hypothesis section is labeled as such — the VMM/FA-switch analysis is from reading the b11047 source, the latency-bound pointer is from #26581, neither is claimed as a diagnosis.

Activity

  1. numsu commented on Sep 29, 2026

    @numsu

    Reproduced on another Blackwell system.

    Setup

    • RTX PRO 1000 Blackwell Laptop GPU (8 GB, CC 12.0)
    • Core Ultra 7 265HX / 64 GB RAM
    • CUDA 13.4
    • PrismML llama.cpp 87268f775 (build 10746)
    • Ternary-Bonsai-2-27B-PTQ1_0.gguf

    At shallow context performance is normal:

    Test Speed
    pp256 593.6 t/s
    pp512 589.5 t/s
    pp2048 574.2 t/s
    tg128 ~27 t/s

    At larger context depth, prompt processing collapses to roughly 20–27 t/s:

    prompt processing, n_tokens = 512, t = 19.15 s / 26.74 tokens per second

    A llama-bench -d 70000 run also exhibits the severe slowdown.

  2. 159753a52 commented on Sep 30, 2026

    @159753a52

    A compute-only sanity check says this is a real kernel problem, not attention FLOPs catching up.

    Bonsai 2 27B has the Qwen3.8 27B layout: 64 layers, 16 with full attention, 48 Gated DeltaNet. For pp512 at depth d, only those 16 layers grow with d. Counting FLOPs per 512-token batch on an RTX 5060 Ti (94.9 FP16 tensor TFLOPS on NVIDIA's spec page):

    depth attention share of FLOPs pp512 relative to d0
    0 ~0% 1.00
    32,768 ~19% ~0.80
    154,855 ~52% ~0.46

    Your d0 is 464.81 t/s, so compute alone predicts roughly 210 t/s at d154855. You measured 21.87, about 10x below that, and numsu sees the same 20–27 t/s floor on an RTX PRO 1000. That fits the "slow path past some KV extent" theory better than a bandwidth or FLOP limit.

    For reference, reading a fresh prompt into an empty cache on this card with Q4_K_M-class kernels, I estimate 7.6–10 s for 8K, 16–21 s for 16K and 33–44 s for 32K for a 27B model of this layout. Your 465 t/s at d0 is about half my ~1,000 t/s estimate, which I'd put down to the PTQ1_0 kernels (not what I calibrated on). The ratios in the table don't depend on that.

    These are estimates: tensor throughput times a share fitted on 32 public llama-bench runs (CUDA/ROCm scoreboards in discussions #15013 and #15021, plus PR #25319), all within ±28% of the measured value. Method and calibration table: [Link removed by moderator]/llm-speed-calculator/ (I built it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions