feat(qwen3_8): support native BF16 precision - #1387
Conversation
Qwen3.8-27B's own checkpoint is natively BF16; qwen3_8 previously only accepted precision=fp16 or fp32, forcing a downcast even when a user wants to match the reference checkpoint's own numeric format. Wires precision="bf16" through model.py's validation and engine_builder.py's work_np_dtype/work_trt_dtype selection. Following families/qwen's existing, proven BF16 pattern: weight constants are staged as FP16 bytes (TensorRT's Weights constructor does not accept ml_dtypes.bfloat16 arrays directly) and the network's runtime dtype is trt.bfloat16; every generic constant-building helper in graph_ops.py already casts its constant to match its activation's runtime dtype via _cast_back_to_trt_dtype, so this propagates automatically through most of the graph. Two DeltaNet-specific call sites built raw elementwise ops directly against a freshly-created constant without going through that cast-matching helper, which is fine when the constant and activation happen to already share a dtype (the previously-only-supported fp16/fp32 modes) but breaks once the constant is staged as FP16 while the activation is genuinely BF16: the conv1d weight multiply in the DeltaNet conv step, and the GQA head-tiling "ones" constant used to expand compact Q/K from num_kv_heads to num_heads. Both now explicitly cast the constant to the activation's dtype before the elementwise op. Verified: builds successfully against the original Qwen/Qwen3.8-27B BF16 checkpoint (precision="bf16"), produces coherent generation output, and IEngineInspector confirms 273 GEMM layers use real BF16 tensor-core tactics (sm80_xmma_gemm_bf16bf16_..., cutlass3x_sm100_tensorop..._bf16...), not a fallback. DeltaNet's recurrent state update correctly stays in FP32 throughout, matching the reference HF implementation's own numerical behavior (unchanged from the existing fp16/fp32 modes). Signed-off-by: Zhenshan Xie <zhenshanx@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: NVIDIA/TensorRT-Model-Connect/.coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review. 📝 SummarySummaryAdds native BF16 support to
Validation covered BF16 engine building, coherent generation, and 273 BF16 GEMM tensor-core tactics. Logit-level Hugging Face comparison and multi-GPU testing were not performed. Architecture impact
HUMAN REVIEW REQUIRED — The supplied evidence cannot resolve the remaining multi-GPU and logit-level compatibility questions. WalkthroughQwen3.8 now accepts BF16 precision. Support defaults and engine construction use BF16, while constants remain stored as FP16. FP32 layer overrides and DeltaNet operand casts also cover BF16 builds. ChangesQwen3.8 BF16 precision support
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Feature 🚥 Pre-merge checks | ✅ 7 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (7 passed)
Full details: Benchmark Validation IntegrityExplanation The BF16 validation does not cover an affected consumer. The head build path now emits Resolution Add BF16 support to Comment |
Qwen3.8-27B's own checkpoint is natively BF16; the family's default_precision was still "fp32" (the base FamilySupport default), so omitting --precision silently built at a precision the checkpoint was never published in. Set default_precision="bf16" on both the alias-matched and config-discriminated FamilySupport instances, so `trtmc build <model> -o out.bundle` (no --precision flag) now matches the checkpoint's own native format by default. Explicit --precision fp16/fp32 continue to work unchanged. Signed-off-by: Zhenshan Xie <zhenshanx@nvidia.com>
Background
Qwen3.8-27B's own checkpoint (
Qwen/Qwen3.8-27B) is natively BF16, butfamilies/qwen3_8only acceptedprecision="fp16"or"fp32", forcing a downcast to FP16 even for users who want to match the reference checkpoint's own numeric format exactly.Exit Criteria
families/qwen3_8supportsprecision="bf16"end to end, building against the original publicQwen/Qwen3.8-27BBF16 checkpoint.precision="fp16"/"fp32"behavior is unaffected.Implementation
Wires
precision="bf16"throughmodel.py's validation andengine_builder.py'swork_np_dtype/work_trt_dtypeselection, followingfamilies/qwen's existing, already-proven BF16 pattern:Weightsconstructor does not acceptml_dtypes.bfloat16arrays directly), while the network's runtime dtype istrt.bfloat16.checkpoint_mapper.py's_target_np_dtype()already anticipated this exact split ("bf16" -> np.float16for storage) before this PR wired anything up to use it.graph_ops.py(add_matmul_rhs_constant,add_bias_sum, etc.) already casts its constant to match its activation's runtime dtype via_cast_back_to_trt_dtype, so BF16-ness propagates automatically through most of the graph with zero changes needed there.Change categories
Validation
Commands and Results
ruff check --config ruff.toml families/qwen3_8/engine_builder.py families/qwen3_8/model.py: all checks passed.Qwen38Model.load_weights->Qwen38Model.build_engine,precision="bf16",Qwen/Qwen3.8-27BBF16 checkpoint: succeeds, producing a 53.8GB engine (same size as the existing FP16 build, as expected: both are 2 bytes/element).IEngineInspector.get_engine_information(trt.LayerInformationFormat.JSON): 273 of 369 GEMM layers show real BF16 tensor-core tactics, not a fallback. The remaining GEMMs are DeltaNet's deliberately-FP32 recurrence math.torch.bfloat16to match the engine's declared I/O dtype): output is coherent.Hardware, Environment, and Revisions
Qwen/Qwen3.8-27B(public, unquantized BF16), local snapshot used for testing.Not Run / Remaining Gaps
Contributor Self-Review
Notes For Future Readers
The FP16-staging step for BF16 constants (storage as
np.float16bytes, explicit in-graphCasttotrt.bfloat16) is a workaround for a TensorRT API gap, not a numeric compromise:Weights(ndarray)doesn't recognizeml_dtypes.bfloat16arrays, and the raw-pointerWeights(type, ptr, count)overload that does acceptDataType.BF16doesn't hold a Python reference to the source array, so using it directly would need carefulkeep_alivelifetime handling across every constant-building call site. Verified viaIEngineInspectorthat TensorRT already constant-folds the staged FP16 + Cast into a native BF16 constant at build time (GEMM weight constants showDatatype: BFloat16directly, notHalf), so there is no leftover runtime or storage cost from this approach.This mirrors
families/qwen's existing, already-proven BF16 pattern closely; the two DeltaNet-specific fixes in this PR (conv1d weight multiply, GQA head-tiling constant) were needed because DeltaNet's custom recurrence code builds some elementwise ops directly rather than through the genericgraph_opsconstant helpers, so it hadn't previously been exercised against a BF16/FP16 storage-dtype mismatch. Worth checking for the same pattern if any other family adds BF16 support to hand-rolled elementwise code.Risk level
Additive change gated behind a new
precision="bf16"request value; existingprecision="fp16"/"fp32"code paths are untouched by this diff.