mamba3-neuron-kernels / examples /05_benchmark.py
jburtoft's picture
v1.0.0: initial release
e95c403
Raw
History Blame Contribute Delete
4.53 kB
"""05_benchmark.py -- reproduce the perf numbers in the README.
Runs the SISO and MIMO forward + decode benchmarks at target dims. Reports
warm median latency + p95, and a summary table matching the README.
Run:
python 05_benchmark.py [--full]
--full: also runs seqlen=512 and seqlen=1024 (adds ~5 minutes)
Note: MIMO does not use torch.compile due to a known regression -- see the
project's ticket 61. SISO uses torch.compile at batch=1 for optimal single-request
latency.
"""
import argparse
import statistics
import time
import torch
from _loader import load_kernel
def _sync():
try:
torch.neuron.synchronize()
except Exception:
pass
def bench_forward(mixer, batch, seqlen, warmup=3, iters=10):
u = torch.randn(batch, seqlen, 1024, device="neuron")
for _ in range(warmup):
y, _ = mixer(u)
_sync()
times = []
for _ in range(iters):
t0 = time.perf_counter()
y, _ = mixer(u)
_sync()
times.append((time.perf_counter() - t0) * 1000)
return statistics.median(times), sorted(times)[int(len(times) * 0.95)]
def bench_decode(mixer, batch, prefill_len, n_steps=32, warmup=3):
u_pre = torch.randn(batch, prefill_len, 1024, device="neuron")
_, cache = mixer(u_pre)
_sync()
tokens = [torch.randn(batch, 1, 1024, device="neuron") for _ in range(warmup + n_steps)]
for tok in tokens[:warmup]:
y, cache = mixer.step(tok, cache)
_sync()
times = []
for tok in tokens[warmup:]:
t0 = time.perf_counter()
y, cache = mixer.step(tok, cache)
_sync()
times.append((time.perf_counter() - t0) * 1000)
return statistics.median(times), sorted(times)[int(len(times) * 0.95)]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--full", action="store_true", help="Include seqlen 512 and 1024")
args = ap.parse_args()
print("=" * 78)
print("mamba3-neuron-kernels benchmark (target dims)")
print("=" * 78)
print("Setup: d_model=1024, d_state=128, headdim=64, nheads=32, batch=1")
print("Env: TORCH_NEURONX_ENABLE_CONCATENATION=1 recommended")
mamba3 = load_kernel()
seqlens = [64, 256, 512, 1024] if args.full else [64, 256]
# ---- SISO Forward ----
print("\n### SISO forward (NKI SSD + torch.compile) ###")
print(f"{'seqlen':>7s} {'median (ms)':>12s} {'p95 (ms)':>10s}")
print("-" * 32)
for seqlen in seqlens:
torch.manual_seed(0)
m = mamba3.NeuronMamba3Mixer(
d_model=1024, d_state=128, headdim=64, chunk_size=64, mimo_rank=1,
use_nki_ssd=True,
).to("neuron")
m_compiled = torch.compile(m, backend="neuron")
try:
med, p95 = bench_forward(m_compiled, 1, seqlen)
print(f"{seqlen:>7d} {med:>10.2f} {p95:>8.2f}")
except Exception as e:
print(f"{seqlen:>7d} FAILED: {type(e).__name__}: {str(e)[:40]}")
del m, m_compiled
# ---- MIMO Forward ----
print("\n### MIMO forward (NKI SSD, eager -- torch.compile disabled per ticket 61) ###")
print(f"{'seqlen':>7s} {'median (ms)':>12s} {'p95 (ms)':>10s}")
print("-" * 32)
for seqlen in seqlens:
torch.manual_seed(0)
m = mamba3.NeuronMamba3Mixer(
d_model=1024, d_state=128, headdim=64, chunk_size=16, mimo_rank=4,
use_nki_ssd=True,
).to("neuron")
try:
med, p95 = bench_forward(m, 1, seqlen)
print(f"{seqlen:>7d} {med:>10.2f} {p95:>8.2f}")
except Exception as e:
print(f"{seqlen:>7d} FAILED: {type(e).__name__}: {str(e)[:40]}")
del m
# ---- Decode step ----
print("\n### Decode step (single-token, prefill_len=64) ###")
print(f"{'mode':>6s} {'median (ms)':>12s} {'p95 (ms)':>10s}")
print("-" * 32)
for mode in ["SISO", "MIMO"]:
torch.manual_seed(0)
if mode == "SISO":
m = mamba3.NeuronMamba3Mixer(
d_model=1024, d_state=128, headdim=64, chunk_size=64, mimo_rank=1,
use_nki_ssd=True,
).to("neuron")
else:
m = mamba3.NeuronMamba3Mixer(
d_model=1024, d_state=128, headdim=64, chunk_size=16, mimo_rank=4,
use_nki_ssd=True,
).to("neuron")
med, p95 = bench_decode(m, 1, prefill_len=64, n_steps=32)
print(f"{mode:>6s} {med:>10.2f} {p95:>8.2f}")
del m
print("\n" + "=" * 78)
print("Done.")
if __name__ == "__main__":
main()