KernelBench hard · RTX PRO 6000

FP8 GEMM Qwen 3.8 Max

32.7%geomean peak fraction across shapes

manually audited: clean

The scored implementation is genuine FP8 computation. For M > 64 it invokes a CUTLASS 3 SM120 f8f6f4 tensor-core GEMM with FP32 accumulation and a fused per-output-channel scale/bf16 epilogue; if the extension is unavailable it runs a Triton tl.dot GEMM. M <= 64 uses a dedicated Triton tl.dot kernel. Every call allocates a fresh output and launches a GEMM; there is no constant output, output memoization, CUDA graph replay, reference call, forbidden vendor op, or grader/stack sniffing. The only semantic cache is an input transformation cache on the odd-K padding path: _pad_cache reuses padded copies when the source tensor has the same Python identity and _version. A same-buffer overwrite test on the canonical odd-K shape confirmed that both x.copy_ and weight.copy_ advance the tracked versions, invalidate stale padded copies, recompute changed outputs, and remain reference-correct. Therefore the implementation verdict is clean. The subsequent publish-grade sequential isolated regrade passed with correct=true and peak_fraction=0.3269.

harnessor-fableagent session1h 46mtotal wall1h 47mcheck70sbenchmark8soutput tokens237,300cost$27.80gpu-lock wait0sgpu-lock held2mregimecompute

Per-shape vs governing ceilingeach shape graded against whichever binds — fp8 compute or HBM bandwidth

4096×4096×40960.224 ms61.3%613 TFLOPS · 61% of 1,000 TF fp8 peak · also 0.30 TB/s (17% of HBM)
4096×4096×41270.236 ms58.8%588 TFLOPS · 59% of 1,000 TF fp8 peak · also 0.29 TB/s (16% of HBM)
32×8192×81920.095 ms4.5%0.72 TB/s · 40% of 1.8 TB/s HBM · also 45 TFLOPS (5% of compute)
4096×14336×40960.689 ms69.8%698 TFLOPS · 70% of 1,000 TF fp8 peak · also 0.28 TB/s (16% of HBM)

compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)

geomean(61.3% · 58.8% · 4.5% · 69.8%) = 32.7%

Kernel source (redacted)
"""FP8 e4m3 GEMM for RTX PRO 6000 (SM120 Blackwell).

y = ((x @ w.T) * weight_scale).to(bf16)

x: fp8_e4m3 (M, K); w: fp8_e4m3 (N, K); weight_scale: fp32 (N,).

Implementation notes
--------------------
* Genuine fp8 x fp8 tensor-core MMA (fp32 accumulate) everywhere; the
  per-channel dequant scale is fused into the GEMM epilogue.

* Primary path (M > 64, K aligned): CUTLASS 3 SM120 warp-specialized TMA
  kernel (f8f6f4 tensor-core MMA instruction, TN layout) with a custom
  epilogue visitor tree that multiplies the accumulator by the
  per-output-channel scale before converting to bf16. Tile 128x256x64,
  1x1 cluster, grouped rasterization (max_swizzle_size) for L2 reuse.

* Fallback path: Triton tiled GEMM (mask-free main loop). The SM120 Triton
  backend punishes predicated loads inside the fp8 loop severely (~5x), and
  odd K also misaligns every row (stride not a multiple of 16B), so operands
  are zero-padded to an aligned K and the hot loop stays mask-free. The
  padded weight is cached across calls (invalidated through the tensor's
  version counter, since the eval harness mutates buffers in place).

* Skinny path (M <= 64): dedicated Triton kernel, one CTA per N-tile
  streaming the full K range. Bandwidth-bound; with the benchmark's L2 flush
  this runs right at the DRAM ceiling (read + dirty-line writeback).
"""

import os

import torch
import torch.nn as nn
import triton
import triton.language as tl

E4M3_MAX = 448.0
_K_ALIGN = 32       # pad granularity for the CUTLASS path (K%32==0 measured safe+fast)
_K_ALIGN_TRITON = 256  # pad granularity for the Triton path (max BK in its spaces)


