Skip to content

[CPU EP] MatMulNBits (accuracy_level=4) produces wrong results on x86-64 AVX2 when loaded from session.save_external_prepacked_constant_initializers output (1.31.1) #33677

Open

Description

@thiennq

Describe the issue

We save an optimized model with prepacked constant initializers and load that saved model later
(the documented "save prepacked weights" flow) to memory-map the packed weights. On x86-64 with AVX2
(no AVX-512) and onnxruntime 1.31.1, graphs containing MatMulNBits (int4, block-wise, accuracy_level=4)
compute wrong values after reload. In our autoregressive TTS model the stop token is never emitted, so
generation runs to the 250-frame cap on every input.

Observations (same model, same inputs, Linux x86-64 GitHub runner, CPU flags avx avx2 f16c fma):

Variant Frames generated (seed 1 / 2) Expected
fp32 reference 27 / 32 stops
int4 MatMulNBits acc4, loaded from prepacked save 250 / 250 does not stop
same int4 graphs, not prepacked (ORT packs in memory) 23 / 27 stops
same int4 graphs with accuracy_level=0, prepacked 26 / 26 stops
int8 MatMulInteger graphs only, prepacked 30 / 31 stops
onnxruntime 1.23.2 (does not write int4 prepacked blobs) 26 / 28 stops
  • Same on windows-latest x86-64 (AMD Family 25, onnxruntime 1.31.1).
  • arm64 (macOS, Linux aarch64) with the same flow is correct; audio is bit-identical to the unpacked model.
  • Under Rosetta (no AVX exposed) onnxruntime 1.31.1 does not write int4 prepacked blobs and the issue does not appear.
  • A single-node probe (one MatMulNBits node, M=1) compares equal between prepacked and unpacked loads, so the
    mismatch seems to need M>1 or appear only after graph fusions.

To reproduce

Save step (Python):

so = ort.SessionOptions()
so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
so.intra_op_num_threads = 1
so.add_session_config_entry("session.save_external_prepacked_constant_initializers", "1")
so.add_session_config_entry("session.optimized_model_external_initializers_file_name", "model.packed.data")
so.add_session_config_entry("session.optimized_model_external_initializers_min_size_in_bytes", "1024")
so.optimized_model_filepath = "model.packed.onnx"
ort.InferenceSession("model.onnx", so, providers=["CPUExecutionProvider"])

Then load model.packed.onnx in a new session and compare its outputs with model.onnx. A self-contained
model is not attached yet (see note above); the failing graphs are a text encoder and an autoregressive
prefix step quantized with MatMulNBits (bits=4, block_size=32, symmetric, accuracy_level=4).

Urgency

Workaround in place: we skip prepacking for graphs with MatMulNBits on x86-64, at the cost of about
100 MB more private memory per process.

Platform / versions

Linux (ubuntu-latest) and Windows (windows-latest), x86-64 AVX2; onnxruntime 1.31.1 from PyPI, Python 3.12, CPUExecutionProvider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions