Skip to content

Commit e768c1a

Browse files
roberteg16claude
andcommitted
CUDA: route single-column f32 mul_mat through mmvf via transpose-free operand swap
A mul_mat of a single-column f32 weight (ne01 == 1) against a wide activation (ne11 > MMVF_MAX_BATCH_SIZE) misses the mmvf vector kernel because ne11 is the batch dimension, and falls back to an f32 cuBLAS/rocBLAS GEMM whose 32x32 macro tile computes just one useful output column. Since the output is a vector, the operands can be swapped transpose-free: the activation becomes the matrix and the single-column weight the lone F32 vector, with dst reinterpreted as [ne11, 1]. This runs the op as a proper GEMV. The graph is unchanged, so LoRA and any other consumer are unaffected. On Qwen3.6-35B-A3B (gfx1151, pp128) the shared-expert gate (weight f32[2048,1]) drops from ~78 us/instance on rocBLAS Sgemm to ~3.8 us on mmvf (~20x), ~3.0 ms saved across the 40 layers. Adds test-backend-ops regression cases (m=1, n sweeping across MMVF_MAX_BATCH_SIZE) that validate both the direct and swapped paths against the CPU reference. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
1 parent 8ff69ec commit e768c1a

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

ggml/src/ggml-cuda/ggml-cuda.cu

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,6 +1759,27 @@ static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor
17591759
ggml_cuda_mul_mat_vec_f(ctx, src0, src1, nullptr, dst);
17601760
return;
17611761
}
1762+
// A single-column f32 weight (ne01 == 1) times a wide activation (ne11 > MMVF_MAX_BATCH_SIZE)
1763+
// misses mmvf above because ne11 is the batch dim, so it falls back to a wasteful f32 cuBLAS
1764+
// GEMM. The output is a vector, so swapping the operands is transpose-free: the activation
1765+
// becomes the matrix and the single-column weight the lone F32 vector, with dst reinterpreted
1766+
// as [ne11, 1].
1767+
if (ne01 == 1 && ne11 > MMVF_MAX_BATCH_SIZE && ne2 == 1 && ne3 == 1
1768+
&& src0->type == GGML_TYPE_F32
1769+
&& ggml_is_contiguous(src0) && ggml_is_contiguous(src1) && ggml_is_contiguous(dst)
1770+
&& ggml_cuda_should_use_mmvf(src1->type, cc, src1->ne, src1->nb, /*ne11 =*/ 1)) {
1771+
// dst is [ne00=1, ne11] (one output feature, ne11 tokens); the swapped GEMV instead
1772+
// produces [ne11, 1] (ne11 rows, a single vector column). Both are the same ne11
1773+
// contiguous floats, so we only relabel the shape of a shallow copy - no data moves:
1774+
ggml_tensor dst_vec = *dst;
1775+
dst_vec.ne[0] = ne11; // rows: one result per token
1776+
dst_vec.ne[1] = 1; // a single output vector column
1777+
dst_vec.nb[1] = dst_vec.nb[0]*ne11; // nb[0] (element stride) stays; row dim spans ne11
1778+
dst_vec.nb[2] = dst_vec.nb[1]; // higher dims are 1 -> same stride
1779+
dst_vec.nb[3] = dst_vec.nb[1];
1780+
ggml_cuda_mul_mat_vec_f(ctx, src1, src0, nullptr, &dst_vec);
1781+
return;
1782+
}
17621783
if (ggml_cuda_should_use_mmf(src0->type, cc, warp_size, src0->ne, src0->nb, ne11, /*mul_mat_id =*/ false)) {
17631784
ggml_cuda_mul_mat_f(ctx, src0, src1, nullptr, dst);
17641785
return;

tests/test-backend-ops.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8555,6 +8555,14 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
85558555
test_cases.emplace_back(new test_mul_mat(GGML_TYPE_Q8_0, GGML_TYPE_F32, 2880, 32, 2880, {1, 1}, {1, 1}));
85568556
test_cases.emplace_back(new test_mul_mat(GGML_TYPE_MXFP4, GGML_TYPE_F32, 2880, 32, 2880, {1, 1}, {1, 1}));
85578557

8558+
// single-row f32 weight (m == 1) against a wide activation (n): the second column count sweeps
8559+
// across MMVF_MAX_BATCH_SIZE (8) so both the direct mmvf path (n <= 8) and the CUDA/HIP
8560+
// operand-swap GEMV path (n > 8, which would otherwise fall back to an f32 cuBLAS GEMM) are
8561+
// exercised and checked against the CPU reference. Regression guard for the mul_mat_vec_f swap.
8562+
for (int64_t n : {1, 7, 8, 9, 16, 128, 512}) {
8563+
test_cases.emplace_back(new test_mul_mat(GGML_TYPE_F32, GGML_TYPE_F32, 1, n, 2048, {1, 1}, {1, 1}));
8564+
}
8565+
85588566

85598567
#if 0
85608568
{

0 commit comments

Comments
 (0)