laya-ad / code /scripts /benchmark.py
x6nux's picture
laya-ad v1.1:六精度权重 + 模型卡 + 项目代码快照
71dabc7 verified
Raw History Blame Contribute Delete
3.89 kB
#!/usr/bin/env python3
"""吞吐基准:测不同 batch × 序列长度 × 精度下的前向吞吐。
输出 samples/s、tokens/s、延迟 p50 与显存峰值,用于选训练 batch 和部署配置。
输入为随机 token(测的是纯算力上限);真实短文本的收益还要叠加动态 padding,
见 README「吞吐优化」一节。
用法:
python scripts/benchmark.py # seq=128/512 × batch=8..128, bf16
python scripts/benchmark.py --fp32 # fp32 对照
python scripts/benchmark.py --compile # 额外测 torch.compile 版本
"""
import argparse
import time
import torch
from transformers import AutoModelForSequenceClassification
def bench_once(model, x, attn, iters):
for _ in range(3):
with torch.inference_mode():
model(input_ids=x, attention_mask=attn)
torch.cuda.synchronize()
lat = []
t0 = time.perf_counter()
for _ in range(iters):
t1 = time.perf_counter()
with torch.inference_mode():
model(input_ids=x, attention_mask=attn)
torch.cuda.synchronize()
lat.append((time.perf_counter() - t1) * 1000)
total = time.perf_counter() - t0
return total, lat
def run(model, args, dtype_name, results):
for seq in args.seqs:
for batch in args.batches:
x = torch.randint(1000, 200000, (batch, seq), device="cuda")
attn = torch.ones_like(x)
torch.cuda.reset_peak_memory_stats()
try:
total, lat = bench_once(model, x, attn, args.iters)
except torch.cuda.OutOfMemoryError:
torch.cuda.empty_cache()
print(f"[{dtype_name}] seq={seq:>4} batch={batch:>4} | OOM,跳过")
continue
row = {
"dtype": dtype_name, "seq": seq, "batch": batch,
"samples_per_s": batch * args.iters / total,
"tokens_per_s": batch * args.iters * seq / total,
"p50_ms": sorted(lat)[len(lat) // 2],
"vram_gb": torch.cuda.max_memory_allocated() / 2**30,
}
results.append(row)
print(f"[{row['dtype']}] seq={seq:>4} batch={batch:>4} | "
f"{row['samples_per_s']:>9.1f} 样本/s | "
f"{row['tokens_per_s'] / 1e6:>6.2f}M tok/s | "
f"p50={row['p50_ms']:>7.1f}ms | VRAM={row['vram_gb']:.2f}GB")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--model", default="models/mmbert-ad")
ap.add_argument("--seqs", type=int, nargs="+", default=[128, 512])
ap.add_argument("--batches", type=int, nargs="+", default=[8, 32, 64, 128])
ap.add_argument("--iters", type=int, default=20)
ap.add_argument("--fp32", action="store_true", help="只测 fp32")
ap.add_argument("--compile", action="store_true", help="追加测 torch.compile")
args = ap.parse_args()
results = []
dtypes = ["fp32"] if args.fp32 else ["bf16"] + (["fp32"] if False else [])
for dt in dtypes:
model = AutoModelForSequenceClassification.from_pretrained(
args.model, attn_implementation="sdpa",
dtype=torch.float32 if dt == "fp32" else torch.bfloat16).eval().cuda()
run(model, args, dt, results)
if args.compile and dt == "bf16":
compiled = torch.compile(model)
print("(torch.compile:每个新 shape 都要重编译,首次会慢)")
run(compiled, args, dt + "-compile", results)
del model
torch.cuda.empty_cache()
if results:
best = max(results, key=lambda r: r["samples_per_s"])
print(f"\n最佳吞吐配置: {best['dtype']} seq={best['seq']} batch={best['batch']} "
f"-> {best['samples_per_s']:.0f} 样本/s ({best['tokens_per_s'] / 1e6:.2f}M tok/s)")
if __name__ == "__main__":
main()