Skip to content

Commit 3a03870

Browse files
kmbandyclaude
andcommitted
cuda: port TQ kernel optimizations from TheTom/llama-cpp-turboquant
mmvq-tq.cu: complete rewrite synced from turboquant PR ggml-org#57 - AMD/ROCm: scalar half path (dp4a regresses on RDNA4) — replaces compute-bound V12 WHT shmem kernel - NVIDIA: dp4a multi-token path with register-based __byte_perm centroid LUT - Multi-token support up to ne[1]=8 for speculative decoding (was ne[1]=1 only) - ggml_cuda_mul_mat_tq4_1s_cublas: large prefill via runtime fp16 dequant + cuBLAS - Load-time q8_0 conversion now opt-in (GGML_TQ_CONVERT_Q8=1), default off ggml-cuda.cu dispatch: - Use ggml_cuda_mul_mat_tq for ne[1]<=8 (was ne[1]==1) - Add cuBLAS path for large TQ4_1S prefill fattn-common.cuh: ROCm FA pool OOM bypass - HIP: raw cudaMalloc/Free for f16 temp buffers instead of pool - Prevents permanent VRAM retention from peak-size pool allocations - Fixes OOM when using quantized KV cache on ROCm fattn.cu: force VEC FA path for quantized KV on HIP - Prevents TILE/MMA paths from allocating temp f16 buffers for quant KV - Also enables mixed f16/turbo KV type combinations turbo-quant.cuh, turbo-innerq.cuh: minor correctness fixes - Wrap bare cuda*Symbol calls in CUDA_CHECK - Fix TURBO_IQ_API DLL export macros for static builds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 90904d3 commit 3a03870

7 files changed

Lines changed: 477 additions & 226 deletions

File tree

ggml/src/ggml-cuda/fattn-common.cuh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,8 +1297,33 @@ void launch_fattn(
12971297
const int cc = ggml_cuda_info().devices[id].cc;
12981298
const int nsm = ggml_cuda_info().devices[id].nsm;
12991299

1300+
#ifdef GGML_USE_HIP
1301+
// HIP/ROCm: bypass the memory pool for f16 temp buffers.
1302+
// The legacy pool (ggml_cuda_pool_leg) retains peak-sized allocations permanently.
1303+
// For quantized KV dequant, this means the f16 temp buffer stays allocated,
1304+
// consuming more VRAM than the quantized KV compression saves — causing OOM.
1305+
// Using raw alloc+free ensures the memory is released after the kernel completes.
1306+
struct hip_f16_alloc {
1307+
half * ptr = nullptr;
1308+
cudaStream_t stream;
1309+
hip_f16_alloc(cudaStream_t s) : stream(s) {}
1310+
~hip_f16_alloc() {
1311+
if (ptr) {
1312+
cudaStreamSynchronize(stream);
1313+
cudaFree(ptr);
1314+
}
1315+
}
1316+
void alloc(size_t nelements) {
1317+
CUDA_CHECK(cudaMalloc(&ptr, nelements * sizeof(half)));
1318+
}
1319+
half * get() { return ptr; }
1320+
};
1321+
hip_f16_alloc K_f16(main_stream);
1322+
hip_f16_alloc V_f16(main_stream);
1323+
#else
13001324
ggml_cuda_pool_alloc<half> K_f16(pool);
13011325
ggml_cuda_pool_alloc<half> V_f16(pool);
1326+
#endif
13021327
ggml_cuda_pool_alloc<int> KV_max(pool);
13031328
ggml_cuda_pool_alloc<float> dst_tmp(pool);
13041329
ggml_cuda_pool_alloc<float2> dst_tmp_meta(pool);

ggml/src/ggml-cuda/fattn.cu

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,19 @@ static best_fattn_kernel ggml_cuda_get_best_fattn_kernel(const int device, const
472472
// For small batch sizes the vector kernel may be preferable over the kernels optimized for large batch sizes:
473473
const bool can_use_vector_kernel = Q->ne[0] <= 256 && Q->ne[0] % 64 == 0 && K->ne[1] % FATTN_KQ_STRIDE == 0;
474474

475+
#ifdef GGML_USE_HIP
476+
// HIP/ROCm: the TILE/MMA/WMMA FA paths allocate unbounded f16 temp buffers
477+
// for quantized KV types (K_f16, V_f16 in launch_fattn). The pool retains
478+
// peak allocation size, so the temp buffer VRAM exceeds KV compression savings.
479+
// This causes quantized KV to OOM before f16 on the same context length.
480+
// Force VEC path which does inline dequant with zero temp buffer overhead.
481+
// Trade-off: prefill is slower (sequential query processing).
482+
// Limitation: head_dim > 256 cannot use VEC (falls through to TILE).
483+
if ((ggml_is_quantized(K->type) || ggml_is_quantized(V->type)) && can_use_vector_kernel) {
484+
return BEST_FATTN_KERNEL_VEC;
485+
}
486+
#endif // GGML_USE_HIP
487+
475488
// If Turing tensor cores are available, use them:
476489
if (turing_mma_available(cc) && Q->ne[0] != 40 && Q->ne[0] != 72) {
477490
if (can_use_vector_kernel) {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,9 +2469,12 @@ static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor
24692469
ggml_cuda_op_mul_mat(ctx, src0, src1, dst, ggml_cuda_op_mul_mat_vec_q, quantize_row_q8_1_cuda);
24702470
} else if (use_mul_mat_q) {
24712471
ggml_cuda_op_mul_mat(ctx, src0, src1, dst, ggml_cuda_op_mul_mat_q, quantize_mmq_q8_1_cuda);
2472-
} else if (!split && is_tq_weight && src1->ne[1] == 1) {
2473-
// Fused TQ weight mul_mat_vec with pre-rotated activations via warp shuffle WHT
2474-
ggml_cuda_mul_mat_vec_tq(ctx, src0, src1, dst);
2472+
} else if (!split && is_tq_weight && src1->ne[1] <= MMVQ_MAX_BATCH_SIZE) {
2473+
// Fused TQ weight mul_mat: handles decode (ne[1]=1) and speculative (ne[1]<=8)
2474+
ggml_cuda_mul_mat_tq(ctx, src0, src1, dst);
2475+
} else if (!split && src0->type == GGML_TYPE_TQ4_1S) {
2476+
// Large prefill: runtime TQ4_1S → fp16 dequant + cuBLAS tensor cores
2477+
ggml_cuda_mul_mat_tq4_1s_cublas(ctx, src0, src1, dst);
24752478
} else {
24762479
ggml_cuda_op_mul_mat(ctx, src0, src1, dst, ggml_cuda_op_mul_mat_cublas, nullptr);
24772480
}

0 commit comments

Comments
 (0)