Skip to content

Commit 846f0e6

Browse files
kmbandyclaude
andcommitted
mt_pagedattn: wire TURBO3_0 K/V cache type through paged framework
Adds paged_cache_ops<TURBO3_0> + cooperative dequant in tile + decode kernels + scatter kernel + dispatch case + supports_op whitelist + bench axis. Mirrors the existing TURBO4_0 plumbing; TURBO3_0 differs only in block payload (qs[32] 2-bit low + signs[16] 1-bit high vs turbo4's nibble-packed qs[64]) and 3-bit centroid table. RHT is skipped on the paged path with the same JL-bypass justification as turbo4 (see mt_scatter_kv_turbo4_0_kernel step 4 comment). Files: - ggml-cuda.cu: TURBO3_0 in PAGED_ATTN_MT supports_op whitelist (without this, the scheduler routes the op to CPU which doesn't implement it and aborts in ggml_get_n_tasks). - mt_pagedattn_ops.cuh: paged_cache_ops<TURBO3_0> specialization. - mt_pagedattn.cu: mt_scatter_kv_turbo3_0_kernel (128-thread cooperative quant, warp-cooperative qs/signs packing via __shfl_xor_sync), launch branch, HEAD_SIZE validation, dispatch case. - mt_pagedattn_tile.cu: coop_stage_turbo3_tile + K/V dispatch + tile/tile_mw template instantiations at HS=128 and HS=256. - mt_pagedattn_decode.cu: decode_coop_stage_turbo3 + K/V dispatch + decode template instantiations at HS=128 and HS=256. Bench (Qwen3.6-35B-A3B-UD-Q4_K_XL @ R9700/gfx1201, n_predict=128): paged_turbo4 8K: 1948 prefill / 32.0 decode | 64K: 952 / 30.7 paged_turbo3 8K: 1924 prefill / 32.5 decode | 64K: 936 / 30.9 TURBO3 == TURBO4 in throughput within run-to-run noise (~1.5%); 24% less KV memory (3.125 vs 4.125 bpv) for the same effective speed. Quality validation (PPL+NIAH sweep) pending — added as task #11. Smoke-tested with --kv-tiered 100,0,0 --kv-tier-paged-blocks --cache-type-k turbo3 --cache-type-v turbo3: server boots, completes a 53-token prompt at 57.7 decode t/s with coherent output. Locked baseline JSON committed alongside as the new perf reference at HEAD. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent bf87261 commit 846f0e6

7 files changed

Lines changed: 528 additions & 14 deletions

File tree

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5663,13 +5663,14 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
56635663
// K/V cache may be quantized; mt::ggml_cuda_op_paged_attn_mt
56645664
// dispatch checks for a specialized paged_cache_ops<TYPE> and
56655665
// aborts with a clear message if unsupported. Currently
5666-
// F16 + Q8_0 + TURBO4_0 are wired (MAD-116).
5666+
// F16 + Q8_0 + TURBO4_0 + TURBO3_0 are wired (MAD-116).
56675667
return op->type == GGML_TYPE_F16
56685668
&& op->src[0]->type == GGML_TYPE_F16 // q
56695669
&& op->src[1]->type == op->src[2]->type // k/v cache same type
56705670
&& (op->src[1]->type == GGML_TYPE_F16
56715671
|| op->src[1]->type == GGML_TYPE_Q8_0
5672-
|| op->src[1]->type == GGML_TYPE_TURBO4_0)
5672+
|| op->src[1]->type == GGML_TYPE_TURBO4_0
5673+
|| op->src[1]->type == GGML_TYPE_TURBO3_0)
56735674
&& op->src[6] // k_cur (fused scatter)
56745675
&& op->src[6]->type == GGML_TYPE_F16
56755676
&& op->src[7] // v_cur (fused scatter)

ggml/src/ggml-cuda/mt_pagedattn.cu

Lines changed: 170 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,153 @@ __global__ void mt_scatter_kv_turbo4_0_kernel(
454454
}
455455
}
456456

457+
// Turbo3_0 scatter: per-128-element-block cooperative quantize.
458+
//
459+
// Same threading and pipeline as turbo4 above, but packs 3-bit indices into
460+
// qs[32] (low 2 bits, 4 per byte) + signs[16] (high 1 bit, 8 per byte).
461+
// 50 bytes payload per 128 elements → 3.125 bpv (vs turbo4's 4.125 bpv).
462+
//
463+
// As with turbo4, RHT is intentionally skipped on the paged path — see the
464+
// step-4 comment in mt_scatter_kv_turbo4_0_kernel for the JL/dot-product
465+
// reasoning. Turbo3 centroids (TURBO_CENTROIDS_3BIT) are Lloyd-Max for
466+
// N(0, 1/d) and remain valid without rotation.
467+
template <int HEAD_SIZE, int BLOCK_SIZE>
468+
__launch_bounds__(QK_TURBO3)
469+
__global__ void mt_scatter_kv_turbo3_0_kernel(
470+
void * __restrict__ k_cache,
471+
void * __restrict__ v_cache,
472+
const __half * __restrict__ k_cur,
473+
const __half * __restrict__ v_cur,
474+
const int32_t * __restrict__ slot_mapping,
475+
int n_kv_heads) {
476+
477+
constexpr int Q_BLOCK = QK_TURBO3; // 128
478+
constexpr int N_QBLOCKS_PER_TOKEN = HEAD_SIZE / Q_BLOCK;
479+
constexpr int N_WARPS = Q_BLOCK / WARP_SIZE; // 4
480+
static_assert(HEAD_SIZE % Q_BLOCK == 0, "HEAD_SIZE must be divisible by QK_TURBO3");
481+
static_assert(Q_BLOCK == 128, "this kernel assumes QK_TURBO3 == 128");
482+
483+
const int j = threadIdx.x; // 0..127, element index within turbo3 block
484+
const int global_token_idx = blockIdx.x;
485+
const int y_idx = blockIdx.y;
486+
const int kv_select = blockIdx.z; // 0 = K, 1 = V
487+
const int kv_head_idx = y_idx / N_QBLOCKS_PER_TOKEN;
488+
const int qb_idx = y_idx % N_QBLOCKS_PER_TOKEN;
489+
490+
const int slot = slot_mapping[global_token_idx];
491+
if (slot < 0) return; // padding token
492+
493+
const int paged_block = slot / BLOCK_SIZE;
494+
const int slot_in_block = slot % BLOCK_SIZE;
495+
496+
const int d = qb_idx * Q_BLOCK + j;
497+
const __half * src = (kv_select == 0) ? k_cur : v_cur;
498+
const size_t src_off = (size_t) global_token_idx * n_kv_heads * HEAD_SIZE
499+
+ (size_t) kv_head_idx * HEAD_SIZE
500+
+ (size_t) d;
501+
502+
// Output block pointer (matches paged_cache_ops<TURBO3_0>::element_block_index)
503+
void * dst_buf = (kv_select == 0) ? k_cache : v_cache;
504+
const int64_t block_ib = ((int64_t) paged_block * n_kv_heads + kv_head_idx) * BLOCK_SIZE * N_QBLOCKS_PER_TOKEN
505+
+ (int64_t) slot_in_block * N_QBLOCKS_PER_TOKEN
506+
+ (int64_t) qb_idx;
507+
block_turbo3_0 * blk = (block_turbo3_0 *) dst_buf + block_ib;
508+
509+
// ---- Step 1: Load into smem (explicit __half2float for HIP safety) ----
510+
__shared__ float x[Q_BLOCK];
511+
x[j] = __half2float(src[src_off]);
512+
__syncthreads();
513+
514+
// ---- Step 2: Parallel L2 norm ----
515+
__shared__ float warp_accum[N_WARPS];
516+
{
517+
float v_sq = x[j] * x[j];
518+
for (int offset = WARP_SIZE / 2; offset > 0; offset >>= 1) {
519+
v_sq += __shfl_xor_sync(0xffffffffu, v_sq, offset);
520+
}
521+
if (j % WARP_SIZE == 0) warp_accum[j / WARP_SIZE] = v_sq;
522+
}
523+
__syncthreads();
524+
525+
__shared__ float s_norm_sq;
526+
if (j == 0) {
527+
float total = 0.0f;
528+
for (int w = 0; w < N_WARPS; ++w) total += warp_accum[w];
529+
s_norm_sq = total;
530+
}
531+
__syncthreads();
532+
const float grp_norm = sqrtf(s_norm_sq);
533+
const float inv_norm = (grp_norm > 1e-10f) ? (1.0f / grp_norm) : 0.0f;
534+
535+
// ---- Step 3: Normalize ----
536+
x[j] *= inv_norm;
537+
__syncthreads();
538+
539+
// ---- Step 4: (intentionally NO Randomized Hadamard Transform) ----
540+
// See mt_scatter_kv_turbo4_0_kernel step 4 comment for justification.
541+
542+
// ---- Step 5: Quantize element j to 3-bit centroid index ----
543+
const float rv = x[j];
544+
const uint8_t idx = turbo_nearest_centroid_3bit(rv); // 0..7
545+
546+
// ---- Step 6: Pack qs[] (4 elements per byte, low 2 bits each) ----
547+
//
548+
// 32 threads in a warp produce 8 bytes of qs. Each byte aggregates 4
549+
// adjacent threads' 2-bit values via warp shuffles. Each warp writes
550+
// to its own non-overlapping chunk of qs[] — no cross-warp conflicts.
551+
{
552+
const int lane = j % WARP_SIZE;
553+
const int warp_id = j / WARP_SIZE;
554+
const uint8_t my2 = idx & 0x3;
555+
uint8_t byte_val = my2 << ((lane & 3) * 2);
556+
byte_val |= __shfl_xor_sync(0xffffffffu, byte_val, 1);
557+
byte_val |= __shfl_xor_sync(0xffffffffu, byte_val, 2);
558+
if ((lane & 3) == 0) {
559+
blk->qs[warp_id * (WARP_SIZE / 4) + lane / 4] = byte_val;
560+
}
561+
}
562+
563+
// ---- Step 7: Pack signs[] (8 elements per byte, high 1 bit each) ----
564+
{
565+
const int lane = j % WARP_SIZE;
566+
const int warp_id = j / WARP_SIZE;
567+
const uint8_t my1 = (idx >> 2) & 0x1;
568+
uint8_t bits = my1 << (lane & 7);
569+
bits |= __shfl_xor_sync(0xffffffffu, bits, 1);
570+
bits |= __shfl_xor_sync(0xffffffffu, bits, 2);
571+
bits |= __shfl_xor_sync(0xffffffffu, bits, 4);
572+
if ((lane & 7) == 0) {
573+
blk->signs[warp_id * (WARP_SIZE / 8) + lane / 8] = bits;
574+
}
575+
}
576+
577+
// ---- Step 8: Reconstruction norm (parallel) ----
578+
{
579+
const float c = TURBO_CENTROIDS_3BIT[idx];
580+
float rc = c * c;
581+
for (int offset = WARP_SIZE / 2; offset > 0; offset >>= 1) {
582+
rc += __shfl_xor_sync(0xffffffffu, rc, offset);
583+
}
584+
if (j % WARP_SIZE == 0) warp_accum[j / WARP_SIZE] = rc;
585+
}
586+
__syncthreads();
587+
588+
__shared__ float s_recon_sq;
589+
if (j == 0) {
590+
float total = 0.0f;
591+
for (int w = 0; w < N_WARPS; ++w) total += warp_accum[w];
592+
s_recon_sq = total;
593+
}
594+
__syncthreads();
595+
const float recon_norm = sqrtf(s_recon_sq);
596+
const float corrected_norm = (recon_norm > 1e-10f) ? (grp_norm / recon_norm) : grp_norm;
597+
598+
// ---- Step 9: Lane 0 writes per-block scalar ----
599+
if (j == 0) {
600+
blk->norm = __float2half(corrected_norm);
601+
}
602+
}
603+
457604
template <ggml_type CACHE_TYPE, typename scalar_t, int HEAD_SIZE, int BLOCK_SIZE>
458605
static void launch_scatter_kv(
459606
void * k_cache,
@@ -494,6 +641,18 @@ static void launch_scatter_kv(
494641
mt_scatter_kv_turbo4_0_kernel<HEAD_SIZE, BLOCK_SIZE>
495642
<<<grid, block, 0, stream>>>(
496643
k_cache, v_cache, k_cur, v_cur, slot_mapping, n_kv_heads);
644+
} else if constexpr (CACHE_TYPE == GGML_TYPE_TURBO3_0) {
645+
// Same grid topology as TURBO4_0 — one CUDA block per
646+
// (token, (kv_head, qb_idx), K-or-V); 128 threads per block.
647+
constexpr int Q_BLOCK = QK_TURBO3;
648+
constexpr int N_QBLOCKS_PER_TOKEN = HEAD_SIZE / Q_BLOCK;
649+
dim3 grid(num_tokens_total, n_kv_heads * N_QBLOCKS_PER_TOKEN, 2);
650+
dim3 block(Q_BLOCK);
651+
GGML_UNUSED(q_lens);
652+
GGML_UNUSED(num_seqs);
653+
mt_scatter_kv_turbo3_0_kernel<HEAD_SIZE, BLOCK_SIZE>
654+
<<<grid, block, 0, stream>>>(
655+
k_cache, v_cache, k_cur, v_cur, slot_mapping, n_kv_heads);
497656
}
498657
// Fall-through for unsupported types is intentional — dispatch in
499658
// ggml_cuda_op_paged_attn_mt switches only over types that have a
@@ -926,18 +1085,21 @@ void ggml_cuda_op_paged_attn_mt(ggml_backend_cuda_context & ctx, ggml_tensor * d
9261085
auto run_typed = [&](auto cache_type_const) {
9271086
constexpr ggml_type CT = decltype(cache_type_const)::value;
9281087

929-
// Cache-type / head-size compatibility: TURBO4_0 needs
930-
// HEAD_SIZE divisible by QK_TURBO4 (128). For smaller heads
931-
// (e.g. HS=64), abort at runtime — we don't even instantiate
932-
// the kernel templates for invalid combinations.
1088+
// Cache-type / head-size compatibility: TURBO4_0 / TURBO3_0 need
1089+
// HEAD_SIZE divisible by their respective QK (both 128). For
1090+
// smaller heads (e.g. HS=64), abort at runtime — we don't even
1091+
// instantiate the kernel templates for invalid combinations.
9331092
if constexpr (CT == GGML_TYPE_TURBO4_0 && HS % QK_TURBO4 != 0) {
9341093
GGML_ABORT("mt_paged_attn: HEAD_SIZE=%d too small for TURBO4_0 (requires multiple of QK_TURBO4=%d)",
9351094
HS, (int) QK_TURBO4);
1095+
} else if constexpr (CT == GGML_TYPE_TURBO3_0 && HS % QK_TURBO3 != 0) {
1096+
GGML_ABORT("mt_paged_attn: HEAD_SIZE=%d too small for TURBO3_0 (requires multiple of QK_TURBO3=%d)",
1097+
HS, (int) QK_TURBO3);
9361098
} else {
9371099

9381100
// MAD-180: WMMA tile FA gate.
9391101
if constexpr (((HS == 128) || (HS == 256)) && (BS == 16)
940-
&& (CT == GGML_TYPE_F16 || CT == GGML_TYPE_TURBO4_0)) {
1102+
&& (CT == GGML_TYPE_F16 || CT == GGML_TYPE_TURBO4_0 || CT == GGML_TYPE_TURBO3_0)) {
9411103
const int cc = ggml_cuda_info().devices[ggml_cuda_get_device()].cc;
9421104
const int total_q_tokens = (int) k_cur->ne[2];
9431105
const int avg_q_len = num_seqs > 0 ? (total_q_tokens / num_seqs) : 0;
@@ -1131,6 +1293,9 @@ void ggml_cuda_op_paged_attn_mt(ggml_backend_cuda_context & ctx, ggml_tensor * d
11311293
case GGML_TYPE_TURBO4_0:
11321294
run_typed(std::integral_constant<ggml_type, GGML_TYPE_TURBO4_0>{});
11331295
break;
1296+
case GGML_TYPE_TURBO3_0:
1297+
run_typed(std::integral_constant<ggml_type, GGML_TYPE_TURBO3_0>{});
1298+
break;
11341299
default:
11351300
GGML_ABORT("mt_paged_attn: unsupported cache type %s — add a paged_cache_ops specialization",
11361301
ggml_type_name(k_cache->type));

ggml/src/ggml-cuda/mt_pagedattn_decode.cu

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,75 @@ static __device__ __forceinline__ void decode_coop_stage_turbo4(
257257
}
258258
}
259259

260+
// TURBO3_0 cooperative dequant for the decode tile. Same threading shape as
261+
// decode_coop_stage_turbo4 (32 lanes × 4 elements per qblock), but unpacks
262+
// the 3-bit index as (qs low-2 | signs hi-1).
263+
template <int HEAD_SIZE, int BLOCK_SIZE>
264+
static __device__ __forceinline__ void decode_coop_stage_turbo3(
265+
__half * __restrict__ smem_dst,
266+
const void * __restrict__ cache,
267+
const int * __restrict__ seq_block_table,
268+
int tile_start,
269+
int valid_ctx,
270+
int kv_head_idx,
271+
int n_kv_heads,
272+
int warp_id,
273+
int lane_id) {
274+
constexpr int Q_BLOCK = QK_TURBO3;
275+
constexpr int QBLOCKS_PER_TOKEN = HEAD_SIZE / Q_BLOCK;
276+
constexpr int N_QBLOCKS_PER_TILE = DECODE_K_TILE_N * QBLOCKS_PER_TOKEN;
277+
static_assert(HEAD_SIZE % Q_BLOCK == 0, "HEAD_SIZE must be multiple of QK_TURBO3=128");
278+
static_assert(Q_BLOCK == 128, "cooperative dequant expects QK_TURBO3=128 (32 lanes × 4 elements)");
279+
280+
const block_turbo3_0 * blocks = (const block_turbo3_0 *) cache;
281+
282+
#pragma unroll
283+
for (int qb = warp_id; qb < N_QBLOCKS_PER_TILE; qb += DECODE_NUM_WARPS) {
284+
const int row = qb / QBLOCKS_PER_TOKEN;
285+
const int qb_in_token = qb % QBLOCKS_PER_TOKEN;
286+
const int token = tile_start + row;
287+
288+
const block_turbo3_0 * blk = nullptr;
289+
float norm_f = 0.0f;
290+
291+
if (token < valid_ctx) {
292+
const int logical_block = token / BLOCK_SIZE;
293+
const int tok_in_block = token % BLOCK_SIZE;
294+
const int physical = seq_block_table[logical_block];
295+
if (physical != kInvalidBlockTableEntry) {
296+
const int64_t ib = ((int64_t) physical * n_kv_heads + kv_head_idx) * BLOCK_SIZE * QBLOCKS_PER_TOKEN
297+
+ (int64_t) tok_in_block * QBLOCKS_PER_TOKEN
298+
+ (int64_t) qb_in_token;
299+
blk = &blocks[ib];
300+
if (lane_id == 0) {
301+
norm_f = __half2float(blk->norm);
302+
}
303+
}
304+
}
305+
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0);
306+
307+
uint8_t qs_byte = 0;
308+
uint8_t signs_byte = 0;
309+
if (blk != nullptr) {
310+
qs_byte = blk->qs[lane_id];
311+
signs_byte = blk->signs[lane_id / 2];
312+
}
313+
const int signs_shift_base = (lane_id % 2) * 4;
314+
315+
const int smem_row_base = row * HEAD_SIZE;
316+
const int smem_col_base = qb_in_token * Q_BLOCK + lane_id * 4;
317+
318+
#pragma unroll
319+
for (int l = 0; l < 4; ++l) {
320+
const uint8_t low2 = (qs_byte >> (l * 2)) & 0x3;
321+
const uint8_t hi1 = (signs_byte >> (signs_shift_base + l)) & 0x1;
322+
const uint8_t idx = low2 | (hi1 << 2);
323+
const float val = TURBO_CENTROIDS_3BIT[idx] * norm_f;
324+
smem_dst[smem_row_base + smem_col_base + l] = __float2half(val);
325+
}
326+
}
327+
}
328+
260329
template <int HEAD_SIZE, int BLOCK_SIZE, ggml_type CACHE_TYPE>
261330
static __device__ __forceinline__ void decode_stage_k(
262331
__half * __restrict__ smem_dst,
@@ -273,6 +342,10 @@ static __device__ __forceinline__ void decode_stage_k(
273342
decode_coop_stage_turbo4<HEAD_SIZE, BLOCK_SIZE>(
274343
smem_dst, cache, seq_block_table, tile_start, valid_ctx,
275344
kv_head_idx, n_kv_heads, warp_id, lane_id);
345+
} else if constexpr (CACHE_TYPE == GGML_TYPE_TURBO3_0) {
346+
decode_coop_stage_turbo3<HEAD_SIZE, BLOCK_SIZE>(
347+
smem_dst, cache, seq_block_table, tile_start, valid_ctx,
348+
kv_head_idx, n_kv_heads, warp_id, lane_id);
276349
} else {
277350
decode_stage_kv_f16<HEAD_SIZE, BLOCK_SIZE>(
278351
smem_dst, (const __half *) cache, seq_block_table, tile_start, valid_ctx,
@@ -296,6 +369,10 @@ static __device__ __forceinline__ void decode_stage_v(
296369
decode_coop_stage_turbo4<HEAD_SIZE, BLOCK_SIZE>(
297370
smem_dst, cache, seq_block_table, tile_start, valid_ctx,
298371
kv_head_idx, n_kv_heads, warp_id, lane_id);
372+
} else if constexpr (CACHE_TYPE == GGML_TYPE_TURBO3_0) {
373+
decode_coop_stage_turbo3<HEAD_SIZE, BLOCK_SIZE>(
374+
smem_dst, cache, seq_block_table, tile_start, valid_ctx,
375+
kv_head_idx, n_kv_heads, warp_id, lane_id);
299376
} else {
300377
decode_stage_kv_f16<HEAD_SIZE, BLOCK_SIZE>(
301378
smem_dst, (const __half *) cache, seq_block_table, tile_start, valid_ctx,
@@ -1065,6 +1142,11 @@ template void launch_paged_attn_decode<128, 16, GGML_TYPE_TURBO4_0>(
10651142
const int32_t *, const int32_t *, const int32_t *,
10661143
float *, int, int, int, int, int, int, float, cudaStream_t);
10671144

1145+
template void launch_paged_attn_decode<128, 16, GGML_TYPE_TURBO3_0>(
1146+
__half *, const __half *, const void *, const void *,
1147+
const int32_t *, const int32_t *, const int32_t *,
1148+
float *, int, int, int, int, int, int, float, cudaStream_t);
1149+
10681150
template void launch_paged_attn_decode<256, 16, GGML_TYPE_F16>(
10691151
__half *, const __half *, const void *, const void *,
10701152
const int32_t *, const int32_t *, const int32_t *,
@@ -1075,4 +1157,9 @@ template void launch_paged_attn_decode<256, 16, GGML_TYPE_TURBO4_0>(
10751157
const int32_t *, const int32_t *, const int32_t *,
10761158
float *, int, int, int, int, int, int, float, cudaStream_t);
10771159

1160+
template void launch_paged_attn_decode<256, 16, GGML_TYPE_TURBO3_0>(
1161+
__half *, const __half *, const void *, const void *,
1162+
const int32_t *, const int32_t *, const int32_t *,
1163+
float *, int, int, int, int, int, int, float, cudaStream_t);
1164+
10781165
} // namespace mt

ggml/src/ggml-cuda/mt_pagedattn_ops.cuh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,38 @@ struct paged_cache_ops<GGML_TYPE_TURBO4_0, HEAD_SIZE, BLOCK_SIZE> {
152152
}
153153
};
154154

155+
// Turbo3_0 specialization: 3-bit PolarQuant with WHT rotation, 128-element blocks.
156+
// Same N_QBLOCKS_PER_TOKEN layout as TURBO4_0; differs in block payload size
157+
// (14 vs 68 bytes) and dequant centroid table (3-bit Lloyd-Max).
158+
// kv_store omitted — handled by a dedicated cooperative scatter kernel.
159+
template <int HEAD_SIZE, int BLOCK_SIZE>
160+
struct paged_cache_ops<GGML_TYPE_TURBO3_0, HEAD_SIZE, BLOCK_SIZE> {
161+
static constexpr int Q_BLOCK = QK_TURBO3; // 128
162+
static constexpr int N_QBLOCKS_PER_TOKEN = HEAD_SIZE / Q_BLOCK;
163+
static_assert(HEAD_SIZE % Q_BLOCK == 0, "HEAD_SIZE must be divisible by QK_TURBO3");
164+
165+
__device__ __forceinline__ static int64_t element_block_index(
166+
int paged_block, int kv_head, int n_kv_heads, int token_in_block, int d) {
167+
return ((int64_t) paged_block * n_kv_heads + kv_head) * BLOCK_SIZE * N_QBLOCKS_PER_TOKEN
168+
+ (int64_t) token_in_block * N_QBLOCKS_PER_TOKEN
169+
+ (int64_t) (d / Q_BLOCK);
170+
}
171+
172+
__device__ __forceinline__ static float k_load(
173+
const void * buf, int paged_block, int kv_head, int n_kv_heads,
174+
int token_in_block, int d) {
175+
const block_turbo3_0 * blocks = (const block_turbo3_0 *) buf;
176+
const int64_t ib = element_block_index(paged_block, kv_head, n_kv_heads, token_in_block, d);
177+
const int iqs = d % Q_BLOCK;
178+
const float norm = __half2float(blocks[ib].norm);
179+
return turbo3_dequant_element(&blocks[ib], iqs, norm);
180+
}
181+
182+
__device__ __forceinline__ static float v_load(
183+
const void * buf, int paged_block, int kv_head, int n_kv_heads,
184+
int token_in_block, int d) {
185+
return k_load(buf, paged_block, kv_head, n_kv_heads, token_in_block, d);
186+
}
187+
};
188+
155189
} // namespace mt

0 commit comments

Comments
 (0)