Skip to content

Qwen3-14B

models/qwen3_14b/ implements the HuggingFace Qwen3-14B checkpoint: a BF16 prefill and decode pair with the serving contract, plus A8W8 and TurboQuant variants and the sampling components. Together with V4-Flash MTP it is one of the two trees wired up for full pypto-serving integration.

Deployment configuration

constants.py is the single source of the model shape and of every constant that is part of the external ABI; config.py adds the pl.dynamic dimensions the kernel signatures bind.

Deployment property Value
Layers / heads 40 layers, 40 attention heads over 8 KV heads (GQA), head_dim = 128
Hidden / MLP 5120 hidden, 17,408 intermediate
Vocabulary 152,064 padded, 151,936 real
Context length up to 4096 positions, paged in 128-token pages (seq_tile)
Decode batch the pipeline is padded to 16 rows (batch_pad); any public batch ≥ 1 runs as ceil(batch / 16) row windows
Parallelism single card — no TP, no EP, no DP
Platform Ascend A2/A3; the BF16 decode attention is A2/A3-only because it calls a CANN operator
Precision, main path BF16 weights and KV cache, FP32 inter-layer residual carry, FP32 RMSNorm weights
Precision, A8W8 path INT8 weights with per-token INT8 activations, in the *_a8w8 entries only
Serving contract.py registers the BF16 prefill and decode stages for pypto-serving

batch_pad is a throughput knob, not a capacity limit: a wider pad does more rows per weight read, but raising it means bumping kMaxBatch and the metadata length arrays on the CCE side, regenerating the RoPE body, and re-validating the M-dimension tiling. Past 16 rows correctness scales and cost does not amortize — windows serialize on the single paged-attention metadata/workspace pair and re-read the weights.

Model structure, top down

prefill_fwd

prefill_fwd.py loops the same fused layer body over all 40 layer rows of the flattened weight tensors:

prefill_fwd   per layer   input RMSNorm → Q/K/V projection → RoPE
                          → KV cache update → causal attention
                          → output projection → post-attention RMSNorm
                          → SwiGLU MLP → residual
              tail        rms_lm_head (final RMSNorm + LM head)

Every batch-dependent signature dim is a pl.dynamic variable (BATCH_DYN, PREFILL_TOKENS_DYN, KV_CACHE_ROWS_DYN, BLOCK_TABLE_FLAT_DYN), so one compiled program serves any batch that fits the host KV cache. Inputs are packed token-major (T = sum(chunk_lens), no [batch, max_seq] padding), the embedding is gathered on device, and hidden state lifetime is bounded by processing 128-token windows.

decode_fwd

decode_fwd.py is a single fused device-side step:

decode_fwd    _token_embed_inline (embed the previously sampled id)
              ×40  _decode_layer
                     RMSNorm → QKV projection → Q/K norm → RoPE
                     → KV cache write
                     → paged_attention_cce  (CANN FusedInferAttentionScore)
                     → output projection → post-attention RMSNorm
                     → SwiGLU MLP → FP32 residual
              rms_lm_head → _greedy_sample_inline → sampled_ids_out

The attention stage is the one part that is not pypto-generated: paged_attention_cce.py binds the hand-written CCE kernel under kernels/paged_attention_cce/ through pl.jit.extern. Its public ABI matches vLLM — Q/O are active TND and the flat paged K/V buffers hold BSND bytes ordered [page, token, kv_head, dim]. The kernel embeds a copied pypto/ptoas codegen artifact, kernel/rope_qkv_generated.hpp, as its in-kernel phase 0; rope_qkv_regen.py is the standalone program that regenerates that header — it is compile-only and never reaches a device.

decode_fwd_layers in the same file is the same fused body over a contiguous layer chunk with no LM head, for callers that compose the stack externally.

Quantized and compressed variants

decode_layer_a8w8   one A8W8 decode layer (INT8 weights, per-token INT8 activations)
prefill_fwd_a8w8    the A8W8 full-layer prefill, imported rather than run directly
prefill_tq_draft    prefill_layer_tq → turboquant_kv_quantize (PolarQuant)
                                     → turboquant_qjl_k       (QJL, K only)
decode_tq_draft     the TurboQuant decode counterpart, built on decode_fwd
decode_ssn_draft    serial 4D-blocked single-layer decode

turboquant_kv.py holds the Lloyd-Max codebook computation, the prefill KV quantization, and the QJL K-residual quantization. The three *_draft.py files are work in progress and excluded from CI.

Sampling and output

rms_lm_head (final RMSNorm plus the LM-head projection, in vocab chunks of 512) is shared by both forwards. greedy_sample.py and topk_select.py are the standalone sampling components; decode_fwd inlines its own greedy sample so a step produces the next token without a host round trip.

Serving glue

weights.py prepares the kernel-ready weight layout and contract.py — colocated with the entry points it names — registers the prefill, decode, and greedy-sample stages, their compile-time argument builders, and the ABI constants an external runtime needs.

Files

Group Files
Forwards decode_fwd.py, prefill_fwd.py
Quantized variants decode_layer_a8w8.py, prefill_fwd_a8w8.py, turboquant_kv.py
CCE attention paged_attention_cce.py, kernels/paged_attention_cce/, rope_qkv_regen.py, test_paged_attention_cce.py
Output and sampling rms_lm_head.py, greedy_sample.py, topk_select.py
Configuration and serving constants.py, config.py, weights.py, contract.py
Drafts (excluded from CI) decode_ssn_draft.py, decode_tq_draft.py, prefill_tq_draft.py

constants.py, config.py, contract.py, weights.py, rms_lm_head.py, paged_attention_cce.py, turboquant_kv.py, and prefill_fwd_a8w8.py have no __main__ block and are imported rather than run. Which entry points CI schedules is defined by the daily model workflow.