CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
176 lines
6.0 KiB
Python
176 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""CPU-only unit tests for the multi_d GEMM bridge.
|
|
|
|
Multi-D GEMM fuses one or more extra D operands into the epilogue
|
|
(E = elementwise_op(A@B, D0, D1, ...)). Its kernel therefore differs from plain
|
|
GEMM by a 4-char (A,B,C,D) layout code plus a
|
|
``_multid_{elementwise_op}_d{num_d_tensors}`` name suffix. That suffix is the
|
|
byte-parity invariant tying config -> codegen -> the compiled kernel name the
|
|
runtime reports, so distinct D-counts / element-wise ops never collapse onto one
|
|
kernel. The same two knobs are surfaced in the codegen JSON's
|
|
``multi_d_config`` block, which is where unified_gemm_codegen expands them.
|
|
|
|
Everything under test is pure host-side logic (name generation, the codegen
|
|
JSON projection, and the shipped configs/*.json). No GPU, hipcc, or dispatcher
|
|
build is required.
|
|
|
|
Run: python3 -m pytest projects/composablekernel/dispatcher/tests/test_multi_d_bridge.py -v
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
SCRIPT_DIR = Path(__file__).parent.resolve()
|
|
DISPATCHER_DIR = SCRIPT_DIR.parent
|
|
REPO_ROOT = DISPATCHER_DIR.parent
|
|
sys.path.insert(0, str(DISPATCHER_DIR / "python"))
|
|
|
|
from gemm_utils import ( # noqa: E402
|
|
GemmKernelConfig,
|
|
_output_dtype,
|
|
_dtype_from_kernel_name,
|
|
)
|
|
|
|
_CONFIG_DIR = (
|
|
REPO_ROOT
|
|
/ "tile_engine"
|
|
/ "ops"
|
|
/ "gemm"
|
|
/ "gemm_multi_d"
|
|
/ "configs"
|
|
)
|
|
|
|
|
|
def _make_config(**overrides):
|
|
kw = dict(
|
|
dtype_a="fp16",
|
|
dtype_b="fp16",
|
|
dtype_c="fp16",
|
|
dtype_acc="fp32",
|
|
layout_a="row",
|
|
layout_b="col",
|
|
layout_c="row",
|
|
d_layout="row",
|
|
variant="multi_d",
|
|
elementwise_op="MultiDAdd",
|
|
num_d_tensors=1,
|
|
)
|
|
kw.update(overrides)
|
|
return GemmKernelConfig(**kw)
|
|
|
|
|
|
class TestMultiDName(unittest.TestCase):
|
|
"""The multi_d kernel-name contract (the byte-parity invariant)."""
|
|
|
|
def test_name_carries_multid_suffix(self):
|
|
cfg = _make_config()
|
|
self.assertTrue(
|
|
cfg.name.endswith("_multid_MultiDAdd_d1"), cfg.name
|
|
)
|
|
|
|
def test_name_uses_four_char_layout(self):
|
|
# multi_d appends the D layout char after the 3-char A,B,C code.
|
|
for la, lb, lc, ld in (
|
|
("row", "col", "row", "row"),
|
|
("row", "row", "row", "col"),
|
|
("col", "col", "row", "row"),
|
|
):
|
|
cfg = _make_config(
|
|
layout_a=la, layout_b=lb, layout_c=lc, d_layout=ld,
|
|
)
|
|
four = cfg.layout + ("r" if ld == "row" else "c")
|
|
self.assertIn(f"_{four}_", cfg.name)
|
|
|
|
def test_full_stem_is_stable(self):
|
|
cfg = _make_config(
|
|
tile_m=128, tile_n=128, tile_k=32,
|
|
wave_m=2, wave_n=2, wave_k=1,
|
|
warp_tile_m=32, warp_tile_n=32, warp_tile_k=16,
|
|
pipeline="compv4", scheduler="intrawave", epilogue="cshuffle",
|
|
pad_m=True, pad_n=True, pad_k=True, persistent=False,
|
|
elementwise_op="MultiDAdd", num_d_tensors=2,
|
|
)
|
|
expected = (
|
|
"gemm_fp16_rcrr_compv4_cshuffle_intrawave"
|
|
"_True_True_True_False"
|
|
"_128x128x32_2x2x1_32x32x16"
|
|
"_multid_MultiDAdd_d2"
|
|
)
|
|
self.assertEqual(cfg.name, expected)
|
|
|
|
def test_d_count_changes_the_name(self):
|
|
one = _make_config(num_d_tensors=1).name
|
|
two = _make_config(num_d_tensors=2).name
|
|
self.assertNotEqual(one, two)
|
|
self.assertTrue(one.endswith("_d1"))
|
|
self.assertTrue(two.endswith("_d2"))
|
|
|
|
def test_elementwise_op_changes_the_name(self):
|
|
add = _make_config(elementwise_op="MultiDAdd").name
|
|
mul = _make_config(elementwise_op="MultiDMultiply").name
|
|
self.assertNotEqual(add, mul)
|
|
self.assertIn("_multid_MultiDMultiply_", mul)
|
|
|
|
def test_dtype_recovers_from_name(self):
|
|
for dtype in ("fp16", "bf16"):
|
|
cfg = _make_config(
|
|
dtype_a=dtype, dtype_b=dtype, dtype_c=_output_dtype(dtype),
|
|
)
|
|
self.assertEqual(_dtype_from_kernel_name(cfg.name), dtype)
|
|
|
|
|
|
class TestMultiDCodegenJson(unittest.TestCase):
|
|
"""The codegen JSON must carry the multi_d_config block."""
|
|
|
|
def test_codegen_json_has_multi_d_block(self):
|
|
cfg = _make_config(elementwise_op="MultiDAdd", num_d_tensors=2)
|
|
j = cfg.to_codegen_json()
|
|
self.assertIn("multi_d_config", j)
|
|
md = j["multi_d_config"]
|
|
self.assertEqual(md["elementwise_ops"], ["MultiDAdd"])
|
|
self.assertEqual(md["num_d_tensors"], [2])
|
|
|
|
def test_codegen_json_core_blocks(self):
|
|
j = _make_config().to_codegen_json()
|
|
self.assertIn("tile_config", j)
|
|
self.assertIn("trait_config", j)
|
|
# dispatcher wave_* is projected onto codegen warp_* (warps per block).
|
|
self.assertEqual(j["tile_config"]["warp_m"], [2])
|
|
|
|
|
|
class TestMultiDShippedConfigs(unittest.TestCase):
|
|
"""The configs/*.json the bridge codegens from must be well-formed sweeps."""
|
|
|
|
def test_config_dir_exists(self):
|
|
self.assertTrue(_CONFIG_DIR.is_dir(), _CONFIG_DIR)
|
|
|
|
def test_configs_are_valid_sweeps(self):
|
|
configs = sorted(_CONFIG_DIR.glob("*.json"))
|
|
self.assertGreater(len(configs), 0, "no multi_d configs shipped")
|
|
for path in configs:
|
|
with self.subTest(config=path.name):
|
|
with open(path) as f:
|
|
data = json.load(f)
|
|
self.assertIn("tile_config", data)
|
|
self.assertIn("trait_config", data)
|
|
tc = data["tile_config"]
|
|
for key in (
|
|
"tile_m", "tile_n", "tile_k",
|
|
"warp_m", "warp_n", "warp_k",
|
|
"warp_tile_m", "warp_tile_n", "warp_tile_k",
|
|
):
|
|
self.assertIn(key, tc, f"{path.name} missing {key}")
|
|
tr = data["trait_config"]
|
|
for key in ("pipeline", "scheduler", "epilogue"):
|
|
self.assertIn(key, tr, f"{path.name} missing {key}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|