Skip to content

Optimize ROCM Prefill and Decode - #18

Merged
gaetan-puleo merged 31 commits into
masterfrom
import/fork-master-optimizations
Sep 5, 2026
Merged

Optimize ROCM Prefill and Decode#18
gaetan-puleo merged 31 commits into
masterfrom
import/fork-master-optimizations

Conversation

@gaetan-puleo

@gaetan-puleo gaetan-puleo commented Sep 1, 2026

Copy link
Copy Markdown

Summary

This PR adds guarded ROCm/HIP inference optimizations for RDNA3.5 and Strix Halo systems, including the Ryzen AI Max+ 395 with Radeon 8060S graphics.

Main improvements:

  • RDNA3.5 MMQ/MMVQ configurations and register prefetching
  • Compact MUL_MAT_ID execution with quant- and geometry-specific tile selection
  • Fused activation quantization for Q8_0 and Q6_K decode
  • Optimized MoE routing, weighted expert reduction, and shared-expert operations
  • DPP and tiled Gated DeltaNet kernels for prefill and decode
  • Fused hyper-connection stream mixing and combination
  • D=256 WMMA Flash Attention for deep-context prefill
  • Device-resident speculative decoding checkpoints
  • Qwen4exp QSA and PLE graph optimizations

Architecture, shape, layout, aliasing, and graph-closure guards preserve existing behavior on unsupported devices and tensor configurations.

Correctness

The complete PR was reviewed by five independent agents, followed by a separate source-level verification of every reported finding.

Confirmed issues were fixed, including:

  • Partial routed-MMQ tile bounds
  • Uninitialized MMVQ prefetch reads
  • All-zero fused SwiGLU quantization
  • Long-context GDN index overflow
  • Fusion closure and external-consumer handling
  • Deterministic MoE ID and weight ordering

Final validation:

  • 14,050/14,050 ROCm backend tests passed
  • 61/61 models and quants completed model-level validation
  • 61/61 tokenizations matched latest master
  • 50/61 greedy generations matched latest master exactly
  • The remaining 11 were independently reviewed and classified as valid floating-point accumulation sensitivity, with no incorrect quantization, routing, state handling, or graph semantics found
  • Qwen3.5 and Qwen3.8 CPU graph spot checks were byte-identical to master
  • Qwen3.8 controlled one-token GPU logits were also byte-identical

Performance

Representative measured improvements include:

  • Qwen3.5 D=256 prefill at depth 32k: approximately +28%
  • Qwen3.5 D=256 prefill at depth 64k: approximately +37%
  • Qwen3.6 fused GDN decode: approximately +9-11%
  • Qwen3.6 tiled GDN prefill: approximately +8-11%
  • Selected routed Q4_K/Q5_K/Q6_K kernels: approximately 20-54% lower kernel time
  • Ling PP2048: approximately +7-11%, depending on quant
  • Qwen3.5-0.8B Q8_K_XL versus latest master:
    • PP2048: 8704.46 -> 9561.11 tok/s, +9.8%
    • TG128: 130.67 -> 131.85 tok/s, +0.9%

Benchmark Coverage

Benchmarked on gfx1151 with ROCm 7.14:

  • 61 models
  • 1,525 configurations
  • PP512, PP1024, PP2048, PP4096, and TG128
  • Depths 0, 12k, 32k, 64k, and 128k
  • Dense models: -ub 512
  • MoE models: -ub 2048
  • Common options: -b 2048 -fa 1 -ngl 999 --load-mode mmap -r 4
  • First repetition discarded; results use repetitions 2-4

Interactive and raw results:

https://gaetan-puleo.github.io/strix-halo-pull-18-results/

The branch includes latest master b212548e0.

Final head: 756f0d443

Tune MMQ and MMVQ scheduling, add routed expert kernels, share Q8_1 activation quantization, and add measured four-column kernels for quant formats that outperform the generic path on gfx1151.

