Skip to content

feat(hipdnn): IngestorGenerator emits per-arch heuristic bindings (engine.heuristics) - #15101

Merged
AviralGoelAMD merged 3 commits into
developfrom
users/avirgoel/hipdnn/ingestor-heuristics-bindings
Nov 5, 2026
Merged

AviralGoelAMD merged 3 commits into
developfrom
users/avirgoel/hipdnn/ingestor-heuristics-bindings

Conversation

@AviralGoelAMD

Copy link
Copy Markdown
Contributor

ISSUE ID : AICK-2545

Why this change

uhd_gen promote binds a trained model per architecture in the UED role map ("sort_kernel_catalog": {"default": <native>, "gfx942": [<model>]}, or a new predict_engine role). The IngestorGenerator only writes sort_kernel_catalog.default (codegen/generator.py:461-463), so a bundle with a promoted model fails the regeneration gate's byte compare of the UED (g2) wherever its KDP is pulled, and the next regeneration drops the binding. Raised on #13030, which makes the same UED edit for gfx950_attention_dense.

What changed

Area / files Crux of change
codegen/config_loader.py, codegen/models.py Optional engine.heuristics: role -> arch -> UHD ids. Roles are the three promote --role accepts; keys one concrete gfx arch (or default, except under sort_kernel_catalog, which the generator owns); values non-empty lists of distinct UUIDs. Refused on a slotted engine and, for sort_kernel_catalog, with engine.heuristic: none. Each refusal names the key (exit 2).
codegen/generator.py (build_ued) Emits the bindings in the key order promote writes: arch keys right after default; other roles after knobs / behavior_notes.
README.md "Shipping a trained model": promote, then put the printed arch and id in engine.heuristics of both the manifest (configs/<bundle>.engine.yaml, which registry_enumerator copies into every config and g4 compares) and the selection, regenerate, dvc add heuristics.
tests/ Loader refusals and order, UED emission order, byte equality with a real promoted UED (fixture from promote on the gfx942 unified 3D bundle), regeneration of a bundle with a heuristics/ subtree (same ids), manifest -> selection -> UED carry-through.
  • Without engine.heuristics every output is byte-identical (existing gates g1-g6 on the shipped bundles).
  • prior_ids is unchanged: it still resolves the native UHD from default only, and the model UHDs under heuristics/ neither mint nor reuse ids.
  • The arch-name check copies tools/device_probe.py's rule (codegen cannot import tools/); a test pins the two to the same answers.

How we validated

Gate Result
IngestorGenerator suite 1702 passed, 41 skipped (develop baseline 1669 / 41; 33 new tests)
Negative control Removing the emission from build_ued fails all 4 new generator tests
Promote equality Generated UED == uhd_gen promote output, byte for byte
pre-commit Clean over origin/develop..HEAD

Notes

…m engine.heuristics

uhd_gen promote binds a trained model per arch in the UED role map
(sort_kernel_catalog.<arch>, predict_engine.<arch>, ...). The generator only wrote
sort_kernel_catalog.default, so a promoted bundle failed the byte-for-byte
regeneration gate. engine.heuristics (role -> arch -> UHD ids) now carries those
bindings; build_ued emits them in the key order promote writes, so a promoted UED
regenerates byte for byte. Absent key: outputs unchanged.
registry_enumerator copies the manifest's engine block into every config it writes
and gate g4 requires the selection's engine to equal it, so engine.heuristics must
be set in configs/<bundle>.engine.yaml as well as the selection. Test that the
enumerator carries it into the generated engine and UED.
@therock-pr-bot

therock-pr-bot Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

therock-pr-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

Windows CI checks the git-tracked fixture out as CRLF while the generator writes LF,
so the byte comparison failed there. Compare text, as the regeneration gates do.
@codecov

codecov Bot commented Nov 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #15101      +/-   ##
===========================================
+ Coverage    77.72%   77.75%   +0.04%     
===========================================
  Files         2963     2963              
  Lines       513494   513263     -231     
  Branches     79567    79567              
===========================================
+ Hits        399071   399072       +1     
+ Misses       86278    86048     -230     
+ Partials     28145    28143       -2     
Flag Coverage Δ *Carryforward flag
TensileLite-CPP 53.94% <ø> (ø) Carriedforward from 49a01e7
TensileLite-Unit 84.76% <ø> (ø) Carriedforward from 49a01e7
hipBLAS 90.62% <ø> (ø) Carriedforward from 49a01e7
hipBLASLt 70.05% <ø> (ø) Carriedforward from 49a01e7
hipCUB 79.17% <ø> (ø) Carriedforward from 49a01e7
hipDNN 87.26% <ø> (+<0.01%) ⬆️
hipFFT 42.57% <ø> (ø) Carriedforward from 49a01e7
hipRAND 76.12% <ø> (ø) Carriedforward from 49a01e7
hipSOLVER 69.67% <ø> (ø) Carriedforward from 49a01e7
hipSPARSE 87.03% <ø> (ø) Carriedforward from 49a01e7
rocBLAS 69.20% <ø> (ø) Carriedforward from 49a01e7
rocFFT 46.49% <ø> (ø) Carriedforward from 49a01e7
rocRAND 57.32% <ø> (ø) Carriedforward from 49a01e7
rocSOLVER 76.65% <ø> (ø) Carriedforward from 49a01e7
rocSPARSE 83.54% <ø> (ø) Carriedforward from 49a01e7
rocSPARSE-integration ?
rocSPARSE-unit ?
rocThrust 93.15% <ø> (ø) Carriedforward from 49a01e7

*This pull request uses carry forward flags. Click here to find out more.
see 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@AviralGoelAMD
AviralGoelAMD marked this pull request as ready for review November 5, 2026 20:01
@AviralGoelAMD
AviralGoelAMD requested a review from a team as a code owner November 5, 2026 20:01
@AviralGoelAMD
AviralGoelAMD merged commit 05fa3eb into develop Nov 5, 2026
41 checks passed
@AviralGoelAMD
AviralGoelAMD deleted the users/avirgoel/hipdnn/ingestor-heuristics-bindings branch November 5, 2026 21:41
ThruptiRajLakshmanaGowda added a commit that referenced this pull request Nov 5, 2026
#15101 adds engine.heuristics (role -> arch -> UHD ids) to the generator
for the same job engine.trained_rankers did here. Adopt its shape, and its
loader, emission order, README section and tests verbatim, so whichever PR
lands second only drops duplicated hunks.

What this PR keeps is the catalog gate, now its own optional key:
engine.trained_catalog_sha256 maps each arch bound under
heuristics.sort_kernel_catalog to the fingerprint of the catalog its models
were trained on. When set, its keys must equal those arch keys.
tests/test_trained_rankers.py still fails when a shipped KDP no longer
matches, so a catalog edit forces a retrain.

The unified 2D/3D configs move to the new keys. Their UEDs regenerate
byte-identical.

Co-Authored-By: Claude <noreply@anthropic.com>
ThruptiRajLakshmanaGowda added a commit that referenced this pull request Nov 5, 2026
#15101 (engine.heuristics) has merged. This branch carried its hunks
verbatim from the draft. The conflicts were where its final form moved
lines next to ours: keep develop's engine.heuristics code and add back
only this PR's engine.trained_catalog_sha256 (EngineSpec field, the
_KNOWN_ENGINE entry, the README schema line).
SUPPORT_MATRIX_OVERVIEW.md is regenerated from the merged claims
(develop's 12335 graphs plus the 6 BshdStats graphs).

Co-Authored-By: Claude <noreply@anthropic.com>
jdcampbe added a commit that referenced this pull request Nov 5, 2026
…nfigs

develop's engine.heuristics (#15101) is how the IngestorGenerator carries the role-map
entries `uhd_gen promote` adds to a UED. Without them, regenerating these bundles drops
their trained models and the regeneration gate's byte compare fails.

- gfx950_attention_dense (manifest and selection): the gfx950 catalog rankers and
  engine-level models.
- gfx1151_wmma_attention and gfx1151_wmma_attention_decode: the gfx1151, gfx1100 and
  gfx1201 engine-level models.

Each config now regenerates its shipped UED byte for byte. Two tests follow the configs:
the unslotted-render digests for the two changed reference configs, and the packer-load
test, which gives the rendered bundle the heuristics/ subtree it ships with.
AviralGoelAMD added a commit that referenced this pull request Nov 7, 2026
…15102)

