Repository navigation
test(ep-cpu): report the leader-cpuset premise instead of inferring it from a check its failure satisfies - #2084
justinchuby wants to merge 7 commits into
Conversation
…atform has #2059's `a_default_width_pool_on_leader_cpus_uses_every_core_it_was_given` narrows its child to one CPU per physical core with `set_current_thread_affinity`, which is implemented on Linux only and returns `Err` everywhere else by construction. The child `.expect()`s it, so the test is an unconditional failure off Linux and `main` has been red on `Rust (Windows ARM64)` and `Rust coverage (Windows x86_64)` since it landed. Report the platform fact instead of panicking on it: - `PROCESS_AFFINITY_MASKING_SUPPORTED` in `decode_affinity`, a compile-time constant for `DETECTION_SUPPORTED`'s reason -- a caller must be able to tell "this platform never had the capability" from "the call failed here" without making the call. A test asserts the constant agrees with the `cfg`-selected implementation, so the two cannot drift. - `restrict_self_to_leader_cpus` returns whether the restriction is attemptable; a failure on a platform that does implement it stays fatal. - The parent test skips with a stated cause where masking is unsupported. While here, close the blind spot the same test carried on Linux: it inferred the restriction from `allowed == cores`, which is satisfied by the failure it was meant to catch. Emulating an unreadable topology in the child (so `cores` takes its `allowed` fallback) with the restriction suppressed, that check passes on an entirely unrestricted process -- measured: `allowed: 8, cores: 8, restricted: false`. What the run then reports is a misattribution: it fails at `workers == cores` naming #1780, a resolver defect, on a host where the resolver was right and the premise was never established; where the cpuset happens to hold one CPU per core it passes outright. The child now reports `restricted=` and the parent requires it -- failing under `NXRT_REQUIRE_PLACEMENT_TESTS=1`, skipping with a cause otherwise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…set-platform-gate
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2084 +/- ##
==========================================
+ Coverage 80.54% 80.73% +0.18%
==========================================
Files 414 429 +15
Lines 199542 214775 +15233
Branches 199542 214775 +15233
==========================================
+ Hits 160715 173388 +12673
- Misses 33339 35620 +2281
- Partials 5488 5767 +279
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…set-platform-gate # Conflicts: # crates/onnx-runtime-ep-cpu/src/kernels/matmul_nbits.rs
…set-platform-gate
|
Status: the only two red lanes here are inherited from
Neither is caused by anything in this branch: this PR touches neither the CUDA crate nor the DFT/STFT kernels. I verified the CUDA one directly — the contract test is a pure source scan, so it reproduces locally without No action needed here beyond a re-run once those land. — Gaff |
…set-platform-gate # Conflicts: # crates/onnx-runtime-ep-cpu/src/kernels/matmul_nbits.rs
|
Merged latest Resolved by keeping main's prose and this PR's constant. Main's comment is better than mine and explains the belt-and-braces structure — if !crate::decode_affinity::PROCESS_AFFINITY_MASKING_SUPPORTED {A bare
— Gaff Run under |
The merge commit that brought origin/main into this branch used `git add -A` to stage the conflict resolution and took my untracked `.review/` scratch directory with it, which failed the Root file allowlist gate. Removed from tracking and added to `.git/info/exclude` so it cannot happen again on this worktree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Self-correction: the Resolving the Two things worth saying rather than quietly fixing. The gate caught what my local validation could not. I ran the full lib suite, clippy I am not adding an exclude rule, deliberately. The obvious fix is No change to the substance of this PR; the merge resolution and its validation stand as posted above. — Gaff |
…set-platform-gate
|
Closing this as superseded by #2034, which landed the same fix independently and better-argued. Verified by merging latest #2034 has, in
Its doc comment also states the core finding more sharply than mine did:
On the one assertion where we differed, #2034 is right and I defer. Mine asserted bidirectional agreement between the flag and the call; #2034 asserts only the fail-open direction ( One thing survives, and it is small: #2034's version of that test skips silently on Third duplicate I've hit in ~24h (#2096/#2093, #2097/#2093, now this). The common factor is that all three pairs were opened within an hour of each other against a red or newly-changed area, so the cost is real but the cause is contention, not carelessness. Not proposing process here; noting it on #1817. |
Rescoped. This opened as a fix for
main's two red Windows lanes; #2078 landed the same platform gate ~20 minutes earlier and I merged it in rather than compete with it. Its skip message and idiom are kept verbatim. What remains is the part #2078 does not cover — and one sentence in it that I can show is false.The claim I am correcting
#2078's comment on the skip reads:
That guard does not fail when the restriction is absent. It passes. It is satisfied by the exact failure it exists to catch.
restrict_self_to_leader_cpusgives up silently when the topology is unreadable, and the child'scoresthen falls back toallowed:So on an entirely unrestricted process the two are trivially equal. Measured, with the narrowing suppressed and the child's topology read forced to its fallback:
allowed == corespasses there. What follows is worse than a vacuous pass — it is a misattribution: the run fails atworkers == coresand blames #1780, a resolver defect, on a host where the resolver was correct and the premise was never established. On a cpuset that already holds one CPU per core, it passes outright.The fix
The premise is reported by the process that established it, not inferred from a consequence. The child prints
restricted=, and the parent requires it:NXRT_REQUIRE_PLACEMENT_TESTS=1→ fail, naming the three causes (unreadable allowed set, unreadable topology, leaderless cpuset);SKIP <test>:idiom.That is the same fail-closed/skip split
require_host_for_placementand #1916'sDETECTION_SUPPORTEDalready draw, and it is whyrequiredis a parameter rather than a global read.The
allowed == corescheck stays, with its comment corrected: it still catches a leader set that is not one CPU per core on a host that answered, which is a different fault.Second change: one spelling of the platform fact
#2078 spells it
cfg!(target_os = "linux")inline, in two places. This replaces both withdecode_affinity::PROCESS_AFFINITY_MASKING_SUPPORTED, next to the twocfgarms it describes, for the reasonDETECTION_SUPPORTEDis a constant: a caller must be able to tell "this platform never had the capability" from "the call failed here" without making the call — and when Windows process-wide masking does land, there is one place to change instead of agrep.A constant that lies about a capability is worse than no constant, so
the_masking_capability_constant_agrees_with_what_this_platform_doesasserts it against the implementation that actually compiled, on every lane. It runs on a spawned thread and re-applies the process's own mask, so it cannot leak an affinity into whatever test the runner schedules on that thread next.Evidence
Mutation results, not readings. All runs
taskset -c 16-23,CARGO_INCREMENTAL=0, underscripts/hostlock.shwith a stated reason. The host was shared throughout — no claim of a quiet machine is made or needed, since none of these are timings.origin/main)NXRT_REQUIRE_PLACEMENT_TESTS=1 but the child could not narrow itself to a leader-only cpusetallowed == corespassed; the run then failed blaming #1780PROCESS_AFFINITY_MASKING_SUPPORTED = falsesays false but re-applying this process's own mask returned ok=true); width test skipped, stated causefalseand the Linux impl forced to the non-LinuxErr— a faithful emulation of a Windows laneSKIPline printedrestrict the child to leader CPUs: "process-wide CPU affinity masking is only implemented on Linux (no-op)"— byte-identical to the Windows ARM64 logF3b was how I confirmed the Windows diagnosis before #2078 was visible to me; it is kept because it is also the falsifier for the constant, which is new here.
Also:
cargo fmt --all -- --checkclean,cargo clippy --locked -p onnx-runtime-ep-cpu --all-targets -- -D warningsclean,scripts/check_cross_compile.shgreen at full scope (full offline set (aarch64 cross toolchain present)) — not the FFI-free subset it silently falls back to without the cross toolchain.Species sweep
Three call sites reach
set_current_thread_affinityoutside its own module. The other two already do the right thing: production code atmatmul_nbits.rs:4379logs theErrand carries on, and the budget-lane child atdecode_spmd.rs:8772prints a skip marker with the reason — "Only Linux implements a process-wide mask, so on other hosts there is no way to manufacture the reduction." That is this shape, one file over, written before it. No other caller.expect()s the capability.Limits
ep-cpu→ep-api→ort-sys, whose build script bindgens the ORT headers, so--target x86_64-pc-windows-msvcdies in the build script before rustc sees this crate;scripts/check_cross_compile.shdocuments the same Windows exclusion. F3/F3b emulate the platform's behaviour on Linux; only this PR's Windows lanes can confirm the compile.main's third red lane,CLI ORT (Linux x86_64), is unrelated and untouched here:plugin_ort_e2e::initializer_chain_still_fuses_into_one_claimfails withOnly one instance of LoggingManager created with InstanceType::Default can exist at any point in time— already tracked as flaky: ORT LoggingManager singleton fails CreateEnv in plugin_ort_e2e as the binary's Env count grows #2065 (and cpu-plugin: ARM64 plugin_ort_e2e failures are one LoggingManager race, not per-test regressions #1123 on ARM64). Checked before saying so: every#[test]in that file that creates anOrtEnvholdsORT_EP_LOCK, none via alet _ =that would drop the guard immediately, and the binary spawns no threads; tests after the failure created their ownEnvand passed, which rules out a leaked one.🤖 Working as Gaff (Code Reviewer / Quality).