aiter-config-shape
DevelopmentHow to add/upload tuned config CSVs under aiter/configs (incl. model_configs/) without introducing duplicate shapes, and how to find & resolve duplicate-shape collisions. Use whenever adding a model's tuned config, merging/uploading config CSVs, editing anything under aiter/configs/**, or when a run hits "duplicate shape entries during merge".
How to use this skill
Bring this guide into your coding agent with a prompt tailored to the tool you use.
- Open your project in Codex.
- Copy and paste the prompt below.
- Review files and risks before approving any action.
Inspect the Agent Skill at https://github.com/ROCm/aiter/blob/HEAD/.claude/skills/aiter-config-shape/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/aiter-config-shape, but wait for my approval before writing files or running scripts.
Copying this prompt does not install or run the skill. Review third-party files before use.
aiter config shape-collision standard
Tuned config CSVs in aiter/configs/ are not read one file at a time. At
runtime aiter/jit/core.py::AITER_CONFIGS.get_config_file merges, per family,
the canonical aiter/configs/<name>.csv plus every
aiter/configs/model_configs/*<name>*.csv, and update_config_files
de-duplicates that merge on a key derived from the matching untuned file's
columns (+ cu_num/gfx/_tag). If two rows across the merged files share that
key, the merge raises RuntimeError: Found N duplicate shape entries during merge of '<family>'.
Consequence: a per-model config you add can be perfectly fine alone yet collide
with a different model's config once both are on main. Single-PR CI only
merges your file with current main, so two PRs that each add the same shape to
different model files both pass, then break main after both land
(cross-PR / merge-skew hazard). Reviewers routinely forget this.
Follow this whenever you add "tuned configs for model Y", upload/merge config
CSVs, or touch anything under aiter/configs/**.
The hard rules
- A key is (untuned columns) +
cu_num, plusgfx/_tagwhen those columns are present. Never hand-pick key columns — they come from the family's*_untuned_*.csvheader, exactly as runtime derives them (update_config_filesappendscu_numif absent,gfxonly when agfxcolumn exists in the merge, and_tagonly when a_tagcolumn exists). Do not assumeM,N,Kalone: e.g.a8w8also keys onq_dtype_w,bf16onbias,dtype,outdtype,scaleAB,bpreshuffle,fmoeon the fulltoken,model_dim,inter_dim,expert,topk,act_type,dtype,q_dtype_*,q_type, use_g1u1,doweight_stage1(+_tag). - Your file is merged with the canonical file AND every other model file of
the same family. Before uploading, check the merge, not just your file in
isolation. The family is decided by the substring in the filename
(
*<tuned_file_name>*), e.g.dsv4_bf16_tuned_gemm.csvjoins thebf16_tuned_gemmfamily. - One shape, one row across the whole family. If the shape already exists (in the canonical file or another model's file), do not re-add it. If yours is genuinely faster, replace the existing row rather than adding a second.
- Run the collision guard and make it pass before you push. (below)
- Resolve, never suppress. Fix by removing the redundant row (keeping the
lowest-
uswinner) — do not widen keys, rename shapes, or delete the guard. gfxmatters. Archs that share acu_num(e.g. gfx950 vs gfx1250 both report 256) are only distinguishable bygfx. Keep the realgfxthe tuner wrote; don't drop the column. Legacy files withoutgfxare backfilled fromcu_num(256→gfx950, 80/304→gfx942) — do not rely on that for new archs.
Detect: run the guard
The authoritative check drives the real runtime merge (no re-implemented
logic, so it can't drift) against a temp copy of aiter/configs/:
python3 -m unittest op_tests.tuning_tests.test_config_shape_collision -v
- Requires torch (importing
aiterpulls it in); it skips cleanly where torch is absent, so run it in an env/container that has torch. - A failing family surfaces the runtime
RuntimeError: aFound N duplicate shape entries during merge of '<family>'message, the colliding rows as aDuplicate rows:table (the merged config columns — no_src/key line is printed), and anUpdated files:list of the source CSVs that were rewritten.
To see it the way production does (runtime prints the colliding rows), trigger the merge directly on a scratch copy:
import shutil, tempfile, aiter.jit.core as core
tmp = tempfile.mkdtemp(); shutil.copytree("aiter/configs", f"{tmp}/aiter/configs")
core.AITER_ROOT_DIR = tmp
type(core.AITER_CONFIGS).get_config_file.cache_clear()
core.AITER_CONFIGS.get_config_file(
"AITER_CONFIG_GEMM_BF16", f"{tmp}/aiter/configs/bf16_tuned_gemm.csv", "bf16_tuned_gemm"
) # raises RuntimeError listing duplicate rows + which files
Find which rows/files collide
The RuntimeError (and the test failure) already prints every duplicate row and
its source file. To locate them yourself for a family, merge its files and group
by the runtime key (read the key from the untuned header — don't invent it):
import glob, os, pandas as pd
name = "bf16_tuned_gemm" # the family
untuned = f"aiter/configs/{name.replace('tuned','untuned')}.csv"
key = pd.read_csv(untuned, nrows=0).columns.str.strip().tolist() + ["cu_num", "gfx"]
files = [f"aiter/configs/{name}.csv"] + [
p for p in glob.glob(f"aiter/configs/model_configs/*{name}*.csv")
if "untuned" not in os.path.basename(p)
]
df = pd.concat([pd.read_csv(f).assign(_src=os.path.basename(f)) for f in files if os.path.exists(f)])
key = [k for k in key if k in df.columns]
print(df[df.duplicated(key, keep=False)].sort_values(key)[key + ["us", "_src"]].to_string(index=False))
Resolve: the auto-dedup already exists — just trigger it on the real tree
Do not write a dedup script. update_config_files (aiter/jit/core.py)
already resolves collisions: for each duplicate key it keeps the lowest-us row,
writes the pruned CSVs back to the source files, and raises asking you to
re-run. Your job is only to trigger that write-back against the real tree and
commit the result.
-
Trigger it (one command): run the guard's
--fixmode. Unlike the default detection (which runs on a temp copy to stay read-only),--fixruns on the real checkout soupdate_config_files' write-back lands where you can commit it:python3 op_tests/tuning_tests/test_config_shape_collision.py --fixIt resolves every family, keeps the lowest-
usrow per shape, rewrites the source CSVs, and prints which files/rows changed. (Equivalent to running the model/op/tuner once, or callingcore.AITER_CONFIGS.get_config_file(...)on the real tree — same built-in auto-dedup, no extra logic.) -
Commit the rewritten config CSVs (
git diffshows the pruned rows), then re-run without--fixto confirm clean. -
Manual alternative: if you'd rather edit by hand, from the duplicate list delete the slower-
uscopy — usually the shape already exists in the canonical or an older model file, so remove it from the file you are adding. Keep exactly one row per key (the lowestus).
After resolving, re-run the guard until it passes.
Cross-PR awareness (for authors and reviewers)
- Your green CI does not prove
mainstays green — it never saw the other in-flight config PRs. When adding shapes shared across models (common decode shapes, MoE token grids), assume another PR may add the same shape. - Backstop (once wired): if this guard is added to a push-to-
mainjob, two PRs that race and both land a colliding shape turnmainred immediately — fix forward by pruning the duplicate (lowest-uswins). The guard is not wired into CI yet, so today this is a manual/local check, not an automatic backstop. - Reviewing a config PR: skim other open PRs touching the same family, and trust the guard rather than eyeballing shapes.
References (single source of truth — keep in sync)
- Guard test:
op_tests/tuning_tests/test_config_shape_collision.py(drives the real merge; add a family here if you register a newAITER_CONFIG_*file). - Runtime merge + dedup:
aiter/jit/core.py::get_config_file/update_config_files(key derivation, gfx backfill,_tag, write-back). - GPU end-to-end counterpart:
op_tests/tuning_tests/test_run_config.py(runs every merged shape).
Anti-patterns
- ❌ Checking only your new file, not the family-wide merge.
- ❌ Hand-picking key columns (missing
gfx, adding non-keylibtype) instead of reading the untuned header. - ❌ Adding a second row for a shape that already exists somewhere in the family.
- ❌ "Fixing" a collision by widening the key, renaming a shape, or disabling the guard.
- ❌ Assuming green PR CI means
mainis safe (ignores concurrent config PRs). - ❌ Dropping/zeroing the
gfxcolumn so shapes look distinct. - ❌ Re-implementing the merge/dedup in a new script — call the runtime or the guard test instead.