Skip to content

Commit 889c02f

Browse files
authored
Merge pull request #13 from kmbandy/fix/lfm25-paged-turbo4-gfx803
fix: LFM2.5 paged turbo4 on gfx803 (warp-width, sub-128 head_dim, parallel partials)
2 parents 8d078ea + 93a1e6a commit 889c02f

7 files changed

Lines changed: 54 additions & 19 deletions

File tree

ggml/src/ggml-cuda/mt_pagedattn.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ __global__ void mt_scatter_kv_turbo4_0_kernel(
421421
// ---- Step 6: Pack qs (nibble packed, warp-cooperative) ----
422422
const int lane = j % WARP_SIZE;
423423
const uint8_t my_nibble = idx & 0xF;
424-
const uint8_t partner_nibble = __shfl_sync(0xffffffffu, my_nibble, lane ^ 1);
424+
const uint8_t partner_nibble = __shfl_sync(0xffffffffu, my_nibble, lane ^ 1, WARP_SIZE);
425425
if ((j & 1) == 0) {
426426
blk->qs[j / 2] = my_nibble | (partner_nibble << 4);
427427
}
@@ -1208,7 +1208,7 @@ void ggml_cuda_op_paged_attn_mt(ggml_backend_cuda_context & ctx, ggml_tensor * d
12081208
num_seqs, (int) k_cur->ne[2], n_kv_heads, stream);
12091209

12101210
const int num_chunks = paged_attn_decode_num_chunks(max_ctx_len);
1211-
const int max_q_len = avg_q_len; // see comment above
1211+
const int max_q_len = total_q_tokens; // partials inner stride: must be >= max per-seq q_len. avg_q_len = total/num_seqs floors to 0 with idle parallel slots (1 active + N idle), collapsing every head/seq/chunk partial offset to 0 -> OOB corruption. total_q_tokens (gate-capped <= 8) is a safe upper bound.
12121212
const size_t partials_n = (size_t) n_heads * (size_t) num_seqs
12131213
* (size_t) num_chunks * (size_t) max_q_len
12141214
* (size_t) (HS + 2);

ggml/src/ggml-cuda/mt_pagedattn_aiter.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ __global__ void mt_scatter_kv_turbo4_aiter_kernel(
322322
{
323323
const int lane = j % WARP_SIZE;
324324
const uint8_t my_nibble = idx & 0xF;
325-
const uint8_t partner_nibble = __shfl_sync(0xffffffffu, my_nibble, lane ^ 1);
325+
const uint8_t partner_nibble = __shfl_sync(0xffffffffu, my_nibble, lane ^ 1, WARP_SIZE);
326326
if ((j & 1) == 0) {
327327
blk->qs[j / 2] = my_nibble | (partner_nibble << 4);
328328
}

ggml/src/ggml-cuda/mt_pagedattn_decode.cu

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ static __device__ __forceinline__ void decode_coop_stage_turbo4(
238238
}
239239
}
240240
}
241-
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0);
241+
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0, WARP_SIZE);
242242

243243
uint16_t packed = 0;
244244
if (blk != nullptr) {
@@ -302,7 +302,7 @@ static __device__ __forceinline__ void decode_coop_stage_turbo3(
302302
}
303303
}
304304
}
305-
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0);
305+
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0, WARP_SIZE);
306306

307307
uint8_t qs_byte = 0;
308308
uint8_t signs_byte = 0;
@@ -386,7 +386,7 @@ template <typename T>
386386
__device__ __forceinline__ T decode_warp_reduce_sum(T v) {
387387
#pragma unroll
388388
for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
389-
v += __shfl_xor_sync(0xFFFFFFFF, v, mask);
389+
v += __shfl_xor_sync(0xFFFFFFFF, v, mask, WARP_SIZE);
390390
}
391391
return v;
392392
}
@@ -395,7 +395,7 @@ template <typename T>
395395
__device__ __forceinline__ T decode_warp_reduce_max(T v) {
396396
#pragma unroll
397397
for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
398-
v = max(v, __shfl_xor_sync(0xFFFFFFFF, v, mask));
398+
v = max(v, __shfl_xor_sync(0xFFFFFFFF, v, mask, WARP_SIZE));
399399
}
400400
return v;
401401
}
@@ -938,7 +938,7 @@ __global__ void mt_paged_attention_decode_kernel_wmma(
938938
float local_max = -INFINITY;
939939
#pragma unroll
940940
for (int l = 0; l < scores.ne; ++l) local_max = max(local_max, scores.x[l]);
941-
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16));
941+
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16, WARP_SIZE));
942942

943943
const float new_max = max(running_max, row_max);
944944
float rescale = 1.0f;
@@ -959,7 +959,7 @@ __global__ void mt_paged_attention_decode_kernel_wmma(
959959
scores.x[l] = e;
960960
local_sum += e;
961961
}
962-
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16);
962+
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16, WARP_SIZE);
963963
running_sum += row_sum;
964964
running_max = new_max;
965965

ggml/src/ggml-cuda/mt_pagedattn_tile.cu

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ static __device__ __forceinline__ void coop_stage_turbo4_tile(
230230
}
231231

232232
// Broadcast norm from lane 0 to all 32 lanes of this warp.
233-
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0);
233+
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0, WARP_SIZE);
234234

235235
// Each lane reads 2 bytes (4 nibbles = 4 elements). qs is uint8_t[64],
236236
// 2-byte aligned at qs[2*lane_id] for any lane.
@@ -308,7 +308,7 @@ static __device__ __forceinline__ void coop_stage_turbo3_tile(
308308
}
309309

310310
// Broadcast norm from lane 0 to all 32 lanes of this warp.
311-
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0);
311+
norm_f = __shfl_sync(0xFFFFFFFF, norm_f, 0, WARP_SIZE);
312312

313313
// Each lane reads 1 byte of qs (its 4 elements' low2 bits) and 1
314314
// byte of signs (covers this lane's 4 elements' high1 bits, plus
@@ -476,7 +476,7 @@ __global__ void mt_paged_attention_tile_kernel(
476476
for (int l = 0; l < scores.ne; ++l) {
477477
local_max = max(local_max, scores.x[l]);
478478
}
479-
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16));
479+
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16, WARP_SIZE));
480480

481481
const float new_max = max(running_max, row_max);
482482

@@ -502,7 +502,7 @@ __global__ void mt_paged_attention_tile_kernel(
502502
scores.x[l] = e;
503503
local_sum += e;
504504
}
505-
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16);
505+
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16, WARP_SIZE);
506506
running_sum += row_sum;
507507
running_max = new_max;
508508

@@ -861,7 +861,7 @@ __global__ void mt_paged_attention_tile_mw_kernel(
861861
for (int l = 0; l < scores.ne; ++l) {
862862
local_max = max(local_max, scores.x[l]);
863863
}
864-
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16));
864+
const float row_max = max(local_max, __shfl_xor_sync(0xFFFFFFFF, local_max, 16, WARP_SIZE));
865865

866866
const float new_max = max(running_max, row_max);
867867

@@ -887,7 +887,7 @@ __global__ void mt_paged_attention_tile_mw_kernel(
887887
scores.x[l] = e;
888888
local_sum += e;
889889
}
890-
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16);
890+
const float row_sum = local_sum + __shfl_xor_sync(0xFFFFFFFF, local_sum, 16, WARP_SIZE);
891891
running_sum += row_sum;
892892
running_max = new_max;
893893

ggml/src/ggml-cuda/set-rows.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ static __global__ void k_set_rows_turbo4(
10681068
const uint8_t my_nibble = idx & 0xF;
10691069
uint8_t qs_byte = 0;
10701070
// Gather nibble from partner thread
1071-
uint8_t partner_nibble = __shfl_sync(0xffffffff, my_nibble, lane ^ 1);
1071+
uint8_t partner_nibble = __shfl_sync(0xffffffff, my_nibble, lane ^ 1, WARP_SIZE);
10721072
if (j % 2 == 0) {
10731073
qs_byte = my_nibble | (partner_nibble << 4);
10741074
blk->qs[j / 2] = qs_byte;

src/llama-graph.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,9 +2659,27 @@ ggml_tensor * llm_graph_context::build_attn(
26592659
}
26602660
return x;
26612661
};
2662-
ggml_tensor * q_cast = to_f16_cont(q_cur);
2663-
ggml_tensor * k_cast = to_f16_cont(k_cur);
2664-
ggml_tensor * v_cast = to_f16_cont(v_cur);
2662+
// Turbo paged kernel quantizes 128-element blocks (QK_TURBO=128); a
2663+
// head_dim < 128 (e.g. LFM2.5 head_dim 64) is smaller than one block,
2664+
// which the kernel rejects. Zero-pad each head to 128 so the proven
2665+
// HS=128 path runs: the turbo WHT identity makes <WHT(Qp),WHT(Kp)> ==
2666+
// <Q,K>, and the padded V dims contribute zero and are sliced off the
2667+
// output below. Mirrors the non-paged turbo path. ggml_pad needs F32,
2668+
// so pad before the F16 cast.
2669+
const bool paged_turbo_pad =
2670+
(layer.k->type == GGML_TYPE_TURBO2_0 ||
2671+
layer.k->type == GGML_TYPE_TURBO3_0 ||
2672+
layer.k->type == GGML_TYPE_TURBO4_0);
2673+
const int64_t paged_orig_head = q_cur->ne[0];
2674+
const bool paged_pad_head = paged_turbo_pad && (paged_orig_head % 128 != 0);
2675+
auto pad_head_to_128 = [&](ggml_tensor * t) -> ggml_tensor * {
2676+
const int64_t pad = ((paged_orig_head + 127) / 128) * 128 - paged_orig_head;
2677+
ggml_tensor * x = (t->type == GGML_TYPE_F32) ? t : ggml_cast(ctx0, t, GGML_TYPE_F32);
2678+
return ggml_pad(ctx0, x, pad, 0, 0, 0);
2679+
};
2680+
ggml_tensor * q_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(q_cur) : q_cur);
2681+
ggml_tensor * k_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(k_cur) : k_cur);
2682+
ggml_tensor * v_cast = to_f16_cont(paged_pad_head ? pad_head_to_128(v_cur) : v_cur);
26652683

26662684
// 2) Forward-expand the source nodes so they're fully computed
26672685
// before paged_attn reads them.
@@ -2692,6 +2710,14 @@ ggml_tensor * llm_graph_context::build_attn(
26922710
// typically wired only for F32 activations.
26932711
cur = ggml_cast(ctx0, cur, GGML_TYPE_F32);
26942712

2713+
// Slice the turbo head padding back off (see paged_pad_head above):
2714+
// output mirrors q as [padded_head, n_heads, n_tokens]; keep the first
2715+
// paged_orig_head dims per head.
2716+
if (paged_pad_head) {
2717+
cur = ggml_cont(ctx0, ggml_view_3d(ctx0, cur, paged_orig_head, cur->ne[1], cur->ne[2],
2718+
cur->nb[1], cur->nb[2], 0));
2719+
}
2720+
26952721
// 6) Reshape to (head_dim*n_heads, n_tokens) for the wo projection.
26962722
const int64_t n_embd_full = q_cur->ne[0] * q_cur->ne[1];
26972723
const int64_t n_tokens = q_cur->ne[2];

src/llama-kv-cache-paged.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ llama_kv_cache_paged::llama_kv_cache_paged(
120120
}
121121
}
122122

123+
// Turbo cache quantizes 128-element blocks; pad a sub-128 head_dim up to
124+
// 128 so each head is exactly one turbo block (matches the graph-level
125+
// padding in llm_graph_context::build_attn for the paged path).
126+
const bool paged_cache_is_turbo =
127+
(type_k == GGML_TYPE_TURBO2_0 || type_k == GGML_TYPE_TURBO3_0 || type_k == GGML_TYPE_TURBO4_0);
128+
if (paged_cache_is_turbo && head_dim % 128 != 0) {
129+
head_dim = ((head_dim + 127) / 128) * 128;
130+
}
131+
123132
GGML_ASSERT(head_dim > 0 && "head_dim must be > 0");
124133
GGML_ASSERT(n_kv_heads > 0 && "n_kv_heads must be > 0");
125134

0 commit comments

Comments
 (0)