ISSUE ID : AICK-2545

> Base is `develop`: #14259 (the engine) and #15101
(`engine.heuristics`) have merged. This PR's diff against `develop` is
the trained kernel ranker: the UED binding, the model folder under
`heuristics/`, the bindings in the two IngestorGenerator configs, and
the packaging and test changes that make an un-pulled model safe and a
bound model checked.

## What this adds
One trained model for `hipkernel:Gfx942AttentionUnified3d` (gfx942
split-KV decode, 560-kernel catalog), on gfx942 only; other
architectures keep the native scorer.

- **L2, `sort_kernel_catalog`** (`ced4e2d1`, LightGBM, 500 trees, 72
features): ranks the engine's kernels and picks the one to run, instead
of the native scorer.

## What changed

| Area / files | Crux of change |
|---|---|
|
`descriptors/rocKE/gfx942_attention_unified_3d/gfx942_attention_unified3d.ued.json`
| `sort_kernel_catalog` gains `"gfx942": ["ced4e2d1-…"]` after the
native `default` (written by `uhd_gen promote`). Nothing else changes. |
| `.../heuristics/<UED id>/sort_kernel_catalog/gfx942/tflops/` |
`heuristic.uhd.json` in git (reviewable diffs); `model.bin` DVC-tracked
beside it (`model.bin.dvc`, `ingestor` remote, pushed and in sync) with
a `.gitignore`, the layout #14831 uses. |
| `IngestorGenerator/configs/gfx942_attention_unified_3d{.engine,}.yaml`
| `engine.heuristics` records the same binding in the manifest and the
selection, so g2 (UED byte compare) and g4 (engine equality) hold. The
manifest comment says when to retrain the L2. |
| `descriptor-packaging/` (`hkp_pack`, `HkpPackaging.cmake`, tests,
READMEs) | A model whose `model.bin` was never pulled is packed without
its UHD and the pack warns; `--require-dvc` (set by
`HIPKERNELPROVIDER_REQUIRE_DVC_DESCRIPTORS=ON`) makes it an error. |
| `IngestorGenerator/tests/test_registry_regeneration.py` | New g8 gate:
the UED binds exactly the UHDs shipped, each was trained against the
shipped descriptors (provenance), and each `model.bin` is one the engine
loads (payload hash, FlatBuffer, feature hash). |
|
`tests/engines/kernel_ingestor_engine/packs/TestGfx942AttentionUnified3dModelBinding.cpp`
| New census suite: a bound model must load and its `model.bin` must be
on disk; an absent UHD passes only when the pack's skip record names it;
a bound engine predictor must record this build's selector revision. |
| `hkp_pack` skip record | `hkp-unpulled-models.record` at each packed
arch root, written only when the pack left a UHD out for an un-pulled
model. Not a `.json`, so the loader and validator ignore it. |
| Bundle README, `descriptors/README.md`, generator README | Document
the per-model `model.bin.dvc` pointers, how to pull them, and the models
the bundle ships. |

