Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
91ec452
conv3d: parameterize tile size + add autotuner
Jul 8, 2026
24a3575
conv3d: fix large-tensor (>2^31 elem) input addressing for bf16 and fp8
Jul 12, 2026
516318f
conv3d: vectorized epilogue store + 2D/1D entry points
jiacao-amd Jul 12, 2026
b291c6a
conv3d: support >2^31-element inputs on the 3x1x1 fast path
jiacao-amd Jul 12, 2026
1a740c3
conv3d fp8: port vectorized epilogue, 2D/1D entry points, and >2^31 s…
jiacao-amd Jul 12, 2026
0191be8
conv3d: remove bench_conv3d_tiles.py
jiacao-amd Jul 12, 2026
45dc8f0
conv3d: unify 1D/2D/3D behind one entry + merge split-K helpers
jiacao-amd Jul 13, 2026
4d51513
conv3d: async global->LDS copy + 4-stage pipeline (+10-14%)
jiacao-amd Jul 14, 2026
2ff124b
conv3d: extend async copy to large (>2^31 elem) tensors
jiacao-amd Jul 14, 2026
84bdb17
conv3d: add 3x1x1 unfold+hgemm fast path
jiacao-amd Jul 14, 2026
74762cb
conv3d: revert 3x1x1 unfold+hgemm (large-M A materialization causes -…
jiacao-amd Jul 14, 2026
4a2d49f
conv3d: restore 1x1x1 fast path with accurate comment
jiacao-amd Jul 14, 2026
e3d4796
conv3d: rename to conv3d_implicit, force async path, drop sync dead code
jiacao-amd Jul 14, 2026
87ebe94
conv3d: force async path, drop sync dead code, drop async naming
jiacao-amd Jul 14, 2026
edde0f0
fp8 conv: add 8-wave GEMM pipeline kernel + fix BIG_IN rebase
jiacao-amd Jul 15, 2026
f109471
conv3d fp8: add 4-wave GEMM pipeline kernel (slower than 8-wave)
Jul 16, 2026
612df86
conv3d fp8: fix split-K heuristic + add WGM L2-swizzle to gemm8w
Jul 16, 2026
ffdd549
conv3d fp8: route aligned shapes to gemm8w fast path + WGM autotune
Jul 16, 2026
1304cb4
conv3d fp8: generalize gemm8w to all shapes (N/K-partial, tiny-K)
Jul 16, 2026
b7f5d31
conv3d fp8: replace general kernel with the 8-wave GEMM-pipeline kernel
Jul 17, 2026
fd07f28
conv3d fp8: drop the 4-wave GEMM-pipeline kernel
Jul 17, 2026
723be7f
conv3d fp8: drop the 4w-vs-8w bench script
Jul 17, 2026
adb1108
conv3d fp8: fix >2 GB (BIG_IN / BIG_OUT) NaN + memory fault
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions kernels/conv/conv3d_autotune.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2025 FlyDSL Project Contributors

"""Manual tile autotuner for the implicit-GEMM conv3d kernels."""

import json
import os
from pathlib import Path

from flydsl.autotune import do_bench


def _device_fingerprint():
"""GPU arch string (e.g. 'gfx950'), or '' if unavailable."""
try:
from flydsl.runtime.device import get_rocm_arch

return str(get_rocm_arch())
except Exception:
return ""


def _toolchain_fingerprint():
"""FlyDSL version, so a rebuild invalidates stale cached configs."""
try:
import flydsl

return str(getattr(flydsl, "__version__", ""))
except Exception:
return ""


# Curated legal candidates (TILE_M, TILE_N, WAVE_M, WAVE_N) from the enumerated
# constraint-satisfying space. Kept small to bound tuning time; illegal configs
# for a given shape are skipped at compile time (try/except in the sweep).
BF16_CANDIDATES = [
(128, 128, 2, 4),
(128, 256, 2, 4),
(256, 128, 2, 4),
(256, 256, 2, 4),
(256, 256, 4, 4),
(128, 128, 4, 2),
(64, 128, 1, 4),
(64, 64, 2, 2),
]

FP8_CANDIDATES = [
(128, 128, 2, 4),
(128, 256, 2, 4),
(256, 128, 2, 4),
(256, 256, 2, 4),
(128, 128, 4, 2),
(64, 128, 1, 4),
]

WGM_VALUES = [1, 4, 8]

_MEM_CACHE = {}
_CACHE_SCHEMA_VERSION = 3


def _cache_dir():
return Path(os.environ.get("FLYDSL_AUTOTUNE_CACHE_DIR", os.path.expanduser("~/.flydsl/autotune")))


def _cache_file(kind):
return _cache_dir() / f"conv3d_{kind}.json"


def _make_key(kind, shape, dtype_str, candidates):
def _canon(c):
if isinstance(c, tuple) and len(c) == 2 and isinstance(c[0], tuple):
return (tuple(c[0]), c[1])
return tuple(c)

return (
kind,
tuple(shape),
dtype_str,
_device_fingerprint(),
_toolchain_fingerprint(),
_CACHE_SCHEMA_VERSION,
tuple(_canon(c) for c in candidates),
)


def _load_disk(kind, key):
f = _cache_file(kind)
if not f.exists():
return None
try:
data = json.loads(f.read_text())
except Exception:
return None
ent = data.get(json.dumps(list(key)))
if ent is None:
return None
if isinstance(ent[0], list):
return (tuple(ent[0]), ent[1])
return tuple(ent)


def _save_disk(kind, key, best):
f = _cache_file(kind)
f.parent.mkdir(parents=True, exist_ok=True)
data = {}
if f.exists():
try:
data = json.loads(f.read_text())
except Exception:
data = {}
if isinstance(best, tuple) and len(best) == 2 and isinstance(best[0], tuple):
data[json.dumps(list(key))] = [list(best[0]), best[1]]
else:
data[json.dumps(list(key))] = list(best)
f.write_text(json.dumps(data, indent=2))


def autotune_conv3d(kind, shape, dtype_str, candidates, device, run_tile, warmup=5, rep=20):
"""Return the best candidate for this problem shape.

Each element of ``candidates`` is either a tile tuple (TILE_M, TILE_N, WAVE_M, WAVE_N)
or a (tile_tuple, wgm) pair when wgm sweep is enabled. ``run_tile(candidate)``
must launch one full conv for the given candidate and return the output tensor.
The winning candidate is cached (in-memory + JSON on disk).
"""
key = _make_key(kind, shape, dtype_str, candidates)
if key in _MEM_CACHE:
return _MEM_CACHE[key]
disk = _load_disk(kind, key)
if disk is not None:
_MEM_CACHE[key] = disk
return disk

results = []
for tile in candidates:
try:
run_tile(tile) # dry run: triggers compile (lru_cache miss) outside do_bench
t = do_bench(lambda: run_tile(tile), warmup=warmup, rep=rep)
results.append((tile, t))
except Exception:
pass # skip illegal / register-spilling / OOM configs
if not results:
raise RuntimeError(f"all conv3d {kind} autotune configs failed for shape {shape}")

best = min(results, key=lambda x: x[1])[0]
_MEM_CACHE[key] = best
_save_disk(kind, key, best)
return best
Loading