Repository navigation
[MHA] Add a delta race, resume, finalist rounds and evidence to the MHA forward tuner - #5849
Draft
amd-bartgips wants to merge 9 commits into
Draft
amd-bartgips wants to merge 9 commits into
amd-bartgips wants to merge 9 commits into
Conversation
This was referenced Sep 25, 2026
amd-bartgips
added a commit
that referenced
this pull request
Sep 28, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes. Co-authored-by: Cursor <cursoragent@cursor.com>
amd-bartgips
added a commit
that referenced
this pull request
Sep 28, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes. Co-authored-by: Cursor <cursoragent@cursor.com>
amd-bartgips
force-pushed
the
users/bartgips/fmha-single-store
branch
from
September 28, 2026 14:17
8776074 to
8a11428
Compare
amd-bartgips
added a commit
that referenced
this pull request
Sep 28, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes.
amd-bartgips
force-pushed
the
users/bartgips/fmha-tuning-advanced
branch
from
September 28, 2026 14:17
ae0d49f to
aa3b347
Compare
amd-bartgips
added a commit
that referenced
this pull request
Sep 28, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes.
amd-bartgips
added a commit
that referenced
this pull request
Sep 30, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes.
amd-bartgips
added a commit
that referenced
this pull request
Oct 1, 2026
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes.
amd-bartgips
force-pushed
the
users/bartgips/fmha-tuning-advanced
branch
from
October 1, 2026 12:38
aa3b347 to
1752978
Compare
Moved unchanged from the MHA forward tuner branch (#5762) so that other tuners can use them without waiting for it: - mp_tuner: optional per-candidate status and detail (return_status), a per-candidate progress queue with result_callback, a faulted group keeping the candidates it had already measured, MpTunerTask, broader accelerator-fault detection, and a pool restart on a stale PID map. - TunerCommon.measurement_kwargs, so --warmup and --iters reach the timed call instead of run_perftest's defaults. - _read_csv builds a new frame rather than assigning into a slice. - FileBaton treats a zombie lock holder as dead, so a builder that exited without releasing its lock no longer wedges the module build.
A caller that does not pass return_status picks the fastest finite latency and cannot tell a partial group from a complete one. Keeping the candidates measured before a fault would let it publish the winner of a search that stopped early, so those callers get every candidate of the group back as failed, as before. Callers with return_status keep the measured candidates and see the crash or timeout status on the rest. test_mp_tuner_fault runs one shape group with a faulting candidate through the real pool on one GPU, in both call forms.
aiter/utility/tuning_policy.py holds the thresholds whose justification does not mention a kernel family: - MeasurementPolicy: warmup, iters, errRatio and timeout, which TunerCommon.ARG_DEFAULTS now reads (same values as before). - COMPARE_MIN_IMPROVEMENT_PCT: the --compare --update_improved bar. - PromotionPolicy: the indifference delta a challenger has to clear over the incumbent, and the finalist count and rounds for re-timing. - gate_against_incumbent: the promote-or-retain decision on those numbers, for tuners that measure the incumbent themselves. Standard library only, so the runtime and CPU tests can import it. A family that needs a different threshold overrides it with dataclasses.replace in its own module.
--compare --update_improved and gate_against_incumbent both decide whether a faster configuration is worth replacing what a shape runs today, but read two different numbers (3% and 2%). They now read one: PromotionPolicy.min_improvement_pct, 3.0, named after the --min_improvement_pct flag whose default it already was. - The gate works in percent and promotes at "at least" the bar, as --compare does, and reports margin_pct. - COMPARE_MIN_IMPROVEMENT_PCT and indifference_delta are gone; neither was on main. - finalists and finalist_rounds move to the follow-up that times finalists (#5849), since nothing on main uses them. - batch comes from a new RunPolicy, so no TunerCommon default is a bare literal any more. It sits apart from MeasurementPolicy because it changes how often the tuned CSV is written, not which candidate wins. No default a tuner sees changes.
test_mp_tuner_logic and test_mp_tuner_fault imported Triton before torch, and the start-time test switched its pool from spawn to fork, both for a dynamic-loader abort in spawned workers. Main runs the same test, and the worker-PID test from #5841, under spawn with no Triton import, and the abort does not reproduce on MI355X. The tests now use spawn, the start method mp_tuner forces, and a CPU test no longer needs Triton installed.
Worker death is now caught by #5841's check, which this branch rebases onto. Without it, the immediate pool restart on a stale PID map resubmitted the dead group with a fresh start time, so a candidate that kills its worker restarted the pool forever. - The worker-exited branch drains the progress queue before failing the group, as the other failure branches do, so a return_status caller keeps the candidates the worker measured before it died. The detail names the exited worker. - With return_status, the candidates behind a fault come back as not_run instead of carrying the fault's status. The callback stream is unchanged: it still receives only the candidate that failed. - The mp_tuner docstring describes both result forms, every status and result_callback. - test_mp_tuner_fault adds a candidate that exits its worker with another shape queued behind it, in both call forms. With the dead-worker check disabled it fails at its deadline after repeated pool restarts. The file now runs in the per-PR shards (67 s on one MI355X).
work_group catches an error raised outside worker(), such as a later candidate's gen_data or reference failing, and returns a result list itself. That list failed every candidate of the group, including those already measured, and put all of them on the progress queue. The parent saw a normal return, so its partial-result handling never ran: a return_status caller lost the measured candidates, a checkpointing caller received a second, crash result for each of them, and the candidates behind the abort were checkpointed as tried, so a resume skipped them. work_group now builds those results with _failed_group_results, the same rules the parent applies to a group the pool lost: with return_status, measured candidates keep their results, the one that aborted gets the failure, and the rest are not_run; without it, the whole group fails as before. Only the aborting candidate is published. test_mp_tuner_fault adds a group whose second candidate fails in gen_data (reported by Araceli); the typed case failed before this change.
RacePolicy holds an elimination race's error budget and block counts, and SAMPLE_SEED seeds candidate sampling and block order, so a run is repeatable. The race's indifference zone defaults to PromotionPolicy.min_improvement_pct, so the race and the gate after it agree on what a tie is. Their first consumer is the MHA forward tuner's race.
…he MHA tuner On top of the exhaustive tuner: - --strategy race: candidates are timed in randomized short blocks and dropped once proven more than delta slower than the leader (aiter/utility/block_race.py), about 3x faster than timing the whole field to full depth on a 200-candidate field. - A journal of finished measurements, so --resume continues an interrupted run; its name digests the shape, the field and the race thresholds, so a changed run never replays another's blocks. - Finalist rounds: the fastest candidates are timed again over fresh workers. FinalistPolicy (finalists, rounds, significance_sigma) sits next to RacePolicy in the shared tuning policy. - An evidence manifest per run: per-shape outcome, every promotion decision with its margin_pct, bar_pct and noise_floor_pct, selection proofs, software and measurement settings, and the promotion bar. - With --all, the published row is the incumbent a challenger has to beat, instead of auto-select. Gate decisions go through tuning_policy.gate_against_incumbent, which takes a noise_pct floor and promotes only at max(min_improvement_pct, noise_pct): the minimum improvement says what is worth a row, the noise says what the search resolved, so enough finalist rounds never publish a real but tiny win that --compare would then refuse. The floor is two combined standard errors of the round means under the exhaustive strategy (standard_error_bar, in percent) and the race's delta under --strategy race. The tuner reads the bar from --min_improvement_pct, so one flag sets it for both the search and --compare; --delta defaults to it and is only needed to make the race's indifference zone differ. mp_tuner's typed statuses keep a faulting candidate from costing its shape the measurements already taken; the tuner checkpoints through result_callback, so candidates not reached before a fault are retried on resume. A re-tune whose published row no longer resolves now says it gated against auto-select, and warns that the row still overrides it.
amd-bartgips
force-pushed
the
users/bartgips/fmha-tuning-advanced
branch
from
October 1, 2026 12:55
1752978 to
6647aaa
Compare
amd-bartgips
force-pushed
the
users/bartgips/fmha-single-store
branch
from
October 1, 2026 12:55
8a11428 to
ab166eb
Compare
This branch has not been deployed
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.
Motivation
The MHA forward tuner in #5762 times every candidate once and publishes a winner only if it beats what the shape runs today by at least 3%. That is enough for a first tuned table, but it has three gaps:
This PR closes those gaps, on top of the shared tuning infrastructure in #5846.
What this PR adds
--strategy race: candidates are timed in randomized short blocks and dropped once they are proven more than delta slower than the leader. The race core is family-independent (aiter/utility/block_race.py).--resumecontinues an interrupted run. Its name digests the shape, the candidate field and the race thresholds, so a changed run never replays another run's blocks.<tune_file>.evidence.json): per-shape outcome (published,retained,failed,unmeasured), every promotion decision with its margin, bar and noise floor, the selection proofs, and the software and measurement settings.--all, a shape that already has a row is gated against that row, not against auto-select. Without--all, already-tuned shapes are still skipped.Reading this diff/PR
Only the last two commits are this PR's:
Add the race parameters to the tuning policyandAdd the delta race, resume journal, finalist rounds and evidence to the MHA tuner, +2925 / −209 across 11 files. Below them are #5846's seven commits, cherry-picked so this branch builds on #5762; they drop out when #5846 merges.aiter/utility/tuning_policy.py(+92):FinalistPolicy(finalists, rounds, significance sigma),RacePolicy(error budget and block counts),SAMPLE_SEED,standard_error_barin percent, and anoise_pctfloor ongate_against_incumbent, which then promotes only atmax(min_improvement_pct, noise_pct)and reports that bar asbar_pct.op_tests/tuners/tune_mha_fwd.py: the race, journal, finalist rounds, evidence and published-row incumbent. Every gate decision goes throughgate_against_incumbent. The minimum improvement comes from--min_improvement_pct, the same flag--compareuses, and--deltadefaults to it. The finalists, rounds, sigma, race parameters and seed come from the shared policy;mha_fwd_policy.pyonly aliases them asMHA_FWD_PROMOTION,MHA_FWD_FINALISTSandMHA_FWD_RACE.aiter/utility/block_race.py(674) andtest_block_race.py(536): the race core and its tests, from synthetic latencies.Nothing here runs in a serving process: the dispatch path is #5762's and unchanged.
Technical Details
One bar, two noise floors. A challenger replaces the incumbent only if it is faster by at least
max(min_improvement_pct, noise floor). The minimum, 3% by default, is the bar--compare --update_improvedapplies, so a row the search publishes is one--comparewould also keep. The noise floor is what the search can resolve: twice the combined standard error of the finalist round means under the exhaustive strategy, and the race's delta under--strategy race. More rounds can therefore reject a win that is within noise, but never publish one below the minimum.--min_improvement_pctsets the minimum for both the search and--compare;--deltaonly changes the race's indifference zone.The race. Each block times every surviving candidate for a few calls in a random order. After a minimum number of blocks, a candidate is eliminated once a paired t-test shows it is more than delta slower than the leader, with a Bonferroni-corrected error budget. The incumbent races as a protected entrant, so the final gate compares two estimates from the same race and a process-wide offset cancels. A race that ends without certifying a winner publishes nothing rather than falling back to the fastest point estimate.
Correctness.
race()only sees timings, never kernel output. Every candidate is correctness-checked in the pass that compiles and warms it, before it enters the race.Faults. With
mp_tuner's typed statuses from #5846, a candidate that faults the worker is reported ascrashand costs only itself, not the measurements already taken for its shape. That holds when the worker dies outright and when the group aborts before launch. Candidates behind the fault arenot_run, never passed toresult_callback, so they have no journal entry and--resumeretries them.When the published row no longer resolves. A re-tune then gates against auto-select instead. If auto-select holds, no row is written, and writing no row cannot remove the old one, so the tuner warns that the row still overrides auto-select and should be deleted.
Test Plan
python3 -m unittestover the level 0 and 1 modules in.github/workflows/tuning-tests.yaml, plustest_compare_logicandop_tests.test_jit_cache_transaction(CPU): race elimination, resume and journal binding, race-versus-exhaustive agreement, finalist rounds and the noise floor on the gate, the published-row incumbent, skipping already-tuned shapes, and the policy module.test_tune_pipeline.TestTunePipeline.test_mha_fwd_mp1(measure, gate, publish, prove and write evidence for one shape) andtest_mp_tuner_fault.Test Result
On gfx950 / MI355X, one GPU (
HIP_VISIBLE_DEVICES=1):ruff checkandblackare clean on the changed files.test_mha_fwd_mp1and bothtest_mp_tuner_faulttests pass.--candidate-sample 8(10 candidates per shape with the shipped defaults and the incumbent): the exhaustive strategy took 64 s and the race 17 s. Both reached the same decision on both shapes: asm_v3 auto-select (35 us and 87 us) was not beaten by 3%, so no row was written.Not validated here: the machine has only gfx950, so the gfx942 paths are untested on this revision.
Related PRs
mp_tunertyped statuses,MpTunerTask,measurement_kwargsand the central tuning policy with its singlemin_improvement_pct, which this PR extends with the finalist and race policies and the noise floor.PromotionPolicy,gate_against_incumbentand, if it wants a race,block_racewithRacePolicy.Submission Checklist