@@ -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+
457604template <ggml_type CACHE_TYPE , typename scalar_t , int HEAD_SIZE , int BLOCK_SIZE >
458605static 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 ));
0 commit comments