Qwen3.5: drop fp32 cast around RMSNorm in builder - #2101
Conversation
Removes the use_fp32 cast flags forcing LayerNorm IO to fp32 for
Qwen3_5TextModel. Keeping RMSNorm in the model's native fp16 eliminates
~216 Cast nodes (108 to-fp32 + 108 to-fp16) across input_layernorm,
post_attention_layernorm, and attention paths in a 24-layer build.
Measured on Qwen3.5-0.8B int4 WebGPU (3-run avg, prefill-1000 / 100 tok):
NVIDIA RTX 5080
gen tps: 76.6 -> 86.8 (+13.2%)
prompt tps: 6937 -> 7153 (+3.1%)
Intel iGPU
gen tps: 33.1 -> 36.1 (+9.1%)
prompt tps: 785 -> 789 (+0.5%)
Output text remains coherent and structurally identical between the two
builds.
There was a problem hiding this comment.
Pull request overview
This PR updates the Qwen3.5 Python model builder to avoid forcing RMSNorm/LayerNorm I/O through fp32, reducing the number of Cast nodes and improving runtime performance (notably on WebGPU) by keeping normalization in the model’s native dtype.
Changes:
- Remove the Qwen3.5-specific
layernorm_attrs["cast"]["use_fp32"]override and associated per-IO cast flags. - Keep OffsetRMSNorm’s
add_offset = 1behavior unchanged while allowing LayerNorm ops to remain inio_dtype.
|
We have typically seen accuracy/quality issues emerge when not upcasting norms from FP16 or BF16 to FP32. Can you share some evaluation data that shows there isn't significant output quality degradation? |
|
Can we test this change on a 36+ layer larger variant like Qwen3.5-4B? |
Thanks for the tips. I have tested it, and it functions as expected. |
Thanks for your information! No regression in output quality. I tested Qwen 4b(q4f16) for WebGPU EP on NV5080. After this PR, prompt: 1777.8 tps, gen: 52.2 tps. (click to see the output)Thinking Process:
Before this PR, prompt: 1694.7 tps, gen: 46.8 tps. (click to see the output)Thinking Process:
|
|
@apsonawane Do you have any other comments on this PR? If not, I’ll go ahead and merge it. |
Removes the use_fp32 cast flags forcing LayerNorm IO to fp32 for
Qwen3_5TextModel. Keeping RMSNorm in the model's native fp16 eliminates
~216 Cast nodes (108 to-fp32 + 108 to-fp16) across input_layernorm,
post_attention_layernorm, and attention paths in a 24-layer build.
Measured on Qwen3.5-0.8B int4 WebGPU (3-run avg, prefill-1000 / 100
tok):
NVIDIA RTX 5080
gen tps: 76.6 -> 86.8 (+13.2%)
prompt tps: 6937 -> 7153 (+3.1%)
Intel iGPU(Xe-LPG, Intel Core Ultra 9 285K)
gen tps: 33.1 -> 36.1 (+9.1%)
prompt tps: 785 -> 789 (+0.5%)
Apple M3 Max
gen tps: 115.7 -> 122.3 (+5.7%)
prompt tps: 3149.3 -> 3199.8 (+1.6%)
Co-authored-by: xiaofeihan1 <107654914+xiaofeihan1@users.noreply.github.com>
Removes the use_fp32 cast flags forcing LayerNorm IO to fp32 for Qwen3_5TextModel. Keeping RMSNorm in the model's native fp16 eliminates ~216 Cast nodes (108 to-fp32 + 108 to-fp16) across input_layernorm, post_attention_layernorm, and attention paths in a 24-layer build.
Measured on Qwen3.5-0.8B int4 WebGPU (3-run avg, prefill-1000 / 100 tok):
NVIDIA RTX 5080
gen tps: 76.6 -> 86.8 (+13.2%)
prompt tps: 6937 -> 7153 (+3.1%)
Intel iGPU(Xe-LPG, Intel Core Ultra 9 285K)
gen tps: 33.1 -> 36.1 (+9.1%)
prompt tps: 785 -> 789 (+0.5%)
Apple M3 Max
gen tps: 115.7 -> 122.3 (+5.7%)
prompt tps: 3149.3 -> 3199.8 (+1.6%)