Files
AstrAI/docs/developer/decode_linear_benchmark.md
T
0z5a d4a292b36b perf: tune bf16 gemv and add opt-in fused swiglu
- deepen common-shape BF16 GEMV tuning with warp-row tiling for LLaMA/Qwen2/GPT-NeoX/OPT decode projections
- add fused BF16 up/gate SwiGLU CUDA primitive with ASTRAI_SWIGLU=0/1/auto dispatch
- keep the unfused linear backend as the default path; auto enables no shape until per-architecture checkpoint gates pass
- fall back to the linear/torch chain when kernels are absent, on CPU, in training, or outside supported M/K/dtype shapes
- add gemv/swiglu benchmark scripts, dispatch and parity tests, and kernel documentation

Benchmark: NVIDIA L20 (sm_89), CUDA 12.8, PyTorch 2.11.0+cu128, idle GPU. AstrAI 1B config (24 layers, hidden 1536, vocab 100000), BF16, prompt 128, 32 greedy decode tokens, CUDA graphs enabled, A/B in separate interleaved processes (3 rounds, 8 trials each, medians). Default vs ASTRAI_SWIGLU=1 per generate call: batch 1 134.8->129.1 ms (+4.44%), batch 2 136.2->130.9 ms (+4.06%), batch 4 145.5->140.3 ms (+3.66%). Greedy output identical at batch 1, differs at batch 2/4, so auto stays unfused by default; kernelless fallback verified bit-identical greedy.
2026-09-03 04:26:53 +08:00

2.0 KiB

Decode linear shape benchmark

scripts/tools/benchmark_gemv.py records the F.linear baseline used to decide whether a BF16 GEMV or small-M kernel should enter automatic inference dispatch. It does not change model execution or select a custom kernel.

The default matrix covers the AstrAI 1B q/k/v/out projections, MLP up/gate/down, and LM head for M=1,2,4,8,16,32. Each shape runs in eager and CUDA Graph replay modes. Results include device-event latency samples, p50/p90/p99, estimated effective IO bandwidth, and CUDA kernel launches per call.

CUDA_VISIBLE_DEVICES=0 python scripts/tools/benchmark_gemv.py \
  --output results/decode_linear.json \
  --markdown-output results/decode_linear.md

Use --shape NAME:N:K repeatedly to override the preset and --m-values to change the decode batch sizes. Compare each GPU architecture only with its own baseline; do not use absolute A100-versus-L20 numbers as a dispatch criterion. Keep the raw JSON as the source of truth and generate tables with --markdown-output rather than transcribing measurements by hand.

For direct A/B coverage of the custom kernel and guarded dispatcher across traditional LLaMA and GPT-NeoX decode shapes, use:

CUDA_VISIBLE_DEVICES=0 PYTHONPATH=. python scripts/tools/benchmark_gemv_common.py \
  --suite all --family traditional --m 2 4 \
  --output results/gemv_common.json

The kernel suite compares the directly callable primitive with F.linear. Use repeatable --shape-label and --chain-label filters for a focused run. The synthetic-chain suite alternates ASTRAI_GEMV=0 and auto, includes dependent MLP work and Python dispatch, and rotates through distinct weights. Pass --candidate-mode 1 to characterize a family before adding it to the automatic shape table; the checked-in final evidence always uses auto. It is deliberately not labeled a whole-model throughput benchmark. Both suites report median/p90 CUDA-event latency plus maximum absolute error, relative L2 error, and row-wise argmax parity.