Assisted-by: OpenCode
Add gfx1151 paths for paired matmuls, SwiGLU quantization, expert aggregation, contiguous binary operations, transposed concat, normalization, and conversion operations.

Assisted-by: OpenCode
Dequantize Q8 KV data directly in tiles, specialize GQA decode grouping, reduce D256 register pressure, and reuse masks across grouped heads on gfx1151.

Assisted-by: OpenCode
Increase gfx1151 column parallelism, tile KDA inputs in LDS, preserve recurrent graph ordering, and schedule compatible Qwen projections together.

Assisted-by: OpenCode
Use full MTP rollback checkpoints and retain speculative sequence state on the device to avoid unnecessary host transfers.

Assisted-by: OpenCode
Permit backends to allocate quantized CONCAT outputs with row padding under the expanded allocation-size invariant.

Assisted-by: OpenCode
Add exact-batch, quantized matmul, routed expert, Flash Attention, concat, normalization, and Gated DeltaNet regression coverage for the optimized gfx1151 paths.

Assisted-by: OpenCode
Keep batch-one MMVQ on one wave for all quant types. Wider wave counts changed FP32 reduction order, causing non-identical logits for Q2-Q5 and deterministic decode divergence in the four-corpus correctness matrix.

Assisted-by: OpenCode
@dzannotti

Copy link
Copy Markdown

Thanks for putting this together — the RDNA3.5 work here is nice, and the 551ce30fe follow-up (keeping batch-one MMVQ on one wave to preserve exact FP32 reduction order) is a good catch and exactly the kind of trade we'd want made that way.

We had a Strix Halo box free tonight, so we ran an independent A/B to help fill in the Measurements section. Sharing the raw numbers in case they're useful — happy to re-run anything with different settings.

Setup

Device:     Ryzen AI Max+ 395 / Radeon 8060S (gfx1151)
Memory:     128 GB LPDDR5X, UMA; 120832 MiB reported addressable
Kernel:     7.1.8-200.fc44.x86_64
Backend:    ROCm 10.0.0 (container), AMDGPU_TARGETS=gfx1151
Build:      Release, GGML_HIP=ON, GGML_HIP_NO_VMM=ON
Baseline:   732484c20 (halo master, this PR's merge-base)
Also ran:   6c84c7d5d (upstream base), 3466812d1 (upstream master today)
Change:     551ce30fe
Models:     Qwen3.8-27B-Q4_K_M; Ornith-1.5-35B-A3B-Q4_K_M
Settings:   -fa on -lm none -b 2048 -ub 2048 -r 3 -ngl 99

All four trees were built and run in the same session, back to back, with nothing else on the GPU.

Prompt processing (tok/s, ± stddev over 3 reps)

model test upstream base upstream today halo master this PR vs halo vs upstream
Qwen3.8-27B Q4_K_M pp9000 329.93±0.29 329.81±0.16 331.33±0.36 396.70±0.73 1.197x 1.203x
Qwen3.8-27B Q4_K_M pp16000 310.71±0.04 310.84±0.14 311.38±0.03 371.41±0.09 1.193x 1.195x
Ornith-1.5-35B-A3B Q4_K_M pp9000 1268.80±2.93 1239.85±1.69 1263.92±6.64 1793.15±3.65 1.419x 1.446x
Ornith-1.5-35B-A3B Q4_K_M pp16000 1176.65±1.44 1153.24±2.50 1175.74±1.35 1629.05±4.94 1.386x 1.413x

Token generation (tok/s)

model test halo master this PR ratio
Qwen3.8-27B Q4_K_M tg128@9000 10.78±0.07 10.83±0.02 1.004x
Qwen3.8-27B Q4_K_M tg128@16000 10.54±0.09 10.61±0.02 1.006x
Ornith-1.5-35B-A3B Q4_K_M tg128@9000 60.55±0.49 60.77±0.46 1.004x
Ornith-1.5-35B-A3B Q4_K_M tg128@16000 58.27±0.48 58.55±0.45 1.005x

Correctness

test-backend-ops -b ROCm0 on 551ce30fe: 13478/13478 tests passed, Backend ROCm0: OK.

We also read through the FA common_mask reuse, since sharing one mask row across grouped heads looked like the highest-risk change. It looks correct to us: slope is pinned to 1.0f whenever ncols2 != 1, so there's no ALiBi-slope hazard, and with ncols=32/ncols2=8 each warp's cpw columns stay inside a single token group. Noting it mainly so the reasoning is written down somewhere.

Observations

  • The prefill win is real and very reproducible — stddev is under 0.7% on every cell.
  • The MoE model benefits about twice as much as the dense one (~1.40x vs ~1.20x), which matches where the diff is concentrated: routed-expert kernels, mul_mat_id tile selection, expert aggregation. Might be worth leading with an MoE model in the PR description, since that's where the change shines.
  • On ROCm the gains are flat between 9k and 16k rather than growing with depth. The baseline doesn't collapse at depth here (27B only loses ~6% from 9k to 16k), so there isn't much depth-scaling headroom to recover — unlike the pattern reported on the Vulkan side in Strix Halo Vulkan stack: FA KV-quant, mmid, dense GEMM, delta-net prefill, DSv4 sparse attention #17.
  • Decode is 1.00x across the board, which we read as the expected consequence of 551ce30fe. Worth stating explicitly in the description so nobody expects a tg change.
  • halo master measures within 1.00x of upstream on all eight cells for these two models, so on ROCm essentially all of the improvement is coming from this PR.

Two small questions

Both look unrelated to prefill and we didn't exercise them, so flagging rather than asserting anything:

  1. common/common.h drops COMMON_SPECULATIVE_TYPE_DRAFT_MTP from need_n_rs_seq(). Is that intended to pair with the LLAMA_STATE_SEQ_FLAGS_ON_DEVICE change in server-context.cpp?
  2. GGML_OP_CONCAT is added to ggml_backend_op_alloc_size_may_expand. Is the padded-allocation invariant guaranteed for every backend that can see a quantized CONCAT, or only the HIP path?

What we did not verify: speculative/MTP decode paths, the server changes, Vulkan, any non-gfx1151 device, and perplexity (we ran op-level correctness only, not end-to-end output quality).

Disclosure: these benchmarks were run and this comment was written by Claude Code, on hardware and at the request of @dzannotti. Numbers are machine-generated but reproducible with the command line above; happy to share the raw JSON.

Use 32 waves for the production non-KDA H32/S128 Gated DeltaNet shape on gfx1151. This reduces kernel latency by 1.34% and improves Qwen3.6 IQ3_XXS PP2048 by up to 0.97% while preserving exact four-corpus logits and decode output.

Assisted-by: OpenCode
With 64 KiB of LDS per CU only 1-2 MMQ blocks are resident on gfx1151, so
the global-load latency of every 256-wide K iteration is exposed (PMC:
VALU busy 6%, 53% of wave-cycles waiting on data). Stage the next
iteration's activation tile (and, for Q8_0, the weight tile) in registers
while the current one is consumed. Same loads, same LDS layout, same
arithmetic: outputs are bit-identical to the previous kernels.

Whitelisted to the specializations where the extra registers do not
spill: Q8_0 J48/J128, Q6_K J32, Q5_K J32, Q4_K J48, IQ2_S/IQ3_XXS J128
(all others keep the original loop; no spill regression in any kernel).

test-backend-ops perf, MUL_MAT_ID 256 experts top-8, 2048 tokens (includes
the quantize/ids overhead): Q8_0 gate/up 2717 -> 2551 us, Q6_K 3600 -> 3481,
Q5_K 2571 -> 2510, Q4_K 2776 -> 2701, IQ2_S 3830 -> 3743, IQ3_XXS 3544 -> 3457.
Also adds these production shapes to the perf test list.
Two exact-preserving changes to the gated delta net recurrence:

* warp_reduce_sum compiled to 5 dependent ds_bpermute round trips per
  reduction (10 per token); on RDNA3/4 use v_permlanex16 + DPP row_xmask
  with the same xor-16/8/4/2/1 pairing, so results are bit-identical.

* For the non-KDA S=128 prompt-processing path add a tiled kernel: q/k/g/
  beta/v of a 16-token tile are loaded cooperatively into LDS and each warp
  owns 4 state columns, so k/q are read once per token for 4 columns and 4x
  fewer waves compete for issue slots (the kernel was issue-bound with
  ~50 VALU per token per wave for 4 state elements). FMA contractions are
  spelled out to match the original kernel's code generation.

Bit-identical to the previous kernels on H=32/64, T=1/17/64/2048
(op-level dump comparison); test-backend-ops GATED_DELTA_NET 43/43.

gfx1151, test-backend-ops perf: H32 T2048 5029 -> 2350 us, H64 T2048
9289 -> 4533 us, H32 T64 158 -> 93 us, T=1 13.3 -> 12.6 us.

Together with the MMQ prefetch, Qwen3.6-35B-A3B PP2048 (llama-bench
-b 2048 -ub 2048): Q8_0 +10.9%, UD-Q6_K_XL +9.4%, UD-Q4_K_XL +9.2%,
UD-IQ3_XXS +7.6%.
The fused weighted expert sum used a volatile product to prevent FMA
contraction and preserve the unfused graph's rounding. On AMD this spills
every product to scratch. Use explicit v_mul_f32/v_add_f32 instructions to
retain the exact operation order without scratch traffic, and process four
adjacent channels per thread to amortize token indexing and weight loads.

The fusion is restricted to contiguous 2048-wide Qwen tensors, so all
float4 accesses are aligned. A direct old/new kernel comparison across
1,048,576 random outputs is bit-identical; MUL and ADD backend tests pass.

gfx1151 weighted_expert_sum_f32<8>: 813 -> 689 us (-15.3%), scratch 8 ->
0 bytes. Qwen3.6-35B-A3B PP2048 improves by 0.35-1.36% across Q8_0,
Q6_K_XL, Q4_K_XL, and IQ3_XXS.
… RDNA3.5

Token generation on gfx1151 launches ~1480 kernels per token with a ~2 us
gap between each, so the quantize_q8_1 kernel in front of every MMVQ costs
about as much as the quantization itself. Add mul_mat_vec_q_fq for the
single-column Q8_0 path: a block of 16 waves (one output row per wave)
quantizes the activation vector once into shared memory, replicating
quantize_q8_1 exactly (same amax, IEEE division and roundf), and then runs
the per-wave K loop of mul_mat_vec_q in the same order against it, with two
K iterations of weight loads issued before the quantization and barrier.

The two-kernel path is kept for other types, batched decode, and unaligned
activations. Dense and MUL_MAT_ID (fused SWIGLU and down) outputs at all
Qwen3.6 decode shapes are byte-identical to the previous path; MUL_MAT and
MUL_MAT_ID backend tests pass. 16 waves per block and a prefetch depth of 2
were the fastest of the measured variants (8/32 waves, depth 1/4/8/16).

Qwen3.6-35B-A3B TG128 on gfx1151: Q8_0 48.83 -> 50.99 t/s (+4.4%) at depth
0 and 42.88 -> 44.47 (+3.7%) at 32K; UD-Q8_K_XL 48.04 -> 49.61 (+3.3%) and
42.24 -> 43.38 (+2.7%). Adds Qwen3.6 batch-1 perf cases to test-backend-ops.

Assisted-by: OpenCode
For a single decode token, every linear-attention layer runs 14 graph nodes
between the qkv projection and the output gate: concat with the conv state,
conv state shift into the cache, state gather, causal conv, SiLU, two L2
norms, alpha bias/softplus/A gate, beta sigmoid, the recurrence, the state
copy into the cache, and the gated RMS norm. On gfx1151 these are 11 small
kernels (~32 us) separated by ~2 us launch gaps, about 50 us per layer.

Match this chain in the CUDA backend and run it as two kernels: one block
per value head computes the conv + SiLU of its q/k/v channels (shifting the
conv state straight into the cache), the L2 norms, gate and beta, and the
recurrence with the state read from and written to the cache in place; a
second tiny kernel applies the gated RMS norm (its destination may alias
inputs of the first kernel that other blocks are still reading). Every op
is spelled with the operation order and FMA contraction of the kernel it
replaces, so the outputs, conv state and recurrent state are byte-identical
to the unfused graph (checked in place and across cache rows). The fusion
requires n_tokens == n_seqs == 1 and S == 128 and falls back otherwise,
including the first token of a sequence where the state reset sits inside
the chain.

Qwen3.6-35B-A3B TG128 on gfx1151 (with the fused activation quantization):
Q8_0 48.74 -> 54.18 t/s (+11.2%) at depth 0 and 42.62 -> 46.62 (+9.4%) at
32K; UD-Q8_K_XL 48.02 -> 52.69 (+9.7%) and 42.23 -> 45.71 (+8.2%).

Assisted-by: OpenCode
ggml_cuda_op_weighted_expert_sum always staged its result in a pool buffer
and copied it into the destination, because the graph allocator places the
output over the expert or routing-weight buffers that die inside the fused
range (in Qwen3.6 decode the eight routing weights sit inside the output).
That copy is one more launch per layer in a decode step that is dominated
by launch gaps.

When the whole sum fits in one block (n_embd/4 * n_tokens <= 1024, i.e.
decode), launch a single block and place a barrier between the reads and
the store, so the output can be written in place whatever it aliases. The
per-thread arithmetic is unchanged; larger grids keep the staging copy.
Greedy 120-token generations with Qwen3.6-35B-A3B are identical.

Qwen3.6-35B-A3B UD-Q8_K_XL TG128 on gfx1151: 52.69 -> 52.91 t/s.

Assisted-by: OpenCode
The shared expert output is gated by sigmoid(w . y) with a 2048-wide f32
weight vector: a one-row mul_mat_vec_f, a sigmoid and the already fused
mul + add + add, i.e. three launches per layer in decode. Match the five
nodes and run them in one single-block kernel: the dot product repeats the
per-thread fma chain and two-level warp reduction of mul_mat_vec_f<256>,
the sigmoid is the same expression, and the elementwise tail uses explicit
v_mul_f32/v_add_f32 like shared_mul_add_f32 (plain __fmul_rn/__fadd_rn are
contracted into an fma by the HIP compiler). One block reads the whole
activation vector before the reduction barrier, so the output may alias it.
Outputs are byte-identical to the unfused graph.

Qwen3.6-35B-A3B UD-Q8_K_XL TG128 on gfx1151: 52.91 -> 53.42 t/s.

Assisted-by: OpenCode
mul_mat_vec_f loops over K with a runtime trip count, so the compiler keeps
one 8-byte load per lane in flight and waits for it every iteration. The f32
MoE router (2048 x 256) of Qwen3.6 ran at 131 GB/s on gfx1151 because of
this. For the single-column, unfused f32 path on HIP, load four K iterations
of weights and activations before consuming them, in the same accumulation
order; the remainder loop is unchanged. Outputs are byte-identical.

gfx1151 Qwen3.6-35B-A3B decode: router matvec 16.0 -> 10.5 us, alpha/beta
projections 3.2 -> 2.6 us; UD-Q8_K_XL TG128 53.42 -> 54.16 t/s.

Assisted-by: OpenCode
mul_mat_vec_f picks a 256-thread block for K=512, so every wave issues a
single 4-byte bf16 load and then reduces; on gfx1151 the bf16 MoE down
projections of Unsloth UD quants (8 x 2048 rows of K=512) run at ~145 GB/s.
Add a wave-per-row kernel that emulates the 256-thread block: lane l
accumulates the eight virtual warps' lane l in the same fma order, reduces
each with the same butterfly and combines them exactly like block_reduce
over lanes 0..7 (including the zero-lane adds), with eight loads in flight
per lane. Used on RDNA for unfused single-column bf16 with K <= 512; for
longer rows the lower wave count loses, so the standard kernel stays.
Outputs are byte-identical (dense and MUL_MAT_ID, odd shapes included).

gfx1151 Qwen3.6-35B-A3B UD-Q8_K_XL: bf16 down projection 93 -> 67 us,
TG128 54.16 -> 54.54 t/s.

Assisted-by: OpenCode
Extend mul_mat_vec_q_fq beyond Q8_0 with a generic path that runs the
per-wave vec_dot loop of mul_mat_vec_q<type, 1> against the shared-memory
activations, and replicate the Q8_1 block sum of quantize_q8_1 in the exact
butterfly order for types that read it. Q6_K (scale only) is enabled: it is
byte-identical (dense and MUL_MAT_ID) and measured +0.8% on Qwen3.6-35B-A3B
UD-Q6_K_XL TG128 (56.64 -> 57.09 t/s). Q4_K/Q5_K are exact as well but
neutral without weight prefetch, so they stay on the two-kernel path.

Assisted-by: OpenCode
…NA3.5

For mul_mat_id the MMQ tile width J is chosen from ncols_max, which is the
total number of token-expert pairs, so the generic path always picks J=128.
With 256 experts and 8 active the per-expert row count is only
n_tokens/32 (16/32/64 rows for ubatch 512/1024/2048), so 50-87% of every
tile was padding. The RDNA3.5 specializations that fixed this were gated on
exact shapes and on 32/64 rows per expert, i.e. only ubatch 1024/2048, and
only Q8_0/Q6_K used the routed-compact kernel.

Replace the shape-specific paths with a per-type rule on rows per expert,
and route all of them through mul_mat_q_routed_compact:
  Q8_0:            16/32/64 rows -> J=48
  Q4_K/Q5_K/Q6_K:  16 rows -> J=32, 32 rows -> J=48, 64 rows -> J=32
Values measured on gfx1151 for the 2048x512 and 3072x1024 expert shapes;
the previous choices (Q8_0 J48, Q6_K J32 at 64 rows) are unchanged, Q4_K at
64 rows moves from tiled J48 to compact J32 (-7% up-proj, -31% down-proj),
Q5_K at 64 rows from tiled J32 to compact J32 (-31% down-proj).

MUL_MAT_ID kernel time, 256 experts, 512x2048 / 2048x512, n=512 tokens:
  Q8_0  2347/2490 us -> 1517/1518 us
  Q6_K  3918/3931 us -> 1344/1300 us
  Q5_K  2606/2607 us -> 1087/1084 us
  Q4_K  2561/2538 us -> 1026/ 991 us
n=1024: Q6_K -54%, Q5_K -50%, Q4_K -54% (Q8_0 unchanged).

Qwen3.6-35B-A3B on gfx1151, llama-bench -b 2048 -ub 2048:
  UD-Q8_K_XL  PP512  1437 -> 1722 t/s (+19.8%); PP1024/2048/4096 unchanged
  UD-Q4_K_XL  PP512  1439 -> 2086 t/s (+44.9%), PP1024 +25.5%, PP2048 +3.5%
  UD-Q8_K_XL  default ubatch 512: PP512 +22.1%, PP2048 +22.4%
TG unchanged. test-backend-ops MUL_MAT_ID: 906/906 passed (adds 16/32
rows-per-expert eval cases and qwen3.6 perf cases).
The previous rule only matched exactly 16/32/64 rows per expert (ubatch
512/1024/2048 with 8 active experts); any other ubatch size, and the
remainder ubatch of every prompt, still fell back to the generic J=128 tiled
grid, which launches ncols_max/J*n_experts mostly-empty blocks.

Use ranges instead, and allow the routed-compact kernel for every selected J:
  Q8_0:            rows <= 12 -> J16, <= 64 -> J48, else J128
  Q4_K/Q5_K/Q6_K:  rows <= 12 -> J16, <= 24 -> J32, <= 48 -> J48,
                   <= 64 -> J32, else J128
The 16/32/64 choices are unchanged from the previous commit. Ranges were
picked from a J sweep over 64..4096 tokens on gfx1151 and validated
end-to-end.

Qwen3.6-35B-A3B on gfx1151, -b 2048 -ub 2048 unless noted, vs cutoff:
                   PP128   PP256   PP384   PP768   PP1536  PP4096@ub4096
  UD-Q8_K_XL      +39.7%  +61.1%  +21.5%  +17.2%  +12.4%   +3.9%
  UD-Q4_K_XL      +93.5%  +75.9%  +60.1%  +39.1%  +18.4%   +2.4%
(PP512/1024/2048 as in the previous commit.)
test-backend-ops MUL_MAT_ID: 914/914 passed (adds 4- and 128-row cases).
On gfx1151 the D=256 flash-attention path always fell back to the tile
kernel, which is FMA-bound (~8 TFLOPS) and dominates prefill time at deep
context (68% of PP time at 32k, 81% at 64k for Qwen3.5-0.8B).

- Enable AMD WMMA device code for DKQ=256 (previously DKQ>128 was excluded).
- Add a RDNA ncols=64 D=256 config tuned for the 256-VGPR budget:
  nwarps=8, nstages=1, nbatch_fa=32, nbatch_K2=128, nbatch_V2=64,
  nbatch_combine=64. Naive settings spill heavily and lose to the tile kernel.
- Dispatch to MMA only when Q->ne[1]*gqa_ratio_eff > 32; the tile kernel is
  still faster for small batches (nb 4/8).
- Fix a latent tile_Q reuse race in the combine loop: when
  DV/2 > nbatch_combine, warps read tile_Q columns written by other warps
  and the buffer is rewritten by the next batch without a barrier.

Qwen3.5-0.8B UD-Q8_K_XL, gfx1151, PP512:
  depth 0:     9361 -> 9473 t/s  (+1.2%)
  depth 32768: 3211 -> 4105 t/s  (+27.9%)
  depth 65536: 1927 -> 2632 t/s  (+36.6%)
TG unchanged. test-backend-ops FLASH_ATTN_EXT: 2939/2939 passed.
Constrain architecture-specific paths, validate fused GDN use counts, narrow graph synchronization, remove disabled experiments, and strengthen fusion tests.

Assisted-by: OpenCode
@gaetan-puleo
gaetan-puleo marked this pull request as ready for review September 5, 2026 10:00
@gaetan-puleo gaetan-puleo changed the title DRAFT Import/fork master optimizations Import/fork master optimizations Sep 5, 2026
@gaetan-puleo gaetan-puleo changed the title Import/fork master optimizations Optimize ROCM Prefill and Decode Sep 5, 2026
@gaetan-puleo

gaetan-puleo commented Sep 5, 2026

Copy link
Copy Markdown
Author

common/common.h drops COMMON_SPECULATIVE_TYPE_DRAFT_MTP from need_n_rs_seq(). Is that intended to pair with the LLAMA_STATE_SEQ_FLAGS_ON_DEVICE change in server-context.cpp?

This was a fix for correctness on MTP

Nathan uses it in the Vulkan Performance toolbox too

@dzannotti

Copy link
Copy Markdown

ship this! this is awesome

@gaetan-puleo
gaetan-puleo merged commit c7af5c6 into master Sep 5, 2026
7 of 9 checks passed
@gaetan-puleo
gaetan-puleo deleted the import/fork-master-optimizations branch September 5, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants