perf(qwen3-a8w8): optimize decode scheduling and data paths - #810
Conversation
📝 WalkthroughWalkthroughThe Qwen3 14B A8W8 prefill and decode paths now store BF16 KV caches, dequantize INT8 gate/up weights with FP32 scales, and use revised decode quantization and split down-projection accumulation flows. ChangesQwen3 A8W8 cache and MLP dataflow
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Input
participant prefill_layer
participant KVCache
participant Attention
participant DecodeMLP
Input->>prefill_layer: BF16 KV cache and INT8 gate/up weights
prefill_layer->>KVCache: write rotated BF16 K/V
Attention->>KVCache: load BF16 K/V tiles
Attention->>DecodeMLP: pass post-attention normalized activations
DecodeMLP->>DecodeMLP: quantize activations and apply gate/up scales
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the Qwen3 14B decode and prefill layers to store BF16 keys and values directly in the KV cache, eliminating the need for cache scales. Additionally, it quantizes the MLP gate and up projections to INT8, introduces corresponding weight scales, and parallelizes the down projection using K-splits. The review feedback recommends specifying mode="rint" in all pl.cast calls converting FP32 to BF16 to ensure standard round-to-nearest-even behavior and avoid precision loss on Ascend hardware.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
models/qwen3/14b/decode_layer_a8w8.py (1)
66-94: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd coverage invariants for the split down-projection constants.
The new down-proj partitioning silently assumes
DOWN_ON % DOWN_DUAL_PAIR == 0andINTERMEDIATE % DOWN_K_SPLITS == 0. The writers indown_dual_projcoverDOWN_DUAL_ON * DOWN_DUAL_PAIRcolumn tiles (DOWN_DUAL_ON = DOWN_ON // DOWN_DUAL_PAIR), whiledown_cast_residualreads allDOWN_ONtiles. IfDOWN_ONwere odd (orINTERMEDIATEnot divisible byDOWN_K_SPLITS), the truncating integer division would leave trailing tiles/rows ofdown_acc_partsunwritten and produce silently wrong output. Both hold for current dims, but this matches the existing defensive assert style on Line 94.🛡️ Proposed defensive asserts
-assert DOWN_TN == 256 and DOWN_TK == 512 and DOWN_K_SLICE % DOWN_DUAL_L0_K == 0 +assert DOWN_TN == 256 and DOWN_TK == 512 and DOWN_K_SLICE % DOWN_DUAL_L0_K == 0 +assert HIDDEN % (DOWN_DUAL_PAIR * DOWN_TN) == 0 and INTERMEDIATE % DOWN_K_SPLITS == 0🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@models/qwen3/14b/decode_layer_a8w8.py` around lines 66 - 94, Extend the existing constant-validation asserts near DOWN_TN and DOWN_TK to require INTERMEDIATE divisibility by DOWN_K_SPLITS and DOWN_ON divisibility by DOWN_DUAL_PAIR. Keep the checks tied to the down-projection constants used by down_dual_proj and down_cast_residual, preserving the current assert style.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@models/qwen3/14b/decode_layer_a8w8.py`:
- Around line 66-94: Extend the existing constant-validation asserts near
DOWN_TN and DOWN_TK to require INTERMEDIATE divisibility by DOWN_K_SPLITS and
DOWN_ON divisibility by DOWN_DUAL_PAIR. Keep the checks tied to the
down-projection constants used by down_dual_proj and down_cast_residual,
preserving the current assert style.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c71609d0-1b77-4c9c-91d3-98dd33c2714d
📒 Files selected for processing (2)
models/qwen3/14b/decode_layer_a8w8.pymodels/qwen3/14b/prefill_fwd_a8w8.py
8b34511 to
c1793df
Compare
c4e14b5 to
1915a3d
Compare
abc0474 to
ea5108a
Compare
Reduce Qwen3-14B A8W8 decode TPOT from the historical ~56 ms/token baseline to ~42 ms/token. Batch activation quantization, reduce output-projection task fanout, remove Gate/Up multi-K-split aggregation, and parallelize Down Projection to lower scheduling overhead. Keep Gate/Up weights in native INT8 with dynamic activation quantization, and use BF16 KV caches to eliminate per-token cache quantization and per-row attention dequantization. Update prefill for the shared INT8 Gate/Up weights, remove the unused KV scale tensors from the kernel interfaces, and use explicit RNE semantics for all newly introduced FP32-to-BF16 narrowing sites. Validated with the decode DSL compile test, targeted serving integration tests, and 48-token end-to-end generation on A2/A3 hardware. Refs: hw-native-sys#665
Summary
Reduce Qwen3-14B A8W8 decode TPOT from the historical ~56 ms baseline to ~42 ms/token (about 25% faster) by optimizing both task scheduling and the decode data path.
Related to #665.
What changed
Decode scheduling
Gate/Up INT8 path
BF16 KV cache
Performance
Measured with Qwen3-14B-W8A8 using a 48-token deterministic generation test:
Recent device-0 integration runs measured 42.0-42.4 ms/token. A strict same-environment experiment measured 46.1 → 41.7 ms/token for the data-path changes alone.
Test configuration:
介绍一下北京故宫max_new_tokens=48max_seq_len=256temperature=0top_p=1The generated output remained coherent with no repeated-output regression.
Validation
mode="rint"to match RNE semantics.Integration note
This kernel ABI requires the matching serving-side update to: