Repository navigation
Fix MatMulNBits CompInt8 on x64 returning zeros with runtime scales or zero points - #34224
Open
Qiming Bao (14H034160212) wants to merge 1 commit into
Open
Qiming Bao (14H034160212) wants to merge 1 commit into
Qiming Bao (14H034160212) wants to merge 1 commit into
Conversation
…ts are runtime inputs The x64 CompInt8 kernels take the scales and the block sums (derived from the scales and zero points) from packed B and ignore QuantBScale. When the scales or zero points are not constant initializers, PrePack(B) packs B without them and they are never folded in, so the node returns zeros. This also hits FP16 DequantizeLinear -> MatMul models at ORT_ENABLE_EXTENDED: the fusion produces MatMulNBits with accuracy level 4 and the CPU EP inserts a Cast on the fp16 scales, which makes them a runtime input. Extend the existing ARM64 fallback (decline prepacking B when it would be incomplete) to x64 CompInt8 for 4- and 8-bit weights, so these nodes use the unpacked path. Fixes microsoft#34216
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The new test fails with zero outputs on ARM64 CPUs with INT8 dot-product support because the 8-bit path reads unpopulated packed scales; it must be restricted to x86/x64 or the ARM64 fallback extended to handle 8-bit runtime scales.
1 open issue
What changed in this PR
Fixes #34216 by routing x64 CPU MatMulNBits CompInt8 operations with runtime scales or zero points through unpacked computation instead of incomplete packed weights.
Changes:
- Makes the prepacking fallback platform-aware for 4- and 8-bit x64 weights while preserving ARM64 behavior.
- Applies the check to both prepacking implementations and shared-buffer reuse.
- Adds float and FP16 regression cases for runtime quantization parameters.
| File | Description |
|---|---|
onnxruntime/test/contrib_ops/matmul_8bits_test.cc |
Enables runtime scales in the helper and adds 8-bit regression coverage. |
onnxruntime/test/contrib_ops/matmul_4bits_test.cc |
Adds regression coverage for runtime scales and zero points. |
onnxruntime/contrib_ops/cpu/quantization/matmul_nbits.cc |
Extends prepacking safeguards to x64 CompInt8 with runtime metadata. |
Review effort: Balanced
|
|
||
| // CompInt8 with scales that are not constant initializers: PrePack(B) can't fold them into packed B, so the | ||
| // node must not prepack B. On x64 it used to return zeros. | ||
| TEST(MatMulNBits, Accuracy4_8b_RuntimeScales) { |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
On x64,
MatMulNBitswithaccuracy_level=4(CompInt8) returns zeros whenscalesorzero_pointsis not a constant initializer.The x64 CompInt8 kernels read the scales and the block sums (derived from the scales and zero points) from packed B, and ignore
QuantBScale.PrePack(B)can only fold in constant scales and zero points. When they are runtime inputs, B is packed without them, they are never added later, and that part of the packed buffer stays zero.ARM64 already handles this case for KleidiAI:
RequiresDynamicQuantizationParameterPrepackFallbackdeclines prepacking B, so the node computes from unpacked B. This change makes that check platform-aware and applies it on x64 to CompInt8 with 4- and 8-bit weights. The ARM64 condition is unchanged. The three call sites (bothPrePacks andUseSharedPrePackedBuffers) are no longer ARM64-only. Nodes with constant scales and zero points are not affected.The x64 test for dynamic metadata (
Float32_4b_Accuracy4_ForceFp32DynamicMetadata) forces CompFp32, so CompInt8 with runtime scales was not covered.Motivation and Context
Fixes #34216.
That issue is one way to hit this: an FP16
DequantizeLinear -> MatMulmodel atORT_ENABLE_EXTENDED. The QDQ fusion producesMatMulNBitswith accuracy level 4 (the default), and the CPU EP inserts aCaston the fp16 scales, which makes them a runtime input. WithORT_ENABLE_ALLthe Cast is constant-folded, so that level is not affected.The same happens for float models whose scales are graph inputs or computed in the graph. With onnxruntime 1.31.1 and a small
MatMulNBits(K=32, N=4, accuracy level 4), runtime scales give an output error equal to the largest output for both 4-bit and 8-bit weights, while constant scales are correct. 1.29.0, 1.30.0 and 1.31.0 behave the same.The fallback path dequantizes B instead of using the int8 kernel, so these nodes are slower than with constant scales, but correct.
#34197 is a separate issue (accuracy level 1 and FP16 rounding of the dequantized weight) and is not changed here.
Tests
MatMulNBits.Accuracy4_4b_RuntimeScalesOrZeroPointscovers runtime scales and, separately, runtime zero points.MatMulNBits.Accuracy4_8b_RuntimeScalescovers runtime scales. It needed ascales_are_initializersoption in the 8-bit test helper.mainboth fail on every case with the output all zero (for examplecur_expected[i] evaluates to -0.2037, difference 0.2037). With this change they pass.onnxruntime_provider_test --gtest_filter='MatMulNBits*:*MatMulNBits*:*QDQ*MatMul*:*DQMatMul*': 78 passed, 9 skipped (ARM64/KleidiAI/WebGPU-only), 0 failed (Linux x64, CPU, Release).onnx_test_runner -e cpu -o 2:main:expected 0.25, got 0.