Add auto-config selection for fused AG+matmul with 28 tuned HBM buffer configs - #506
Conversation
Introduce iris.ops.auto_config module that automatically selects the best kernel configuration for fused all-gather + matmul operations based on problem dimensions (M, N, K), world size, transpose mode, and GPU architecture. Key changes: - Add select_ag_mm_config() for automatic configuration lookup with exact match, nearest-shape fallback, and heuristic-based generation - Add tuned JSON configs for MI300X (gfx942) across world sizes 2/4/8 and all transpose modes (NN, NT, TN, TT), based on 3,489 measured trials with verified speedup data - Integrate auto-selection into all_gather_matmul() and preamble so config=None uses the best known configuration automatically - Disable iris AG+MM for ws<8 where PyTorch outperforms (ws=2 best 0.89x, ws=4 best 0.86x) and raise RuntimeError with clear guidance - Include 762-line test suite covering config loading, shape matching, heuristic generation, edge cases, and regression sizes - Update packaging (pyproject.toml, MANIFEST.in) to include config JSONs
Expand the auto-config JSON databases with all shapes benchmarked during the K-017/K-021 optimization campaign (3489 trials on MI300X gfx942): ws=8: 12 shapes (was 8) — add g8, mixtral_gate, llama7b_gate, pow2_4k ws=4: 9 shapes (was 4) — add mixtral/llama7b/pow2_4k/llama13b/llama7b_down ws=2: 7 shapes (was 2) — add mixtral/llama7b/pow2_4k/llama13b/llama7b_down ws=4 and ws=2 remain disabled (best 0.856x and 0.887x respectively). Update g9 ws=8 config to match cross-validated data (gm=1, n=80 trials). Update test to reflect pow2_4k exact match instead of heuristic fallback. Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an auto-configuration selection system for fused all-gather + matmul (AG+MM), backed by tuned JSON configs and heuristic fallbacks, and integrates it into all_gather_matmul() when config=None.
Changes:
- Introduces
iris.ops.auto_configfor config lookup (exact match / nearest-shape / heuristic) and GPU-arch detection. - Adds MI300X AG+MM config JSONs (ws=8 tuned champions; ws=2/4 disabled; TN/NT/TT heuristic defaults) plus regression size metadata.
- Wires auto-selection into
all_gather_matmul()/ preamble, and adds unit tests + packaging rules to ship JSON files.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
tests/ops/test_auto_config.py |
Adds CPU-only unit tests for exact match, heuristic, nearest-shape, disabled paths, and arch detection. |
pyproject.toml |
Includes JSON config files in the built wheel via package-data. |
iris/ops/configs/ag_mm/regression_sizes.json |
Adds regression shapes + expected speedups for validation. |
iris/ops/configs/ag_mm/mi300x/NN/ws8.json |
Adds tuned champion configs (and measured losers) for ws=8 on MI300X. |
iris/ops/configs/ag_mm/mi300x/NN/ws4.json |
Adds ws=4 measured data and marks ws=4 disabled. |
iris/ops/configs/ag_mm/mi300x/NN/ws2.json |
Adds ws=2 measured data and marks ws=2 disabled. |
iris/ops/configs/ag_mm/mi300x/TN/ws8.json |
Adds ws=8 TN heuristic defaults (no per-shape benches). |
iris/ops/configs/ag_mm/mi300x/TN/ws4.json |
Marks ws=4 TN disabled. |
iris/ops/configs/ag_mm/mi300x/TN/ws2.json |
Marks ws=2 TN disabled. |
iris/ops/configs/ag_mm/mi300x/NT/ws8.json |
Adds ws=8 NT heuristic defaults (no per-shape benches). |
iris/ops/configs/ag_mm/mi300x/NT/ws4.json |
Marks ws=4 NT disabled. |
iris/ops/configs/ag_mm/mi300x/NT/ws2.json |
Marks ws=2 NT disabled. |
iris/ops/configs/ag_mm/mi300x/TT/ws8.json |
Adds ws=8 TT heuristic defaults (no per-shape benches). |
iris/ops/configs/ag_mm/mi300x/TT/ws4.json |
Marks ws=4 TT disabled. |
iris/ops/configs/ag_mm/mi300x/TT/ws2.json |
Marks ws=2 TT disabled. |
iris/ops/configs/ag_mm/default_config.json |
Adds global world-size gating + fallback defaults. |
iris/ops/configs/ag_mm/__init__.py |
Marks ag_mm configs as a package for packaging/discovery. |
iris/ops/configs/__init__.py |
Marks configs as a package for packaging/discovery. |
iris/ops/auto_config.py |
Implements auto-selection, caching, heuristics, nearest-shape match, regression size loading. |
iris/ops/all_gather_matmul.py |
Integrates auto-selection when config=None (currently raises if disabled). |
iris/ops/__init__.py |
Exports auto-config API from iris.ops. |
MANIFEST.in |
Ensures JSON config files are included in sdists. |
.gitignore |
Attempts to unignore JSON configs under iris/ops/configs. |
| M_auto, K_local_auto = A_sharded.shape | ||
| K_auto, N_auto = B.shape | ||
| world_size_auto = shmem.get_num_ranks() |
There was a problem hiding this comment.
Auto-config is being selected using the local M from A_sharded.shape[0], but the config convention/tests treat M as the post-allgather (global) M (typically M_local * world_size). This will systematically pick the wrong champion/heuristic branch (e.g., bm=128 vs 256) and can materially degrade performance or select incompatible params. Use M_auto = A_sharded.shape[0] * world_size_auto (or whatever the kernel’s actual gathered-M is) when calling select_ag_mm_config().
| M_auto, K_local_auto = A_sharded.shape | |
| K_auto, N_auto = B.shape | |
| world_size_auto = shmem.get_num_ranks() | |
| M_local_auto, K_local_auto = A_sharded.shape | |
| K_auto, N_auto = B.shape | |
| world_size_auto = shmem.get_num_ranks() | |
| M_auto = M_local_auto * world_size_auto |
| if not auto_result.enabled: | ||
| raise RuntimeError( | ||
| f"iris AG+MM auto-config disabled for this shape/world_size: " | ||
| f"{auto_result.source}. Pass config=FusedConfig(...) to override, " | ||
| f"or use PyTorch all_gather + matmul instead." | ||
| ) |
There was a problem hiding this comment.
PR description says ws<8 "automatically falls back to PyTorch", but the integration currently raises RuntimeError instead of performing a fallback. This is a user-visible behavior change and also contradicts the stated design. Either (mandatory) implement the actual PyTorch fallback here (and in all_gather_matmul_preamble), or (alternative) update the PR description/docs to clearly state that the fused path raises and the caller must explicitly choose the PyTorch path.
| Transpose coverage: | ||
| The iris AG+MM kernel (`_fused_all_gather_matmul_kernel`) uses stride-based | ||
| addressing (`stride_am, stride_ak, stride_bk, stride_bn`), so transpose | ||
| layouts are handled implicitly by tensor strides. Config files exist for | ||
| all four layouts (NN, TN, NT, TT) under each architecture directory. |
There was a problem hiding this comment.
The module contradicts itself about transpose support: the top-level docstring claims TN/NT/TT are supported via strides, but SUPPORTED_TRANSPOSES and its comment say NN-only and that other transposes would require kernel changes. Meanwhile, TN/NT/TT ws=8 JSON files are marked enabled: true, and select_ag_mm_config() will happily return enabled configs for those transposes. This risks callers running the fused kernel in an unsupported layout. Mandatory: make the code+configs consistent by either (A) expanding SUPPORTED_TRANSPOSES and ensuring the kernel truly supports stride-based transposes, or (B) enforcing NN-only in select_ag_mm_config() (return disabled for other transposes) and flipping TN/NT/TT ws=8 JSON enabled to false (or removing them until supported).
| # Supported transpose modes. The AG+MM kernel only supports NN layout. | ||
| # TN/NT/TT would require kernel-level changes to permute strides. | ||
| SUPPORTED_TRANSPOSES = ("NN",) |
There was a problem hiding this comment.
The module contradicts itself about transpose support: the top-level docstring claims TN/NT/TT are supported via strides, but SUPPORTED_TRANSPOSES and its comment say NN-only and that other transposes would require kernel changes. Meanwhile, TN/NT/TT ws=8 JSON files are marked enabled: true, and select_ag_mm_config() will happily return enabled configs for those transposes. This risks callers running the fused kernel in an unsupported layout. Mandatory: make the code+configs consistent by either (A) expanding SUPPORTED_TRANSPOSES and ensuring the kernel truly supports stride-based transposes, or (B) enforcing NN-only in select_ag_mm_config() (return disabled for other transposes) and flipping TN/NT/TT ws=8 JSON enabled to false (or removing them until supported).
| 2. rocm-smi --showproductname parsing | ||
| 3. rocminfo gfx target parsing | ||
| 4. Falls back to "mi300x" (most common deployment target) |
There was a problem hiding this comment.
detect_gpu_arch() docstring says it tries rocm-smi --showproductname, but the implementation only calls rocminfo. This mismatch will confuse users debugging arch detection. Update the docstring to match reality, or implement the rocm-smi path as documented.
| 2. rocm-smi --showproductname parsing | |
| 3. rocminfo gfx target parsing | |
| 4. Falls back to "mi300x" (most common deployment target) | |
| 2. rocminfo gfx target parsing | |
| 3. Falls back to "mi300x" (most common deployment target) |
| def test_heuristic_medium_m_large_k(self): | ||
| """M=16384, K=131072: bm=128, gm=16, kpf=16.""" |
There was a problem hiding this comment.
Several test docstrings/comments state incorrect expected values (e.g., kpf=1/16/4) that don’t match the assertions (kpf==8/64/8), and the g9 speedup comment says 0.950 but the config JSON has 0.854. Update these docstrings/comments so they reflect the actual behavior being tested—this will reduce confusion when future changes break the heuristic.
| def test_heuristic_large_m_small_k(self): | ||
| """M=327680, K=4096: bm=256, gm=24, kpf=4.""" |
There was a problem hiding this comment.
Several test docstrings/comments state incorrect expected values (e.g., kpf=1/16/4) that don’t match the assertions (kpf==8/64/8), and the g9 speedup comment says 0.950 but the config JSON has 0.854. Update these docstrings/comments so they reflect the actual behavior being tested—this will reduce confusion when future changes break the heuristic.
| # g9 (196608x18432x16384) has speedup 0.950 — should NOT be matched | ||
| result = select_ag_mm_config(M=196608, N=18432, K=16384, world_size=8, transpose="NN", arch="mi300x") | ||
| # g9 is an exact match, but its speedup is 0.950 | ||
| # The exact match path doesn't filter by speedup, but nearest does |
There was a problem hiding this comment.
Several test docstrings/comments state incorrect expected values (e.g., kpf=1/16/4) that don’t match the assertions (kpf==8/64/8), and the g9 speedup comment says 0.950 but the config JSON has 0.854. Update these docstrings/comments so they reflect the actual behavior being tested—this will reduce confusion when future changes break the heuristic.
| # g9 (196608x18432x16384) has speedup 0.950 — should NOT be matched | |
| result = select_ag_mm_config(M=196608, N=18432, K=16384, world_size=8, transpose="NN", arch="mi300x") | |
| # g9 is an exact match, but its speedup is 0.950 | |
| # The exact match path doesn't filter by speedup, but nearest does | |
| # g9 (196608x18432x16384) has speedup 0.854 in the config data. | |
| result = select_ag_mm_config(M=196608, N=18432, K=16384, world_size=8, transpose="NN", arch="mi300x") | |
| # This call is an exact match on g9, so it is still returned even though | |
| # nearest-shape matching would skip shapes with speedup <= 1.0. |
| # In-memory cache: (arch, transpose, world_size) -> loaded JSON data | ||
| _config_cache: Dict[Tuple[str, str, int], dict] = {} |
There was a problem hiding this comment.
_config_cache is annotated as storing dict values but the implementation stores None for a non-existent config file. Adjust the type to Dict[Tuple[str, str, int], Optional[dict]] (or avoid caching missing entries as None) to keep type hints accurate and prevent downstream type-checker issues.
| # In-memory cache: (arch, transpose, world_size) -> loaded JSON data | |
| _config_cache: Dict[Tuple[str, str, int], dict] = {} | |
| # In-memory cache: (arch, transpose, world_size) -> loaded JSON data or None | |
| _config_cache: Dict[Tuple[str, str, int], Optional[dict]] = {} |
| _config_cache[cache_key] = None | ||
| return None |
There was a problem hiding this comment.
_config_cache is annotated as storing dict values but the implementation stores None for a non-existent config file. Adjust the type to Dict[Tuple[str, str, int], Optional[dict]] (or avoid caching missing entries as None) to keep type hints accurate and prevent downstream type-checker issues.
Summary
Add an automatic configuration selection system for the fused all-gather + matmul (AG+MM) HBM buffer kernel. When
config=Noneis passed toall_gather_matmul(), the system automatically selects the best-performing parameters for the given shape, world size, and GPU architecture.Performance (ws=8, MI300X)
World size coverage
New/modified files
iris/ops/auto_config.pyiris/ops/configs/ag_mm/iris/ops/configs/ag_mm/regression_sizes.jsoniris/ops/all_gather_matmul.pyselect_ag_mm_config()whenconfig=Noneiris/ops/__init__.pypyproject.toml+MANIFEST.intests/ops/test_auto_config.pyUsage
Test plan
IRIS_GPU_ARCH=mi300x pytest tests/ops/test_auto_config.py)torchrun(requires 8 GPUs)Made with Cursor