perf(qwen3): compile prefill/decode/greedy straight from the signature - #73
perf(qwen3): compile prefill/decode/greedy straight from the signature#73lyfne123 wants to merge 2 commits into
Conversation
The three HOST wrappers were compiled by hand-building large throwaway torch.empty(...) tensors that compile() only inspects for shape/dtype. Several are big -- the prefill/decode k_cache/v_cache dummies are sized like a full KV cache (num_layers*batch*cache_blocks*kv_heads*page_size x head_dim), plus lm_head/embed (VOCAB x HIDDEN) and the stacked weights -- so every model compile wasted that much host RAM and time. With pypto #2014, compile() reads the shape/dtype contract from a fully-annotated signature when called with no sample tensors. Annotate all three wrappers (pl.Tensor[[...], dtype]) and drop the dummy builders: - qwen3_l3_dispatch: full annotations on prefill/decode/greedy params. Static extents come from model constants; runtime-varying dims reuse the kernels' shared pl.dynamic vars so host<->kernel dims unify. Dep-graph inference still re-marks kernel-bound dims dynamic, so the artifact is identical to the old compile(sample_tensors) call. Bind decode's first return to the `out` param name so signature-mode specialization fills the return tuple element type (a renamed local stays a bare pl.Tensor, which the parser rejects inside tuple[...]). - npu_executor: inject the annotation globals (validated model constants + the kernels' pl.dynamic instances) before compiling; delete the three dummy-arg factories; compile() with no tensors. - test_batching: refresh two tests that referenced the removed decode hidden buffer / the pre-fusion decode kernel arg order. Verified: each wrapper's signature-mode compile returns the SAME cached DistributedCompiledProgram as the old dummy-arg compile (shared cache entry), and an on-device generate (--max-seq-len 512) produces correct tokens. Requires pypto with #2014 (signature-mode compile()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughQwen3 host wrappers now use explicit tensor shape contracts and signature-based JIT compilation. Dispatch dimensions are injected before compilation, specialized dummy arguments are removed, and batching tests reflect fused decode inputs and flexible copy-kernel signatures. ChangesQwen3 signature compilation
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant Qwen314BPyptoExecutor
participant qwen3_l3_dispatch
participant prefill_kernel
Qwen314BPyptoExecutor->>qwen3_l3_dispatch: Inject static shapes and dynamic dimensions
qwen3_l3_dispatch->>prefill_kernel: Read loaded kernel dynamic-dimension objects
Qwen314BPyptoExecutor->>qwen3_l3_dispatch: Compile typed host wrappers by signature
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 compilation of the Qwen3-14B host wrappers to use signature-mode compilation instead of compiling with dummy tensors. It introduces _inject_dispatch_shapes to dynamically bind shape and dynamic dimension variables to the dispatch module's globals prior to compilation, and updates the host wrappers with explicit tensor annotations. The feedback suggests wrapping the dynamic attribute lookup in a try-except block to provide a clearer error message if any expected dynamic dimension variables are missing from the prefill module.
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.
| for dyn_name in ( | ||
| "USER_BATCH_DYN", | ||
| "PREFILL_TOKENS_DYN", | ||
| "KV_CACHE_ROWS_DYN", | ||
| "BLOCK_TABLE_FLAT_DYN", | ||
| "LAYER_DYN", | ||
| "LAYER_HIDDEN_ROWS_DYN", | ||
| "LAYER_INTER_ROWS_DYN", | ||
| ): | ||
| setattr(qwen3_l3_dispatch, dyn_name, getattr(prefill_module, dyn_name)) |
There was a problem hiding this comment.
When dynamically retrieving dynamic dimension variables from the loaded prefill module, if any of the expected attributes are missing (e.g., due to a version mismatch between the runner and the kernel module), a generic AttributeError will be raised. Wrapping this lookup in a try-except block to raise a clearer, more descriptive error message will greatly improve debuggability.
for dyn_name in (
"USER_BATCH_DYN",
"PREFILL_TOKENS_DYN",
"KV_CACHE_ROWS_DYN",
"BLOCK_TABLE_FLAT_DYN",
"LAYER_DYN",
"LAYER_HIDDEN_ROWS_DYN",
"LAYER_INTER_ROWS_DYN",
):
try:
dyn_var = getattr(prefill_module, dyn_name)
except AttributeError as e:
raise AttributeError(
f"Prefill kernel module is missing required dynamic dimension variable '{dyn_name}'. "
"Please ensure your kernel module is up to date."
) from e
setattr(qwen3_l3_dispatch, dyn_name, dyn_var)_inject_dispatch_shapes was passing the static model constants (HIDDEN, VOCAB, ...) as explicit kwargs even though the prefill kernel module already re-exports every one of them (and they are validated equal to the model in _compile_model). Copy them in one loop alongside the pl.dynamic vars; only the per-runtime / per-kernel-scalar extents (SAMPLED_IDS_PAD, ROPE_SEQ, DEC_BLOCK_TABLE_FLAT) stay as args. Artifact-equivalence smoke test still passes for all three wrappers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
The three Qwen3-14B HOST wrappers (
qwen3_prefill_host,qwen3_decode_host,qwen3_greedy_sample_host) were compiled by hand-building large throwawaytorch.empty(...)tensors that.compile()only inspects for shape/dtype — the data is never read, yet the memory is really allocated. Several are large:k_cache/v_cachedummies are sized like a full KV cache (num_layers·batch·cache_blocks·kv_heads·page_size × head_dim),lm_head/embed(VOCAB × HIDDEN) and the stacked projection weights.So every model compile wasted that much host RAM (and time), and re-declared the shape contract already implied by the kernels.
With pypto #2014 (
compile()straight from the signature), a fully-annotated wrapper is compiled with no sample tensors — the shape/dtype contract is read from the parameter annotations.Changes
qwen3_l3_dispatch.py— fullpl.Tensor[[...], dtype]annotations on all three wrappers. Static extents come from validated model constants; runtime-varying dims reuse the kernels' sharedpl.dynamicvars so host↔kernel dims unify. Dep-graph inference still re-marks kernel-bound dims dynamic, so the compiled artifact is identical to the oldcompile(sample_tensors)call. Decode's first return is bound to theoutparam name so signature-mode specialization can fill the return-tuple element type (a renamed local stays a barepl.Tensor, which the parser rejects insidetuple[...]).npu_executor.py— inject the annotation globals (model constants + the kernels'pl.dynamicinstances) before compiling; delete the three dummy-arg factory methods (_compile_{prefill,decode,greedy_sample}_*_callable);compile()with no tensors (−208 / +156 lines net in the two runner files).test_batching.py— refresh two tests that referenced the removed decodehiddenbuffer / the pre-fusion decode kernel arg order.Verification
codegen_only): for each wrapper, signature-modecompile()returns the same cachedDistributedCompiledProgramas the oldcompile(*dummy_tensors)— proving the annotations reproduce the exact static/dynamic shape contract.tests/test_batching.py+tests/test_parallel.py→ 28 passed.npu_generate.py --max-seq-len 512 --max-new-tokens 5(the runtime-max_seq<kernelMAX_SEQcase) → correct coherent output.Dependency
Requires pypto with #2014 (signature-mode
compile()).🤖 Generated with Claude Code