## Why it works
The model reads graph features hipDNN publishes plus the kernel's
`block_size`/`num_segments` knobs. It reads **no scale feature**: every
training graph had `attn_scale_value = 1/sqrt(d)`, so an earlier model
(`8b3c960a`) split on the scale as a stand-in for head size, and a graph
with no scale or a custom scale could rank like another head size
(@jdcampbe's review; on device, canary v2 showed D256 picking different
kernels at scale 1/16 and 1.0). Removing both scale features makes the
ranking independent of the scale by construction. Provenance (UED
`4208dcf6`, KMD `fa42fab2`, UMD `557a7c3d`) equals the shipped bundle's,
so the loader admits it.

## How we validated

| Gate | Result |
|---|---|
| L2 held-out ranking (221 problems never trained on) | Throughput
regret mean **2.08%** (p95 10.1%, worst 21.9%) vs native scorer
**4.4%**; the earlier scale-reading model was 1.96% |
| L2 on device vs native (gfx942 device, 200 held-out graphs) | Model
picks take **0.980x** native's time over 197 paired graphs. The
availability gate failed: 3 of 400 `hipdnn_bench` starts (2 model-side,
1 native-side) exited with no output. Rerun of those 3 graphs, 10 starts
each per ranker: **60/60** ran, same kernel every time |
| Scale canary v3 (gfx942 device) | D64/128/256 x {scale 1/sqrt(d),
absent, 1.0, 0.5}: the model decided 12/12 runs and picked **one kernel
per head size** in every case |
| Ground truth vs owner (rocKE) times, margin 10% | Run on the earlier
model: strict fails for model **and** native, on the smallest shapes
every row misses with either ranker, which points to a fixed per-call
cost rather than the pick (AICK-3031); on the largest shapes the model
kept 96/96 within 10% (native 91/96). Not re-run for `ced4e2d1` |
| Packaging | `hkp_packaging_product` ships `heuristics/`
(`heuristic.uhd.json` + `model.bin`, no DVC files) in the gfx942 product
tree; `hipdnn_validate_descriptors`: exit 0, loads the native default
and the L2 |
| N1: un-pulled build | With the L2's `model.bin` absent and its pointer
present: a default build packs with a warning that names the pointer and
the `dvc pull` command, and the census passes; with `--require-dvc` the
pack fails with the same command. With the file present the census loads
the L2. |
| N2: bindings checked | g8 passes on the shipped L2 (binding,
provenance, payload). The census checks that the bound L2 loads and that
its `model.bin` is on disk; it also fails if the UED ever binds an
engine predictor whose recorded selector revision differs from the
build's (shown against the held predictor). It accepts an absent UHD
only in a default build and only when the pack's skip record names it.
Controls on scratch copies of the packed tree: normal pack, census 2/2
pass; packed UHD deleted: census fails ("has no UHD in the packed tree,
and hkp-unpulled-models.record does not name it"); packed `model.bin`
deleted: census fails ("loaded, but its model file ... is absent from
the packed tree"); source `model.bin` absent, default pack: warns,
record lists the UHD, census passes with a note; same with
`--require-dvc`: pack fails. |
| Host | gfx942 unified 3D host tests 69/69; gfx942 census 15/15;
regeneration gates for gfx942 16/16; IngestorGenerator suite 1777 passed
(3 local-only `test_strict_skips_plugin.py` failures from another user's
`/tmp/pyproject.toml`, same without this change); descriptor-packaging
suite: 9 local-only failures, same before and after; pre-commit clean |

All timings are warm (the owner accepted warm labels for v1; cold labels
and acceptance are AICK-3028).

## Notes
- The L1 (`predict_engine`) is held for a follow-up: it must be
re-measured on a build of current source (its selector revision was
recorded on an older build).
- #13030 ships models for other engines and makes the same UED edit; the
generator approach is under discussion there.

---------

Co-authored-by: David Langbehn <47395968+dlangbe@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: dlangbe <David.Langbehn@amd.com>
Co-authored-by: AnaghaRaoAMD <anarao@amd.com>
Co-authored-by: ThruptiRajLakshmanaGowda <Thruptiraj.LakshmanaGowda@amd.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants