[BugFix][CPU] Compute scalar GEMM products in accum dtype instead of input dtype - #2917
Conversation
…input dtype
The CPU fallback GEMM computed A*B in the input dtype (e.g. fp16) and then
widened the already-rounded product to the accumulator dtype:
C[i, j] += (float)(A[i, k] * B[k, j])
This diverges from CUDA mma, which computes fp16 products exactly and
accumulates in fp32. Rounding each product to fp16 before widening adds up
to ~2^-11 relative error per product, which dominates the fp32 accumulation
error, and products with |a*b| > 65504 overflow the fp16 product to inf,
poisoning the fp32 accumulator with inf/nan.
Cast both operands to accum_dtype before multiplying (exact for fp16/bf16/
fp8 products, which fit exactly in fp32), matching mma semantics and the
behavior of torch CPU fp16 matmul and oneDNN (f32 accumulation).
|
👋 Hi! Thank you for contributing to the TileLang project. Please remember to run We appreciate you taking this step! Our team will review your contribution, and we look forward to your awesome work! 🚀 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe scalar GEMM kernel now casts operands A and B to ChangesScalar GEMM
Estimated code review effort: 2 (Simple) | ~5 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hi @LeiWang1999 and maintainers, While auditing TileLang I focused on the CPU backend, and wanted to confirm the current support status and direction before investing effort, to avoid overlapping with community work and wasting your review time. Observation: The CPU backend (target="c" / target="llvm") is marked Experimental in the README. From my testing:
Questions:
Thanks for your time! If CPU isn't a near-term priority, please let me know and I'll adjust accordingly. |
Summary
GemmScalar— the CPU/LLVM fallback forT.gemm— evaluates each scalar multiply in the input dtype and only then widens the already-rounded product to the accumulator dtype:For fp16 inputs this diverges from CUDA/ROCm mma, which computes fp16 products exactly (the 22-bit result fits in fp32's 24-bit mantissa) and accumulates in fp32. It also diverges from TileLang's own CUDA FMA fallback (
tilelang/cuda/op/gemm/gemm_fma.py), whose docstring documents the correct convention: "Casts widen operands toaccum_dtypebefore multiplication so narrow input dtypes stay numerically sound (e.g. BF16 to FP32 accumulation on Volta). This path favors correctness and coverage over peak throughput."Problem
Rounding each product to fp16 before widening has two consequences:
Accuracy. Every product carries an extra ~2^-11 relative rounding (fp32 accumulation is ~2^-24), which dominates the error for realistic K. Measured on normal-range inputs: the fp16 GEMM deviates from exact-product accumulation by up to ~1e-2 absolute (K=256), whereas exact products are accurate to fp32-accumulation level (~1e-5).
Overflow -> NaN/Inf. Products with |a·b| > 65504 (fp16 max) overflow to ±inf; mixed-sign infs then produce NaN inside the fp32 accumulator. Measured with |a|,|b| ~ 600: 100% of outputs were NaN/Inf before the fix, while exact products stay finite (they fit in fp32).
Fix
Cast both operands to
accum_dtypebefore multiplying (exact for fp16/bf16/fp8 products, all of which fit exactly in fp32):This matches mma semantics and the convention already established in
gemm_fma.py.Verification
fp16
T.gemmontarget="c"(compiled and executed): normal-range inputs match an exact-product fp64 reference to fp32-accumulation level (max abs err 2e-5 vs ~1e-2 before); large-magnitude inputs (products > 65504) no longer produce NaN/Inf. Existing CPU GEMM tests (test_tilelang_cpu_tgemm.py,test_tilelang_cpu_gemm.py) pass.Summary
T.gemmaccumulation.accum_dtypebefore multiplication.InforNaN.