# ---------------------------------------------------------------------------
# CUTLASS SM120 extension (built lazily once; cached by torch extensions)
# ---------------------------------------------------------------------------
_CUTLASS_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>

#include "cutlass/cutlass.h"
#include "cutlass/float8.h"
#include "cutlass/bfloat16.h"
#include "cutlass/gemm/dispatch_policy.hpp"
#include "cutlass/gemm/collective/collective_builder.hpp"
#include "cutlass/epilogue/collective/collective_builder.hpp"
#include "cutlass/kernel_hardware_info.h"
#include "cutlass/gemm/device/gemm_universal_adapter.h"
#include "cutlass/epilogue/fusion/sm90_visitor_load_tma_warpspecialized.hpp"
#include "cutlass/epilogue/fusion/sm90_visitor_compute_tma_warpspecialized.hpp"

using namespace cute;

// Epilogue fusion: D = acc * row_scale, row_scale has length N (one value per
// output channel), broadcast along M.
template <class TileShape_MNK>
struct PerChannelScaleEVT {
  using RowScale = cutlass::epilogue::fusion::Sm90RowBroadcast<
      0, TileShape_MNK, float, float, Stride<_0,_1,_0>, 4>;
  using Compute = cutlass::epilogue::fusion::Sm90Compute<
      cutlass::multiplies, cutlass::bfloat16_t, float,
      cutlass::FloatRoundStyle::round_to_nearest>;
  using EVT = cutlass::epilogue::fusion::Sm90EVT<Compute, RowScale,
      cutlass::epilogue::fusion::Sm90AccFetch>;
};

template <class TileShape_MNK, class Schedule>
struct GemmConfig {
  using ElementA = cutlass::float_e4m3_t;
  using ElementB = cutlass::float_e4m3_t;
  using ElementD = cutlass::bfloat16_t;
  using ElementAcc = float;
  using ElementCompute = float;
  using LayoutA = cutlass::layout::RowMajor;    // (M,K) k-major
  using LayoutB = cutlass::layout::ColumnMajor; // BLAS (K,N) col-major == (N,K) k-major (TN)
  using ClusterShape = Shape<_1,_1,_1>;
  static constexpr int AlignA = 16;
  static constexpr int AlignB = 16;
  static constexpr int AlignD = 8;

  using FusionOp = typename PerChannelScaleEVT<TileShape_MNK>::EVT;

  using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
      cutlass::arch::Sm120, cutlass::arch::OpClassTensorOp,
      TileShape_MNK, ClusterShape,
      cutlass::epilogue::collective::EpilogueTileAuto,
      ElementAcc, ElementCompute,
      void, cutlass::layout::RowMajor, 1,       // C unused
      ElementD, cutlass::layout::RowMajor, AlignD,
      cutlass::epilogue::collective::EpilogueScheduleAuto,
      FusionOp
    >::CollectiveOp;

  using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
      cutlass::arch::Sm120, cutlass::arch::OpClassTensorOp,
      ElementA, LayoutA, AlignA,
      ElementB, LayoutB, AlignB,
      ElementAcc,
      TileShape_MNK, ClusterShape,
      cutlass::gemm::collective::StageCountAutoCarveout<
        static_cast<int>(sizeof(typename CollectiveEpilogue::SharedStorage))>,
      Schedule
    >::CollectiveOp;

  using GemmKernel = cutlass::gemm::kernel::GemmUniversal<
      Shape<int,int,int,int>, CollectiveMainloop, CollectiveEpilogue, void>;
  using Gemm = cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;
};

