Files
TheRock/rocm-libraries/projects/composablekernel/dispatcher/heuristics/dispatcher_integration.py
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

325 lines
10 KiB
Python

#!/usr/bin/env python3
# Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""
Dispatcher integration for ML-based kernel selection.
Bridges the trained LightGBM Predictor with the CK Tile dispatcher's
kernel selection flow. Provides heuristic functions compatible with
both the Python pre-selection pattern (08_heuristics.py style) and
the C++ HeuristicFunction signature.
Name mapping between feature engine and dispatcher KernelConfig:
Feature engine Dispatcher KernelConfig
--------------------- ----------------------
warp_m (warps/block) wave_m
warp_n wave_n
warp_k wave_k
warp_tile_m warp_m
warp_tile_n warp_n
warp_tile_k warp_k
Usage:
from dispatcher_integration import create_ml_heuristic
heuristic = create_ml_heuristic("models/gemm_universal_fp8_gfx950")
best_spec = heuristic(M=1024, N=1024, K=1024, kernel_pool=KERNEL_POOL)
"""
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
from data_pipeline import parse_kernel_name
from predict import Predictor
LAYOUT_TO_DISPATCHER = {
"rcr": ("row", "col", "row"),
"rrr": ("row", "row", "row"),
"crr": ("col", "row", "row"),
"ccr": ("col", "col", "row"),
}
DTYPE_TO_C_DTYPE = {
"fp8": "fp16",
"fp16": "fp16",
"bf16": "bf16",
"fp32": "fp32",
}
@dataclass
class MLKernelSpec:
"""Kernel spec returned by the ML heuristic, compatible with the dispatcher
example pattern. Carries both the feature-engine-space config and the
dispatcher-space KernelConfig fields."""
kernel_name: str
predicted_tflops: float
tile_m: int
tile_n: int
tile_k: int
wave_m: int
wave_n: int
wave_k: int
warp_m: int
warp_n: int
warp_k: int
pipeline: str
scheduler: str
epilogue: str
pad_m: bool
pad_n: bool
pad_k: bool
persistent: bool
def kernel_config_to_feature_dict(kernel_name: str) -> dict:
"""Parse a tile-engine kernel name into a feature-engine-compatible dict.
Returns a dict with fields matching what GemmUniversalFeatureEngine.extract()
expects for the kernel parameter: tile_m/n/k, warp_m/n/k (warps per block),
warp_tile_m/n/k, pipeline, scheduler, epilogue, pad_m/n/k, persistent.
"""
parsed = parse_kernel_name(kernel_name)
if not parsed:
return {}
parsed["kernel_name"] = kernel_name
return parsed
def feature_dict_to_dispatcher_config(
feat: dict, dtype: str = "fp8", arch: str = "gfx950"
) -> dict:
"""Convert a feature-engine kernel dict to dispatcher KernelConfig fields.
Handles the naming inversion:
feature engine warp_m -> KernelConfig wave_m (warps per block)
feature engine warp_tile_m -> KernelConfig warp_m (elements per warp)
"""
layout = feat.get("layout", "rcr")
la, lb, lc = LAYOUT_TO_DISPATCHER.get(layout, ("row", "col", "row"))
c_dtype = DTYPE_TO_C_DTYPE.get(dtype, dtype)
return {
"dtype_a": dtype,
"dtype_b": dtype,
"dtype_c": c_dtype,
"dtype_acc": "fp32",
"layout_a": la,
"layout_b": lb,
"layout_c": lc,
"tile_m": feat.get("tile_m", 128),
"tile_n": feat.get("tile_n", 128),
"tile_k": feat.get("tile_k", 64),
"wave_m": feat.get("warp_m", 2),
"wave_n": feat.get("warp_n", 2),
"wave_k": feat.get("warp_k", 1),
"warp_m": feat.get("warp_tile_m", 32),
"warp_n": feat.get("warp_tile_n", 32),
"warp_k": feat.get("warp_tile_k", 16),
"pipeline": feat.get("pipeline", "compv3"),
"scheduler": feat.get("scheduler", "intrawave"),
"epilogue": feat.get("epilogue", "cshuffle"),
"pad_m": feat.get("pad_m", True),
"pad_n": feat.get("pad_n", True),
"pad_k": feat.get("pad_k", True),
"gfx_arch": arch,
}
def feature_dict_to_ml_spec(feat: dict, predicted_tflops: float = 0.0) -> MLKernelSpec:
"""Convert a feature-engine kernel dict + prediction to an MLKernelSpec."""
return MLKernelSpec(
kernel_name=feat.get("kernel_name", "unknown"),
predicted_tflops=predicted_tflops,
tile_m=feat.get("tile_m", 128),
tile_n=feat.get("tile_n", 128),
tile_k=feat.get("tile_k", 64),
wave_m=feat.get("warp_m", 2),
wave_n=feat.get("warp_n", 2),
wave_k=feat.get("warp_k", 1),
warp_m=feat.get("warp_tile_m", 32),
warp_n=feat.get("warp_tile_n", 32),
warp_k=feat.get("warp_tile_k", 16),
pipeline=feat.get("pipeline", "compv3"),
scheduler=feat.get("scheduler", "intrawave"),
epilogue=feat.get("epilogue", "cshuffle"),
pad_m=feat.get("pad_m", False),
pad_n=feat.get("pad_n", False),
pad_k=feat.get("pad_k", False),
persistent=feat.get("persistent", False),
)
def load_kernel_pool_from_binaries(bin_dir: str | Path) -> list[dict]:
"""Discover benchmark executables and parse their names into feature dicts.
Each executable name encodes the full kernel config. This creates the
candidate pool for the ML heuristic without needing a registry JSON export.
"""
bin_dir = Path(bin_dir)
configs = []
for exe in sorted(bin_dir.glob("benchmark_gemm_universal_*")):
name = exe.stem.replace("benchmark_", "")
feat = kernel_config_to_feature_dict(name)
if feat and "tile_m" in feat:
configs.append(feat)
return configs
def create_ml_heuristic(
model_dir: str | Path,
dtype: str = "fp8",
arch: str = "gfx950",
layout: str = "rcr",
kernel_pool: Optional[list[dict]] = None,
bin_dir: Optional[str | Path] = None,
):
"""Create an ML heuristic function for kernel selection.
Returns a callable with signature:
(M: int, N: int, K: int) -> MLKernelSpec
The returned function scores all candidate kernels using the trained
LightGBM regressor and returns the best one as an MLKernelSpec.
Parameters
----------
model_dir : str or Path
Path to trained model directory (must contain model_tflops.lgbm or
model_tflops_log_big.lgbm and feature_spec.json).
dtype : str
Data type for the problem (fp8, fp16, bf16).
arch : str
GPU architecture (gfx942, gfx950).
layout : str
Matrix layout (rcr, rrr, crr, ccr).
kernel_pool : list of dict, optional
Pre-parsed kernel configs. If None, loads from bin_dir.
bin_dir : str or Path, optional
Directory with benchmark executables. Used to build kernel_pool if
kernel_pool is not provided. Defaults to /workspace/ck_tile/bin.
"""
model_dir = Path(model_dir)
predictor = Predictor(model_dir)
if kernel_pool is None:
if bin_dir is None:
bin_dir = Path("/workspace/ck_tile/bin")
kernel_pool = load_kernel_pool_from_binaries(bin_dir)
if not kernel_pool:
raise ValueError(
"No kernel configs found. Check bin_dir or provide kernel_pool."
)
def heuristic(M: int, N: int, K: int) -> MLKernelSpec:
problem = {
"m": M,
"n": N,
"k": K,
"dtype": dtype,
"layout": layout,
"split_k": 1,
}
ranked = predictor.rank_kernels(problem, kernel_pool)
if not ranked:
feat = kernel_pool[0]
return feature_dict_to_ml_spec(feat, 0.0)
best_name, best_tflops = ranked[0]
best_feat = next(
(kp for kp in kernel_pool if kp.get("kernel_name") == best_name),
kernel_pool[0],
)
return feature_dict_to_ml_spec(best_feat, best_tflops)
return heuristic
def create_ranked_heuristic(
model_dir: str | Path,
dtype: str = "fp8",
arch: str = "gfx950",
layout: str = "rcr",
kernel_pool: Optional[list[dict]] = None,
bin_dir: Optional[str | Path] = None,
top_k: int = 5,
):
"""Create an ML heuristic that returns the top-K ranked kernel specs.
Returns a callable with signature:
(M: int, N: int, K: int) -> list[MLKernelSpec]
Useful when you want fallback options if the top-1 kernel fails to build.
"""
model_dir = Path(model_dir)
predictor = Predictor(model_dir)
if kernel_pool is None:
if bin_dir is None:
bin_dir = Path("/workspace/ck_tile/bin")
kernel_pool = load_kernel_pool_from_binaries(bin_dir)
name_to_feat = {kp.get("kernel_name", ""): kp for kp in kernel_pool}
def heuristic(M: int, N: int, K: int) -> list[MLKernelSpec]:
problem = {
"m": M,
"n": N,
"k": K,
"dtype": dtype,
"layout": layout,
"split_k": 1,
}
ranked = predictor.rank_kernels(problem, kernel_pool)
results = []
for name, tflops in ranked[:top_k]:
feat = name_to_feat.get(name, kernel_pool[0])
results.append(feature_dict_to_ml_spec(feat, tflops))
return results
return heuristic
def ml_spec_to_dispatcher_config(
spec: MLKernelSpec, dtype: str = "fp8", arch: str = "gfx950"
) -> dict:
"""Convert an MLKernelSpec to a dict compatible with ctypes_utils.KernelConfig."""
layout_a, layout_b, layout_c = "row", "col", "row"
c_dtype = DTYPE_TO_C_DTYPE.get(dtype, dtype)
return {
"dtype_a": dtype,
"dtype_b": dtype,
"dtype_c": c_dtype,
"dtype_acc": "fp32",
"layout_a": layout_a,
"layout_b": layout_b,
"layout_c": layout_c,
"tile_m": spec.tile_m,
"tile_n": spec.tile_n,
"tile_k": spec.tile_k,
"wave_m": spec.wave_m,
"wave_n": spec.wave_n,
"wave_k": spec.wave_k,
"warp_m": spec.warp_m,
"warp_n": spec.warp_n,
"warp_k": spec.warp_k,
"pipeline": spec.pipeline,
"scheduler": spec.scheduler,
"epilogue": spec.epilogue,
"pad_m": spec.pad_m,
"pad_n": spec.pad_n,
"pad_k": spec.pad_k,
"gfx_arch": arch,
}