grouped-moe-gemv / benchmarks /benchmark.py
liangsu9988's picture
Uploaded using `kernel-builder`.
5a76465 verified
Raw
History Blame
6.03 kB
#!/usr/bin/env python3
"""RTX benchmark for grouped W4A4 expert projection launch aggregation."""
from __future__ import annotations
import argparse
import importlib
import sys
from pathlib import Path
import torch
PACKAGE = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PACKAGE / "tests"))
from test_grouped_moe_gemv import load_source_ops, sfb_bytes # noqa: E402
def elapsed_us(fn, warmup: int = 20, repeats: int = 100) -> float:
for _ in range(warmup):
fn()
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(repeats):
fn()
end.record()
end.synchronize()
return start.elapsed_time(end) * 1000.0 / repeats
def load_ops(backend: str, artifact: str | None):
if backend == "source":
return load_source_ops()
if artifact:
sys.path.insert(0, artifact)
return importlib.import_module("grouped_moe_gemv")
def run_case(ops, label: str, m: int, top_k: int, n: int, k: int) -> dict:
experts = 8
x = torch.randn((m, k), device="cuda", dtype=torch.bfloat16) * 0.2
packed = torch.empty((m, k // 2), device="cuda", dtype=torch.uint8)
sfa = torch.empty((sfb_bytes(m, k),), device="cuda", dtype=torch.uint8)
weights = torch.full(
(experts, n, k // 2), 0x11, device="cuda", dtype=torch.uint8
)
sfb = torch.full(
(experts, sfb_bytes(n, k)), 0x38, device="cuda", dtype=torch.uint8
)
alpha = torch.ones((experts,), device="cuda", dtype=torch.float32)
idx = (
torch.arange(m * top_k, device="cuda", dtype=torch.int32)
.reshape(m, top_k)
.remainder(experts)
.contiguous()
)
out = torch.empty((m, top_k, n), device="cuda", dtype=torch.bfloat16)
routed_x = x[:, None, :].expand(m, top_k, k).reshape(m * top_k, k).contiguous()
w4a16_out = torch.empty((m * top_k, n), device="cuda", dtype=torch.bfloat16)
def grouped_region():
ops.quantize_activations_nvfp4_bf16(x, packed=packed, sfa=sfa)
ops.grouped_w4a4_gemv_bf16(
packed, weights, sfa, sfb, alpha, idx, out=out
)
def grouped_kernel():
ops.grouped_w4a4_gemv_bf16(
packed, weights, sfa, sfb, alpha, idx, out=out
)
route_out = torch.empty((m, 1, n), device="cuda", dtype=torch.bfloat16)
route_idx = [idx[:, route : route + 1].contiguous() for route in range(top_k)]
def route_loop():
ops.quantize_activations_nvfp4_bf16(x, packed=packed, sfa=sfa)
for route in range(top_k):
ops.grouped_w4a4_gemv_bf16(
packed,
weights,
sfa,
sfb,
alpha,
route_idx[route],
out=route_out,
)
pair_packed = torch.empty((1, k // 2), device="cuda", dtype=torch.uint8)
pair_sfa = torch.empty((sfb_bytes(1, k),), device="cuda", dtype=torch.uint8)
pair_out = torch.empty((1, 1, n), device="cuda", dtype=torch.bfloat16)
pair_idx = [
idx[token : token + 1, route : route + 1].contiguous()
for token in range(m)
for route in range(top_k)
]
def pair_loop():
pair = 0
for token in range(m):
for _route in range(top_k):
ops.quantize_activations_nvfp4_bf16(
x[token : token + 1], packed=pair_packed, sfa=pair_sfa
)
ops.grouped_w4a4_gemv_bf16(
pair_packed,
weights,
pair_sfa,
sfb,
alpha,
pair_idx[pair],
out=pair_out,
)
pair += 1
grouped_us = elapsed_us(grouped_region)
grouped_kernel_us = elapsed_us(grouped_kernel)
def w4a16_region():
ops.grouped_w4a16_gemv_bf16(
routed_x,
weights,
sfb,
alpha,
idx.reshape(-1),
w_stride=n * k // 2,
sfb_stride=sfb.shape[1],
n=n,
out=w4a16_out,
)
w4a16_us = elapsed_us(w4a16_region)
route_us = elapsed_us(route_loop) if top_k > 1 else grouped_us
pair_repeats = 30 if m * top_k >= 32 else 100
pair_us = elapsed_us(pair_loop, warmup=5, repeats=pair_repeats)
return {
"label": label,
"M": m,
"top_k": top_k,
"pairs": m * top_k,
"N": n,
"K": k,
"grouped_us": grouped_us,
"grouped_kernel_us": grouped_kernel_us,
"route_loop_us": route_us,
"pair_loop_us": pair_us,
"w4a16_us": w4a16_us,
"vs_route_loop": route_us / grouped_us,
"vs_pair_loop": pair_us / grouped_us,
"vs_w4a16": w4a16_us / grouped_us,
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--backend", choices=["source", "installed"], default="source")
parser.add_argument("--artifact")
args = parser.parse_args()
ops = load_ops(args.backend, args.artifact)
cases = [
("gate_up_decode", 1, 8, 1024, 2048),
("gate_up_verify", 7, 8, 1024, 2048),
("down_decode", 8, 1, 2048, 512),
("down_verify", 56, 1, 2048, 512),
]
print("label,M,top_k,pairs,N,K,w4a4_region_us,w4a4_kernel_us,w4a16_us,route_loop_us,pair_loop_us,vs_w4a16,vs_route,vs_pair")
for case in cases:
result = run_case(ops, *case)
print(
f"{result['label']},{result['M']},{result['top_k']},{result['pairs']},"
f"{result['N']},{result['K']},{result['grouped_us']:.3f},"
f"{result['grouped_kernel_us']:.3f},{result['w4a16_us']:.3f},"
f"{result['route_loop_us']:.3f},"
f"{result['pair_loop_us']:.3f},{result['vs_w4a16']:.2f}x,"
f"{result['vs_route_loop']:.2f}x,{result['vs_pair_loop']:.2f}x"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())