pnnx: write bool attributes as fp32 so the ncnn .bin stays in step (fixes #6855) - #6866
Open
giuliocorradi wants to merge 1 commit into
Open
pnnx: write bool attributes as fp32 so the ncnn .bin stays in step (fixes #6855)#6866giuliocorradi wants to merge 1 commit into
giuliocorradi wants to merge 1 commit into
Conversation
A bool tensor attribute is written to model.ncnn.bin raw, one byte per
element, but the MemoryData layer emitted for it is sized from the tensor
SHAPE and ncnn reads that many float32 (load_type=1, untagged).
ModelBin is a sequential reader, so after an n-element bool the cursor is
3n/4 floats out of step and every weight-bearing layer after that point
loads shifted data. Nothing reports it: pnnx exits 0, load_param() and
load_model() both return 0, and inference produces plausible numbers that
are simply wrong.
Widen bool to fp32 on write, mirroring the existing i64 --> i32 handling
directly above, so what is written matches what will be read.
Verified with two binaries built from identical source differing only in
this file, on a 400-element folded bool constant:
construct unpatched patched delta
torch.where + folded bool 113240 114440 +1200
logical_and + folded bool 113240 114440 +1200
400 * (4 - 1) = 1200 bytes, exactly the shortfall.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Member
|
|
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.
Fixes #6855.
A bool tensor attribute is written to
model.ncnn.binraw — one byte per element — but theMemoryDatalayer emitted for it is sized from the tensor shape, andMemoryData::load_model()reads that many float32 (load_type=1, untagged).ModelBinis a sequential reader, so after an n-element bool the cursor is3n/4floats out of step and every weight-bearing layer after that point loads shifted data. Nothing reports it:pnnxexits 0,load_param()andload_model()both return 0, and inference produces plausible numbers that are simply wrong.The fix widens bool to fp32 on write, mirroring the
i64 -> i32handling directly above it.Correction to the repro in the issue
The reproduction I posted in #6855 does not actually trigger the bug — I should have checked it before filing. In that snippet the bool is a
register_bufferconsumed by a multiply, so torch type-promotes it to float during tracing and a type-9 attribute never reachessave_ncnn. Both patched and unpatched builds emit an identical 13224-byte.binfor it.The real trigger is a bool that is folded into a constant and stays bool. A minimal one:
torch.logical_and(self.a > 0, self.b > 0)in place of the comparison behaves identically.Evidence
Two
pnnxbinaries built from identical source, differing only insave_ncnn.cpp:torch.where+ folded boollogical_and+ folded bool400 elements × (4 − 1) bytes = 1200, exactly the shortfall.
On a real model — an ultralytics YOLOE export, which is where I hit this — the graph carries
MemoryData pnnx_fold_idx.1 0 1 335 0=400, a 400-element folded bool, and the same +1200 delta appears (47467496 → 47468696). There the shifted reads corrupted the anchor grid, which arrived as[-0.032, -0.04, ...]instead of[0.5, 1.5, 2.5, ...], along with the mask and proto convolutions. Layers earlier in the file were untouched, which is what makes the damage look selective and misleading.One caveat on method, since it nearly misled me: I first tried to A/B the PyPI wheel against a locally built binary. That is not a valid comparison — the two differ in more than this patch (they emit different blob numbering and a different
pnnx_188shape), so the byte-level tail comparison does not line up. The numbers above are from same-source builds only.Note for a runnable check
The minimal repro above exercises the writer but produces a graph containing
torch.where, which ncnn has no layer for, so it cannot be executed end to end to show wrong output — only the wrong.binsize. The end-to-end corruption is visible on the YOLOE export.🤖 Generated with Claude Code