a5 AICPU filter affinity gate: Scenario B fail-fast guard not added¶
Date: 2026-06-02 Verdict: dropped — behavior is empirically deterministic per SKU; documented instead of code-guarded
Question¶
PR #963 ships a filter-style AICPU affinity gate on a5 (platform_aicpu_affinity_gate_filter)
driven by a host-computed ALLOWED_CPUS table. The design assumes:
- CANN's AICPU dispatch set == the device-side
halGetDeviceInfo(AICPU, OCCUPY)bitmap exactly. Empirically verified on the Scenario A SKU (Ascend950PR, OCCUPY=0x1f8) usingtools/cann-examples/aicpu-thread-spread/. - Launching
popcount(OCCUPY)threads spreads exactly one thread per user cpu_id, so every cpu inALLOWED_CPUS(a subset of OCCUPY) gets a representative thread, and the orch slot atexec_idx == allowed_count - 1is always populated.
CodeRabbit asked, on the PR: what if a future SKU or CANN version
breaks (1) or (2)? The orch slot would be empty, the scheduler would
wait for orchestrator_done_ that never gets set, and the symptom
upstream would be a stream-sync timeout
(aclrtSynchronizeStream rc=507000) — slow to diagnose without
context.
Proposed change: add a host-side post-launch check that every
exec_idx slot got populated (need a device→host signal of which
slots survived the gate), fail-fast if not.
What was tried¶
Nothing yet beyond the design discussion. The "fail-fast" path would need a fresh device→host signal channel:
- the gate runs inside the AICPU kernel,
- the orch-empty stall happens later in the scheduler's drain (not the kernel that's spinning at the gate barrier),
- nothing today reports the surviving
exec_idxset back to the host in time for a pre-execution check.
So it's not a one-line guard. Estimated effort: small (~50-100 lines) but requires a Runtime field for "exec_idx populated bitmap" written by the CAS-winner thread of the filter gate, and a host-side post-sync check.
Result¶
- Scenario A SKU (OCCUPY=0x1f8, 6 user cpus): runtime produces
ALLOWED_CPUS = {5,6,7,8,3},launch_count = 6, every slot populated, all 17 a5 ST tests pass. - Scenario B SKU (OCCUPY=0x7ffe, 14 user cpus): not available on the
dev box this PR was developed on. Predicted layout from the same
algorithm:
ALLOWED_CPUS = {11,12,13,14,7},launch_count = 14. Unmeasured.
Why not (now)¶
- The CANN AICPU dispatcher's "set == OCCUPY" property has held on every a5 sample we have. It's a behavior of the kernel-launch dispatch policy, not a property that would change per workload or per launch.
- If the property ever stops holding, the symptom is loud enough
(
507000after the AICPU sync timeout) to spot quickly, and the failed run's stderr already contains the diagnostic gate logs (AICPU filter gate: allowed[i] = cpu_id N role=...followed by per-threadidx=X cpu=Y exec_idx=Z ACTIVE/DROPPED). The empty slot shows up by inspection: anallowed[i]with no matchingACTIVE(sched)/ACTIVE(orch)line. - The probability-weighted cost (failure × diagnosis-time) is below the work of the fail-fast plumbing and its ongoing maintenance.
If you reach for this doc because you hit 507000 on an a5 onboard
test, the diagnosis path is:
- Grep the failing run's stderr for
AICPU filter gate:— that's the gate's per-launch dump. - Cross-check
allowed[i] = cpu_id Nlines against the per-threadthread idx=X cpu=Y exec_idx=Z ACTIVE/DROPPEDlines. - For each
i, exactly one thread should showexec_idx=i ACTIVE. If anyihas zero ACTIVE matches, CANN didn't dispatch to that cpu_id and the slot is empty — that's the Scenario-B-style regression this investigation documented. - Cross-check device-side OCCUPY with
tools/cann-examples/aicpu-device-query/to confirm the static topology still matches what the runtime probe sees. - Cross-check CANN's actual dispatch with
tools/cann-examples/aicpu-thread-spread/— that's the empirical "what cpu_ids did N threads land on" tool. - If
aicpu-thread-spreadconfirms CANN dispatch ⊊ OCCUPY, updatecompute_allowed_cpus(insrc/a5/platform/onboard/host/aicpu_topology_probe.cpp) to pickALLOWED_CPUSfrom the reachable set, not OCCUPY.
When to reconsider¶
Implement the fail-fast guard if any of these become true:
- A Scenario B SKU run shows an empty
exec_idxslot in the gate diagnostic (AICPU filter gate:lines), proving thedispatch ⊆ OCCUPYassumption is tighter than assumed. - A new CANN version changes the AICPU dispatch policy and the same empty-slot pattern shows up on a previously working SKU.
- A future test makes the gate enter a hot path (it currently runs
once per Worker::run; if it ever runs per-iteration on a streaming
workload, the
507000-after-timeout cost becomes proportional to hot-loop count, not per-launch).
References¶
- PR #963 — the affinity gate implementation.
src/a5/docs/hardware.md§"CANN AICPU thread dispatch under varying launch budgets" — the empirical data behind the design.tools/cann-examples/aicpu-thread-spread/— diagnostic spike tool for reproducing CANN dispatch shape on any SKU.tools/cann-examples/aicpu-device-query/— device-sidehalGetDeviceInfoprobe for verifying OCCUPY.- CodeRabbit review thread on PR #963 (the proposal that led to this doc).