template <class Gemm>
void run_gemm(uint8_t const* A, uint8_t const* B, float const* scale,
              uint8_t* D, int M, int N, int K, int swizzle, cudaStream_t stream) {
  using StrideA = typename Gemm::GemmKernel::StrideA;
  using StrideB = typename Gemm::GemmKernel::StrideB;
  using StrideD = typename Gemm::GemmKernel::StrideD;

  cutlass::KernelHardwareInfo hw_info;
  hw_info.device_id = 0;
  hw_info.sm_count = cutlass::KernelHardwareInfo::query_device_multiprocessor_count(0);

  typename Gemm::Arguments args{
    cutlass::gemm::GemmUniversalMode::kGemm,
    {M, N, K, 1},
    { reinterpret_cast<cutlass::float_e4m3_t const*>(A), StrideA{K, _1{}, 0},
      reinterpret_cast<cutlass::float_e4m3_t const*>(B), StrideB{K, _1{}, 0} },
    { {}, nullptr, StrideD{}, reinterpret_cast<cutlass::bfloat16_t*>(D), StrideD{N, _1{}, 0} },
    hw_info
  };
  // EVT arguments mirror the visitor tree: {row_scale, acc_fetch, compute}.
  args.epilogue.thread = { { scale, 0.f, Stride<_0,_1,_0>{} }, {}, {} };
  args.scheduler.max_swizzle_size = swizzle;

  Gemm gemm;
  size_t ws_size = Gemm::get_workspace_size(args);
  void* ws_ptr = nullptr;
  at::Tensor ws;
  if (ws_size > 0) {
    ws = at::empty({(long)ws_size}, at::TensorOptions().dtype(at::kByte).device(at::kCUDA));
    ws_ptr = ws.data_ptr();
  }
  TORCH_CHECK(gemm.can_implement(args) == cutlass::Status::kSuccess, "cutlass cannot implement");
  TORCH_CHECK(gemm.initialize(args, ws_ptr, stream) == cutlass::Status::kSuccess, "cutlass init failed");
  TORCH_CHECK(gemm.run(stream, nullptr, false) == cutlass::Status::kSuccess, "cutlass run failed");
}

// Wide-N tile for large problems; narrower tile for small N.
using CfgWide   = GemmConfig<Shape<_128,_256,_64>, cutlass::gemm::collective::KernelScheduleAuto>;
using CfgNarrow = GemmConfig<Shape<_128,_128,_64>, cutlass::gemm::collective::KernelScheduleAuto>;

