prain: Triton GEMV for decode GEMMs on gfx10 (rocBLAS ran bf16 GEMVs at 6% of the bandwidth; agent setup 1.58 -> 2.77 tok/s, same answers)

This commit is contained in:
2026-10-28 22:07:25 +01:00
parent 708e152549
commit 216cd76a77
3 changed files with 146 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
import unittest
import torch
import torch.nn.functional as F
from prain import gemv
GPU = torch.cuda.is_available() and gemv.fast_device(0)
def rel(a, b):
return ((a.float() - b.float()).norm() / b.float().norm()).item()
@unittest.skipUnless(GPU, "needs a gfx10 GPU")
class TestGemv(unittest.TestCase):
def test_up_to_four_rows_match_a_float32_linear(self):
torch.manual_seed(0)
for n, k in ((1000, 2816), (2816, 2112), (130, 70)):
w = torch.randn(n, k, dtype=torch.bfloat16, device="cuda") * 0.02
for rows in (1, 2, 3, 4):
x = torch.randn(rows, k, dtype=torch.bfloat16, device="cuda")
y = gemv.linear(x, w)
self.assertEqual((y.shape, y.dtype), ((rows, n), torch.bfloat16))
self.assertLess(rel(y, F.linear(x.float(), w.float())), 1e-2, (n, k, rows))
x = torch.randn(2, 1, 70, dtype=torch.float16, device="cuda") # leading dims, fp16
w = torch.randn(130, 70, dtype=torch.float16, device="cuda")
self.assertEqual(gemv.linear(x, w).shape, (2, 1, 130))
self.assertLess(rel(gemv.linear(x, w), F.linear(x.float(), w.float())), 1e-2)
def test_more_rows_bias_or_strided_weights_use_f_linear(self):
w = torch.randn(64, 96, dtype=torch.bfloat16, device="cuda")
bias = torch.randn(64, dtype=torch.bfloat16, device="cuda")
x = torch.randn(5, 96, dtype=torch.bfloat16, device="cuda")
self.assertTrue(torch.equal(gemv.linear(x, w), F.linear(x, w)))
self.assertTrue(torch.equal(gemv.linear(x[:1], w, bias), F.linear(x[:1], w, bias)))
strided = torch.randn(96, 64, dtype=torch.bfloat16, device="cuda").t()
self.assertTrue(torch.equal(gemv.linear(x[:1], strided), F.linear(x[:1], strided)))
def test_wrapped_dispatch_sends_only_decode_shapes_to_the_kernel(self):
calls = []
def fallback(layer, x, weight, bias=None):
calls.append(x.shape[0])
return F.linear(x, weight, bias)
gemm = gemv.wrap(lambda *args, **kwargs: fallback)("auto")
w = torch.randn(64, 96, dtype=torch.bfloat16, device="cuda")
gemm(None, torch.randn(1, 96, dtype=torch.bfloat16, device="cuda"), w)
gemm(None, torch.randn(9, 96, dtype=torch.bfloat16, device="cuda"), w)
self.assertEqual(calls, [9])
if __name__ == "__main__":
unittest.main()