Repository navigation
feat(ck-tile): add dispatcher correctness and performance CI lanes for gfx942/gfx950 - #10897
Conversation
✅ All Policy Checks Passed
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🎉 All checks passed! This PR is ready for review. |
There was a problem hiding this comment.
Pull request overview
Adds CI coverage for the CK-Tile universal GEMM dispatcher by introducing new Jenkins pipeline stages and a Python-based, budget-limited search-space runner that builds and validates sampled dispatcher GEMM configurations.
Changes:
- Extend
projects/composablekernel/Jenkinsfilewith new boolean parameters and parallel stages to run dispatcher-focused tests. - Add dispatcher CI entrypoints in
groovy/vars/ck.groovyto configure/build dispatcher artifacts and invoke the new runner and correctness tests. - Introduce
dispatcher/tests/test_gemm_search_space.pyto enumerate, sample, build, run, and emit JSON results for dispatcher GEMM configs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| projects/composablekernel/Jenkinsfile | Adds CI parameters and stages for running dispatcher GEMM and universal dispatcher tests on specific GPUs. |
| projects/composablekernel/groovy/vars/ck.groovy | Adds Jenkins shared-library helpers to build dispatcher components and run/archive dispatcher test outputs. |
| projects/composablekernel/dispatcher/tests/test_gemm_search_space.py | New Python runner for stratified sampling of universal GEMM configs with correctness/perf reporting to JSON. |
Suppressed comments (1)
projects/composablekernel/dispatcher/tests/test_gemm_search_space.py:74
- _emulate() currently rounds fp8/bf8 inputs as fp16, which makes the CPU reference diverge from what the GPU kernel actually multiplies. That can hide correctness issues for fp8/bf8. Use the same fp8/bf8 encode/decode path as the existing dispatcher parity tests so the reference matches the kernel’s storage dtype.
def _emulate(x: np.ndarray, dtype: str) -> np.ndarray:
if dtype == "bf16":
u32 = np.ascontiguousarray(x, dtype=np.float32).view(np.uint32)
rounded = (u32 + ((u32 >> 16) & 1) + np.uint32(0x7FFF)) >> 16
return (rounded.astype(np.uint32) << 16).view(np.float32)
return x.astype(np.float16).astype(np.float32)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Adds a new 'Run DISPATCHER Tests' Jenkins stage parallel to the existing tile engine stages, targeting gfx942 with dispatcher_gemm_lib build and the tile_engine_dispatcher_bridge.py sweep runner. - Jenkinsfile: add RUN_DISPATCHER_TESTS bool param (default OFF) - Jenkinsfile: wire RUN_DISPATCHER_TESTS into the daily 22:00 cron - Jenkinsfile: add 'Run DISPATCHER Tests' stage on gfx942 - ck.groovy: add runDispatcherTests(arch, compiler) with DISPATCHER_RULE_SET=tests Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the 4-algo hardcoded sweep with a budget-limited stratified search over the full (dtype × layout × tile × trait) space, matching tile_engine's daily-tier coverage for universal GEMM. New files: - dispatcher/codegen/gemm_search_space.py: enumerate_configs wraps expand_sweep across all (dtype, layout) strata; sample_configs does stratified budget-limited sampling with daily rotating seed. - dispatcher/tests/run_gemm_search_space.py: CLI runner -- enumerate, sample, parallel JIT build, GPU run, correctness check, JSON results. Updated: - dispatcher/CMakeLists.txt: add DISPATCHER_GEMM_BUDGET cache var (500). - groovy/vars/ck.groovy: runDispatcherTests now calls run_gemm_search_space.py with --budget 500, all dtypes and layouts, writing dispatcher_gemm_results.json as an artifact. Coverage after this change: ~500 stratified configs across fp16/bf16/fp8/bf8 × rcr/rrr/crr/ccr (vs 4 hardcoded before). Co-Authored-By: Claude <noreply@anthropic.com>
…a file gemm_search_space.py was an unnecessary indirection -- tile engine has no equivalent; sampling lives inside the instance builder itself. Move the _daily_seed and _sample helpers directly into run_gemm_search_space.py and call expand_sweep from gemm_utils directly, matching the tile engine pattern more closely. Co-Authored-By: Claude <noreply@anthropic.com>
Budget is passed directly as --budget CLI arg to run_gemm_search_space.py from the groovy script; CMake is not in the loop for sampling, so the cache variable was dead code. Co-Authored-By: Claude <noreply@anthropic.com>
Three fixes: 1. Remove spurious 'cd build &&' -- cmake_build already cds into build/ before running execute_cmd; the extra cd would fail (build/build/). 2. Add archiveArtifacts for dispatcher_gemm_results.json to match tile_engine's JSON artifact pattern. 3. Add --warmup/--repeat args to run_gemm_search_space.py (default 5/5) and wire them in from the groovy call, matching tile_engine convention. Co-Authored-By: Claude <noreply@anthropic.com>
Prefer the values already computed by GpuGemmRunner over recomputing them manually. Fall back to manual computation only when result.max_rel is None (GpuGemmRunner does not populate it; GpuMultiABDRunner does). Co-Authored-By: Claude <noreply@anthropic.com>
CI requires changed code files to have an accompanying test file named test_<name>.py. Rename the runner to match the convention. Co-Authored-By: Claude <noreply@anthropic.com>
- Fix script name: run_gemm_search_space.py -> test_gemm_search_space.py - Build ck_tile_dispatcher (static lib) alongside dispatcher_gemm_lib so runtime-compiled tests can link against it - Add test_gemm_parity.py, test_batched_gemm_gpu_correctness.py, and test_batched_contraction_gpu_correctness.py to the gfx942 dispatcher stage - Add new runDispatcherTestsGfx950 groovy function and a parallel Jenkinsfile stage that runs test_bquant_gpu_correctness.py on gfx950 Co-Authored-By: Claude <noreply@anthropic.com>
Adds RUN_DISPATCHER_GEMM_UNIVERSAL_TESTS parameter and a new "Run DISPATCHER_GEMM_UNIVERSAL Tests on gfx942" stage that builds and runs only the gemm_universal benchmark (fp8/fp16/bf8/bf16, rcr/rrr/crr/ccr layouts) via the new ck.runDispatcherGemmUniversalTests() groovy function. Co-Authored-By: Claude <noreply@anthropic.com>
…CHER_GEMM_TESTS Renames the parameter and stage to RUN_DISPATCHER_GEMM_TESTS / runDispatcherGemmTests to allow future expansion beyond gemm_universal. Co-Authored-By: Claude <noreply@anthropic.com>
…batched_contraction Adds batched_gemm and batched_contraction to the RUN_DISPATCHER_GEMM_TESTS stage on gfx942: builds the tile engine benchmarks alongside dispatcher bindings, runs the benchmark scripts, and runs the GPU correctness tests for both operators. bquant is intentionally excluded (gfx950-only). Co-Authored-By: Claude <noreply@anthropic.com>
…eam_k, mx_gemm
Before this, the dispatcher CI covered gemm_universal, batched_gemm,
batched_contraction and bquant. Everything else in the dispatcher shipped with
only CPU-side/mocked unit tests, so a numeric regression in those bridges could
reach develop undetected.
Rather than fork five near-identical runners, generalize the existing
test_gemm_search_space.py with --variant {standard,grouped,multi_d,multi_abd,
stream_k}: expand_sweep, _ctypes_source_name and setup_multiple_gemm_dispatchers
already route every variant, so only the Gpu*Runner choice and the numpy
reference differ per variant. This keeps one sampling/reporting/JSON path
instead of five that drift.
The CI mirrors the shape of the tile_engine benchmark stages (per-op runner,
--warmup/--repeat/--json, per-arch stages, archived JSON) but deliberately not
their failure semantics: the tile_engine *_benchmark.py scripts never pass
--verify and return 0 unconditionally, so copying them literally would give
five permanently-green lanes. These lanes validate and exit 1 on mismatch.
multi_d runs the PassThrough epilogue. default_ci_config.json carries no
multi_d_config, so expand_sweep would otherwise fall back to MultiDAdd; the new
--elementwise-op flag pins it, and _verify reads cfg.elementwise_op so kernel
and reference always agree on the op. Note the similarly named
cde_elementwise_op is the multi_abd field, not this one.
mx_gemm has no expand_sweep -- mx_gemm_utils exposes only default_fp8_config()
/default_fp4_config() -- so it cannot join the sweep and instead runs the
two-config smoke test merged in #10132 on gfx950.
Also fixed along the way:
- expand_sweep emitted stream_k configs with non-cshuffle epilogues, which the
codegen then skips, surfacing later as a spurious build failure. Gate them.
- test_batched_{gemm,contraction}_gpu_correctness.py returned exit 0 with no
GPU while their add_test entries carried no SKIP_RETURN_CODE, so a CPU-only
runner reported a green PASS for a test that never ran. They now exit 77 and
the ctest properties record it as a skip. test_bquant gets the same 77 path
plus a real arch gate.
- ml_dtypes was in no CI dependency manifest, yet every dispatcher lane sweeps
bf16/fp8/bf8, which raises "requires the 'ml_dtypes' package" and counts as a
failure. Add it to the Docker image.
Arch matrix is bounded by the tile_engine CMake guards: stream_k is gfx942-only
(TODO: add gfx950 in the stream-K CMakeLists), mx_gemm is gfx950-only. gfx1201
is excluded -- expand_sweep yields zero configs for it in all 16 dtype x layout
strata because RDNA4 is warp_size 32 and the config's warp tiles are all 64
wide, so such a lane could only run red.
Verified on gfx90a: all five variants pass (20/20 kernels, exit 0), each lane
confirmed able to go red (_RTOL 1e-12 -> exit 1) and to skip (exit 77).
mx_gemm is unverified on hardware -- it needs a gfx950 node.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dfdfb43 to
4c0f0a3
Compare
…tifact path Three defects found reviewing the dispatcher CI before merge. 1. fp8/bf8 numerics were verified against a wrong reference. _emulate() fell through to float16 for fp8/bf8, so the reference rounded operands to fp16 while the kernel consumed real float8_e4m3fnuz / float8_e5m2fnuz. That left the reference strictly more precise than the kernel, failing every fp8/bf8 config by construction. A 4-dtype x 4-layout sweep (budget 64, size 512) went 32 pass / 32 fail, with all 32 failures fp8/bf8 at max_rel 0.033-0.065 against an _RTOL of 0.02. This predates the new variant lanes: the gemm_universal lane already sweeps all four dtypes, so it would have gone red on the first nightly. Quantize operands at the input dtype and the result at cfg.dtype_c -- they differ, and conflating them was the bug. The same sweep is now 64 pass / 0 fail, worst max_rel 0.0064 (bf16). _emulate had no other call site and is removed in favour of _quantize, which the grouped/multi_d branches already used correctly. 2. Build failures produced a green lane. The exit status ignored n_build_fail, so a total codegen or compile breakage -- every kernel failing to build, nothing verified -- still exited 0, and build failures were filtered out of the printed listing so the log gave no hint either. Verified by stubbing setup_multiple_gemm_dispatchers to return None: 0 pass / 0 fail / 4 build-fail now exits 1 and names all four. 3. The result JSONs were never archived. cmake_build runs execute_cmd from projects/composablekernel/build, but archiveArtifacts resolves against the workspace root, so the bare "dispatcher_*_results.json" glob matched nothing -- silently, because allowEmptyArchive is set. Qualify the path, and move the call into a finally block so a red lane keeps the JSON that explains why it went red. Re-ran grouped, multi_d, multi_abd and stream_k after the reference change: 4 pass / 0 fail each, exit 0.
Wrap runDispatcherGemmTests in try/finally so its gemm_universal /
batched_gemm / batched_contraction result JSON is archived on a red lane
too, matching runDispatcherTests. Remove the single-use
runDispatcherTestsGfx950 wrapper and call runDispatcherTests("gfx950", ...)
directly, consistent with the two-arg convention used everywhere else.
Co-Authored-By: Claude <noreply@anthropic.com>
…ne lanes runDispatcherTests called rocmnode() directly, but rocmnode is defined in the Jenkinsfile and a vars/ script does not inherit Jenkinsfile-local methods. All four call sites would have thrown MissingMethodException before reaching runOnHealthyNode. Both params default to false so PR builds never hit it, but the 22:00 develop cron sets both true. Now passed as the first parameter, the same convention runDownstreamTestsStages / runFMHATestsStages / runBuildCKStages already use. Delete runTileEngineBasicTests and runTileEngineGemmTests. Their parameters and stages were removed earlier on this branch, leaving both helpers with no callers. Retiring them on purpose: the operators they covered (preshuffle, the block_scale_gemm quant variants, batched contraction multiple ABD) are the next-phase set and mostly have no dispatcher bridge on develop yet, so a lane for them can only run red. The coverage delta belongs in the PR description. Fix the perf lane archive glob. That lane drives tile_engine's own benchmark drivers, so every file it writes is a tile_engine JSON -- gemm_universal_results .json and friends. Nothing there carries a dispatcher_ prefix; only the correctness lane writes those. The narrower glob matched zero files and allowEmptyArchive turned that into a silent success. allowEmptyArchive stays true because the call is in a finally block: on a cmake/ninja failure no JSON exists yet and a throwing archive step would mask the real exception. The signal that the run produced results moves into the drivers instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every driver returned 0 unconditionally. A run where the build directory held
no kernels, or where every launch failed at runtime, exited clean and the CI
lane went green with an empty or absent results JSON. Fixing this in the Jenkins
lane alone would only paper over it for one caller, so the accounting moves into
the drivers themselves.
GemmBenchmark now counts launch_attempted and launch_failed around run_kernel;
the seven standalone drivers that carry their own run_kernel loop count the same
way. export_json takes an optional launch_counts and adds
launches_attempted/succeeded/failed to the metadata when given, so the trend is
recoverable from an archived artifact.
Exit codes:
no launches attempted -> 1 (no kernels discovered; the build never produced
anything to run)
no results collected -> 1 (everything that was attempted failed)
some launches failed -> 0, with a WARNING line
The partial-failure case is deliberately a warning. This sweep runs every
generated config and individual ones legitimately fail to launch -- a tile shape
the arch does not support, a workspace too large for the problem size. Making
any single failure red trades a permanently-green lane for a permanently-red
one, which is no more informative. Total failure is already caught by the
empty-result check, and the per-run counts are in the JSON for anyone tracking
the trend.
The three out-of-scope positional callers of export_json (gemm_rowcolquant,
gemm_tensor_quant, gemm_preshuffle) are unaffected: launch_counts is the last
parameter and defaults to None.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…op false-green ctests
Four related test-side defects, all of which made a lane report success without
having exercised what it claims to cover.
_sample handed out the divmod remainder in sorted stratum order. With
budget < n_strata every per-stratum share is 0, so the entire budget was
remainder and always landed on the first four keys -- bf16/{ccr,crr,rcr,rrr}.
The ctest registration used --budget 4 against 16 strata, so no run in CI ever
compiled an fp8 or bf8 config, and the daily rotating seed changed only which
config was drawn inside those four fixed strata. Shuffle the stratum order
before the remainder loop, and raise the ctest budget to 16 so every stratum is
represented at least once.
The skip path called detect_gpu_arch() bare. That helper defaults to
fallback="gfx942" and so never returns falsy, making the `if not arch: return
_SKIP` branch dead -- a CPU-only runner walked past it and compiled gfx942
kernels for hardware it does not have. Ask for fallback="" so the branch is
reachable.
The three unittest.TestCase suites (grouped_gemm / multi_abd / mx_gemm GPU
correctness) were registered as bare `unittest discover`, which exits 0 when
every test skips. mx_gemm is gfx950-only, so on gfx942 that is a green tick
backed by zero assertions. They now run through run_unittest_77.py, which maps
all-skipped onto 77 and pairs with SKIP_RETURN_CODE. A driver rather than
converting the suites, so the skip decision stays in each test's own setUp.
Discovery collecting nothing returns 1, not 77, so a renamed file cannot report
Skipped forever.
Add test_search_space_sampling.py pinning both the sampler and the skip path.
CPU-only -- no GPU, hipcc or built kernels -- and both key tests were mutation
checked against the old implementations to confirm they fail there. The sampler
test asserts on stratum keys rather than whole samples because the buggy version
did vary the configs within its four fixed strata; the skip test stubs
detect_gpu_arch to honour the fallback it is handed, so it only passes if the
caller actually asks for fallback="".
Rename the quant helper functions from test_* to case_*. They take non-fixture
positional args, so pytest collected them as tests and errored at fixture
resolution -- 12 collection errors across the repo, now 0 with 1075 tests
collected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The gfx950 perf lane previously ran only gemm_aquant, gemm_bquant,
gemm_abquant, mx_gemm, and gemm_streamk. Every other operator --
gemm_universal, batched_gemm, batched_contraction, grouped_gemm,
gemm_multi_d, gemm_multi_abd, grouped_gemm_rowcolquant,
grouped_gemm_tensorquant -- was CMake-legal on gfx950 (all list gfx950
in DESIRED_TARGETS) but absent from the gfx950 command because
_dispatcherPerfCmd used a hard arch branch that built a completely
different operator set on each side.
Collapse the branch into a shared base (all arch-agnostic ops) with a
gfx950-only appendix (gemm_aquant/bquant/abquant/mx_gemm). GPU_TARGETS
is now passed through from the caller rather than hardcoded.
Remaining gfx950-only set and why:
gemm_aquant/bquant/abquant SUPPORTED_ARCHS = ("gfx950",) in their
Python layers; gfx942 has fp8 FNUZ and
is in DESIRED_TARGETS, but the guards have
not been lifted yet.
mx_gemm CMakeLists filters with target MATCHES
"^gfx950", so benchmark_mx_gemm_all is
never created for gfx942; also has no fp16
path, hence quantDatatype.
Deferred (next PR, same list as the correctness lane):
gemm_preshuffle no dispatcher bridge on develop
contraction_multi_abd no Python benchmark driver yet
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jenkins ignores the dynamic library("ck@branch") load when ck is already
registered as a global shared library, causing the call to fail with
'No such DSL method'. Inline the parallel block directly in the Jenkinsfile
and drop the groovy wrapper.
Co-Authored-By: Claude <noreply@anthropic.com>
Same fix as runDownstreamTestsStages: Jenkins ignores the dynamic library load when ck is already registered globally, so any function that exists only on this branch is invisible at call time. Co-Authored-By: Claude <noreply@anthropic.com>
Part 2 of the dispatcher CI work started in #10897, which wired 13 operators and deferred these seven on the grounds that they had no dispatcher bridge on develop: gemm_aquant, gemm_abquant, gemm_bquant, gemm_rowcolquant, gemm_tensorquant (block_scale_gemm), preshuffled GEMM, batched contraction multi-ABD That premise was wrong. The bridges landed on develop as squash-merge #10439; `git merge-base --is-ancestor` reported the feature branches as unmerged because squash rewrites history. `git ls-tree -r origin/develop` confirms every bridge file is present. Preshuffle never needed its own bridge -- it rides gemm_utils.py via variant="preshuffle". Correctness lane (ck.groovy): - Wire the four already-registered tests: test_gemm_rowcolquant, test_tensor_quant, test_preshuffle, test_contraction_multi_abd. - Add three new single-GEMM GPU correctness tests. The existing test_{a,ab,b}quant_gpu_correctness.py files import grouped_gemm_*_utils and cover the GROUPED ops; the single-GEMM bridges export identical class names, so the import line is the only distinguishing token. The single-GEMM problem descriptors also differ (no QM_A / no aquant_group_m), so the grouped reference math would produce a plausible but wrong answer. - Each new test checks for all-zero output explicitly: that is the failure mode of a wrong warp_tile_k, and it is silent -- no build error, and a relative-tolerance gate alone will not catch it. - fp8 encoding goes through dispatcher_common.fp8_uses_ocp rather than a local copy, so the gfx942 FNUZ / gfx950 OCP split follows the compiled arch. Perf lane (ck.groovy): - Wire gemm_rowcolquant, gemm_tensor_quant, gemm_preshuffle, contraction_multi_abd; move gemm_aquant/gemm_abquant off the gfx950-only branch (their bridges and DESIRED_TARGETS both include gfx942). gemm_bquant and mx_gemm stay gfx950-only. - Every new op carries an explicit <OP>_MAX_INSTANCES. TILE_ENGINE_SAMPLING_TIER is a TOTAL budget split across active ops, so omitting it on even one op silently shrinks coverage for every operator already in the lane -- a green run with less testing. New driver: contraction_multi_abd_benchmark.py. Modelled on batched_contraction_benchmark.py, with the same exit-code contract (empty run returns 1, partial launch failure warns and returns 0). Two forced divergences: the executable has no -json_output flag, so results are parsed from its stdout perf line; and it has no split_k / flush_cache / rotating_count arguments. contraction_multi_abd also gets its own multiAbdConfigs rather than sharing problemConfigs. Its kernels compile with NUM_DIM_M/N = 2, and the executable rejects a dim list whose LENGTH differs from the compiled count -- "m=1024" is one M dimension where the kernel wants two, which is EXIT_FAILURE for every kernel rather than a smaller problem. Extents are the product of each list, so m=4,256 is the same M=1024. The driver also parses the compiled arity out of the kernel name and reports one clear message instead of N opaque failures. Also hardens test_preshuffle_gpu_correctness.py, which trusted --gfx with no allow-list: a typo'd arch went straight to --offload-arch and reported a build FAIL instead of a clean skip. Verified without a GPU: codegen --list-name run to obtain a real kernel name and validate the driver's filename parsing; driver exercised end-to-end against stub executables for all four exit paths; ck.groovy structurally checked (balanced triple-quotes, no dangling backslashes, signature/call arity, each op present in the cmake/ninja/driver triple); the three new tests exit 77 with correct skip reasons on gfx90a. Still needs a gfx942/gfx950 node: the on-GPU pass path for the three new tests, a real perf-driver run, and the budget regression check. The fp8 OCP/FNUZ path in particular must be run on BOTH arches -- wired backwards it passes on one and returns NaN on the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ozturkosu
left a comment
There was a problem hiding this comment.
My request has been resolved.
brockhargreaves-amd
left a comment
There was a problem hiding this comment.
Looks good, lgtm after the merge conflicts are fixed.
feat(ck-tile): add dispatcher correctness and performance CI
lanes for gfx942/gfx950 (#10897)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Motivation
ISSUE ID: 10897
A change to the CK-Tile dispatcher bridge or codegen can break numeric
correctness,
kernel build, or kernel launch for any of the 13 bridged GEMM operators,
and today
nothing catches it until someone runs the tests by hand. Three operators
(stream-K, aquant, abquant) had no GPU correctness test at all, and four
more were
registered in ctest but never invoked from CI.
This adds two opt-in Jenkins lanes that fan out across gfx942 and gfx950
and cover
every operator with a bridge on `develop`, plus the missing tests. Both
parameters
default to **OFF**, so no existing CI run gets slower.
## Dependencies
**Depends on #11591** — `fix(ck-tile): guard the gfx1250 unscaled WMMA
builtin behind __gfx1250__`.
`ck_tile/ops/gemm.hpp` does not compile on any non-gfx1250 target on
current
`develop` (19 errors, in both the host and the device pass), which
breaks four of the
dispatcher GPU correctness tests this PR adds. That fix is unrelated to
this change and
is split out into its own PR. Default CI here is unaffected — both lanes
are
`defaultValue: false` — but Test Plan steps 1, 2 and 4 will not pass
until #11591 merges.
## Technical Details
### 1. `RUN_DISPATCHER_CORRECTNESS_TESTS` — JIT-compiled kernels vs.
host reference
Covers all 13 operators rather than a representative sample: a developer
touching
the bridge needs signal on all of them, and a lane covering four cannot
give that.
Cost is held down by the *search budget* instead — `--budget 64` for the
standard
sweep, `--budget 16` per variant sweep.
| | gfx942 | gfx950 |
|---|---|---|
| shared | `gemm_universal` sweep + parity, `grouped`, `multi_d` (4
layouts), `multi_abd`, `batched_gemm`, `batched_contraction`
(fp16+bf16+fp32), `stream_k`, `rowcolquant`, `tensorquant`, `aquant`,
`abquant` | same |
| arch-only | — | `mx_gemm`, `bquant` C4 (fp8/bf8), C (fp8i4/bf8i4), H3
(`mx_*`) |
Only two things stay gfx950-only, both for ISA reasons rather than
policy. `mx_gemm`
needs the scale-MFMA builtins; it also cannot join the `--variant` sweep
at all,
because `mx_gemm_utils` exposes only `default_fp8_config()` /
`default_fp4_config()`
and never plugs into `expand_sweep`. The `bquant` H3 (`mx_*`) configs
need the same
builtins. `bquant`'s C4/C paths are held to gfx950 for a softer reason:
its config
builders are arch-aware now, so the gate is pending one validating
gfx942 run rather
than a known defect. Everything else — including
stream-K and all three quant operators — was widened to run on gfx942 as
well over
the course of this branch. Both rationales are recorded inline in
`ck.groovy`.
There is deliberately **no gfx1201 stage**:
`gemm_utils._SUPPORTED_ARCHES` excludes
it, and widening that guard does not help, because every warp tile in
`default_ci_config.json` is 64 wide while RDNA4 is warp_size 32, so all
16 strata
enumerate 0 configs and the runner exits 1 on "no configs to run".
One narrowed surface is documented rather than closed:
`grouped_gemm_{a,ab,b}quant`
run only on `rcr`, because all three config builders hardcode
`layout="rcr"` and the
ctypes libs assert packed rcr strides.
### 2. `RUN_DISPATCHER_PERF_TESTS` — ahead-of-time tile_engine instances
Smoke-tier benchmark covering the **same 13 operators** as the
correctness lane, so
the AOT instance path gets the same operator coverage the JIT path gets.
The two are
gated separately and neither substitutes for the other: correctness
JIT-builds each
config with hipcc and checks it against a host reference, while this
lane exercises
tile_engine's codegen, instance builder and benchmark harness.
One dtype, one layout, one problem size, and a fixed **64 AOT instances
per operator**.
| | gfx942 | gfx950 |
|---|---|---|
| fp16 / `rcr` | `gemm_universal`, `batched_gemm`,
`batched_contraction`, `gemm_streamk`, `grouped_gemm` | `gemm_streamk` |
| fp16 / `rcrr` | `gemm_multi_d`, `gemm_multi_abd` | — |
| fp8 / `rcr` | `grouped_gemm_rowcolquant`, `grouped_gemm_tensorquant` |
`gemm_aquant`, `gemm_bquant`, `gemm_abquant`, `mx_gemm` |
Three details that are load-bearing rather than incidental:
- **The per-op cap is explicit, not derived.**
`TILE_ENGINE_SAMPLING_TIER` is a
*total* budget split evenly across every op with a non-empty
`<OP>_DATATYPE`
(`ops/gemm/CMakeLists.txt:16-23`), so adding operators silently shrinks
the coverage
of the ones already there — gfx942 alone would have gone from 125 to 55
instances
per op. Pinning `<OP>_MAX_INSTANCES` uses the first-class override path
(same file,
`:50-59`) and keeps lane cost fixed and independent of how many ops the
lane builds.
The tier is still passed, because each op also forwards it to the
sampler as
`--tier` to select the sampling strategy.
- **One dtype and one layout per op is not just smoke sizing.** The
per-op budget is
divided again across `(dtype × layout)` combos with integer truncation,
so a
multi-combo op can floor a combo to zero instances and build nothing.
- **`gemm_multi_d` and `gemm_multi_abd` take 4-character layouts** (A,
B, D, E).
Passing the `rcr` the other ops use is a parse error in the instance
builder, not a
narrower selection.
`mx_gemm` cannot leave the gfx950 branch: its CMakeLists filters targets
with a
literal regex, `target MATCHES "^gfx950"`, so on any other arch
`benchmark_mx_gemm_all` is never created and ninja fails on an unknown
target rather
than skipping. It also has no fp16 path.
`gemm_streamk` builds on both arches. `DESIRED_TARGETS` was widened to
include gfx950
earlier on this branch, but that CMake change is the one part that could
not be
exercised locally; this lane is its first real confirmation, and it
matches the
correctness lane, which already runs stream-K on both. If the gfx950 AOT
build turns
out to be broken, that `DESIRED_TARGETS` line is revertible on its own
without
touching the lane.
### 3. New search-space runner —
`dispatcher/tests/test_gemm_search_space.py`
One runner covering every variant `expand_sweep` understands (standard,
grouped,
multi_d, multi_abd, stream_k), selected with `--variant` — they share
the same
enumerate/sample/build/report machinery and differ only in which
`Gpu*Runner` to
drive and how the numpy reference is computed.
It enumerates the `GemmKernelConfig` space against tile_engine's
`default_ci_config.json`, samples a budget-limited subset with a daily
rotating seed
**per (dtype, layout) stratum** — so even budget 64 spreads ~4 configs
across all 16
combinations rather than testing one deeply — compiles via the bridge,
runs on GPU,
and reports correctness + TFLOPS as JSON.
This is the dispatcher equivalent of tile_engine's per-op benchmark
scripts, with one
deliberate difference: those never verify numerics and always exit 0.
This one
validates every kernel against a numpy reference and **exits 1 on any
mismatch or
build failure**.
### 4. New GPU correctness tests
- **`test_streamk_gpu_correctness.py`** — all three reduction
strategies. The shared
sweep only ever builds `atomic` (`default_ci_config.json` carries no
`streamk_config`,
so `expand_sweep` falls back to `["atomic"]`), leaving `linear`/`tree`
unverified
through the ctypes bridge. Two shapes: an occupancy-bound 4096³ case,
and a
Stream-K-heavy `M=N=128, K=8192` case that only linear/tree can assert
tightly,
since `estimate_num_wgs_per_tile` is gated on Atomic and returns 1
otherwise.
Tolerance is CK's own split-K-aware element-wise bound, ported from
`streamk_driver_common.hpp` + `check_err.hpp`.
- **`test_aquant_gpu_correctness.py`** — A-side per-(m,k)-group scales;
fp8, bf8, and a
tiled shape spanning a 4×4 output-tile grid that the 1×1-tile cases
cannot catch.
- **`test_abquant_gpu_correctness.py`** — scales on *both* operands,
completing the quant
trio; fp8 + bf8 on `compv3`, plus the gfx950-native `eightwaves`
pipeline (192×256
tiles, `TransposeC=True`, `bquant_group_n=128`), a different MFMA path
and B-scale
granularity than compv3.
All four quant/stream-K bridge tests are now registered in ctest with
`SKIP_RETURN_CODE 77`. They intentionally take **no** `--gfx` from
CMake: each
hipcc-compiles with `--offload-arch` and then runs, so the arch must
match the
*physical* GPU, and the configured `GPU_TARGETS` may legitimately differ
from the card
in the box.
### 5. Skip semantics — exit 77 instead of exit 0
The script-style tests previously returned 0 when no GPU was present, so
a CPU-only
runner reported a green PASS for a test that never touched the GPU. They
now return 77,
which ctest maps to *skipped* via `SKIP_RETURN_CODE` and the Jenkins
lane maps via a
`run_ok` shell helper. Only 77 is swallowed — any other non-zero still
fails the lane.
This matters because `execute_cmd` is one `&&`-joined string: without
it, a single
unsupported arch would abort the lane and every later operator would
silently never run.
`--gfx` also loses its hardcoded `gfx950` default in the quant tests, so
"user asked for
gfx950" stays distinguishable from "we are on an unrelated box" and the
skip can fire.
### 6. Fixes and coverage extensions found while wiring this up
- `gemm_utils.expand_sweep`: skip non-`cshuffle` epilogues for
`stream_k` — the codegen
emits only cshuffle for that variant, so those configs surfaced as
spurious build
failures.
- `grouped_gemm_aquant_utils._compile_aquant_kernel`: add the arch
defines
(`CK_USE_OCP_FP8`, `CK_TILE_USE_OCP_FP8`, `CK_USE_NATIVE_MX_SUPPORT`,
`CK_GFX950_SUPPORT`) that CMake normally injects but the standalone
hipcc path omitted,
matching the bquant path. gfx942 is FNUZ, gfx950 is OCP; without this
the encoded
inputs and the compiled kernel disagree and the device returns NaN.
- `tile_engine/ops/gemm_streamk/CMakeLists.txt`: widen AOT
`DESIRED_TARGETS` to include
gfx950, and correct the skip warning, which still listed only `(gfx90a,
gfx942)`
afterwards.
- `streamk_gemm_ctypes_lib.cpp`: correct a stale comment claiming the
lib is Atomic-only.
The strategy is a codegen-time template constant and `GetWorkSpaceSize`
returns 0 for
Atomic / non-zero for Linear/Tree, so the one allocate-and-launch path
serves all three.
- Widened two existing GPU tests that were narrower than the operator
they cover:
batched-contraction now exercises bf16 and fp32 alongside fp16, and the
Multi-D GEMM
test exercises all four layouts rather than one.
- `Dockerfile`: add `ml_dtypes`, required for host-side fp8/bf8
encoding.
Both lanes archive their per-kernel JSON from a `finally` block — a red
lane is exactly
when that artifact is worth having, and `buildAndTest` throws on
failure.
## Test Plan
1. **gfx942 correctness lane** —
`RUN_DISPATCHER_CORRECTNESS_TESTS=true`: the
`gemm_universal` sweep at budget 64 across all 16 dtype × layout strata,
the parity
test, the
grouped/multi_d/multi_abd/batched_gemm/batched_contraction/rowcolquant/
tensorquant/aquant/abquant/bquant bridge tests, the
`grouped`/`multi_d`/`multi_abd`/
`stream_k` variant sweeps at budget 16, and the stream-K registry +
bridge tests.
2. **gfx950 correctness lane** — the same set, plus `mx_gemm` and the
`bquant` H3
(`mx_*`) configs.
3. **Both perf lanes** — `RUN_DISPATCHER_PERF_TESTS=true` on gfx942 and
gfx950; confirm
all 9 gfx942 and all 5 gfx950 `benchmark_*_all` targets build, every
script runs, and
each `*_results.json` lands as an archived artifact. The gfx950
`gemm_streamk` result
is the specific new signal — it is the first exercise of the
`DESIRED_TARGETS`
widening.
4. **Skip path** — run the ctest suite on a non-gfx942/gfx950 box and
confirm the
arch-gated tests report *Skipped*, not Passed and not Failed.
5. **Artifact archiving** — confirm `dispatcher_*_results.json` and
`*_results.json`
appear as build artifacts, including on a deliberately failed run.
## Test Result
- Will be run on CI
## Submission Checklist
- [ ] Look over the contributing guidelines at
https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests.
TheRock Submodule Bump ActivityNewest first
|
… success (#12320) ISSUE ID : AICK-2170 PR #10897 converted three declarative `parallel { stage { when {} } }` blocks into scripted `parallel([...])` maps with plain `if` guards inside each closure: "Run Downstream Tests", "Run CK_TILE_FMHA Tests" and "Build CK and run Tests". The two forms are not equivalent in how Jenkins reports them. A declarative `when` that evaluates false marks the stage NOT_EXECUTED, so it shows as Skipped and never claims to have passed. A scripted parallel branch always runs; with the parameter off the branch body is an empty `if`, the closure returns normally, and Jenkins records the branch as SUCCESS. Because RUN_PYTORCH_TESTS, RUN_AITER_TESTS, RUN_FA_TESTS and RUN_CK_TILE_FMHA_TESTS all default to false, every DOWNSTREAM and FMHA branch executed zero work and reported green on a normal PR build. The arch-gated branches under "Build CK and run Tests" had the same problem. The conversion also silently dropped fail-fast: `parallelsAlwaysFailFast()` in the options block applies only to declarative parallel, so a red branch no longer aborted its siblings. Restore all three stages to the declarative form they had before #10897. The DISPATCHER stage stays scripted on purpose -- its branch map is built dynamically in ck.groovy to keep the declarative method under the JVM 64KB limit -- and it is gated at the parent `when` on (RUN_DISPATCHER_CORRECTNESS_TESTS || RUN_DISPATCHER_PERF_TESTS), so when both are off the whole stage skips rather than reporting a hollow success. For that one remaining scripted parallel, set failFast explicitly in runDispatcherTests(). No change to the dispatcher parameters, cron settings, or the DISPATCHER stage body. The restored file is 895 lines against 1009 before #10897, so returning to declarative form does not reintroduce the 64KB method-size pressure. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…11879) ## Motivation ISSUE ID : AICK-2171 #10897 wired 13 operators into the CK-Tile dispatcher CI lanes and deferred seven, on the grounds that they had no dispatcher bridge on `develop`. That premise was wrong — the bridges landed as squash-merge #10439, and `git merge-base --is-ancestor` gave a false negative because squash rewrites history (`git ls-tree -r origin/develop` confirms every file is present). Preshuffle never needed its own bridge; it rides `gemm_utils.py` via `variant="preshuffle"`. This PR closes that deferral. All seven now run in **both** dispatcher lanes on gfx942/gfx950: `gemm_aquant`, `gemm_abquant`, `gemm_bquant`, `gemm_rowcolquant`, `gemm_tensorquant`, Preshuffled GEMM, batched contraction multi-ABD. It also restores the gfx942 preshuffle / rowcolquant / tensor_quant coverage dropped when the orphaned `runTileEngine*` helpers were retired in #10897. Stacked on #10897 — base is `users/tlakshma/ck/dispatcher_ci`, merge after it. ## Technical Details **Correctness lane** — wire 4 already-registered tests (`test_gemm_rowcolquant`, `test_tensor_quant`, `test_preshuffle`, `test_contraction_multi_abd`) and add 3 new single-GEMM tests. The existing `test_{a,ab,b}quant_*` files cover the *grouped* ops and export identical class names, so the import line is the only distinguishing token; the single-GEMM problem descriptors also differ (no `QM_A`, no `aquant_group_m`), so grouped reference math would give a plausible but wrong answer. Each new test checks for **all-zero output** — the silent failure mode of a wrong `warp_tile_k`, which a tolerance gate alone won't catch — and routes fp8 encoding through `dispatcher_common.fp8_uses_ocp` so the gfx942 FNUZ / gfx950 OCP split follows the compiled arch. ctest registration uses `SKIP_RETURN_CODE 77` and no `--gfx` (autodetect must match the physical GPU). **Perf lane** — wire 4 ops across all three sections (cmake `-D`, ninja target, driver call); move aquant/abquant off the gfx950-only branch (their bridges and `DESIRED_TARGETS` include gfx942); bquant and mx_gemm stay gfx950-only. Every new op gets an explicit `<OP>_MAX_INSTANCES` — `TILE_ENGINE_SAMPLING_TIER` is a *total* budget split across active ops, so omitting it on one op silently shrinks coverage for every op already in the lane. **New `contraction_multi_abd_benchmark.py`** — the one real gap. Modelled on `batched_contraction_benchmark.py` with the same exit-code contract (empty run → 1, partial failure → WARNING + 0); diverges in parsing stdout (no `-json_output`) and dropping `split_k`/`flush_cache`/`rotating_count`. Two bugs caught while verifying it, both of which would have failed the lane on a GPU node: - **Dim arity** — kernels compile with `NUM_DIM_M/N = 2`, and the executable rejects a dim list of the wrong *length*, so the shared `m=1024` was `EXIT_FAILURE` on every kernel. Now has its own `multiAbdConfigs = "g=2;m=4,256;n=4,256;k=64"` (same M=1024, right arity), plus a driver guard that reports one clear message instead of N opaque failures. - **Doubled name prefix** — codegen and CMake both prepend `contraction_multi_abd_`; a single-slice strip shifted every parsed field by three tokens. **Also** — `test_preshuffle_gpu_correctness.py` trusted `--gfx` with no allow-list, so a typo'd arch reported a build FAIL instead of a clean skip. Three stale `ck.groovy` comment blocks corrected. Files: `groovy/vars/ck.groovy`, `dispatcher/tests/CMakeLists.txt`, `test_preshuffle_gpu_correctness.py` (M); `test_gemm_{aquant,abquant,bquant}_gpu_correctness.py`, `contraction_multi_abd_benchmark.py` (A). No `Jenkinsfile` change; no C/C++ touched. ## Test Plan Without a target GPU (dev box is gfx90a): validate the driver's filename parsing against a real codegen kernel name; exercise all four driver exit paths against stub executables; structurally check the generated `ck.groovy` command strings; confirm the three new tests take the skip path. On a gfx942/gfx950 node before merge: on-GPU pass path for the new tests (**both** arches, to catch a backwards `fp8_uses_ocp`); a real multi-ABD perf run; budget regression check; the full lane across all four branches. ## Test Result gfx90a, all **PASS**: - Filename parsing against a real kernel name → `dim_counts {g:1,m:2,n:2,k:1}`, `tensor_counts {num_a:1,num_b:1,num_d:1}` (this run exposed the doubled-prefix bug, now fixed). - Exit contract: partial failure → `rc=0` + WARNING; wrong arity → `rc=1` + arity message; empty `bin/` → `rc=1`; malformed config → clear error. - `ck.groovy`: balanced triple-quotes, no dangling backslashes, signature/call arity 12/12, each op present in all three sections. - The three new tests exit **77** with correct skip reasons. GPU steps **not yet run**. When reviewing lane logs, confirm each new op printed a real result or an explicit `SKIP(77):` — a lane green because every new op silently skipped looks identical to success at the stage level. ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests. - [ ] Dependent PR #10897 merged first. Co-authored-by: Claude <noreply@anthropic.com>
…12392) ## Proposed changes Backs the dispatcher CI lanes out of `develop` after they broke the pipeline, by restoring the three pipeline files to their state immediately before #10897: - `projects/composablekernel/Jenkinsfile` - `projects/composablekernel/Dockerfile` - `projects/composablekernel/groovy/vars/ck.groovy` All three are **byte-identical to `7080fbe1ca^`**, so the pipeline returns to a previously-green configuration. This is a **CI-files-only** revert. The dispatcher Python, codegen and test sources are deliberately left untouched on `develop`. It is the minimal-blast-radius alternative to #12390, which reverts those three PRs in full (51 files). Reverts the pipeline changes from: | PR | Merge commit | |---|---| | #10897 — feat(ck-tile): add dispatcher correctness and performance CI lanes for gfx942/gfx950 | `7080fbe1ca` | | #12320 — fix(ck): restore declarative stages so skipped CI lanes stop reporting success | `40995238ed` | | #11879 — feat(ck): add 7 operators to dispatcher correctness and perf lanes | `5873d0a0f6` | ### Why restoring only these three files is safe - **No other author's work is affected.** Every commit that touched these three files since #10897 is one of the three PRs being backed out. Verified per file with `git log 7080fbe..develop -- <file>`. This matters because three gfx1250 PRs (#11043, #10934, #12216) landed on the *dispatcher* files after #10897 — but none of them touched the pipeline files, so this revert cannot disturb them. - **The leftover dispatcher sources are inert for the main build.** `dispatcher/` is a standalone CMake project and is never reached by an `add_subdirectory()` from the top-level `CMakeLists.txt`. `CK_TILE_DISPATCHER` only adds a compile definition and an include directory, so `dispatcher/tests/CMakeLists.txt` is not configured by a normal build. - **The one pre-existing `CK_TILE_DISPATCHER=ON` lane is unaffected.** `runFullGroupedConvTileTests` predates #10897, uses `DISPATCHER_RULE_SET=tests` for conv only, and builds and runs three named conv binaries rather than invoking `ctest`. It does not reach `dispatcher/tests`. - **No dangling callers.** `ck.groovy` defines the same 54 top-level functions before and after; the reverted PRs changed function bodies, not the API. ### Verification - The three files are byte-identical to `7080fbe1ca^`, and to the corresponding files on `users/tlakshma/ck/revert-dispatcher-ci` (#12390) - Diff is confined to `projects/composablekernel/`; no dispatcher or tile_engine source is touched - Per-file authorship of every commit since #10897 confirmed - `ck.groovy` top-level function set compared before/after Not verified: the `Jenkinsfile` and `ck.groovy` could not be syntax-checked locally (no `groovy`/`groovyc` available). Mitigated by both being byte-identical to a revision that was previously green. ### Relationship to #12390 #12390 and this PR are **alternatives, not companions** — they overlap on these three files. Whichever lands second will need a rebase, or should be closed. Pick based on whether the leftover dispatcher sources on `develop` are acceptable: - **This PR** — smallest change, restores CI immediately, but leaves dispatcher Python and its CTest registrations on `develop` with no lane exercising them. - **#12390** — also removes that now-unexercised code, at the cost of a 51-file diff with hand-resolved conflicts around the gfx1250 work. ### Follow-up #12320 was itself a *fix* for skipped lanes falsely reporting success. Reverting it restores that false-green behaviour. If the breakage is traced to #10897 or #11879 alone, #12320 should be re-landed on top rather than left reverted. Issue IDs: AICK-2170 (#12320), AICK-2171 (#11879). #10897 had no issue ID. ## Checklist - [ ] I have added tests relevant to the introduced functionality, and the unit tests are passing locally - [ ] I have added the test to REGRESSION_TESTS list defined at the top of CMakeLists.txt in tests/CMakeLists.txt, **IF** the test takes more than 30 seconds to run. - [x] I have added inline documentation which enables the maintainers with understanding the motivation - [ ] I have removed the stale documentation which is no longer relevant after this pull request - [ ] (If this change is user-facing) I have added release notes which provide the end users with a brief summary of the improvement from this pull request - [ ] I have run `clang-format` on all changed files - [x] Any dependent changes have been merged No tests are added: this PR only restores CI pipeline configuration to a prior state and changes no library or test source. ## Discussion The alternative is the full revert in #12390. A full revert is tidier in that it does not leave unexercised code behind, but it requires hand-resolving conflicts against three gfx1250 PRs that landed on the dispatcher files after #10897, where a naive resolution silently deletes another author's work. This PR avoids that risk entirely by touching only files that no one else has modified, at the cost of leaving the dispatcher sources in place until they are cleaned up or re-landed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
… success (#12320) ISSUE ID : AICK-2170 PR #10897 converted three declarative `parallel { stage { when {} } }` blocks into scripted `parallel([...])` maps with plain `if` guards inside each closure: "Run Downstream Tests", "Run CK_TILE_FMHA Tests" and "Build CK and run Tests". The two forms are not equivalent in how Jenkins reports them. A declarative `when` that evaluates false marks the stage NOT_EXECUTED, so it shows as Skipped and never claims to have passed. A scripted parallel branch always runs; with the parameter off the branch body is an empty `if`, the closure returns normally, and Jenkins records the branch as SUCCESS. Because RUN_PYTORCH_TESTS, RUN_AITER_TESTS, RUN_FA_TESTS and RUN_CK_TILE_FMHA_TESTS all default to false, every DOWNSTREAM and FMHA branch executed zero work and reported green on a normal PR build. The arch-gated branches under "Build CK and run Tests" had the same problem. The conversion also silently dropped fail-fast: `parallelsAlwaysFailFast()` in the options block applies only to declarative parallel, so a red branch no longer aborted its siblings. Restore all three stages to the declarative form they had before #10897. The DISPATCHER stage stays scripted on purpose -- its branch map is built dynamically in ck.groovy to keep the declarative method under the JVM 64KB limit -- and it is gated at the parent `when` on (RUN_DISPATCHER_CORRECTNESS_TESTS || RUN_DISPATCHER_PERF_TESTS), so when both are off the whole stage skips rather than reporting a hollow success. For that one remaining scripted parallel, set failFast explicitly in runDispatcherTests(). No change to the dispatcher parameters, cron settings, or the DISPATCHER stage body. The restored file is 895 lines against 1009 before #10897, so returning to declarative form does not reintroduce the 64KB method-size pressure. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…11879) ## Motivation ISSUE ID : AICK-2171 #10897 wired 13 operators into the CK-Tile dispatcher CI lanes and deferred seven, on the grounds that they had no dispatcher bridge on `develop`. That premise was wrong — the bridges landed as squash-merge #10439, and `git merge-base --is-ancestor` gave a false negative because squash rewrites history (`git ls-tree -r origin/develop` confirms every file is present). Preshuffle never needed its own bridge; it rides `gemm_utils.py` via `variant="preshuffle"`. This PR closes that deferral. All seven now run in **both** dispatcher lanes on gfx942/gfx950: `gemm_aquant`, `gemm_abquant`, `gemm_bquant`, `gemm_rowcolquant`, `gemm_tensorquant`, Preshuffled GEMM, batched contraction multi-ABD. It also restores the gfx942 preshuffle / rowcolquant / tensor_quant coverage dropped when the orphaned `runTileEngine*` helpers were retired in #10897. Stacked on #10897 — base is `users/tlakshma/ck/dispatcher_ci`, merge after it. ## Technical Details **Correctness lane** — wire 4 already-registered tests (`test_gemm_rowcolquant`, `test_tensor_quant`, `test_preshuffle`, `test_contraction_multi_abd`) and add 3 new single-GEMM tests. The existing `test_{a,ab,b}quant_*` files cover the *grouped* ops and export identical class names, so the import line is the only distinguishing token; the single-GEMM problem descriptors also differ (no `QM_A`, no `aquant_group_m`), so grouped reference math would give a plausible but wrong answer. Each new test checks for **all-zero output** — the silent failure mode of a wrong `warp_tile_k`, which a tolerance gate alone won't catch — and routes fp8 encoding through `dispatcher_common.fp8_uses_ocp` so the gfx942 FNUZ / gfx950 OCP split follows the compiled arch. ctest registration uses `SKIP_RETURN_CODE 77` and no `--gfx` (autodetect must match the physical GPU). **Perf lane** — wire 4 ops across all three sections (cmake `-D`, ninja target, driver call); move aquant/abquant off the gfx950-only branch (their bridges and `DESIRED_TARGETS` include gfx942); bquant and mx_gemm stay gfx950-only. Every new op gets an explicit `<OP>_MAX_INSTANCES` — `TILE_ENGINE_SAMPLING_TIER` is a *total* budget split across active ops, so omitting it on one op silently shrinks coverage for every op already in the lane. **New `contraction_multi_abd_benchmark.py`** — the one real gap. Modelled on `batched_contraction_benchmark.py` with the same exit-code contract (empty run → 1, partial failure → WARNING + 0); diverges in parsing stdout (no `-json_output`) and dropping `split_k`/`flush_cache`/`rotating_count`. Two bugs caught while verifying it, both of which would have failed the lane on a GPU node: - **Dim arity** — kernels compile with `NUM_DIM_M/N = 2`, and the executable rejects a dim list of the wrong *length*, so the shared `m=1024` was `EXIT_FAILURE` on every kernel. Now has its own `multiAbdConfigs = "g=2;m=4,256;n=4,256;k=64"` (same M=1024, right arity), plus a driver guard that reports one clear message instead of N opaque failures. - **Doubled name prefix** — codegen and CMake both prepend `contraction_multi_abd_`; a single-slice strip shifted every parsed field by three tokens. **Also** — `test_preshuffle_gpu_correctness.py` trusted `--gfx` with no allow-list, so a typo'd arch reported a build FAIL instead of a clean skip. Three stale `ck.groovy` comment blocks corrected. Files: `groovy/vars/ck.groovy`, `dispatcher/tests/CMakeLists.txt`, `test_preshuffle_gpu_correctness.py` (M); `test_gemm_{aquant,abquant,bquant}_gpu_correctness.py`, `contraction_multi_abd_benchmark.py` (A). No `Jenkinsfile` change; no C/C++ touched. ## Test Plan Without a target GPU (dev box is gfx90a): validate the driver's filename parsing against a real codegen kernel name; exercise all four driver exit paths against stub executables; structurally check the generated `ck.groovy` command strings; confirm the three new tests take the skip path. On a gfx942/gfx950 node before merge: on-GPU pass path for the new tests (**both** arches, to catch a backwards `fp8_uses_ocp`); a real multi-ABD perf run; budget regression check; the full lane across all four branches. ## Test Result gfx90a, all **PASS**: - Filename parsing against a real kernel name → `dim_counts {g:1,m:2,n:2,k:1}`, `tensor_counts {num_a:1,num_b:1,num_d:1}` (this run exposed the doubled-prefix bug, now fixed). - Exit contract: partial failure → `rc=0` + WARNING; wrong arity → `rc=1` + arity message; empty `bin/` → `rc=1`; malformed config → clear error. - `ck.groovy`: balanced triple-quotes, no dangling backslashes, signature/call arity 12/12, each op present in all three sections. - The three new tests exit **77** with correct skip reasons. GPU steps **not yet run**. When reviewing lane logs, confirm each new op printed a real result or an explicit `SKIP(77):` — a lane green because every new op silently skipped looks identical to success at the stage level. ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests. - [ ] Dependent PR #10897 merged first. Co-authored-by: Claude <noreply@anthropic.com>
…12392) ## Proposed changes Backs the dispatcher CI lanes out of `develop` after they broke the pipeline, by restoring the three pipeline files to their state immediately before #10897: - `projects/composablekernel/Jenkinsfile` - `projects/composablekernel/Dockerfile` - `projects/composablekernel/groovy/vars/ck.groovy` All three are **byte-identical to `7080fbe1ca^`**, so the pipeline returns to a previously-green configuration. This is a **CI-files-only** revert. The dispatcher Python, codegen and test sources are deliberately left untouched on `develop`. It is the minimal-blast-radius alternative to #12390, which reverts those three PRs in full (51 files). Reverts the pipeline changes from: | PR | Merge commit | |---|---| | #10897 — feat(ck-tile): add dispatcher correctness and performance CI lanes for gfx942/gfx950 | `7080fbe1ca` | | #12320 — fix(ck): restore declarative stages so skipped CI lanes stop reporting success | `40995238ed` | | #11879 — feat(ck): add 7 operators to dispatcher correctness and perf lanes | `5873d0a0f6` | ### Why restoring only these three files is safe - **No other author's work is affected.** Every commit that touched these three files since #10897 is one of the three PRs being backed out. Verified per file with `git log 7080fbe..develop -- <file>`. This matters because three gfx1250 PRs (#11043, #10934, #12216) landed on the *dispatcher* files after #10897 — but none of them touched the pipeline files, so this revert cannot disturb them. - **The leftover dispatcher sources are inert for the main build.** `dispatcher/` is a standalone CMake project and is never reached by an `add_subdirectory()` from the top-level `CMakeLists.txt`. `CK_TILE_DISPATCHER` only adds a compile definition and an include directory, so `dispatcher/tests/CMakeLists.txt` is not configured by a normal build. - **The one pre-existing `CK_TILE_DISPATCHER=ON` lane is unaffected.** `runFullGroupedConvTileTests` predates #10897, uses `DISPATCHER_RULE_SET=tests` for conv only, and builds and runs three named conv binaries rather than invoking `ctest`. It does not reach `dispatcher/tests`. - **No dangling callers.** `ck.groovy` defines the same 54 top-level functions before and after; the reverted PRs changed function bodies, not the API. ### Verification - The three files are byte-identical to `7080fbe1ca^`, and to the corresponding files on `users/tlakshma/ck/revert-dispatcher-ci` (#12390) - Diff is confined to `projects/composablekernel/`; no dispatcher or tile_engine source is touched - Per-file authorship of every commit since #10897 confirmed - `ck.groovy` top-level function set compared before/after Not verified: the `Jenkinsfile` and `ck.groovy` could not be syntax-checked locally (no `groovy`/`groovyc` available). Mitigated by both being byte-identical to a revision that was previously green. ### Relationship to #12390 #12390 and this PR are **alternatives, not companions** — they overlap on these three files. Whichever lands second will need a rebase, or should be closed. Pick based on whether the leftover dispatcher sources on `develop` are acceptable: - **This PR** — smallest change, restores CI immediately, but leaves dispatcher Python and its CTest registrations on `develop` with no lane exercising them. - **#12390** — also removes that now-unexercised code, at the cost of a 51-file diff with hand-resolved conflicts around the gfx1250 work. ### Follow-up #12320 was itself a *fix* for skipped lanes falsely reporting success. Reverting it restores that false-green behaviour. If the breakage is traced to #10897 or #11879 alone, #12320 should be re-landed on top rather than left reverted. Issue IDs: AICK-2170 (#12320), AICK-2171 (#11879). #10897 had no issue ID. ## Checklist - [ ] I have added tests relevant to the introduced functionality, and the unit tests are passing locally - [ ] I have added the test to REGRESSION_TESTS list defined at the top of CMakeLists.txt in tests/CMakeLists.txt, **IF** the test takes more than 30 seconds to run. - [x] I have added inline documentation which enables the maintainers with understanding the motivation - [ ] I have removed the stale documentation which is no longer relevant after this pull request - [ ] (If this change is user-facing) I have added release notes which provide the end users with a brief summary of the improvement from this pull request - [ ] I have run `clang-format` on all changed files - [x] Any dependent changes have been merged No tests are added: this PR only restores CI pipeline configuration to a prior state and changes no library or test source. ## Discussion The alternative is the full revert in #12390. A full revert is tidier in that it does not leave unexercised code behind, but it requires hand-resolving conflicts against three gfx1250 PRs that landed on the dispatcher files after #10897, where a naive resolution silently deletes another author's work. This PR avoids that risk entirely by touching only files that no one else has modified, at the cost of leaving the dispatcher sources in place until they are cleaned up or re-landed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ISSUE ID : AICK-2171 ## Motivation PR #10897 added dispatcher correctness and performance CI lanes for gfx942/gfx950 but was reverted in #12392 due to unrelated gfx1250 conflicts in other files. This re-lands the CI infrastructure only — Dockerfile, Jenkinsfile, and ck.groovy — leaving the Python dispatcher source at the develop state to preserve Ozturk's gfx1250 work (#11043, #10934, #12216). ## Technical Details **Re-landed from #10897:** - `Dockerfile`: re-pin `ml_dtypes==0.6.0` for consistent fp8 OCP/FNUZ reference values across gfx942 (FNUZ) and gfx950 (OCP) - `Jenkinsfile`: restore `RUN_DISPATCHER_CORRECTNESS_TESTS` and `RUN_DISPATCHER_PERF_TESTS` boolean params (default: off), the declarative dispatcher stage, and nightly cron entry at 22:00 UTC - `groovy/vars/ck.groovy`: restore `runDispatcherTests`, `runDispatcherCorrectnessTests`, `runDispatcherPerfTests` and helpers (573 lines) — correctness lane covers 13+ operators on gfx942/gfx950 with arch-aware fp8 encoding; bquant/mx_gemm gfx950-only **New fixes over #10897:** - `dispatcher/CMakeLists.txt`: add `HIP` to project languages and `set_source_files_properties(LANGUAGE HIP)` for `fmha_dispatcher.cpp` and `fmha_registry.cpp` — required for the standalone cmake build used by the correctness lane (these files use `<<<>>>` kernel launch syntax) - `ck.groovy` correctness cmd: point cmake at `-S ../dispatcher -B ../dispatcher/build` and ninja at `-C ../dispatcher/build` so `libck_tile_dispatcher.a` lands in `dispatcher/build/` where `dispatcher_common.get_build_dir()` expects it - `tile_engine/sampling/op_weights.json`: register `contraction_multi_abd` with weight `0.0625` — the perf lane activates it via `CONTRACTION_MULTI_ABD_DATATYPE=fp16` and `allocate_budget()` hard-fails in strict mode for any unregistered active op ## Test Plan - `RUN_DISPATCHER_CORRECTNESS_TESTS=true` on gfx942 and gfx950: cmake + ninja build of `ck_tile_dispatcher`, then 13+ operator sweeps via `test_gemm_search_space.py` and per-op correctness scripts - `RUN_DISPATCHER_PERF_TESTS=true` on gfx942 and gfx950: tile_engine AOT build of all dispatcher-bridged operators, then benchmark drivers for each ## Test Result CI triggered on this branch — results pending. ## Submission Checklist - [x] CI infrastructure only; Python dispatcher source unchanged from develop - [x] `defaultValue: false` for both dispatcher params (opt-in, not opt-out) - [x] `contraction_multi_abd` added to `op_weights.json` - [x] HIP language fix in `dispatcher/CMakeLists.txt` for standalone build --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Muhammed Emin Ozturk <Muhammed.Ozturk@amd.com> Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
Motivation
ISSUE ID : AICK-2170
A change to the CK-Tile dispatcher bridge or codegen can break numeric correctness,
kernel build, or kernel launch for any of the 13 bridged GEMM operators, and today
nothing catches it until someone runs the tests by hand. Three operators
(stream-K, aquant, abquant) had no GPU correctness test at all, and four more were
registered in ctest but never invoked from CI.
This adds two opt-in Jenkins lanes that fan out across gfx942 and gfx950 and cover
every operator with a bridge on
develop, plus the missing tests. Both parametersdefault to OFF, so no existing CI run gets slower.
Dependencies
Depends on #11591 —
fix(ck-tile): guard the gfx1250 unscaled WMMA builtin behind __gfx1250__.ck_tile/ops/gemm.hppdoes not compile on any non-gfx1250 target on currentdevelop(19 errors, in both the host and the device pass), which breaks four of thedispatcher GPU correctness tests this PR adds. That fix is unrelated to this change and
is split out into its own PR. Default CI here is unaffected — both lanes are
defaultValue: false— but Test Plan steps 1, 2 and 4 will not pass until #11591 merges.Technical Details
1.
RUN_DISPATCHER_CORRECTNESS_TESTS— JIT-compiled kernels vs. host referenceCovers all 13 operators rather than a representative sample: a developer touching
the bridge needs signal on all of them, and a lane covering four cannot give that.
Cost is held down by the search budget instead —
--budget 64for the standardsweep,
--budget 16per variant sweep.gemm_universalsweep + parity,grouped,multi_d(4 layouts),multi_abd,batched_gemm,batched_contraction(fp16+bf16+fp32),stream_k,rowcolquant,tensorquant,aquant,abquantmx_gemm,bquantC4 (fp8/bf8), C (fp8i4/bf8i4), H3 (mx_*)Only two things stay gfx950-only, both for ISA reasons rather than policy.
mx_gemmneeds the scale-MFMA builtins; it also cannot join the
--variantsweep at all,because
mx_gemm_utilsexposes onlydefault_fp8_config()/default_fp4_config()and never plugs into
expand_sweep. ThebquantH3 (mx_*) configs need the samebuiltins.
bquant's C4/C paths are held to gfx950 for a softer reason: its configbuilders are arch-aware now, so the gate is pending one validating gfx942 run rather
than a known defect. Everything else — including
stream-K and all three quant operators — was widened to run on gfx942 as well over
the course of this branch. Both rationales are recorded inline in
ck.groovy.There is deliberately no gfx1201 stage:
gemm_utils._SUPPORTED_ARCHESexcludesit, and widening that guard does not help, because every warp tile in
default_ci_config.jsonis 64 wide while RDNA4 is warp_size 32, so all 16 strataenumerate 0 configs and the runner exits 1 on "no configs to run".
One narrowed surface is documented rather than closed:
grouped_gemm_{a,ab,b}quantrun only on
rcr, because all three config builders hardcodelayout="rcr"and thectypes libs assert packed rcr strides.
2.
RUN_DISPATCHER_PERF_TESTS— ahead-of-time tile_engine instancesSmoke-tier benchmark covering the same 13 operators as the correctness lane, so
the AOT instance path gets the same operator coverage the JIT path gets. The two are
gated separately and neither substitutes for the other: correctness JIT-builds each
config with hipcc and checks it against a host reference, while this lane exercises
tile_engine's codegen, instance builder and benchmark harness.
One dtype, one layout, one problem size, and a fixed 64 AOT instances per operator.
rcrgemm_universal,batched_gemm,batched_contraction,gemm_streamk,grouped_gemmgemm_streamkrcrrgemm_multi_d,gemm_multi_abdrcrgrouped_gemm_rowcolquant,grouped_gemm_tensorquantgemm_aquant,gemm_bquant,gemm_abquant,mx_gemmThree details that are load-bearing rather than incidental:
TILE_ENGINE_SAMPLING_TIERis atotal budget split evenly across every op with a non-empty
<OP>_DATATYPE(
ops/gemm/CMakeLists.txt:16-23), so adding operators silently shrinks the coverageof the ones already there — gfx942 alone would have gone from 125 to 55 instances
per op. Pinning
<OP>_MAX_INSTANCESuses the first-class override path (same file,:50-59) and keeps lane cost fixed and independent of how many ops the lane builds.The tier is still passed, because each op also forwards it to the sampler as
--tierto select the sampling strategy.divided again across
(dtype × layout)combos with integer truncation, so amulti-combo op can floor a combo to zero instances and build nothing.
gemm_multi_dandgemm_multi_abdtake 4-character layouts (A, B, D, E).Passing the
rcrthe other ops use is a parse error in the instance builder, not anarrower selection.
mx_gemmcannot leave the gfx950 branch: its CMakeLists filters targets with aliteral regex,
target MATCHES "^gfx950", so on any other archbenchmark_mx_gemm_allis never created and ninja fails on an unknown target ratherthan skipping. It also has no fp16 path.
gemm_streamkbuilds on both arches.DESIRED_TARGETSwas widened to include gfx950earlier on this branch, but that CMake change is the one part that could not be
exercised locally; this lane is its first real confirmation, and it matches the
correctness lane, which already runs stream-K on both. If the gfx950 AOT build turns
out to be broken, that
DESIRED_TARGETSline is revertible on its own withouttouching the lane.
3. New search-space runner —
dispatcher/tests/test_gemm_search_space.pyOne runner covering every variant
expand_sweepunderstands (standard, grouped,multi_d, multi_abd, stream_k), selected with
--variant— they share the sameenumerate/sample/build/report machinery and differ only in which
Gpu*Runnertodrive and how the numpy reference is computed.
It enumerates the
GemmKernelConfigspace against tile_engine'sdefault_ci_config.json, samples a budget-limited subset with a daily rotating seedper (dtype, layout) stratum — so even budget 64 spreads ~4 configs across all 16
combinations rather than testing one deeply — compiles via the bridge, runs on GPU,
and reports correctness + TFLOPS as JSON.
This is the dispatcher equivalent of tile_engine's per-op benchmark scripts, with one
deliberate difference: those never verify numerics and always exit 0. This one
validates every kernel against a numpy reference and exits 1 on any mismatch or
build failure.
4. New GPU correctness tests
test_streamk_gpu_correctness.py— all three reduction strategies. The sharedsweep only ever builds
atomic(default_ci_config.jsoncarries nostreamk_config,so
expand_sweepfalls back to["atomic"]), leavinglinear/treeunverifiedthrough the ctypes bridge. Two shapes: an occupancy-bound 4096³ case, and a
Stream-K-heavy
M=N=128, K=8192case that only linear/tree can assert tightly,since
estimate_num_wgs_per_tileis gated on Atomic and returns 1 otherwise.Tolerance is CK's own split-K-aware element-wise bound, ported from
streamk_driver_common.hpp+check_err.hpp.test_aquant_gpu_correctness.py— A-side per-(m,k)-group scales; fp8, bf8, and atiled shape spanning a 4×4 output-tile grid that the 1×1-tile cases cannot catch.
test_abquant_gpu_correctness.py— scales on both operands, completing the quanttrio; fp8 + bf8 on
compv3, plus the gfx950-nativeeightwavespipeline (192×256tiles,
TransposeC=True,bquant_group_n=128), a different MFMA path and B-scalegranularity than compv3.
All four quant/stream-K bridge tests are now registered in ctest with
SKIP_RETURN_CODE 77. They intentionally take no--gfxfrom CMake: eachhipcc-compiles with
--offload-archand then runs, so the arch must match thephysical GPU, and the configured
GPU_TARGETSmay legitimately differ from the cardin the box.
5. Skip semantics — exit 77 instead of exit 0
The script-style tests previously returned 0 when no GPU was present, so a CPU-only
runner reported a green PASS for a test that never touched the GPU. They now return 77,
which ctest maps to skipped via
SKIP_RETURN_CODEand the Jenkins lane maps via arun_okshell helper. Only 77 is swallowed — any other non-zero still fails the lane.This matters because
execute_cmdis one&&-joined string: without it, a singleunsupported arch would abort the lane and every later operator would silently never run.
--gfxalso loses its hardcodedgfx950default in the quant tests, so "user asked forgfx950" stays distinguishable from "we are on an unrelated box" and the skip can fire.
6. Fixes and coverage extensions found while wiring this up
gemm_utils.expand_sweep: skip non-cshuffleepilogues forstream_k— the codegenemits only cshuffle for that variant, so those configs surfaced as spurious build
failures.
grouped_gemm_aquant_utils._compile_aquant_kernel: add the arch defines(
CK_USE_OCP_FP8,CK_TILE_USE_OCP_FP8,CK_USE_NATIVE_MX_SUPPORT,CK_GFX950_SUPPORT) that CMake normally injects but the standalone hipcc path omitted,matching the bquant path. gfx942 is FNUZ, gfx950 is OCP; without this the encoded
inputs and the compiled kernel disagree and the device returns NaN.
tile_engine/ops/gemm_streamk/CMakeLists.txt: widen AOTDESIRED_TARGETSto includegfx950, and correct the skip warning, which still listed only
(gfx90a, gfx942)afterwards.
streamk_gemm_ctypes_lib.cpp: correct a stale comment claiming the lib is Atomic-only.The strategy is a codegen-time template constant and
GetWorkSpaceSizereturns 0 forAtomic / non-zero for Linear/Tree, so the one allocate-and-launch path serves all three.
batched-contraction now exercises bf16 and fp32 alongside fp16, and the Multi-D GEMM
test exercises all four layouts rather than one.
Dockerfile: addml_dtypes, required for host-side fp8/bf8 encoding.Both lanes archive their per-kernel JSON from a
finallyblock — a red lane is exactlywhen that artifact is worth having, and
buildAndTestthrows on failure.Test Plan
RUN_DISPATCHER_CORRECTNESS_TESTS=true: thegemm_universalsweep at budget 64 across all 16 dtype × layout strata, the paritytest, the grouped/multi_d/multi_abd/batched_gemm/batched_contraction/rowcolquant/
tensorquant/aquant/abquant/bquant bridge tests, the
grouped/multi_d/multi_abd/stream_kvariant sweeps at budget 16, and the stream-K registry + bridge tests.mx_gemmand thebquantH3(
mx_*) configs.RUN_DISPATCHER_PERF_TESTS=trueon gfx942 and gfx950; confirmall 9 gfx942 and all 5 gfx950
benchmark_*_alltargets build, every script runs, andeach
*_results.jsonlands as an archived artifact. The gfx950gemm_streamkresultis the specific new signal — it is the first exercise of the
DESIRED_TARGETSwidening.
arch-gated tests report Skipped, not Passed and not Failed.
dispatcher_*_results.jsonand*_results.jsonappear as build artifacts, including on a deliberately failed run.
Test Result
Submission Checklist