Repository navigation
ARCH: shared attention/FFN building blocks exist but 0% adopted — new architectures still cost ~1,600 lines each #3422
Description
tl;dr
#3418 named the quant-dispatch duplication (30 files, no single source of truth) as
one reason a new model architecture is expensive to add. It is not the only reason,
and on its own it will not get aprender to "add Qwen3-Next MoE the way llama.cpp adds
it" — llama.cpp's qwen3moe.cpp is 179 lines because architectures are graphs
assembled from shared, quant-agnostic building blocks (build_attn, build_moe_ffn,
build_norm, ggml_rope_ext). This ticket is about that layer in aprender.
The good news, measured just now: aprender already has a version of this layer —
crates/aprender-serve/src/gguf/inference/forward/attention.rs (25 functions:
parallel_multihead_attention_gpu, standard_softmax, online_softmax,
tiled_single_head_attention, reshape_for_parallel_heads, …) and ffn_block.rs
(24 functions). This is not a from-scratch design problem.
The bad news: adoption is zero for the two newest, largest architectures.
Evidence (2026-09-17, origin/main @ 1d7dcc5e9)
$ for f in gguf/inference/forward/{single,forward_qwen35,forward_qwen3_moe,forward_qwen3_moe_gpu,forward_qwen3_moe_traced,forward_cached,gemma_dispatch,ffn_block,batch,batched,encoder_decoder}.rs; do
p="crates/aprender-serve/src/$f"; [ -f "$p" ] && echo "$(grep -vc '^\s*//\|^\s*$' "$p") $f"
done | sort -rn
939 gguf/inference/forward/forward_qwen35.rs
633 gguf/inference/forward/ffn_block.rs
328 gguf/inference/forward/forward_qwen3_moe_traced.rs
321 gguf/inference/forward/forward_qwen3_moe.rs
314 gguf/inference/forward/forward_cached.rs
284 gguf/inference/forward/batched.rs
167 gguf/inference/forward/encoder_decoder.rs
66 gguf/inference/forward/forward_qwen3_moe_gpu.rs
32 gguf/inference/forward/gemma_dispatch.rs
$ grep -cE 'standard_single_head_attention|parallel_multihead_attention|parallel_batched_qk_scores|standard_softmax|online_softmax|tiled_single_head_attention|reshape_for_parallel_heads' \
gguf/inference/forward/forward_qwen35.rs gguf/inference/forward/forward_qwen3_moe.rs
forward_qwen35.rs:0
forward_qwen3_moe.rs:0
$ grep -cE 'ffn_block::|adaptive_ffn::' gguf/inference/forward/forward_qwen35.rs gguf/inference/forward/forward_qwen3_moe.rs
forward_qwen35.rs:0
forward_qwen3_moe.rs:0
Neither of the two newest architecture forwards calls a single function from either
shared module. Both reimplement their own attention inline (forward_attention at
forward_qwen35.rs:896) with their own scaling, softmax, and head-splitting logic.
Also measured, and this is the part #3418 doesn't cover: RoPE is implemented
independently in ~25 files, attention math in 20+, RMSNorm in 38
(grep -rl 'fn.*rmsnorm' crates/aprender-serve/src = 38). Quant-type dispatch (#3418)
is one axis of the duplication; forward-pass logic is a second, larger axis, and it's
the one that actually determines how many lines a new architecture costs.
Not all of it is avoidable duplication. forward_qwen35.rs also has legitimately
novel operators for the Qwen3.5/Next hybrid architecture — delta_rule_recurrence,
causal_conv1d, gated_rmsnorm, apply_partial_neox_rope (gated DeltaNet linear
attention interleaved with full attention) — that have no equivalent anywhere in the
shared modules, because nothing else in the tree needs them yet. llama.cpp had to add
graph ops for the same thing. The claim here is narrower: of that ~1,600-line surface,
some fraction is genuinely new math (unavoidable, needs a slot to plug into) and some
fraction is reimplementation of attention/softmax/head-reshaping that already exists
elsewhere in the same crate (avoidable, and today has zero enforcement stopping it).
Per-backend duplication compounds it. forward_qwen3_moe.rs (CPU, 321 lines) and
forward_qwen3_moe_gpu.rs (GPU, 66 lines) are separate entry points per architecture,
not one graph two backends walk — the same multiplication #3418 described for quant
dispatch, one layer up.
Relationship to #3418 / PP-QUANT-001
#3418 (quant-type dispatch, tracked as PP-QUANT-001, epic #3421) is a prerequisite,
not a substitute: a shared attn_block/moe_ffn_block composition layer needs to be
quant-agnostic to be reusable across architectures, which needs the quant dispatch
table PP-QUANT-001 is building. This ticket is the layer above it.
Ask
A dedicated design + implementation cycle (companion to PP-QUANT-001, own spec) whose
exit criterion is measurable: a new dense or MoE architecture (the next real test case
being Qwen3-Next / Qwen3.8-Flash, already sitting untested per #3418) lands as a small
loader-mapping file plus a forward composed from shared blocks — not a
~1,600-line reimplementation — while the Qwen2.5-Coder GPU-parity measurement
(docs/BEATS.md) shows no regression.
Related
- ARCH: quant-type dispatch is duplicated across ~30 files with no single source of truth — propose a dedicated consolidation release #3418 / PP-QUANT-001 (epic EPIC: PP-QUANT-001 — quant-type dispatch, Phase 0/1 (0.69 train) #3421) — prerequisite: quant-agnostic dispatch table
- P0: dense Qwen3 (Qwen3ForCausalLM) has no current proof it runs — last verification is 7 months stale and CI never covers it #3413 — dense Qwen3 proof-of-life gap, downstream of the same forward-path duplication
crates/aprender-serve/src/gguf/inference/forward/attention.rs,
ffn_block.rs— the existing, under-adopted shared layer
Activity
docs(spec): PP-QUANT-001 — cross-link downstream PP-ARCH-001, close o…
alfredodeza commentedon Sep 17, 2026
Part of #3418 — the forward-pass-duplication half of "new architectures are too expensive to add" (#3418's own scope was quant dispatch only; this is the larger factor, per the comment on #3418).
docs(spec): PP-QUANT-001 — Phase 2/3 gets its real milestone, not "ma…
docs(spec): PP-ARCH-001 — reflect ticket/epic consolidation (#3422)
noahgift commentedon Sep 23, 2026
triage 2026-09-23 (aprender-dd, standing triage owner): milestoned to 0.74.0, theme Any Model (architectures, formats); it had no release milestone.
feat(session): one engine — Session<ArchForward> owns generation for …
11 remaining items
noahgift commentedon Sep 26, 2026
Phase 2 step 3 (RMSNorm) landed on stage/0.70.1 @ b753a2a. The ratchet baseline drops from 61 to 48 rows: 13 scalar rmsnorm duplicates now call
ops::rms_norm_scalar_into,rms_norm_scalar_in_placeandrms_scalar. Each site keeps its own float ordering throughRmsScale {Divide, ScaleThenWeight, WeightedScale}, so every migration is bit-identical to the old loop; a frozen-reference test checks 144 cases. The gated pair (gated_rmsnormandrms_norm_gated) is left forops::rms_norm_gated_into. Checks: fmt 0; clippy (default, gpu and cuda) 0; serve lib tests 2424 passed, 0 failed. Spec §9.8. Next up: softmax.refactor(serve): PP-ARCH-001 Phase 2 step 4 — scalar softmax shared h…
fix(contracts): kernel-fusion call_site lines drifted +9 in kernels_g…
refactor(serve): PP-ARCH-001 Phase 2 step 5a — tiled attention home, …
refactor(serve): PP-ARCH-001 Phase 2 step 5b — scalar one-row attenti…
refactor(serve): PP-ARCH-001 step 5c — MulInv attention family onto a…
refactor(serve): PP-ARCH-001 step 5d — cached-GQA SIMD attention home (…
WIP refactor(serve): PP-ARCH-001 step 5f — scheduler GQA on the cache…
noahgift commentedon Oct 14, 2026
merged into #5315 (master ticket for epic #4001; triage rule 7b, 2026-10-14). It is a checklist row there; reopen this issue when work on it starts or its PR opens.