liangsu9988's picture
Promote latest kernel artifacts to main
36cbc8c verified
Raw History Blame Contribute Delete
10.1 kB
#!/usr/bin/env python3
"""Benchmark adaptive-layernorm-producers against eager producer chains."""
from __future__ import annotations
import argparse
import importlib
import os
import sys
from pathlib import Path
import torch
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "adaptive-layernorm-producers" / "tests"))
from test_adaptive_layernorm_producers import ( # noqa: E402
_nvfp4_out,
load_source_ops,
make_case,
quant_fp8,
ref_adaln,
ref_layer_norm_no_affine,
)
def load_installed_ops(artifact: str | None):
if artifact:
sys.path.insert(0, artifact)
try:
return importlib.import_module("adaptive_layernorm_producers")
finally:
if artifact:
sys.path.remove(artifact)
def time_cuda(fn, iters: int = 200, warmup: int = 50) -> 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(iters):
fn()
end.record()
torch.cuda.synchronize()
return start.elapsed_time(end) * 1000.0 / iters
def graph_time_cuda(
fn, iters: int = 200, warmup: int = 50, launches_per_graph: int = 32
) -> float:
side = torch.cuda.Stream()
side.wait_stream(torch.cuda.current_stream())
with torch.cuda.stream(side):
for _ in range(3):
fn()
torch.cuda.current_stream().wait_stream(side)
graph = torch.cuda.CUDAGraph()
with torch.cuda.graph(graph):
for _ in range(launches_per_graph):
fn()
return (
time_cuda(graph.replay, iters=iters, warmup=warmup)
/ launches_per_graph
)
def load_native():
from torch.utils.cpp_extension import load
major, minor = torch.cuda.get_device_capability()
os.environ.setdefault(
"TORCH_CUDA_ARCH_LIST", "12.0a" if major >= 12 else f"{major}.{minor}"
)
package = ROOT / "adaptive-layernorm-producers"
return load(
name="adaptive_layernorm_producers_native_bench",
sources=[
str(package / "benchmarks" / "native_binding.cpp"),
str(package / "csrc" / "adaln_modulation6.cu"),
],
extra_include_paths=[str(package / "csrc")],
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3", "--expt-relaxed-constexpr"],
verbose=False,
)
def run_modulation6(ops, native, batch, sequence, dim, iters):
params = torch.randn(
(batch, sequence, 6, dim), device="cuda", dtype=torch.float32
)
modulation = torch.randn((6, dim), device="cuda", dtype=torch.float32)
native_out = tuple(
torch.empty(
(batch, sequence, dim), device="cuda", dtype=torch.bfloat16
)
for _ in range(6)
)
wrapper_out = tuple(torch.empty_like(native_out[0]) for _ in range(6))
def native_call():
native.adaln_modulation6(
params.data_ptr(), modulation.data_ptr(),
*(value.data_ptr() for value in native_out),
batch, sequence, dim,
)
def wrapper_call():
return ops.adaln_modulation6_bf16(
params, modulation, out=wrapper_out
)
def eager_call():
return tuple(
(params[:, :, index, :] + modulation[index]).bfloat16()
for index in range(6)
)
compiled_call = torch.compile(eager_call, fullgraph=True)
native_call()
wrapper_call()
for raw, wrapped in zip(native_out, wrapper_out, strict=True):
torch.testing.assert_close(raw, wrapped, rtol=0, atol=0)
native_us = time_cuda(native_call, iters=iters)
wrapper_us = time_cuda(wrapper_call, iters=iters)
eager_us = time_cuda(eager_call, iters=iters)
compile_us = time_cuda(compiled_call, iters=iters)
graph_native_us = graph_time_cuda(native_call, iters=iters)
graph_wrapper_us = graph_time_cuda(wrapper_call, iters=iters)
return (
native_us, wrapper_us, graph_native_us, graph_wrapper_us,
eager_us, compile_us
)
def run_case(ops, name: str, rows: int, dim: int, eps: float, iters: int) -> dict[str, float | str | int]:
x, scale, shift, _inv_s, act_scale, _scale_fp8, _shift_fp8, _scale_deq, _shift_deq = make_case(rows, dim)
out = torch.empty_like(x, dtype=torch.float8_e4m3fn)
def fused():
ops.ada_layer_norm_quant_fp8_bf16(x, scale, shift, act_scale, eps, out=out)
def eager():
quant_fp8(ref_adaln(x, scale, shift, eps), act_scale)
no_affine_out = torch.empty_like(x, dtype=torch.float8_e4m3fn)
def fused_no_affine():
ops.layer_norm_no_affine_quant_fp8_static_bf16(x, act_scale, eps, out=no_affine_out)
def eager_no_affine():
quant_fp8(ref_layer_norm_no_affine(x, eps), act_scale)
fused_us = time_cuda(fused, iters=iters)
eager_us = time_cuda(eager, iters=iters)
fused_no_affine_us = time_cuda(fused_no_affine, iters=iters)
eager_no_affine_us = time_cuda(eager_no_affine, iters=iters)
return {
"shape": name,
"rows": rows,
"dim": dim,
"ada_fp8_us": fused_us,
"ada_eager_us": eager_us,
"ada_speedup": eager_us / fused_us,
"no_affine_fp8_us": fused_no_affine_us,
"no_affine_eager_us": eager_no_affine_us,
"no_affine_speedup": eager_no_affine_us / fused_no_affine_us,
}
def run_ptok_table(ops, name: str, rows: int, dim: int, eps: float, iters: int):
chunks = 6
x = torch.randn((rows, dim), device="cuda", dtype=torch.bfloat16)
temb = torch.randn(
(rows, chunks, dim), device="cuda", dtype=torch.bfloat16
)
table = torch.randn((chunks, dim), device="cuda", dtype=torch.float32)
out = torch.empty_like(x)
packed, sf = _nvfp4_out(x)
def fused_bf16():
return ops.ada_layer_norm_ptok_table_bf16(
x, temb, table, 0, 1, eps, out=out
)
def fused_nvfp4():
return ops.ada_layer_norm_quant_nvfp4_swizzled_ptok_table_bf16(
x, temb, table, 0, 1, eps, packed=packed, sf_swizzled=sf
)
def eager_bf16():
xf = x.float()
mean = xf.mean(dim=-1, keepdim=True)
var = (xf - mean).square().mean(dim=-1, keepdim=True)
norm = (xf - mean) * torch.rsqrt(var + eps)
shift = temb[:, 0].float() + table[0]
scale = temb[:, 1].float() + table[1]
return (norm * (1.0 + scale) + shift).to(torch.bfloat16)
return {
"shape": name,
"rows": rows,
"dim": dim,
"bf16_us": time_cuda(fused_bf16, iters=iters),
"eager_us": time_cuda(eager_bf16, iters=iters),
"nvfp4_us": time_cuda(fused_nvfp4, iters=iters),
}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--backend", choices=["source", "installed"], default="source")
parser.add_argument("--artifact", default=None)
parser.add_argument("--iters", type=int, default=200)
parser.add_argument("--markdown", default=None)
args = parser.parse_args()
if not torch.cuda.is_available():
raise SystemExit("CUDA is required")
torch.manual_seed(2026)
ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
native = load_native()
shapes = [
("decode_action", 16, 2048),
("wan_video_short", 64, 3072),
("wan_video_ctx", 256, 3072),
("wan_video_2k", 2520, 3072),
("wan_video_4k", 4096, 3072),
]
rows = [run_case(ops, name, r, d, 1e-5, args.iters) for name, r, d in shapes]
table_rows = [
run_ptok_table(ops, name, r, d, 1e-5, args.iters)
for name, r, d in [
("video_short", 64, 3072),
("video_long", 2520, 3072),
]
]
modulation_rows = []
for name, batch, sequence, dim in [
("groot_dit", 1, 51, 1536),
("motus", 1, 2520, 3072),
("video_long", 1, 5070, 3072),
]:
modulation_rows.append(
(name, batch, sequence, dim)
+ run_modulation6(ops, native, batch, sequence, dim, args.iters)
)
lines = [
"| Shape | Rows | Dim | AdaLN->FP8 us | Eager chain us | Speedup | LN->FP8 us | Eager LN chain us | Speedup |",
"|---|---:|---:|---:|---:|---:|---:|---:|---:|",
]
for row in rows:
line = (
f"| {row['shape']} | {row['rows']} | {row['dim']} | "
f"{row['ada_fp8_us']:.3f} | {row['ada_eager_us']:.3f} | {row['ada_speedup']:.2f}x | "
f"{row['no_affine_fp8_us']:.3f} | {row['no_affine_eager_us']:.3f} | {row['no_affine_speedup']:.2f}x |"
)
lines.append(line)
print(line)
lines.extend([
"",
"| Per-token table shape | BF16 fused us | BF16 eager us | Speedup | NVFP4 fused us |",
"|---|---:|---:|---:|---:|",
])
for row in table_rows:
line = (
f"| {row['shape']} M{row['rows']} D{row['dim']} | "
f"{row['bf16_us']:.3f} | {row['eager_us']:.3f} | "
f"{row['eager_us'] / row['bf16_us']:.2f}x | {row['nvfp4_us']:.3f} |"
)
lines.append(line)
print(line)
lines.extend(
[
"",
"| Modulation6 shape | Native us | Wrapper us | Graph native us | Graph wrapper us | Eager us | Compile us | Wrapper/native | Graph wrapper/native |",
"|---|---:|---:|---:|---:|---:|---:|---:|---:|",
]
)
for (
name, batch, sequence, dim, native_us, wrapper_us, graph_native_us,
graph_wrapper_us, eager_us, compile_us
) in modulation_rows:
line = (
f"| {name} B{batch} S{sequence} D{dim} | {native_us:.3f} | "
f"{wrapper_us:.3f} | {graph_native_us:.3f} | "
f"{graph_wrapper_us:.3f} | {eager_us:.3f} | {compile_us:.3f} | "
f"{wrapper_us / native_us:.3f} | "
f"{graph_wrapper_us / graph_native_us:.3f} |"
)
lines.append(line)
print(line)
if args.markdown:
Path(args.markdown).write_text("\n".join(lines) + "\n", encoding="utf-8")
if __name__ == "__main__":
main()