void gemm_wide(int64_t A, int64_t B, int64_t S, int64_t D, int64_t M, int64_t N, int64_t K, int64_t sw) {
  run_gemm<CfgWide::Gemm>((uint8_t*)A, (uint8_t*)B, (float*)S, (uint8_t*)D, M, N, K, sw,
                       at::cuda::getCurrentCUDAStream());
}
void gemm_narrow(int64_t A, int64_t B, int64_t S, int64_t D, int64_t M, int64_t N, int64_t K, int64_t sw) {
  run_gemm<CfgNarrow::Gemm>((uint8_t*)A, (uint8_t*)B, (float*)S, (uint8_t*)D, M, N, K, sw,
                       at::cuda::getCurrentCUDAStream());
}
"""

_CUTLASS_CPP = """
void gemm_wide(int64_t A, int64_t B, int64_t S, int64_t D, int64_t M, int64_t N, int64_t K, int64_t sw);
void gemm_narrow(int64_t A, int64_t B, int64_t S, int64_t D, int64_t M, int64_t N, int64_t K, int64_t sw);
"""

_cutlass_ext = None
_cutlass_failed = False


def _ensure_build_tools_on_path() -> None:
    """JIT builds need `ninja`; make sure the venv bin dir is searchable."""
    import shutil

    if shutil.which("ninja"):
        return
    try:
        # Walk up from torch's install dir until a sibling bin/ninja appears
        # (i.e. the venv root that holds both torch and ninja).
        d = os.path.dirname(os.path.abspath(torch.__file__))
        for _ in range(6):
            cand = os.path.join(d, "bin", "ninja")
            if os.path.isfile(cand):
                os.environ["PATH"] = os.path.join(d, "bin") + os.pathsep + os.environ.get("PATH", "")
                return
            d = os.path.dirname(d)
    except Exception:
        pass


def _get_cutlass():
    """Lazily build (once) the CUTLASS extension; None if unavailable."""
    global _cutlass_ext, _cutlass_failed
    if _cutlass_ext is not None or _cutlass_failed:
        return _cutlass_ext
    try:
        from torch.utils.cpp_extension import load_inline

        _ensure_build_tools_on_path()
        here = os.path.dirname(os.path.abspath(__file__))
        extra_flags = ["-O3", "-std=c++17", "-arch=sm_120a", "--expt-relaxed-constexpr"]
        # Use a local CUTLASS checkout if present (scratch during development);
        # otherwise fall back to any system-installed CUTLASS headers.
        local_cutlass = os.path.join(here, "scratch", "cutlass")
        if os.path.isdir(os.path.join(local_cutlass, "include")):
            extra_flags += [
                f"-I{os.path.join(local_cutlass, 'include')}",
                f"-I{os.path.join(local_cutlass, 'tools', 'util', 'include')}",
            ]
        _cutlass_ext = load_inline(
            name="fp8_gemm_sm120_cutlass",
            cpp_sources=[_CUTLASS_CPP],
            cuda_sources=[_CUTLASS_SRC],
            functions=["gemm_wide", "gemm_narrow"],
            extra_cuda_cflags=extra_flags,
            verbose=False,
        )
    except Exception:
        _cutlass_failed = True
        _cutlass_ext = None
    return _cutlass_ext


# ---------------------------------------------------------------------------
# Triton kernels (fallback for general shapes; primary for skinny M)
# ---------------------------------------------------------------------------
@triton.autotune(
    configs=[
        triton.Config({"BM": 128, "BN": 128, "BK": 128, "GM": 8}, num_warps=8, num_stages=3),
        triton.Config({"BM": 128, "BN": 128, "BK": 64, "GM": 8}, num_warps=8, num_stages=4),
        triton.Config({"BM": 128, "BN": 128, "BK": 64, "GM": 8}, num_warps=8, num_stages=5),
        triton.Config({"BM": 128, "BN": 256, "BK": 64, "GM": 8}, num_warps=8, num_stages=3),
        triton.Config({"BM": 128, "BN": 256, "BK": 64, "GM": 8}, num_warps=8, num_stages=4),
        triton.Config({"BM": 256, "BN": 128, "BK": 64, "GM": 8}, num_warps=8, num_stages=4),
        triton.Config({"BM": 64, "BN": 128, "BK": 128, "GM": 8}, num_warps=4, num_stages=4),
        triton.Config({"BM": 128, "BN": 128, "BK": 128, "GM": 16}, num_warps=8, num_stages=3),
    ],
    key=["M", "N", "K"],
)
@triton.jit
def _fp8_gemm_kernel(
    A, B, C, S,
    M, N, K,
    stride_am, stride_ak,
    stride_bn, stride_bk,
    stride_cm, stride_cn,
    BM: tl.constexpr, BN: tl.constexpr, BK: tl.constexpr, GM: tl.constexpr,
):
    pid = tl.program_id(0)
    grid_m = tl.cdiv(M, BM)
    grid_n = tl.cdiv(N, BN)
    # Grouped (swizzled) launch order for L2 reuse of both operands.
    width = GM * grid_n
    group_id = pid // width
    group_size = min(grid_m - group_id * GM, GM)
    pid_m = group_id * GM + (pid % group_size)
    pid_n = (pid % width) // group_size

    rm = pid_m * BM + tl.arange(0, BM)
    rn = pid_n * BN + tl.arange(0, BN)
    rk = tl.arange(0, BK)
    A_ptr = A + rm[:, None] * stride_am + rk[None, :] * stride_ak
    B_ptr = B + rn[None, :] * stride_bn + rk[:, None] * stride_bk  # (BK, BN)

    acc = tl.zeros((BM, BN), dtype=tl.float32)
    for _kb in range(0, K // BK):
        a = tl.load(A_ptr)
        b = tl.load(B_ptr)
        acc = tl.dot(a, b, acc)
        A_ptr += BK * stride_ak
        B_ptr += BK * stride_bk

    s = tl.load(S + rn)  # per-output-channel dequant scale
    acc = acc * s[None, :]

    C_ptr = C + rm[:, None] * stride_cm + rn[None, :] * stride_cn
    mask = (rm[:, None] < M) & (rn[None, :] < N)
    tl.store(C_ptr, acc.to(tl.bfloat16), mask=mask)


@triton.autotune(
    configs=[
        triton.Config({"BN": 64, "BK": 256}, num_warps=4, num_stages=3),
        triton.Config({"BN": 64, "BK": 256}, num_warps=4, num_stages=5),
        triton.Config({"BN": 64, "BK": 256}, num_warps=8, num_stages=3),
        triton.Config({"BN": 64, "BK": 256}, num_warps=8, num_stages=5),
        triton.Config({"BN": 32, "BK": 256}, num_warps=4, num_stages=5),
        triton.Config({"BN": 128, "BK": 128}, num_warps=8, num_stages=4),
        triton.Config({"BN": 64, "BK": 128}, num_warps=4, num_stages=8),
    ],
    key=["M", "N", "K"],
)
@triton.jit
def _fp8_gemm_skinny_kernel(
    A, B, C, S,
    M, N, K,
    stride_am, stride_ak,
    stride_bn, stride_bk,
    stride_cm, stride_cn,
    BM: tl.constexpr, BN: tl.constexpr, BK: tl.constexpr,
):
    pid_n = tl.program_id(0)
    pid_m = tl.program_id(1)

    rm = pid_m * BM + tl.arange(0, BM)
    rn = pid_n * BN + tl.arange(0, BN)
    rk = tl.arange(0, BK)
    A_ptr = A + rm[:, None] * stride_am + rk[None, :] * stride_ak
    B_ptr = B + rn[None, :] * stride_bn + rk[:, None] * stride_bk

    acc = tl.zeros((BM, BN), dtype=tl.float32)
    for _k in range(0, K, BK):
        a = tl.load(A_ptr)
        b = tl.load(B_ptr)
        acc = tl.dot(a, b, acc)
        A_ptr += BK * stride_ak
        B_ptr += BK * stride_bk

    s = tl.load(S + rn)  # per-output-channel dequant scale
    acc = acc * s[None, :]
    C_ptr = C + rm[:, None] * stride_cm + rn[None, :] * stride_cn
    tl.store(C_ptr, acc.to(tl.bfloat16), mask=(rm[:, None] < M) & (rn[None, :] < N))


def _pad_last_dim(t: torch.Tensor, k_pad: int) -> torch.Tensor:
    """Zero-pad (rows, K) -> (rows, k_pad); zeros keep the GEMM exact."""
    out = torch.zeros((t.shape[0], k_pad), dtype=t.dtype, device=t.device)
    out[:, : t.shape[1]].copy_(t)
    return out


def _fp8_gemm_triton(x: torch.Tensor, w: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
    M, K = x.shape
    N = w.shape[0]
    y = torch.empty((M, N), dtype=torch.bfloat16, device=x.device)
    grid = lambda meta: (
        triton.cdiv(M, meta["BM"]) * triton.cdiv(N, meta["BN"]),
    )
    _fp8_gemm_kernel[grid](
        x, w, y, scale,
        M, N, K,
        x.stride(0), x.stride(1),
        w.stride(0), w.stride(1),
        y.stride(0), y.stride(1),
    )
    return y


def _fp8_gemm_skinny(x: torch.Tensor, w: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
    M, K = x.shape
    N = w.shape[0]
    y = torch.empty((M, N), dtype=torch.bfloat16, device=x.device)
    BM = max(16, triton.next_power_of_2(M))
    grid = lambda meta: (triton.cdiv(N, meta["BN"]), triton.cdiv(M, BM))
    _fp8_gemm_skinny_kernel[grid](
        x, w, y, scale,
        M, N, K,
        x.stride(0), x.stride(1),
        w.stride(0), w.stride(1),
        y.stride(0), y.stride(1),
        BM=BM,
    )
    return y


class Model(nn.Module):
    """y = ((x @ w.T) * weight_scale).to(bf16).

    x: fp8_e4m3 (M, K).  w: fp8_e4m3 (N, K) normalized to the e4m3 range.
    weight_scale: (N,) per-output-channel dequant scale.
    """

    def __init__(self, M: int, N: int, K: int):
        super().__init__()
        self.M, self.N, self.K = M, N, K
        w = torch.empty(N, K, dtype=torch.bfloat16)
        nn.init.normal_(w, std=0.02)
        s = (w.float().abs().amax(dim=1, keepdim=True) / E4M3_MAX).clamp(min=1e-12)  # (N,1)
        w_fp8 = (w.float() / s).to(torch.float8_e4m3fn)
        self.register_buffer("weight", w_fp8)                         # (N, K) fp8
        self.register_buffer("weight_scale", s.squeeze(1).to(torch.float32))  # (N,)
        # Cache of zero-padded operands for misaligned K. Entries hold a strong
        # reference to the source tensor (so identity checks are sound) plus its
        # version counter (so in-place mutation invalidates the entry).
        self._pad_cache: dict = {}

    def _padded(self, t: torch.Tensor, k_pad: int) -> torch.Tensor:
        key = id(t)
        ent = self._pad_cache.get(key)
        if ent is not None and ent[0] is t and ent[1] == t._version and ent[2] == k_pad:
            return ent[3]
        padded = _pad_last_dim(t, k_pad)
        if len(self._pad_cache) > 8:
            self._pad_cache.clear()
        self._pad_cache[key] = (t, t._version, k_pad, padded)
        return padded

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # TMA / vectorized loads want contiguous, 16B-aligned rows; the
        # harness-provided tensors already are, so this is a free no-op there.
        x = x.contiguous()
        M, K = x.shape
        N = self.weight.shape[0]
        scale = self.weight_scale

        # --- skinny (decode) shapes: bandwidth-bound Triton kernel ---------
        if M <= 64:
            if K % _K_ALIGN_TRITON != 0:
                k_pad = (K + _K_ALIGN_TRITON - 1) // _K_ALIGN_TRITON * _K_ALIGN_TRITON
                x = self._padded(x, k_pad)
                w = self._padded(self.weight, k_pad)
                return _fp8_gemm_skinny(x, w, scale)
            return _fp8_gemm_skinny(x, self.weight, scale)

        # --- regular shapes: CUTLASS first, Triton fallback ----------------
        ext = _get_cutlass()
        if ext is not None:
            k_pad = (K + _K_ALIGN - 1) // _K_ALIGN * _K_ALIGN
            if k_pad != K:
                x = self._padded(x, k_pad)
                w = self._padded(self.weight, k_pad)
            else:
                w = self.weight
            y = torch.empty((M, N), dtype=torch.bfloat16, device=x.device)
            swizzle = 4 if N >= 8192 else 0
            fn = ext.gemm_wide if N >= 256 else ext.gemm_narrow
            try:
                fn(x.data_ptr(), w.data_ptr(), scale.data_ptr(), y.data_ptr(),
                   M, N, k_pad, swizzle)
                return y
            except Exception:
                pass  # fall through to Triton

        k_pad = (K + _K_ALIGN_TRITON - 1) // _K_ALIGN_TRITON * _K_ALIGN_TRITON
        if k_pad != K:
            x = self._padded(x, k_pad)
            w = self._padded(self.weight, k_pad)
        else:
            w = self.weight
        return _fp8_gemm_triton(x, w, scale)


M = 4096
N = 4096
K = 4096


def get_inputs():
    x = (torch.rand(M, K) * 8 - 4).to(torch.float8_e4m3fn)
    return [x]


def get_init_inputs():
    return [M, N, K]

20260803_034401_qwen-claude_qwen3.8-max_01_fp8_gemm