Skip to content

fix(bufid_sync): make optimizeSamePipeMerge deterministic - #1304

Open
Adamkh329 wants to merge 1 commit into
hw-native-sys:mainfrom
Adamkh329:bufid-determinism-main
Open

fix(bufid_sync): make optimizeSamePipeMerge deterministic#1304
Adamkh329 wants to merge 1 commit into
hw-native-sys:mainfrom
Adamkh329:bufid-determinism-main

Conversation

@Adamkh329

Copy link
Copy Markdown

--enable-bufid_sync emitted different code on repeated runs of the same binary
over the same input.

Cause

optimizeSamePipeMerge iterated op2BufSync_, a DenseMap<Operation *, ...>, so
visit order followed pointer values. The merge survivor is *std::min_element(ids)
over whichever ids the first-visited op groups, recorded first-writer-wins — so a
different visit order picks a different survivor and emits different code.

Evidence

Eight runs of the same command, each pinned with taskset -c 0 to rule out a
thread race:

get_buf distinct emissions
before 24 24 24 24 23 24 24 24 3 of 8
after stable 1 of 8

mergeMap size was stable while its targets drifted (9→2 / 9→6 / 9→8, 10→3 /
10→2 / 10→6). Pinning rules out concurrency: it is bucket order.

The exact sequence is deliberately not presented as reproducible — it cannot be,
since that is the defect. What reproduces is the variance, not the pattern.

Fix

Order ops by earliest bracket syncIRIndex, tie-broken by smallest logicId
both come from the SyncIR rather than the heap, so the order is stable across
runs. Only the two loops reading op2BufSync_ needed it: the int-keyed maps
inside were already deterministic given a deterministic outer order, and the
final rewrite loop is order-independent because it writes a fresh map.

Why it matters beyond reproducible builds

Every A/B measurement previously taken through this pass carried roughly one
get_buf per kernel of unattributed run-to-run variance. Any buffer-id
comparison predating this fix should be treated as carrying that noise.

This also restores what docs/bufid_sync_a5_design.md:300 already requires:
"The traversal order should be deterministic so generated IDs are stable."

Verification

  • 40 pinned runs across three kernels post-fix: zero variance
  • lit 1664 passed / 0 failed; ctest 50/50
  • The unified allocator never calls this pass (BufidSyncPass.cpp:78 is the only
    caller), so that path was already deterministic and is unaffected

`--enable-bufid_sync` emitted DIFFERENT CODE on repeated runs of the same
binary over the same input.

`optimizeSamePipeMerge` iterated `op2BufSync_`, a `DenseMap<Operation *, ...>`,
so visit order followed the ops' POINTER VALUES. The survivor of a merge group
is `*std::min_element(ids)` over whichever ids the first-visited op happens to
group, recorded first-writer-wins, so a different visit order picks a different
survivor and emits different code.

MEASURED before, on Qwen3DecodeA5/rope_kv_cache (a5, level3), six runs each
pinned with `taskset -c 0`:

    get_buf count:  24 24 24 23 23 23

The exact sequence is NOT itself reproducible -- it cannot be, since that is
the defect. A later eight-run repeat of the same command gave
24 24 24 24 23 24 24 24. What reproduces is the VARIANCE, not the pattern.

`mergeMap` SIZE was stable at 4 every run; the TARGETS drifted -- 9->2 / 9->6 /
9->8 and 10->3 / 10->2 / 10->6, with only 3->0 and 8->3 stable. Pinning to one
CPU rules out a thread race: it is bucket order.

Ordering the ops by their earliest bracket `syncIRIndex`, tie-broken by smallest
`logicId`, is stable across runs because both come from the SyncIR rather than
from the heap. Only the two loops that read `op2BufSync_` needed it; the
int-keyed maps inside were already deterministic given a deterministic outer
order, and the final rewrite loop is order-independent because it writes a fresh
map.

This restores what docs/bufid_sync_a5_design.md:300 requires, and it matters
beyond reproducible builds: every A/B measurement previously taken through this
pass carried an unattributed run-to-run variance of about one get_buf per
kernel.

The unified allocator never calls this pass (it is the standalone
BufidSyncPass's only caller), so that path was already deterministic and is
unaffected.

Verified: rope_kv_cache emits a byte-identical kernel across 8 pinned runs; the
full corpus -- 101 kernels that compile with --enable-bufid_sync, 3 reps each
with the CPU pin VARIED between reps to be harsher than the original repro --
reports 0 non-deterministic kernels; lit 1664 passed / 0 failed; ctest 50/50.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant