1use crate::codegen_cuda::qwen_emit::Q8MatKind;
31
32pub fn mode(kind: Q8MatKind) -> u32 {
34 match kind {
35 Q8MatKind::Dequant => 0,
36 Q8MatKind::Int8Act => 1,
37 Q8MatKind::TwoPassInt8 => 2,
38 }
39}
40
41pub fn head_mode(kind: Q8MatKind) -> u32 {
45 match kind {
46 Q8MatKind::Dequant => 0,
47 Q8MatKind::Int8Act | Q8MatKind::TwoPassInt8 => 3,
48 }
49}
50
51pub const PRELUDE: &str = r#"
54#include <cstddef>
55#include <cuda_fp16.h>
56#include <cuda_bf16.h>
57// An element as the storage type `T` holds it — `f32` as computed, binary16 and bfloat16 rounded to the nearest, ties
58// to even — and as the `f32` every kernel computes in, widened exactly.
59template<typename T> __device__ __forceinline__ T lk_to(float v){ return v; }
60template<> __device__ __forceinline__ __half lk_to<__half>(float v){ return __float2half_rn(v); }
61template<> __device__ __forceinline__ __nv_bfloat16 lk_to<__nv_bfloat16>(float v){ return __float2bfloat16_rn(v); }
62__device__ __forceinline__ float lk_from(float v){ return v; }
63__device__ __forceinline__ float lk_from(__half v){ return __half2float(v); }
64__device__ __forceinline__ float lk_from(__nv_bfloat16 v){ return __bfloat162float(v); }
65template<typename T> __device__ __forceinline__ float lk_kept(float v){ return lk_from(lk_to<T>(v)); }
66// Two and four consecutive elements from `p` as `f32`: one load of their bytes, eight-byte aligned for four binary16
67// values as for two `f32`.
68__device__ __forceinline__ float2 lk_from2(const float* p){ return *(const float2*)p; }
69__device__ __forceinline__ float2 lk_from2(const __half* p){ return __half22float2(*(const __half2*)p); }
70__device__ __forceinline__ float4 lk_from4(const float* p){ return *(const float4*)p; }
71__device__ __forceinline__ float4 lk_from4(const __half* p){
72 uint2 u = *(const uint2*)p;
73 float2 a = __half22float2(*(const __half2*)&u.x), b = __half22float2(*(const __half2*)&u.y);
74 return make_float4(a.x, a.y, b.x, b.y);
75}
76// One element of a result's array as it is written: `f32`, or binary16 rounded to the nearest.
77struct lk_ref {
78 void* p; bool h;
79 __device__ __forceinline__ void operator=(float v) const { if(h) *(__half*)p = __float2half_rn(v); else *(float*)p = v; }
80};
81// A result's array — `f32`, or binary16 that every value written is rounded to — taken from a typed pointer, so a kernel
82// writes either through one pointer type. It can be offset and written, never read.
83struct lk_out {
84 void* p = nullptr; bool h = false;
85 lk_out() = default;
86 __host__ __device__ lk_out(std::nullptr_t) {}
87 __host__ __device__ lk_out(float* q) : p(q) {}
88 __host__ __device__ lk_out(__half* q) : p(q), h(true) {}
89 __host__ __device__ explicit operator bool() const { return p != nullptr; }
90 __device__ __forceinline__ void* at(long i) const { return h ? (void*)((__half*)p + i) : (void*)((float*)p + i); }
91 __device__ __forceinline__ lk_out operator+(long i) const { lk_out o; o.p = at(i); o.h = h; return o; }
92 __device__ __forceinline__ lk_ref operator[](long i) const { return lk_ref{at(i), h}; }
93};
94// A result written into the row a 1-based position names, or the buffer itself when no position is given.
95#define LK_ROW(p, row, pitch) ((p) + ((row) ? (long)(*(row) - 1) * (long)(pitch) : 0L))
96
97__device__ __forceinline__ float lk_warp_sum(float v){
98 for(int o = 16; o > 0; o >>= 1) v += __shfl_down_sync(0xffffffffu, v, o);
99 return __shfl_sync(0xffffffffu, v, 0);
100}
101__device__ __forceinline__ double lk_warp_sum_d(double v){
102 for(int o = 16; o > 0; o >>= 1) v += __shfl_down_sync(0xffffffffu, v, o);
103 return __shfl_sync(0xffffffffu, v, 0);
104}
105__device__ __forceinline__ float lk_warp_max(float v){
106 for(int o = 16; o > 0; o >>= 1) v = fmaxf(v, __shfl_down_sync(0xffffffffu, v, o));
107 return __shfl_sync(0xffffffffu, v, 0);
108}
109__device__ __forceinline__ double lk_warp_sum(double v){ return lk_warp_sum_d(v); }
110// The `V` values each lane holds summed over the warp's lanes, `V` a power of two to 32: lane `l` returns the total of
111// value `l % V`. At a distance `W` below `V` each lane keeps one half of its values and sends the other, the halves
112// summed apart; at and past `V` the whole set is summed. Starting at a distance `W` below 16 sums over each run of
113// `2·W` lanes apart.
114template<int V, int W = 16>
115__device__ __forceinline__ float lk_lane_sums(float (&v)[V]){
116 if constexpr(W >= V){
117 #pragma unroll
118 for(int i = 0; i < V; ++i) v[i] += __shfl_xor_sync(0xffffffffu, v[i], W);
119 } else {
120 bool up = threadIdx.x & W;
121 #pragma unroll
122 for(int i = 0; i < W; ++i){
123 float send = up ? v[i] : v[i + W], keep = up ? v[i + W] : v[i];
124 v[i] = keep + __shfl_xor_sync(0xffffffffu, send, W);
125 }
126 }
127 if constexpr(W > 1) return lk_lane_sums<V, W / 2>(v);
128 else return v[0];
129}
130// The precise numerics (Q 0) sum in f64, the quantized in f32.
131template<int Q> struct lk_acc { typedef float t; };
132template<> struct lk_acc<0> { typedef double t; };
133// The products that read their activation `f32`: every one under the precise numerics (Q 0), and under Q 3 — the
134// latent's expansion halves under the quantized numerics, which sum in f32.
135template<int Q> __host__ __device__ constexpr bool lk_f32_acts(){ return Q == 0 || Q == 3; }
136// Every thread of the block gets the total over `vw` warps, in the accumulator `A` (`float` or `double`); `red` is 32 of
137// them in shared memory. Warp `w` of the block's `nw` plays the warps `w`, `w + nw`, … below `vw`, value `j` of each
138// lane its lane's of the `j`-th: each warp summed over its lanes, then the warps' sums in order — the tree a block of
139// `vw` warps sums its values by.
140template<int VW, typename A>
141__device__ __forceinline__ A lk_warps_sum(const A (&v)[VW], int vw, A* red){
142 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5;
143 A s[VW];
144 #pragma unroll
145 for(int j = 0; j < VW; ++j) s[j] = lk_warp_sum(v[j]);
146 __syncthreads();
147 #pragma unroll
148 for(int j = 0; j < VW; ++j) if(lane == 0 && warp + j * nw < vw) red[warp + j * nw] = s[j];
149 __syncthreads();
150 return lk_warp_sum(lane < vw ? red[lane] : (A)0);
151}
152// Every thread of the block gets the block's total.
153template<typename A>
154__device__ __forceinline__ A lk_block_sum(A v, A* red){
155 const A one[1] = {v};
156 return lk_warps_sum<1>(one, (blockDim.x + 31) >> 5, red);
157}
158__device__ __forceinline__ float lk_block_max(float v, float* red){
159 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
160 v = lk_warp_max(v);
161 __syncthreads();
162 if(lane == 0) red[warp] = v;
163 __syncthreads();
164 return lk_warp_max(lane < nw ? red[lane] : -INFINITY);
165}
166// An activation: ACT 0 silu `x / (1 + e^-x)`, 1 sigmoid `1 / (1 + e^-x)`, 2 gelu in ggml's tanh form
167// `x / (1 + e^-2z)`, `z = √(2/π)·(x + 0.044715·x³)`.
168template<int ACT>
169__device__ __forceinline__ float lk_act(float v){
170 if(ACT == 0) return v / (1.0f + expf(-v));
171 if(ACT == 1) return 1.0f / (1.0f + expf(-v));
172 float z = 0.7978845608028654f * (v + 0.044715f * v * v * v);
173 return v / (1.0f + expf(-2.0f * z));
174}
175// The two-word scale of values whose largest magnitude is `a`, and value `v`'s two words under scale `s`, by quotients:
176// `v ≈ s·(h + l/128)` (the two-word activation quantizer's); by products with the scale's reciprocal `r` where a kernel
177// takes many values under one scale; four words packed into one, the first lowest.
178__device__ __forceinline__ float lk_two_scale(float a){ return (a > 1e-30f ? a : 1e-30f) / 127.0f; }
179__device__ __forceinline__ void lk_two_words(float v, float s, int& h, int& l){
180 h = __float2int_rn(v / s); h = h > 127 ? 127 : (h < -128 ? -128 : h);
181 l = __float2int_rn(__fmaf_rn(-(float)h, s, v) / (s * 0.0078125f)); l = l > 127 ? 127 : (l < -128 ? -128 : l);
182}
183__device__ __forceinline__ void lk_two_words_r(float v, float s, float r, int& h, int& l){
184 h = __float2int_rn(v * r); h = h > 127 ? 127 : (h < -128 ? -128 : h);
185 l = __float2int_rn(__fmaf_rn(-(float)h, s, v) * r * 128.f); l = l > 127 ? 127 : (l < -128 ? -128 : l);
186}
187__device__ __forceinline__ int lk_int8x4(int a, int b, int c, int d){
188 return (int)((unsigned)(a & 255) | ((unsigned)(b & 255) << 8) | ((unsigned)(c & 255) << 16) | ((unsigned)(d & 255) << 24));
189}
190// Quantize the 32-value group this warp holds, one value per lane, as group `g`.
191template<int Q>
192__device__ __forceinline__ void lk_quant_lane(float v, signed char* __restrict__ qh, signed char* __restrict__ ql,
193 float* __restrict__ qs, long i, long g){
194 float a = lk_warp_max(fabsf(v));
195 if(Q == 1){
196 float s = a > 0.f ? a / 127.0f : 1.0f;
197 if((threadIdx.x & 31) == 0) qs[g] = s;
198 qh[i] = (signed char)__float2int_rn(v / s);
199 } else {
200 float s = lk_two_scale(a);
201 int h, l;
202 lk_two_words(v, s, h, l);
203 if((threadIdx.x & 31) == 0) qs[g] = s;
204 qh[i] = (signed char)h; ql[i] = (signed char)l;
205 }
206}
207// Quantize a pair of values `i`, `i + 1` of a 32-value group sixteen lanes of a warp hold, a pair a lane, as group
208// `i >> 5` — the group's largest magnitude taken over the sixteen lanes on every lane (a lane `on` false holds none of
209// it), and each value and the scale then what `lk_quant_lane` makes of the group.
210template<int Q>
211__device__ __forceinline__ void lk_quant_pair(float x, float y, bool on, signed char* __restrict__ qh, signed char* __restrict__ ql,
212 float* __restrict__ qs, long i){
213 float a = on ? fmaxf(fabsf(x), fabsf(y)) : 0.f;
214 #pragma unroll
215 for(int o = 8; o > 0; o >>= 1) a = fmaxf(a, __shfl_xor_sync(0xffffffffu, a, o));
216 if(!on) return;
217 if(Q == 1){
218 float s = a > 0.f ? a / 127.0f : 1.0f;
219 if((threadIdx.x & 15) == 0) qs[i >> 5] = s;
220 qh[i] = (signed char)__float2int_rn(x / s);
221 qh[i + 1] = (signed char)__float2int_rn(y / s);
222 } else {
223 float s = lk_two_scale(a);
224 int h, l;
225 lk_two_words(x, s, h, l);
226 qh[i] = (signed char)h; ql[i] = (signed char)l;
227 lk_two_words(y, s, h, l);
228 qh[i + 1] = (signed char)h; ql[i + 1] = (signed char)l;
229 if((threadIdx.x & 15) == 0) qs[i >> 5] = s;
230 }
231}
232// Where an attention quantizes its output for the projection that reads it: its words, low words and scales.
233struct lk_qout { signed char* q; signed char* l; float* s; const float* g; int ga; };
234// Values `i`, `i + 1` of an attention's output: gated by `lk_act` (`ga`) of the gate's own values `g[i]`, `g[i + 1]` where
235// `g` is given, as a product with the activation computes it; written `f32` where `out` is given; quantized under `QO`
236// into `qo` (`lk_quant_pair`) — every lane of the warp calls it, `on` the lanes that hold a pair.
237template<int QO>
238__device__ __forceinline__ void lk_attn_store(float* __restrict__ out, lk_qout qo, long i, bool on, float x, float y){
239 if(on && qo.g){
240 const float gx = qo.g[i], gy = qo.g[i + 1];
241 x *= qo.ga == 0 ? lk_act<0>(gx) : qo.ga == 1 ? lk_act<1>(gx) : lk_act<2>(gx);
242 y *= qo.ga == 0 ? lk_act<0>(gy) : qo.ga == 1 ? lk_act<1>(gy) : lk_act<2>(gy);
243 }
244 if(on && out) *(float2*)(out + i) = make_float2(x, y);
245 if(QO > 0) lk_quant_pair<QO>(x, y, on, qo.q, qo.l, qo.s, i);
246}
247// One step of a Q8_0 dot's partial sum, as its two factors: the partial `p` becomes `fmaf(a, b, p)` for the int8 dot
248// `d` (and, in two words, the low words' `e`) of a block of weight scale `w` and activation scale `s`, each operation
249// spelled out, so every kernel that sums as the GEMV sums takes the same steps (`lk_q8_acc` takes one).
250template<int Q>
251__device__ __forceinline__ float2 lk_q8_term(float w, float s, float d, float e){
252 if(Q == 1) return make_float2(__fmul_rn(w, s), d);
253 return make_float2(w, __fmaf_rn(s, d, __fmul_rn(__fmul_rn(s, 0.0078125f), e)));
254}
255template<int Q>
256__device__ __forceinline__ float lk_q8_acc(float acc, float w, float s, float d, float e){
257 const float2 t = lk_q8_term<Q>(w, s, d, e);
258 return __fmaf_rn(t.x, t.y, acc);
259}
260// The four partials of a quantized Q8_0 dot summed in the one order every kernel that sums as the GEMV sums takes.
261__device__ __forceinline__ float lk_q8_sum4(float p0, float p1, float p2, float p3){ return (p0 + p2) + (p1 + p3); }
262// The int8 dot of one 32-value block two lanes hold sixteen values each of (`w` and `x` their words), exact, in both.
263__device__ __forceinline__ int lk_q8_block_dot(int4 w, int4 x){
264 int d = __dp4a(w.x, x.x, 0) + __dp4a(w.y, x.y, 0) + __dp4a(w.z, x.z, 0) + __dp4a(w.w, x.w, 0);
265 return d + __shfl_xor_sync(0xffffffffu, d, 1);
266}
267// The scratch, in `float2`, a warp of a quantized Q8_0 GEMV takes for `U` dots at once over more than one activation
268// row: a round's steps of up to eight dots at a time, a dot's sixteen 20 apart, so a half-warp's chain reads fall on
269// distinct banks.
270template<int U> __host__ __device__ constexpr int lk_q8_scratch(){ return (U < 8 ? U : 8) * 20; }
271// Round `c` of rows `o … o + W − 1` dotted with one activation row (`lk_q8_rows_dot`): its first `n` blocks' steps
272// into each row's partials in lanes `2q`, chained in order from pairs `q`, `q + 4`, `q + 8`, `q + 12` by `shfl_down`;
273// a whole round's sixteen (`WHOLE`) need no test of the blocks past the row's end.
274template<int Q, int W, bool WHOLE>
275__device__ __forceinline__ void lk_q8_round(float (&acc)[W], const signed char* __restrict__ wq, const __half* __restrict__ ws, long o, int K,
276 const signed char* __restrict__ xq, const signed char* __restrict__ xl, const float* __restrict__ xs,
277 int c, int n){
278 const int lane = threadIdx.x & 31, j = lane >> 1, nb = K >> 5, blk = c * 16 + j;
279 const bool in = WHOLE || j < n;
280 const int4 zero = make_int4(0, 0, 0, 0);
281 const int4 h = in ? ((const int4*)xq)[c * 32 + lane] : zero;
282 const int4 l = Q == 2 && in ? ((const int4*)xl)[c * 32 + lane] : zero;
283 const float s = in ? xs[blk] : 0.f;
284 #pragma unroll
285 for(int r = 0; r < W; ++r){
286 const int4 w = in ? ((const int4*)(wq + (o + r) * K))[c * 32 + lane] : zero;
287 const float wsc = in ? __half2float(ws[(o + r) * nb + blk]) : 0.f;
288 const int d = lk_q8_block_dot(w, h), e = Q == 2 ? lk_q8_block_dot(w, l) : 0;
289 const float2 t = lk_q8_term<Q>(wsc, s, (float)d, (float)e);
290 if(in) acc[r] = __fmaf_rn(t.x, t.y, acc[r]);
291 #pragma unroll
292 for(int k = 1; k < 4; ++k){
293 const float a = __shfl_down_sync(0xffffffffu, t.x, 8 * k), b = __shfl_down_sync(0xffffffffu, t.y, 8 * k);
294 if(WHOLE || j + 4 * k < n) acc[r] = __fmaf_rn(a, b, acc[r]);
295 }
296 }
297}
298// Rows `o … o + W − 1` of a Q8_0 matrix (int8 values `wq`, a half scale per 32 in `ws`) dotted with the `M` activation
299// rows of `K` values — one warp of a block of at most `WARPS` — row `r`'s dots at `out + r·M`, in every lane. Under the
300// precise numerics (Q 0) the activations are `f32` and the sums `f64`, four values a lane at a time. Quantized (int8
301// words, two under Q 2), each 32-value block's dot is exact in int32 and its step (`lk_q8_term`) goes into partial
302// `b mod 4` of block `b`, a partial's blocks in order; the four partials sum as `lk_q8_sum4` sums them. The row goes
303// 512 values (16 blocks) a round, sixteen bytes a lane: lanes `2j`, `2j + 1` block `j` of each (`lk_q8_block_dot`),
304// so a round's steps of partial `q` are pairs `q`, `q + 4`, `q + 8` and `q + 12`'s. With one activation row each row's
305// partials chain in lanes `2q`, reading the steps by `shfl_down`; with more, the round's steps go through the warp's
306// scratch (`lk_q8_scratch`), up to eight dots' a time, and lane `4u + q` chains the time's dot `u`'s partial `q`. The
307// blocks past the row's end are none of its partials'.
308template<int Q, int M, int W, int WARPS = 8>
309__device__ __forceinline__ void lk_q8_rows_dot(const signed char* __restrict__ wq, const __half* __restrict__ ws, long o, int K,
310 const float* __restrict__ x, const signed char* __restrict__ xq,
311 const signed char* __restrict__ xl, const float* __restrict__ xs, float* out){
312 const int lane = threadIdx.x & 31, nb = K >> 5;
313 if constexpr(Q == 0){
314 double acc[W][M];
315 #pragma unroll
316 for(int r = 0; r < W; ++r)
317 #pragma unroll
318 for(int t = 0; t < M; ++t) acc[r][t] = 0.0;
319 for(int g = lane; g < (K >> 2); g += 32){
320 char4 w[W];
321 double sc[W];
322 #pragma unroll
323 for(int r = 0; r < W; ++r){ w[r] = ((const char4*)(wq + (o + r) * K))[g]; sc[r] = (double)__half2float(ws[(o + r) * nb + (g >> 3)]); }
324 #pragma unroll
325 for(int t = 0; t < M; ++t){
326 const float* xt = x + (long)t * K + 4 * g;
327 float x0 = xt[0], x1 = xt[1], x2 = xt[2], x3 = xt[3];
328 #pragma unroll
329 for(int r = 0; r < W; ++r) acc[r][t] += sc[r] * ((double)w[r].x * x0 + (double)w[r].y * x1 + (double)w[r].z * x2 + (double)w[r].w * x3);
330 }
331 }
332 #pragma unroll
333 for(int r = 0; r < W; ++r)
334 #pragma unroll
335 for(int t = 0; t < M; ++t) out[r * M + t] = (float)lk_warp_sum_d(acc[r][t]);
336 } else if constexpr(M == 1){
337 float acc[W];
338 #pragma unroll
339 for(int r = 0; r < W; ++r) acc[r] = 0.f;
340 const int full = nb >> 4, rest = nb & 15;
341 for(int c = 0; c < full; ++c) lk_q8_round<Q, W, true>(acc, wq, ws, o, K, xq, xl, xs, c, 16);
342 if(rest) lk_q8_round<Q, W, false>(acc, wq, ws, o, K, xq, xl, xs, full, rest);
343 #pragma unroll
344 for(int r = 0; r < W; ++r){
345 float v = acc[r];
346 v += __shfl_down_sync(0xffffffffu, v, 4);
347 v += __shfl_down_sync(0xffffffffu, v, 2);
348 out[r] = __shfl_sync(0xffffffffu, v, 0);
349 }
350 } else {
351 constexpr int U = W * M, G = U < 8 ? U : 8, R = (U + G - 1) / G;
352 __shared__ float2 lk_q8s[WARPS][lk_q8_scratch<U>()];
353 const int warp = threadIdx.x >> 5, j = lane >> 1, u0 = lane >> 2, q = lane & 3;
354 if(warp >= WARPS) __trap();
355 float2* sm = lk_q8s[warp];
356 float acc[R];
357 #pragma unroll
358 for(int i = 0; i < R; ++i) acc[i] = 0.f;
359 const int4 zero = make_int4(0, 0, 0, 0);
360 for(int c = 0; c * 16 < nb; ++c){
361 const int blk = c * 16 + j, n = min(16, nb - c * 16);
362 const bool in = j < n;
363 int4 w[W];
364 float wsc[W];
365 #pragma unroll
366 for(int r = 0; r < W; ++r){
367 w[r] = in ? ((const int4*)(wq + (o + r) * K))[c * 32 + lane] : zero;
368 wsc[r] = in ? __half2float(ws[(o + r) * nb + blk]) : 0.f;
369 }
370 float2 st[U];
371 #pragma unroll
372 for(int t = 0; t < M; ++t){
373 const int4 h = in ? ((const int4*)(xq + (long)t * K))[c * 32 + lane] : zero;
374 const int4 l = Q == 2 && in ? ((const int4*)(xl + (long)t * K))[c * 32 + lane] : zero;
375 const float s = in ? xs[(long)t * nb + blk] : 0.f;
376 #pragma unroll
377 for(int r = 0; r < W; ++r){
378 const int d = lk_q8_block_dot(w[r], h), e = Q == 2 ? lk_q8_block_dot(w[r], l) : 0;
379 st[r * M + t] = lk_q8_term<Q>(wsc[r], s, (float)d, (float)e);
380 }
381 }
382 // Up to eight dots' steps a time: pair `j` writes its block's (the even lane an even dot's, the odd an odd's),
383 // then lane `4u + q` chains dot `u`'s partial `q` over blocks `q`, `q + 4`, `q + 8`, `q + 12`.
384 #pragma unroll
385 for(int i = 0; i < R; ++i){
386 #pragma unroll
387 for(int g = 0; g < G; ++g) if(i * G + g < U && (lane & 1) == (g & 1)) sm[g * 20 + j] = st[i * G + g];
388 __syncwarp();
389 if(u0 < G && i * G + u0 < U){
390 #pragma unroll
391 for(int k = 0; k < 4; ++k) if(q + 4 * k < n){ const float2 v = sm[u0 * 20 + q + 4 * k]; acc[i] = __fmaf_rn(v.x, v.y, acc[i]); }
392 }
393 __syncwarp();
394 }
395 }
396 #pragma unroll
397 for(int i = 0; i < R; ++i){
398 float v = acc[i];
399 v += __shfl_down_sync(0xffffffffu, v, 2);
400 v += __shfl_down_sync(0xffffffffu, v, 1);
401 #pragma unroll
402 for(int g = 0; g < G; ++g) if(i * G + g < U) out[i * G + g] = __shfl_sync(0xffffffffu, v, 4 * g);
403 }
404 }
405}
406"#;
407
408pub const STALL: &str = r#"
412__global__ void lk_stall(unsigned long long ns){
413 unsigned long long t0, t;
414 asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t0));
415 do { __nanosleep(256); asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t)); } while(t - t0 < ns);
416}
417// The stall before launch `i` of the form of `rows` rows under `seed`: under 128 µs, drawn by splitmix64.
418static unsigned long long lk_stall_ns(unsigned long long seed, unsigned long long rows, unsigned long long i){
419 unsigned long long z = seed * 0x9e3779b97f4a7c15ULL + rows * 0xbf58476d1ce4e5b9ULL + i * 0x94d049bb133111ebULL;
420 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
421 z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
422 return (z ^ (z >> 31)) % 128000ULL;
423}
424"#;
425
426const GLU: &str = r#"
433template<int FG, int FU, int Q, int M, int ACT, bool OUTF, int QO, int W = 1>
434__global__ void __launch_bounds__(QO > 0 ? 1024 / W : 256) lk_gemv_glu(lk_w wg, lk_w wu, const float* __restrict__ x,
435 const signed char* __restrict__ xq, const signed char* __restrict__ xl, const float* __restrict__ xs,
436 const float* __restrict__ bg, const float* __restrict__ bu, float* __restrict__ y, signed char* __restrict__ yq,
437 signed char* __restrict__ yl, float* __restrict__ ys, int K, int N){
438 __shared__ float v[M][32];
439 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
440 long o = ((long)blockIdx.x * (blockDim.x >> 5) + warp) * W;
441 if(o < N){
442 float g[W * M], u[W * M];
443 lk_gemv_dot<FG, Q, M, W, (QO > 0 ? 32 / W : 8)>(wg, o, K, x, xq, xl, xs, g);
444 lk_gemv_dot<FU, Q, M, W, (QO > 0 ? 32 / W : 8)>(wu, o, K, x, xq, xl, xs, u);
445 if(lane == 0){
446 #pragma unroll
447 for(int r = 0; r < W; ++r){
448 #pragma unroll
449 for(int t = 0; t < M; ++t){
450 float gv = bg ? g[r * M + t] + bg[o + r] : g[r * M + t], uv = bu ? u[r * M + t] + bu[o + r] : u[r * M + t];
451 float val = ACT < 0 ? gv / (1.0f + expf(-gv)) * uv : uv * lk_act<(ACT < 0 ? 0 : ACT)>(gv);
452 if(QO > 0) v[t][warp * W + r] = val;
453 else if(OUTF) y[(long)t * N + o + r] = val;
454 }
455 }
456 }
457 }
458 if(QO > 0){
459 __syncthreads();
460 if(warp < M){
461 long at = (long)warp * N + (long)blockIdx.x * 32 + lane;
462 float val = v[warp][lane];
463 if(OUTF) y[at] = val;
464 lk_quant_lane<QO>(val, yq, yl, ys, at, at >> 5);
465 }
466 }
467}
468"#;
469
470pub const ATTN_TILE: usize = 64;
472
473pub const ATTN_MIN_SPLIT: usize = 32;
475
476pub const ATTN_BLOCK_VALUES: usize = 4352;
480
481pub fn attn_smem_bytes(g: usize, hd: usize, splits: usize, acc: usize) -> usize {
484 (g * hd * 4).next_multiple_of(16) + (g * ATTN_TILE + 3 * g * splits) * acc
485}
486
487pub fn attn_values_per_thread(vd: usize) -> usize {
490 vd.div_ceil(512)
491}
492
493pub fn attn_blocks_a_multiprocessor(blocks: usize, sms: usize, acc: usize) -> usize {
499 if acc == 4 && blocks > 2 * sms {
500 3
501 } else {
502 2
503 }
504}
505
506pub fn attn_path(z: usize, side: usize, splits: usize) -> &'static str {
511 if z > 1 {
512 "LK_ATTN_SPLIT"
513 } else if side > 0 {
514 "LK_ATTN_HELD"
515 } else if splits > 1 {
516 "LK_ATTN_CHUNKS"
517 } else {
518 "LK_ATTN_ONE"
519 }
520}
521
522pub fn attn_run_width(hd: usize) -> usize {
525 if hd / 4 > 8 && hd / 4 <= 16 {
526 16
527 } else {
528 32
529 }
530}
531
532pub const ATTN_HELD_BYTES: usize = 32 * 1024;
536
537pub fn attn_held_bytes(g: usize, vd: usize, splits: usize, acc: usize) -> usize {
541 g * splits * (vd + 2) * acc
542}
543pub fn attn_holds(g: usize, vd: usize, splits: usize, acc: usize) -> bool {
544 splits > 1 && attn_held_bytes(g, vd, splits, acc) <= ATTN_HELD_BYTES
545}
546
547pub fn attn_side_chunks(vd: usize, splits: usize) -> usize {
551 (512 * attn_values_per_thread(vd) / vd).clamp(1, splits.max(1))
552}
553
554pub fn attn_side_bytes(g: usize, side: usize, acc: usize) -> usize {
557 side.saturating_sub(1) * g * ATTN_TILE * acc
558}
559
560pub fn attn_splits(blocks: usize, span: usize, sms: usize) -> usize {
564 (2 * sms).div_ceil(blocks).clamp(1, span.div_ceil(ATTN_MIN_SPLIT).max(1))
565}
566
567pub fn attn_heads_per_block(group: usize, hd: usize, vd: usize, span: usize, sms: usize, smem: usize, acc: usize) -> Option<usize> {
572 let vpt = attn_values_per_thread(vd);
573 (1..=group.min(16)).rev().find(|&g| {
574 group % g == 0
575 && (g == 1 || g * (hd + vd) <= ATTN_BLOCK_VALUES)
576 && g * vpt <= 16
577 && attn_smem_bytes(g, hd, attn_splits(1, span, sms), acc) <= smem
578 })
579}
580
581pub const ATTN_TILE_Q: usize = 32;
583
584pub fn attn_tile_heads(group: usize) -> usize {
587 1 << group.trailing_zeros().min(5)
588}
589
590pub fn attn_flash_queries(hd: usize, vd: usize) -> usize {
593 if hd <= 128 && vd <= 128 {
594 64
595 } else {
596 32
597 }
598}
599
600pub fn attn_flash_smem_bytes(hd: usize, vd: usize) -> usize {
603 let bq = attn_flash_queries(hd, vd);
604 (bq * (hd + 4) + 2 * 64 * 36 + 2 * 16 * (vd + 4) + 64 * (bq + 4)) * 4
605}
606
607pub fn attn_flash_threads(hd: usize, vd: usize) -> usize {
610 if attn_flash_queries(hd, vd) == 64 {
611 256
612 } else {
613 128
614 }
615}
616
617pub fn attn_tile_values(vd: usize) -> usize {
619 vd.div_ceil(128)
620}
621
622pub fn attn_tile_smem_bytes(vd: usize) -> usize {
625 (32 * 32 + 32 * 64 + 64 * 32 + 8 * vd) * 4
626}
627
628pub fn moe_group_threads(experts: usize) -> usize {
631 32 * (48 * 1024 / (4 * experts)).saturating_sub(1).clamp(1, 32)
632}
633
634pub fn moe_group_smem_bytes(experts: usize, threads: usize) -> usize {
636 (1 + threads / 32) * experts * 4
637}
638
639pub fn accumulator(mode: u32) -> (usize, &'static str) {
643 if mode == 0 {
644 (8, "double")
645 } else {
646 (4, "float")
647 }
648}
649
650pub fn format_arg(ty: crate::model::quant::GgmlType) -> u32 {
652 ty.id()
653}
654
655pub const GEMV_F32_ROWS: usize = 4;
658
659pub fn moe_down_rows(d: usize, dff: usize, sdff: usize) -> (usize, usize) {
666 let w = [4, 2, 1].into_iter().find(|w| d % w == 0).expect("one divides every width");
667 (w, if sdff > dff { w } else { 1 })
668}
669
670pub fn moe_down_launch(d: usize, dff: usize, k: usize, sdff: usize) -> (usize, usize) {
674 let (rows, shared) = moe_down_rows(d, dff, sdff);
675 (d / rows, 32 * (k + if sdff > 0 { shared } else { 0 }))
676}
677
678pub fn gemv_blk_norm_threads(per_warp: usize) -> usize {
681 32 * if per_warp == 1 { 8 } else { 4 }
682}
683
684pub fn block_holds_its_norm(threads: usize, cols: usize) -> bool {
689 threads >= norm_threads(cols)
690}
691
692pub const GEMV_MMA_MIN_ROWS: usize = 2;
694pub const GEMV_MMA_ROWS: usize = 64;
696
697pub fn gemv_mma_tiles(m: usize) -> usize {
699 match m {
700 0..=16 => 1,
701 17..=32 => 2,
702 _ => 4,
703 }
704}
705
706pub fn gemv_mma_shape(mt: usize) -> (usize, usize, usize) {
713 match mt {
714 4 => (16, 2, 2),
715 _ => (4 * mt, mt, 1),
716 }
717}
718
719pub fn gemv_mma_launch(m: usize, n: usize, k: usize, sms: usize) -> (usize, usize, usize, usize) {
727 let mt = gemv_mma_tiles(m);
728 let (nt, mw, nw) = gemv_mma_shape(mt);
729 if mt == 4 && 2 * n.div_ceil(8 * nt) <= sms && k >= 2048 {
730 return (mt, nt / 2, mw, nw);
731 }
732 (mt, nt, mw, nw)
733}
734
735pub fn gemv_mma_threads(mt: usize, nt: usize, mw: usize, nw: usize) -> usize {
737 32 * (nt / nw) * (mt / mw)
738}
739
740pub fn gemv_mma_smem_bytes(two: bool, mt: usize, nt: usize) -> usize {
744 let stage = 8 * nt * 144 + if two { 2 } else { 1 } * 16 * mt * 144 + 8 * nt * 8 + 16 * mt * 16;
745 stage * if two { 2 } else { 3 }
746}
747
748pub fn rows_a_warp(ty: crate::model::quant::GgmlType, n: usize, k: usize, m: usize) -> usize {
753 let four = ty != crate::model::quant::GgmlType::Q8_0 && m == 1 && n >= 2048 && k / 32 >= 64 && n % 32 == 0;
754 if four {
755 4
756 } else {
757 1
758 }
759}
760
761pub fn axpy_staged(ty: crate::model::quant::GgmlType, numerics_mode: u32) -> bool {
768 use crate::model::quant::GgmlType as T;
769 numerics_mode != 0 && ty != T::Q6K && ty != T::Q8_0
770}
771
772pub const AXPY_W: usize = 4;
774pub const AXPY_R: usize = 2;
775
776pub const AXPY_SUMS: usize = AXPY_R * AXPY_W * 32 * 4;
779
780pub fn axpy_pitch(ty: crate::model::quant::GgmlType) -> usize {
783 use crate::model::quant::GgmlType as T;
784 let w = AXPY_W;
785 let bytes = match ty {
786 T::Q8_0 => w * 34,
787 _ => {
788 let (unit, subs) = match ty {
789 T::Q4_0 => (18, 1),
790 T::Q4_1 => (20, 1),
791 T::Q5_0 => (22, 1),
792 T::Q5_1 => (24, 1),
793 T::Mxfp4 => (17, 1),
794 T::Iq4Nl => (18, 1),
795 T::Iq4Xs => (136, 8),
796 T::Q2K => (84, 8),
797 T::Q3K => (110, 8),
798 T::Q4K => (144, 8),
799 T::Q5K => (176, 8),
800 T::Q6K => (210, 8),
801 other => panic!("no staged decode of {other:?}"),
802 };
803 (w.div_ceil(subs) + 1) * unit + 15
804 }
805 };
806 let words = bytes.div_ceil(4);
807 4 * if words % 2 == 0 { words + 1 } else { words }
808}
809
810pub fn argmax_blocks(n: usize) -> usize {
812 n.div_ceil(2048).clamp(1, 64)
813}
814
815pub fn route_warps(f32_router: bool, d: usize) -> usize {
819 if f32_router && d % 16 == 0 && d >= 512 {
820 4
821 } else {
822 1
823 }
824}
825
826pub const ROPE_HEAD: usize = 1024;
828
829pub fn norm_threads(cols: usize) -> usize {
832 cols.next_multiple_of(32).min(1024)
833}
834
835pub fn decodes(ty: crate::model::quant::GgmlType) -> bool {
837 use crate::model::quant::GgmlType as T;
838 matches!(ty, T::Q4_0 | T::Q4_1 | T::Q5_0 | T::Q5_1 | T::Q2K | T::Q3K | T::Q4K | T::Q5K | T::Q6K | T::Mxfp4 | T::Iq4Nl | T::Iq4Xs)
839}
840
841#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
843pub enum Kernel {
844 Blocks,
845 GatherQ8,
846 GatherBlk,
847 GatherF32,
848 RmsNorm,
849 GatherNorm,
850 Quant,
851 GemvQ8,
852 GemvQ8Norm,
853 GemvBlk,
854 GemvBlkNorm,
855 GemvParts,
856 Mma,
857 GemmBlk,
858 GemmF32,
859 GemvF32,
860 Binary,
861 Scale,
862 SwiGlu,
863 Unary,
864 Rope,
865 GemvPartsRope,
866 SetRow,
867 Attention,
868 AttentionTiled,
869 AttentionShared,
870 AttentionFlash,
871 AttentionMma,
872 AttentionI8,
873 ArgMax,
874 Conv,
875 L2Norm,
876 Decay,
877 Gates,
878 DeltaStep,
879 DeltaNorm,
880 DeltaStepNorm,
881 DeltaChunks,
882 Moe,
883 MoeNorm,
884 Slice,
885 Join,
886 Heads,
887 HeadTiled,
888 Glu,
889 GemvMma,
890}
891
892impl Kernel {
893 pub fn name(self) -> &'static str {
895 match self {
896 Kernel::Blocks => "lk_w",
897 Kernel::GatherQ8 => "lk_gather_q8",
898 Kernel::GatherBlk => "lk_gather_blk",
899 Kernel::GatherF32 => "lk_gather_f32",
900 Kernel::RmsNorm => "lk_rmsnorm",
901 Kernel::GatherNorm => "lk_gather_norm",
902 Kernel::Quant => "lk_quant",
903 Kernel::GemvQ8 => "lk_gemv_q8",
904 Kernel::GemvQ8Norm => "lk_gemv_q8_norm",
905 Kernel::GemvBlk => "lk_gemv_blk",
906 Kernel::GemvBlkNorm => "lk_gemv_blk_norm",
907 Kernel::GemvParts => "lk_gemv_parts",
908 Kernel::Mma => "lk_mma_s8",
909 Kernel::GemmBlk => "lk_gemm_blk",
910 Kernel::GemmF32 => "lk_gemm_f32",
911 Kernel::GemvF32 => "lk_gemv_f32",
912 Kernel::Binary => "lk_binary",
913 Kernel::Scale => "lk_scale",
914 Kernel::SwiGlu => "lk_swiglu",
915 Kernel::Unary => "lk_unary",
916 Kernel::Rope => "lk_rope",
917 Kernel::GemvPartsRope => "lk_gemv_parts_rope",
918 Kernel::SetRow => "lk_set_row",
919 Kernel::Attention => "lk_attention",
920 Kernel::AttentionTiled => "lk_attention_tiled",
921 Kernel::AttentionShared => "lk_attention_shared",
922 Kernel::AttentionFlash => "lk_attention_flash",
923 Kernel::AttentionMma => "lk_attention_mma",
924 Kernel::AttentionI8 => "lk_attention_i8",
925 Kernel::ArgMax => "lk_argmax",
926 Kernel::Conv => "lk_conv",
927 Kernel::L2Norm => "lk_l2norm",
928 Kernel::Decay => "lk_decay",
929 Kernel::Gates => "lk_gates",
930 Kernel::DeltaStep => "lk_delta_step",
931 Kernel::DeltaNorm => "lk_delta_norm",
932 Kernel::DeltaStepNorm => "lk_delta_step",
933 Kernel::DeltaChunks => "lk_delta_state",
934 Kernel::Moe => "lk_moe",
935 Kernel::MoeNorm => "lk_moe_norm",
936 Kernel::Slice => "lk_slice",
937 Kernel::Join => "lk_join",
938 Kernel::Heads => "lk_head",
939 Kernel::HeadTiled => "lk_head_tiled",
940 Kernel::Glu => "lk_gemv_glu",
941 Kernel::GemvMma => "lk_gemv_mma",
942 }
943 }
944
945 pub fn source(self) -> &'static str {
947 match self {
948 Kernel::Blocks => BLOCKS,
949 Kernel::GatherQ8 => GATHER_Q8,
950 Kernel::GatherBlk => GATHER_BLK,
951 Kernel::GatherF32 => GATHER_F32,
952 Kernel::RmsNorm => RMSNORM,
953 Kernel::GatherNorm => GATHER_NORM,
954 Kernel::Quant => QUANT,
955 Kernel::GemvQ8 => GEMV_Q8,
956 Kernel::GemvQ8Norm => GEMV_Q8_NORM,
957 Kernel::GemvBlk => GEMV_BLK,
958 Kernel::GemvBlkNorm => GEMV_BLK_NORM,
959 Kernel::GemvParts => GEMV_PARTS,
960 Kernel::Mma => MMA,
961 Kernel::GemmBlk => GEMM_BLK,
962 Kernel::GemmF32 => GEMM_F32,
963 Kernel::GemvF32 => GEMV_F32,
964 Kernel::Binary => BINARY,
965 Kernel::Scale => SCALE,
966 Kernel::SwiGlu => SWIGLU,
967 Kernel::Unary => UNARY,
968 Kernel::Rope => ROPE,
969 Kernel::GemvPartsRope => GEMV_PARTS_ROPE,
970 Kernel::SetRow => SET_ROW,
971 Kernel::Attention => ATTENTION,
972 Kernel::AttentionTiled => ATTENTION_TILED,
973 Kernel::AttentionShared => ATTENTION_SHARED,
974 Kernel::AttentionFlash => ATTENTION_FLASH,
975 Kernel::AttentionMma => ATTENTION_MMA,
976 Kernel::AttentionI8 => ATTENTION_I8,
977 Kernel::ArgMax => ARGMAX,
978 Kernel::Conv => CONV,
979 Kernel::L2Norm => L2NORM,
980 Kernel::Decay => DECAY,
981 Kernel::Gates => GATES,
982 Kernel::DeltaStep => DELTA_STEP,
983 Kernel::DeltaNorm => DELTA_NORM,
984 Kernel::DeltaStepNorm => DELTA_STEP_NORM,
985 Kernel::DeltaChunks => DELTA_CHUNKS,
986 Kernel::Moe => MOE,
987 Kernel::MoeNorm => MOE_NORM,
988 Kernel::Slice => SLICE,
989 Kernel::Join => JOIN,
990 Kernel::Heads => HEADS,
991 Kernel::HeadTiled => HEAD_TILED,
992 Kernel::Glu => GLU,
993 Kernel::GemvMma => GEMV_MMA,
994 }
995 }
996}
997
998pub const BLOCKS: &str = r#"
1007struct lk_w { const signed char* q; const __half* s; const unsigned char* b; long rb; };
1008// A part's outputs: an `f32` array or a binary16 table, set from either pointer; the host reads an `f32` part's back as
1009// its pointer, a kernel through `lk_part_out`.
1010struct lk_part_y {
1011 void* p = nullptr;
1012 bool h = false;
1013 lk_part_y() = default;
1014 __host__ __device__ lk_part_y(std::nullptr_t) {}
1015 __host__ __device__ lk_part_y(float* q) : p(q) {}
1016 __host__ __device__ lk_part_y(__half* q) : p(q), h(true) {}
1017 __host__ operator float*() const { return (float*)p; }
1018};
1019// Up to `LK_PARTS` matrices one activation is projected by (the planner's `GEMM_PARTS`), their outputs side by side:
1020// part `p`'s matrix `w[p]`, outputs `y[p]` (rows from the one-based position `row[p]` names, `pitch[p]` apart, where it
1021// names one), bias `bias[p]`, and its outputs `end[p − 1] … end[p] − 1` of the launch's.
1022#define LK_PARTS 6
1023struct lk_parts {
1024 lk_w w[LK_PARTS];
1025 lk_part_y y[LK_PARTS];
1026 const float* bias[LK_PARTS];
1027 int end[LK_PARTS];
1028 const int* row[LK_PARTS];
1029 long pitch[LK_PARTS];
1030};
1031__device__ __forceinline__ lk_out lk_part_out(const lk_parts& parts, int p){
1032 return parts.y[p].h ? lk_out((__half*)parts.y[p].p) : lk_out((float*)parts.y[p].p);
1033}
1034// The little-endian f16 at `p`, as an `f32`.
1035__device__ __forceinline__ float lk_h(const unsigned char* p){ return __half2float(__ushort_as_half((unsigned short)(p[0] | (p[1] << 8)))); }
1036__device__ __forceinline__ float lk_e8m0_half(unsigned char x){
1037 unsigned int bits = x < 2 ? 0x00200000u << x : ((unsigned int)(x - 1)) << 23;
1038 return __uint_as_float(bits);
1039}
1040__device__ __forceinline__ void lk_scale_min_k4(int j, const unsigned char* q, int& sc, int& m){
1041 if(j < 4){ sc = q[j] & 63; m = q[j + 4] & 63; }
1042 else { sc = (q[j + 4] & 0xf) | ((q[j - 4] >> 6) << 4); m = (q[j + 4] >> 4) | ((q[j] >> 6) << 4); }
1043}
1044// Word `i` of four, by selects rather than an index, so the four stay in registers.
1045__device__ __forceinline__ unsigned int lk_pick4(const unsigned int* a, int i){ return i == 0 ? a[0] : i == 1 ? a[1] : i == 2 ? a[2] : a[3]; }
1046// The IQ4 formats' sixteen non-linear levels (ggml's `kvalues_iq4nl`): one by its nibble, and four to a word.
1047__device__ __forceinline__ int lk_iq4_level(int n){
1048 const signed char kv[16] = {-127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113};
1049 return kv[n];
1050}
1051// The levels of the low nibbles of `q`'s four bytes (`lo`) and of their high nibbles (`hi`), four to a word, from a
1052// table of sixteen int8 levels four to a word (`t0` levels 0–3 … `t3` 12–15), by byte permutes: each nibble's low three
1053// bits pick a byte from levels 0–7 and from 8–15, its fourth bit picks between them — no memory read.
1054__device__ __forceinline__ void lk_table16(unsigned int q, unsigned int t0, unsigned int t1, unsigned int t2, unsigned int t3,
1055 int& lo, int& hi){
1056 unsigned int t[2], sel = 0x32103210u | ((q & 0x88888888u) >> 1);
1057 #pragma unroll
1058 for(int i = 0; i < 2; ++i){
1059 unsigned int s = q >> (16 * i);
1060 t[i] = __byte_perm(__byte_perm(t0, t1, s), __byte_perm(t2, t3, s), sel >> (16 * i));
1061 }
1062 lo = (int)__byte_perm(t[0], t[1], 0x6420);
1063 hi = (int)__byte_perm(t[0], t[1], 0x7531);
1064}
1065// IQ4's levels and MXFP4's doubled E2M1 values (ggml's `kvalues_mxfp4`), four to a word.
1066__device__ __forceinline__ void lk_iq4_word(unsigned int q, int& lo, int& hi){
1067 lk_table16(q, 0xBFAD9881u, 0xF6EADDCFu, 0x26190D01u, 0x71594535u, lo, hi);
1068}
1069__device__ __forceinline__ void lk_mxfp4_word(unsigned int q, int& lo, int& hi){
1070 lk_table16(q, 0x03020100u, 0x0C080604u, 0xFDFEFF00u, 0xF4F8FAFCu, lo, hi);
1071}
1072// The `n` bytes from `p`, any alignment, as words: aligned word loads joined by funnel shifts. Reads up to three bytes
1073// past them, which every block-format upload's 32 trailing bytes cover.
1074template<int N>
1075__device__ __forceinline__ void lk_words(const unsigned char* p, unsigned int* q){
1076 const unsigned int* a = (const unsigned int*)((size_t)p & ~(size_t)3);
1077 unsigned int sh = 8 * (unsigned int)((size_t)p & 3), w[N + 1];
1078 #pragma unroll
1079 for(int i = 0; i <= N; ++i) w[i] = a[i];
1080 #pragma unroll
1081 for(int i = 0; i < N; ++i) q[i] = __funnelshift_r(w[i], w[i + 1], sh);
1082}
1083// An IQ4_XS sub-block's scale: the super-block's `d` times its 6-bit signed scale (low four bits from `sl`, the byte of
1084// `scales_l` it shares with its neighbour, high two from `sh`, the super-block's `scales_h`), as the CPU dequantizer
1085// rounds it.
1086__device__ __forceinline__ float lk_iq4xs_scale(float d, unsigned int sh, unsigned int sl, int ib){
1087 int ls = (int)((sl >> (4 * (ib & 1))) & 0xf) | (int)(((sh >> (2 * ib)) & 3) << 4);
1088 return d * (float)(ls - 32);
1089}
1090template<int F>
1091__device__ __forceinline__ void lk_sub(const unsigned char* __restrict__ row, int sb, signed char* v, float* d, float* c){
1092 if(F == 2 || F == 3){
1093 const unsigned char* b = row + (long)sb * (F == 2 ? 18 : 20);
1094 float dd = lk_h(b), cc = F == 3 ? lk_h(b + 2) : 0.f;
1095 const unsigned char* qs = b + (F == 2 ? 2 : 4);
1096 #pragma unroll
1097 for(int j = 0; j < 16; ++j){ v[j] = (qs[j] & 0xf) - (F == 2 ? 8 : 0); v[j + 16] = (qs[j] >> 4) - (F == 2 ? 8 : 0); }
1098 d[0] = d[1] = dd; c[0] = c[1] = cc;
1099 } else if(F == 6 || F == 7){
1100 const unsigned char* b = row + (long)sb * (F == 6 ? 22 : 24);
1101 float dd = lk_h(b), cc = F == 7 ? lk_h(b + 2) : 0.f;
1102 const unsigned char* h = b + (F == 6 ? 2 : 4);
1103 unsigned int qh = h[0] | (h[1] << 8) | (h[2] << 16) | ((unsigned int)h[3] << 24);
1104 const unsigned char* qs = h + 4;
1105 #pragma unroll
1106 for(int j = 0; j < 16; ++j){
1107 v[j] = ((qs[j] & 0xf) | (((qh >> j) << 4) & 0x10)) - (F == 6 ? 16 : 0);
1108 v[j + 16] = ((qs[j] >> 4) | ((qh >> (j + 12)) & 0x10)) - (F == 6 ? 16 : 0);
1109 }
1110 d[0] = d[1] = dd; c[0] = c[1] = cc;
1111 } else if(F == 39){
1112 const unsigned char* b = row + (long)sb * 17;
1113 const signed char kv[16] = {0, 1, 2, 3, 4, 6, 8, 12, 0, -1, -2, -3, -4, -6, -8, -12};
1114 float dd = lk_e8m0_half(b[0]);
1115 #pragma unroll
1116 for(int j = 0; j < 16; ++j){ v[j] = kv[b[1 + j] & 0xf]; v[j + 16] = kv[b[1 + j] >> 4]; }
1117 d[0] = d[1] = dd; c[0] = c[1] = 0.f;
1118 } else if(F == 20 || F == 23){
1119 const unsigned char* b = F == 20 ? row + (long)sb * 18 : row + (long)(sb >> 3) * 136;
1120 int ib = sb & 7;
1121 float dd = F == 20 ? lk_h(b) : lk_iq4xs_scale(lk_h(b), b[2] | (b[3] << 8), b[4 + (ib >> 1)], ib);
1122 const unsigned char* qs = F == 20 ? b + 2 : b + 8 + 16 * ib;
1123 #pragma unroll
1124 for(int j = 0; j < 16; ++j){ v[j] = lk_iq4_level(qs[j] & 0xf); v[j + 16] = lk_iq4_level(qs[j] >> 4); }
1125 d[0] = d[1] = dd; c[0] = c[1] = 0.f;
1126 } else if(F == 12 || F == 13){
1127 const unsigned char* b = row + (long)(sb >> 3) * (F == 12 ? 144 : 176);
1128 int j = sb & 7, p = j >> 1, hi = j & 1, sc, m;
1129 lk_scale_min_k4(j, b + 4, sc, m);
1130 float dd = lk_h(b) * (float)sc, cc = -lk_h(b + 2) * (float)m;
1131 const unsigned char* ql = b + (F == 12 ? 16 : 48) + p * 32;
1132 #pragma unroll
1133 for(int l = 0; l < 32; ++l){
1134 int lv = hi ? ql[l] >> 4 : ql[l] & 0xf;
1135 if(F == 13) lv += (b[16 + l] & (1 << (2 * p + hi))) ? 16 : 0;
1136 v[l] = (signed char)lv;
1137 }
1138 d[0] = d[1] = dd; c[0] = c[1] = cc;
1139 } else if(F == 10){
1140 const unsigned char* b = row + (long)(sb >> 3) * 84;
1141 int j = sb & 7, part = j >> 2, k = j & 3;
1142 const unsigned char* q = b + 16 + part * 32;
1143 float dd = lk_h(b + 80), dm = lk_h(b + 82);
1144 #pragma unroll
1145 for(int l = 0; l < 32; ++l) v[l] = (signed char)((q[(l & 15) + (l >> 4) * 16] >> (2 * k)) & 3);
1146 #pragma unroll
1147 for(int h = 0; h < 2; ++h){ int sc = b[part * 8 + k * 2 + h]; d[h] = dd * (float)(sc & 0xf); c[h] = -(dm * (float)(sc >> 4)); }
1148 } else if(F == 11){
1149 const unsigned char* b = row + (long)(sb >> 3) * 110;
1150 int j = sb & 7, part = j >> 2, k = j & 3, mbit = 1 << (part * 4 + k);
1151 const unsigned char* q = b + 32 + part * 32;
1152 const unsigned char* p = b + 96;
1153 unsigned int a0 = p[0] | (p[1] << 8) | (p[2] << 16) | ((unsigned int)p[3] << 24);
1154 unsigned int a1 = p[4] | (p[5] << 8) | (p[6] << 16) | ((unsigned int)p[7] << 24);
1155 unsigned int t = p[8] | (p[9] << 8) | (p[10] << 16) | ((unsigned int)p[11] << 24);
1156 unsigned int aux[4] = {(a0 & 0x0f0f0f0fu) | ((t & 0x03030303u) << 4), (a1 & 0x0f0f0f0fu) | (((t >> 2) & 0x03030303u) << 4),
1157 ((a0 >> 4) & 0x0f0f0f0fu) | (((t >> 4) & 0x03030303u) << 4), ((a1 >> 4) & 0x0f0f0f0fu) | (((t >> 6) & 0x03030303u) << 4)};
1158 float dd = lk_h(b + 108);
1159 #pragma unroll
1160 for(int l = 0; l < 32; ++l){
1161 int at = (l & 15) + (l >> 4) * 16;
1162 v[l] = (signed char)(((q[at] >> (2 * k)) & 3) - ((b[at] & mbit) ? 0 : 4));
1163 }
1164 #pragma unroll
1165 for(int h = 0; h < 2; ++h){
1166 int is = part * 8 + k * 2 + h;
1167 int sc = (signed char)((lk_pick4(aux, is >> 2) >> (8 * (is & 3))) & 0xff);
1168 d[h] = dd * (float)(sc - 32); c[h] = 0.f;
1169 }
1170 } else if(F == 14){
1171 const unsigned char* b = row + (long)(sb >> 3) * 210;
1172 int j = sb & 7, part = j >> 2, k = j & 3;
1173 const unsigned char* ql = b + part * 64;
1174 const unsigned char* qh = b + 128 + part * 32;
1175 const signed char* sc = (const signed char*)(b + 192 + part * 8);
1176 float dd = lk_h(b + 208);
1177 #pragma unroll
1178 for(int l = 0; l < 32; ++l){
1179 int lo = (k & 1) ? ql[l + 32] : ql[l];
1180 lo = (k & 2) ? lo >> 4 : lo & 0xf;
1181 v[l] = (signed char)((lo | (((qh[l] >> (2 * k)) & 3) << 4)) - 32);
1182 }
1183 d[0] = dd * (float)sc[2 * k]; d[1] = dd * (float)sc[2 * k + 1]; c[0] = c[1] = 0.f;
1184 }
1185}
1186// Element `l` of sub-block `sb` of a row in format `F`, as `lk_sub` decodes it: its int8 level, and the scale `d` and
1187// offset `c` of its half — the decoder of one weight a lane reads alone.
1188template<int F>
1189__device__ __forceinline__ int lk_level(const unsigned char* __restrict__ row, int sb, int l, float& d, float& c){
1190 if(F == 2 || F == 3){
1191 const unsigned char* b = row + (long)sb * (F == 2 ? 18 : 20);
1192 d = lk_h(b); c = F == 3 ? lk_h(b + 2) : 0.f;
1193 const unsigned char* qs = b + (F == 2 ? 2 : 4);
1194 return (l < 16 ? (qs[l] & 0xf) : (qs[l - 16] >> 4)) - (F == 2 ? 8 : 0);
1195 } else if(F == 6 || F == 7){
1196 const unsigned char* b = row + (long)sb * (F == 6 ? 22 : 24);
1197 d = lk_h(b); c = F == 7 ? lk_h(b + 2) : 0.f;
1198 const unsigned char* h = b + (F == 6 ? 2 : 4);
1199 unsigned int qh = h[0] | (h[1] << 8) | (h[2] << 16) | ((unsigned int)h[3] << 24);
1200 const unsigned char* qs = h + 4;
1201 int q = l < 16 ? ((qs[l] & 0xf) | (((qh >> l) << 4) & 0x10)) : ((qs[l - 16] >> 4) | ((qh >> (l - 4)) & 0x10));
1202 return q - (F == 6 ? 16 : 0);
1203 } else if(F == 39){
1204 const unsigned char* b = row + (long)sb * 17;
1205 const signed char kv[16] = {0, 1, 2, 3, 4, 6, 8, 12, 0, -1, -2, -3, -4, -6, -8, -12};
1206 d = lk_e8m0_half(b[0]); c = 0.f;
1207 return kv[l < 16 ? (b[1 + l] & 0xf) : (b[1 + l - 16] >> 4)];
1208 } else if(F == 20 || F == 23){
1209 const unsigned char* b = F == 20 ? row + (long)sb * 18 : row + (long)(sb >> 3) * 136;
1210 int ib = sb & 7;
1211 d = F == 20 ? lk_h(b) : lk_iq4xs_scale(lk_h(b), b[2] | (b[3] << 8), b[4 + (ib >> 1)], ib); c = 0.f;
1212 const unsigned char* qs = F == 20 ? b + 2 : b + 8 + 16 * ib;
1213 return lk_iq4_level(l < 16 ? (qs[l] & 0xf) : (qs[l - 16] >> 4));
1214 } else if(F == 12 || F == 13){
1215 const unsigned char* b = row + (long)(sb >> 3) * (F == 12 ? 144 : 176);
1216 int j = sb & 7, p = j >> 1, hi = j & 1, sc, m;
1217 lk_scale_min_k4(j, b + 4, sc, m);
1218 d = lk_h(b) * (float)sc; c = -lk_h(b + 2) * (float)m;
1219 const unsigned char* ql = b + (F == 12 ? 16 : 48) + p * 32;
1220 int lv = hi ? ql[l] >> 4 : ql[l] & 0xf;
1221 if(F == 13) lv += (b[16 + l] & (1 << (2 * p + hi))) ? 16 : 0;
1222 return lv;
1223 } else if(F == 10){
1224 const unsigned char* b = row + (long)(sb >> 3) * 84;
1225 int j = sb & 7, part = j >> 2, k = j & 3;
1226 const unsigned char* q = b + 16 + part * 32;
1227 float dd = lk_h(b + 80), dm = lk_h(b + 82);
1228 int sc = b[part * 8 + k * 2 + (l >> 4)];
1229 d = dd * (float)(sc & 0xf); c = -(dm * (float)(sc >> 4));
1230 return (q[(l & 15) + (l >> 4) * 16] >> (2 * k)) & 3;
1231 } else if(F == 11){
1232 const unsigned char* b = row + (long)(sb >> 3) * 110;
1233 int j = sb & 7, part = j >> 2, k = j & 3, mbit = 1 << (part * 4 + k);
1234 const unsigned char* q = b + 32 + part * 32;
1235 const unsigned char* p = b + 96;
1236 unsigned int a0 = p[0] | (p[1] << 8) | (p[2] << 16) | ((unsigned int)p[3] << 24);
1237 unsigned int a1 = p[4] | (p[5] << 8) | (p[6] << 16) | ((unsigned int)p[7] << 24);
1238 unsigned int t = p[8] | (p[9] << 8) | (p[10] << 16) | ((unsigned int)p[11] << 24);
1239 unsigned int aux[4] = {(a0 & 0x0f0f0f0fu) | ((t & 0x03030303u) << 4), (a1 & 0x0f0f0f0fu) | (((t >> 2) & 0x03030303u) << 4),
1240 ((a0 >> 4) & 0x0f0f0f0fu) | (((t >> 4) & 0x03030303u) << 4), ((a1 >> 4) & 0x0f0f0f0fu) | (((t >> 6) & 0x03030303u) << 4)};
1241 int is = part * 8 + k * 2 + (l >> 4);
1242 int sc = (signed char)((lk_pick4(aux, is >> 2) >> (8 * (is & 3))) & 0xff);
1243 d = lk_h(b + 108) * (float)(sc - 32); c = 0.f;
1244 int at = (l & 15) + (l >> 4) * 16;
1245 return ((q[at] >> (2 * k)) & 3) - ((b[at] & mbit) ? 0 : 4);
1246 } else {
1247 const unsigned char* b = row + (long)(sb >> 3) * 210;
1248 int j = sb & 7, part = j >> 2, k = j & 3;
1249 const unsigned char* ql = b + part * 64;
1250 const unsigned char* qh = b + 128 + part * 32;
1251 const signed char* sc = (const signed char*)(b + 192 + part * 8);
1252 d = lk_h(b + 208) * (float)sc[2 * k + (l >> 4)]; c = 0.f;
1253 int lo = (k & 1) ? ql[l + 32] : ql[l];
1254 lo = (k & 2) ? lo >> 4 : lo & 0xf;
1255 return (lo | (((qh[l] >> (2 * k)) & 3) << 4)) - 32;
1256 }
1257}
1258__device__ __forceinline__ int lk_pack4(const signed char* v){
1259 return (v[0] & 0xff) | ((v[1] & 0xff) << 8) | ((v[2] & 0xff) << 16) | ((unsigned int)(v[3] & 0xff) << 24);
1260}
1261// A word of four bytes from an address 2-byte aligned, or 4-byte aligned.
1262__device__ __forceinline__ int lk_w16(const unsigned char* p){
1263 const unsigned short* h = (const unsigned short*)p;
1264 return (int)((unsigned int)h[0] | ((unsigned int)h[1] << 16));
1265}
1266__device__ __forceinline__ int lk_w32(const unsigned char* p){ return *(const int*)p; }
1267// The fifth bits of four levels, from four consecutive bits of `qh`, placed at bit 4 of each byte.
1268__device__ __forceinline__ int lk_bit4(unsigned int b){
1269 return (int)(((b << 4) & 0x10u) | ((b << 11) & 0x1000u) | ((b << 18) & 0x100000u) | ((b << 25) & 0x10000000u));
1270}
1271// Sub-block `sb` of a row in format `F`, as `lk_sub` decodes it, its 32 levels packed four to an int for `__dp4a` (`w[i]`
1272// the levels 4i … 4i+3) — read a word at a time, never a level at a time; the scale and offset of each half as `lk_sub`.
1273template<int F>
1274__device__ __forceinline__ void lk_sub4(const unsigned char* __restrict__ row, int sb, int* w, float* d, float* c){
1275 if(F == 2 || F == 3){
1276 const unsigned char* b = row + (long)sb * (F == 2 ? 18 : 20);
1277 float dd = lk_h(b), cc = F == 3 ? lk_h(b + 2) : 0.f;
1278 const unsigned char* qs = b + (F == 2 ? 2 : 4);
1279 #pragma unroll
1280 for(int i = 0; i < 4; ++i){
1281 int q = F == 2 ? lk_w16(qs + 4 * i) : lk_w32(qs + 4 * i);
1282 w[i] = q & 0x0F0F0F0F; w[i + 4] = (q >> 4) & 0x0F0F0F0F;
1283 if(F == 2){ w[i] = __vsub4(w[i], 0x08080808); w[i + 4] = __vsub4(w[i + 4], 0x08080808); }
1284 }
1285 d[0] = d[1] = dd; c[0] = c[1] = cc;
1286 } else if(F == 6 || F == 7){
1287 const unsigned char* b = row + (long)sb * (F == 6 ? 22 : 24);
1288 float dd = lk_h(b), cc = F == 7 ? lk_h(b + 2) : 0.f;
1289 const unsigned char* h = b + (F == 6 ? 2 : 4);
1290 unsigned int qh = F == 6 ? (unsigned int)lk_w16(h) : (unsigned int)lk_w32(h);
1291 const unsigned char* qs = h + 4;
1292 #pragma unroll
1293 for(int i = 0; i < 4; ++i){
1294 int q = F == 6 ? lk_w16(qs + 4 * i) : lk_w32(qs + 4 * i);
1295 w[i] = (q & 0x0F0F0F0F) | lk_bit4((qh >> (4 * i)) & 0xF);
1296 w[i + 4] = ((q >> 4) & 0x0F0F0F0F) | lk_bit4((qh >> (16 + 4 * i)) & 0xF);
1297 if(F == 6){ w[i] = __vsub4(w[i], 0x10101010); w[i + 4] = __vsub4(w[i + 4], 0x10101010); }
1298 }
1299 d[0] = d[1] = dd; c[0] = c[1] = cc;
1300 } else if(F == 12 || F == 13){
1301 const unsigned char* b = row + (long)(sb >> 3) * (F == 12 ? 144 : 176);
1302 int j = sb & 7, p = j >> 1, hi = j & 1, sc, m;
1303 lk_scale_min_k4(j, b + 4, sc, m);
1304 float dd = lk_h(b) * (float)sc, cc = -lk_h(b + 2) * (float)m;
1305 const unsigned char* ql = b + (F == 12 ? 16 : 48) + p * 32;
1306 #pragma unroll
1307 for(int i = 0; i < 8; ++i){
1308 int q = (lk_w32(ql + 4 * i) >> (4 * hi)) & 0x0F0F0F0F;
1309 if(F == 13) q |= ((lk_w32(b + 16 + 4 * i) >> (2 * p + hi)) & 0x01010101) << 4;
1310 w[i] = q;
1311 }
1312 d[0] = d[1] = dd; c[0] = c[1] = cc;
1313 } else if(F == 10){
1314 const unsigned char* b = row + (long)(sb >> 3) * 84;
1315 int j = sb & 7, part = j >> 2, k = j & 3;
1316 const unsigned char* q = b + 16 + part * 32;
1317 float dd = lk_h(b + 80), dm = lk_h(b + 82);
1318 #pragma unroll
1319 for(int i = 0; i < 8; ++i) w[i] = (lk_w32(q + 4 * i) >> (2 * k)) & 0x03030303;
1320 #pragma unroll
1321 for(int h = 0; h < 2; ++h){ int sc = b[part * 8 + k * 2 + h]; d[h] = dd * (float)(sc & 0xf); c[h] = -(dm * (float)(sc >> 4)); }
1322 } else if(F == 11){
1323 const unsigned char* b = row + (long)(sb >> 3) * 110;
1324 int j = sb & 7, part = j >> 2, k = j & 3, bit = part * 4 + k;
1325 const unsigned char* q = b + 32 + part * 32;
1326 const unsigned char* p = b + 96;
1327 unsigned int a0 = (unsigned int)lk_w16(p), a1 = (unsigned int)lk_w16(p + 4), t = (unsigned int)lk_w16(p + 8);
1328 unsigned int aux[4] = {(a0 & 0x0f0f0f0fu) | ((t & 0x03030303u) << 4), (a1 & 0x0f0f0f0fu) | (((t >> 2) & 0x03030303u) << 4),
1329 ((a0 >> 4) & 0x0f0f0f0fu) | (((t >> 4) & 0x03030303u) << 4), ((a1 >> 4) & 0x0f0f0f0fu) | (((t >> 6) & 0x03030303u) << 4)};
1330 float dd = lk_h(b + 108);
1331 #pragma unroll
1332 for(int i = 0; i < 8; ++i){
1333 int lo = (lk_w16(q + 4 * i) >> (2 * k)) & 0x03030303;
1334 int missing = (~(lk_w16(b + 4 * i) >> bit) & 0x01010101) << 2;
1335 w[i] = __vsub4(lo, missing);
1336 }
1337 #pragma unroll
1338 for(int h = 0; h < 2; ++h){
1339 int is = part * 8 + k * 2 + h;
1340 int sc = (signed char)((lk_pick4(aux, is >> 2) >> (8 * (is & 3))) & 0xff);
1341 d[h] = dd * (float)(sc - 32); c[h] = 0.f;
1342 }
1343 } else if(F == 14){
1344 const unsigned char* b = row + (long)(sb >> 3) * 210;
1345 int j = sb & 7, part = j >> 2, k = j & 3;
1346 const unsigned char* ql = b + part * 64 + ((k & 1) ? 32 : 0);
1347 const unsigned char* qh = b + 128 + part * 32;
1348 const signed char* sc = (const signed char*)(b + 192 + part * 8);
1349 float dd = lk_h(b + 208);
1350 #pragma unroll
1351 for(int i = 0; i < 8; ++i){
1352 int lo = (lk_w16(ql + 4 * i) >> (4 * (k >> 1))) & 0x0F0F0F0F;
1353 int hi = ((lk_w16(qh + 4 * i) >> (2 * k)) & 0x03030303) << 4;
1354 w[i] = __vsub4(lo | hi, 0x20202020);
1355 }
1356 d[0] = dd * (float)sc[2 * k]; d[1] = dd * (float)sc[2 * k + 1]; c[0] = c[1] = 0.f;
1357 } else if(F == 20 || F == 23){
1358 const unsigned char* b = F == 20 ? row + (long)sb * 18 : row + (long)(sb >> 3) * 136;
1359 int ib = sb & 7;
1360 float dd = F == 20 ? lk_h(b) : lk_iq4xs_scale(lk_h(b), b[2] | (b[3] << 8), b[4 + (ib >> 1)], ib);
1361 const unsigned char* qs = F == 20 ? b + 2 : b + 8 + 16 * ib;
1362 #pragma unroll
1363 for(int i = 0; i < 4; ++i) lk_iq4_word((unsigned int)(F == 20 ? lk_w16(qs + 4 * i) : lk_w32(qs + 4 * i)), w[i], w[i + 4]);
1364 d[0] = d[1] = dd; c[0] = c[1] = 0.f;
1365 } else if(F == 39){
1366 const unsigned char* b = row + (long)sb * 17;
1367 unsigned int q[4];
1368 lk_words<4>(b + 1, q);
1369 #pragma unroll
1370 for(int i = 0; i < 4; ++i) lk_mxfp4_word(q[i], w[i], w[i + 4]);
1371 d[0] = d[1] = lk_e8m0_half(b[0]); c[0] = c[1] = 0.f;
1372 } else {
1373 signed char v[32];
1374 lk_sub<F>(row, sb, v, d, c);
1375 #pragma unroll
1376 for(int i = 0; i < 8; ++i) w[i] = lk_pack4(v + 4 * i);
1377 }
1378}
1379// A sub-block's 32 weights (its levels `wv`, four to a word, and each half's scale `d` and offset `c`) dotted with an
1380// activation's 32 int8 words `h` (and its low words `l` under Q 2), before the activation's scale.
1381template<int Q>
1382__device__ __forceinline__ float lk_sub_dot(const int* wv, const float* d, const float* c, const int* h, const int* l){
1383 int dh0 = 0, dh1 = 0, sh0 = 0, sh1 = 0;
1384 #pragma unroll
1385 for(int i = 0; i < 4; ++i){ dh0 = __dp4a(wv[i], h[i], dh0); sh0 = __dp4a(0x01010101, h[i], sh0); }
1386 #pragma unroll
1387 for(int i = 4; i < 8; ++i){ dh1 = __dp4a(wv[i], h[i], dh1); sh1 = __dp4a(0x01010101, h[i], sh1); }
1388 float part = d[0] * (float)dh0 + c[0] * (float)sh0 + d[1] * (float)dh1 + c[1] * (float)sh1;
1389 if(Q == 2){
1390 int dl0 = 0, dl1 = 0, sl0 = 0, sl1 = 0;
1391 #pragma unroll
1392 for(int i = 0; i < 4; ++i){ dl0 = __dp4a(wv[i], l[i], dl0); sl0 = __dp4a(0x01010101, l[i], sl0); }
1393 #pragma unroll
1394 for(int i = 4; i < 8; ++i){ dl1 = __dp4a(wv[i], l[i], dl1); sl1 = __dp4a(0x01010101, l[i], sl1); }
1395 part += 0.0078125f * (d[0] * (float)dl0 + c[0] * (float)sl0 + d[1] * (float)dl1 + c[1] * (float)sl1);
1396 }
1397 return part;
1398}
1399// Rows `row … row + W − 1` of `w` dotted with the `M` activation rows `ld` apart (f32 `x` under Q 0 and 3, the int8
1400// words `xq` (and `xl`) with a scale per 32 in `xs` otherwise), row `r`'s dot with activation row `t` in `out[r·M + t]` —
1401// one warp, every lane's sums reduced. Each row's dot is the same whatever `W`: the rows only share the activation's
1402// loads. Under Q 0 each weight is dequantized as the CPU dequantizer rounds it and summed in f64; under Q 3 a lane
1403// takes half a sub-block, its levels a word at a time and its inputs sixteen bytes at a time, and sums each half's
1404// `d·Σ level·x + c·Σ x` in f32.
1405template<int F, int M, int W>
1406__device__ __forceinline__ void lk_row_dot_f32(lk_w w, long row, int K, const float* __restrict__ x, long ld, float* out){
1407 int lane = threadIdx.x & 31, nb = K >> 5;
1408 const unsigned char* r0 = w.b + row * w.rb;
1409 float acc[W][M];
1410 #pragma unroll
1411 for(int r = 0; r < W; ++r)
1412 #pragma unroll
1413 for(int t = 0; t < M; ++t) acc[r][t] = 0.f;
1414 for(int u = lane; u < 2 * nb; u += 32){
1415 int sb = u >> 1, hf = u & 1;
1416 #pragma unroll
1417 for(int r = 0; r < W; ++r){
1418 int wv[8]; float d[2], c[2];
1419 lk_sub4<F>(r0 + r * w.rb, sb, wv, d, c);
1420 #pragma unroll
1421 for(int t = 0; t < M; ++t){
1422 const float4* xt = (const float4*)(x + (long)t * ld + sb * 32 + 16 * hf);
1423 float sd = 0.f, sx = 0.f;
1424 #pragma unroll
1425 for(int k = 0; k < 4; ++k){
1426 float4 xv = xt[k];
1427 int lv = wv[4 * hf + k];
1428 sd = fmaf((float)(signed char)lv, xv.x, sd); sd = fmaf((float)(signed char)(lv >> 8), xv.y, sd);
1429 sd = fmaf((float)(signed char)(lv >> 16), xv.z, sd); sd = fmaf((float)(signed char)(lv >> 24), xv.w, sd);
1430 sx += (xv.x + xv.y) + (xv.z + xv.w);
1431 }
1432 acc[r][t] = fmaf(d[hf], sd, fmaf(c[hf], sx, acc[r][t]));
1433 }
1434 }
1435 }
1436 #pragma unroll
1437 for(int r = 0; r < W; ++r)
1438 #pragma unroll
1439 for(int t = 0; t < M; ++t) out[r * M + t] = lk_warp_sum(acc[r][t]);
1440}
1441template<int F, int Q, int M, int W = 1>
1442__device__ __forceinline__ void lk_row_dot(lk_w w, long row, int K, const float* __restrict__ x,
1443 const signed char* __restrict__ xq, const signed char* __restrict__ xl,
1444 const float* __restrict__ xs, long ld, float* out){
1445 int lane = threadIdx.x & 31, nb = K >> 5;
1446 const unsigned char* r0 = w.b + row * w.rb;
1447 if(Q == 3){
1448 lk_row_dot_f32<F, M, W>(w, row, K, x, ld, out);
1449 } else if(Q == 0){
1450 typedef typename lk_acc<Q>::t A;
1451 A acc[W][M];
1452 #pragma unroll
1453 for(int r = 0; r < W; ++r)
1454 #pragma unroll
1455 for(int t = 0; t < M; ++t) acc[r][t] = 0;
1456 for(int sb = lane; sb < nb; sb += 32){
1457 #pragma unroll
1458 for(int r = 0; r < W; ++r){
1459 signed char v[32]; float d[2], c[2];
1460 lk_sub<F>(r0 + r * w.rb, sb, v, d, c);
1461 #pragma unroll
1462 for(int t = 0; t < M; ++t){
1463 const float* xt = x + (long)t * ld + sb * 32;
1464 #pragma unroll
1465 for(int i = 0; i < 32; ++i) acc[r][t] += (A)__fadd_rn(__fmul_rn(d[i >> 4], (float)v[i]), c[i >> 4]) * (A)xt[i];
1466 }
1467 }
1468 }
1469 #pragma unroll
1470 for(int r = 0; r < W; ++r)
1471 #pragma unroll
1472 for(int t = 0; t < M; ++t) out[r * M + t] = (float)lk_warp_sum(acc[r][t]);
1473 } else {
1474 float acc[W][M];
1475 #pragma unroll
1476 for(int r = 0; r < W; ++r)
1477 #pragma unroll
1478 for(int t = 0; t < M; ++t) acc[r][t] = 0.f;
1479 static_assert(W == 1 || M == 1, "several rows a warp meet one activation row");
1480 for(int sb = lane; sb < nb; sb += 32){
1481 if(W == 1){
1482 // The weight row decoded once, dotted with each activation row in turn.
1483 int wv[8]; float d[2], c[2];
1484 lk_sub4<F>(r0, sb, wv, d, c);
1485 #pragma unroll
1486 for(int t = 0; t < M; ++t){
1487 const int* l = Q == 2 ? (const int*)(xl + (long)t * ld) + sb * 8 : nullptr;
1488 acc[0][t] += xs[(long)t * (ld >> 5) + sb] * lk_sub_dot<Q>(wv, d, c, (const int*)(xq + (long)t * ld) + sb * 8, l);
1489 }
1490 } else {
1491 // The activation's words loaded once, each weight row decoded and dotted in turn.
1492 const int* hp = (const int*)xq + sb * 8;
1493 const int* lp = Q == 2 ? (const int*)xl + sb * 8 : nullptr;
1494 int h[8], l[8];
1495 #pragma unroll
1496 for(int i = 0; i < 8; ++i){ h[i] = hp[i]; l[i] = Q == 2 ? lp[i] : 0; }
1497 float s = xs[sb];
1498 #pragma unroll
1499 for(int r = 0; r < W; ++r){
1500 int wv[8]; float d[2], c[2];
1501 lk_sub4<F>(r0 + r * w.rb, sb, wv, d, c);
1502 acc[r][0] += s * lk_sub_dot<Q>(wv, d, c, h, Q == 2 ? l : nullptr);
1503 }
1504 }
1505 }
1506 #pragma unroll
1507 for(int r = 0; r < W; ++r)
1508 #pragma unroll
1509 for(int t = 0; t < M; ++t) out[r * M + t] = lk_warp_sum(acc[r][t]);
1510 }
1511}
1512template<int F, int Q, int M>
1513__device__ __forceinline__ void lk_dot(lk_w w, long row, int K, const float* __restrict__ x,
1514 const signed char* __restrict__ xq, const signed char* __restrict__ xl,
1515 const float* __restrict__ xs, long ld, float* out){
1516 if(F != 8){ lk_row_dot<F, Q, M>(w, row, K, x, xq, xl, xs, ld, out); return; }
1517 int lane = threadIdx.x & 31;
1518 const signed char* wq = w.q + row * K;
1519 const __half* ws = w.s + row * (K >> 5);
1520 if(Q == 3){
1521 // A lane four levels at a time, each block's `d·Σ level·x` in f32.
1522 float acc[M];
1523 #pragma unroll
1524 for(int t = 0; t < M; ++t) acc[t] = 0.f;
1525 const int* w4 = (const int*)wq;
1526 for(int g = lane; g < (K >> 2); g += 32){
1527 int lv = w4[g];
1528 float d = __half2float(ws[g >> 3]);
1529 #pragma unroll
1530 for(int t = 0; t < M; ++t){
1531 float4 xv = *(const float4*)(x + (long)t * ld + 4 * g);
1532 float sd = (float)(signed char)lv * xv.x;
1533 sd = fmaf((float)(signed char)(lv >> 8), xv.y, sd); sd = fmaf((float)(signed char)(lv >> 16), xv.z, sd);
1534 sd = fmaf((float)(signed char)(lv >> 24), xv.w, sd);
1535 acc[t] = fmaf(d, sd, acc[t]);
1536 }
1537 }
1538 #pragma unroll
1539 for(int t = 0; t < M; ++t) out[t] = lk_warp_sum(acc[t]);
1540 } else if(Q == 0){
1541 typedef typename lk_acc<Q>::t A;
1542 A acc[M];
1543 #pragma unroll
1544 for(int t = 0; t < M; ++t) acc[t] = 0;
1545 for(int k = lane; k < K; k += 32){
1546 A wv = (A)__half2float(ws[k >> 5]) * (A)wq[k];
1547 #pragma unroll
1548 for(int t = 0; t < M; ++t) acc[t] += wv * (A)x[(long)t * ld + k];
1549 }
1550 #pragma unroll
1551 for(int t = 0; t < M; ++t) out[t] = (float)lk_warp_sum(acc[t]);
1552 } else {
1553 float acc[M];
1554 #pragma unroll
1555 for(int t = 0; t < M; ++t) acc[t] = 0.f;
1556 const int* w4 = (const int*)wq;
1557 for(int g = lane; g < (K >> 2); g += 32){
1558 int blk = g >> 3, wv = w4[g];
1559 float wsc = __half2float(ws[blk]);
1560 #pragma unroll
1561 for(int t = 0; t < M; ++t){
1562 float s = xs[(long)t * (ld >> 5) + blk];
1563 int d = __dp4a(wv, ((const int*)(xq + (long)t * ld))[g], 0);
1564 if(Q == 1) acc[t] += (float)d * (wsc * s);
1565 else acc[t] += wsc * (s * (float)d + s * 0.0078125f * (float)__dp4a(wv, ((const int*)(xl + (long)t * ld))[g], 0));
1566 }
1567 }
1568 #pragma unroll
1569 for(int t = 0; t < M; ++t) out[t] = lk_warp_sum(acc[t]);
1570 }
1571}
1572// Rows `o … o + W − 1` of a matrix in format `F` (8: Q8_0, split) dotted with the `M` activation rows of `K` values as
1573// the plain GEMV of that format dots them, row `r`'s dots at `out + r·M` — one warp of a block of at most `WARPS`.
1574template<int F, int Q, int M, int W = 1, int WARPS = 8>
1575__device__ __forceinline__ void lk_gemv_dot(lk_w w, long o, int K, const float* __restrict__ x,
1576 const signed char* __restrict__ xq, const signed char* __restrict__ xl,
1577 const float* __restrict__ xs, float* out){
1578 if constexpr(F == 8) lk_q8_rows_dot<Q, M, W, WARPS>(w.q, w.s, o, K, x, xq, xl, xs, out);
1579 else lk_row_dot<F, Q, M, W>(w, o, K, x, xq, xl, xs, K, out);
1580}
1581// A weight sub-block's raw words, fetched ahead of their decoding so their loads are in flight while the kernel works
1582// on what it fetched before: the bytes `lk_sub4` reads for it, by the widest loads the format's alignment allows (16
1583// bytes in Q4_K, Q5_K and Q8_0's levels, 4 in Q4_1, Q5_1, Q2_K and IQ4_XS, 2 in Q4_0, Q5_0, Q3_K, Q6_K and IQ4_NL, 1 in
1584// MXFP4).
1585template<int F> struct lk_raw { };
1586template<> struct lk_raw<2> { unsigned int q[4]; unsigned short d; };
1587template<> struct lk_raw<3> { unsigned int q[4], dm; };
1588template<> struct lk_raw<6> { unsigned int q[4], qh; unsigned short d; };
1589template<> struct lk_raw<7> { unsigned int q[4], qh, dm; };
1590template<> struct lk_raw<8> { uint4 q0, q1; unsigned short d; };
1591template<> struct lk_raw<10> { unsigned int q[8], dm; unsigned short sc; };
1592template<> struct lk_raw<11> { unsigned int q[8], hm[8], a0, a1, t; unsigned short d; };
1593template<> struct lk_raw<12> { uint4 h, q0, q1; };
1594template<> struct lk_raw<13> { uint4 h, qh0, qh1, q0, q1; };
1595template<> struct lk_raw<14> { unsigned int ql[8], qh[8]; unsigned short sc, d; };
1596template<> struct lk_raw<39> { unsigned int q[4]; unsigned char e; };
1597template<> struct lk_raw<20> { unsigned int q[4]; unsigned short d; };
1598template<> struct lk_raw<23> { unsigned int q[4]; unsigned short d, sh; unsigned char sl; };
1599__device__ __forceinline__ unsigned short lk_u16(const unsigned char* p){ return *(const unsigned short*)p; }
1600// Sub-block `ks` of matrix row `row` (`K` values, `nb` sub-blocks), its raw words.
1601template<int F> __device__ __forceinline__ void lk_fetch(lk_raw<F>& raw, lk_w w, long row, int K, int nb, int ks){
1602 const unsigned char* r = w.b + row * w.rb;
1603 int j = ks & 7, part = j >> 2, k = j & 3;
1604 if constexpr(F == 2 || F == 6){
1605 const unsigned char* b = r + (long)ks * (F == 2 ? 18 : 22);
1606 raw.d = lk_u16(b);
1607 if constexpr(F == 6) raw.qh = (unsigned int)lk_w16(b + 2);
1608 #pragma unroll
1609 for(int i = 0; i < 4; ++i) raw.q[i] = (unsigned int)lk_w16(b + (F == 2 ? 2 : 6) + 4 * i);
1610 } else if constexpr(F == 3 || F == 7){
1611 const unsigned char* b = r + (long)ks * (F == 3 ? 20 : 24);
1612 raw.dm = (unsigned int)lk_w32(b);
1613 if constexpr(F == 7) raw.qh = (unsigned int)lk_w32(b + 4);
1614 #pragma unroll
1615 for(int i = 0; i < 4; ++i) raw.q[i] = (unsigned int)lk_w32(b + (F == 3 ? 4 : 8) + 4 * i);
1616 } else if constexpr(F == 8){
1617 const uint4* q = (const uint4*)(w.q + row * K + (long)ks * 32);
1618 raw.q0 = __ldg(q); raw.q1 = __ldg(q + 1);
1619 raw.d = __half_as_ushort(w.s[row * nb + ks]);
1620 } else if constexpr(F == 10){
1621 const unsigned char* b = r + (long)(ks >> 3) * 84;
1622 #pragma unroll
1623 for(int i = 0; i < 8; ++i) raw.q[i] = (unsigned int)lk_w32(b + 16 + part * 32 + 4 * i);
1624 raw.sc = lk_u16(b + part * 8 + k * 2);
1625 raw.dm = (unsigned int)lk_w32(b + 80);
1626 } else if constexpr(F == 11){
1627 const unsigned char* b = r + (long)(ks >> 3) * 110;
1628 #pragma unroll
1629 for(int i = 0; i < 8; ++i){ raw.q[i] = (unsigned int)lk_w16(b + 32 + part * 32 + 4 * i); raw.hm[i] = (unsigned int)lk_w16(b + 4 * i); }
1630 raw.a0 = (unsigned int)lk_w16(b + 96); raw.a1 = (unsigned int)lk_w16(b + 100); raw.t = (unsigned int)lk_w16(b + 104);
1631 raw.d = lk_u16(b + 108);
1632 } else if constexpr(F == 12 || F == 13){
1633 const unsigned char* b = r + (long)(ks >> 3) * (F == 12 ? 144 : 176);
1634 const uint4* q = (const uint4*)(b + (F == 12 ? 16 : 48) + (j >> 1) * 32);
1635 raw.h = __ldg((const uint4*)b); raw.q0 = __ldg(q); raw.q1 = __ldg(q + 1);
1636 if constexpr(F == 13){ raw.qh0 = __ldg((const uint4*)(b + 16)); raw.qh1 = __ldg((const uint4*)(b + 32)); }
1637 } else if constexpr(F == 14){
1638 const unsigned char* b = r + (long)(ks >> 3) * 210;
1639 #pragma unroll
1640 for(int i = 0; i < 8; ++i){
1641 raw.ql[i] = (unsigned int)lk_w16(b + part * 64 + ((k & 1) ? 32 : 0) + 4 * i);
1642 raw.qh[i] = (unsigned int)lk_w16(b + 128 + part * 32 + 4 * i);
1643 }
1644 raw.sc = lk_u16(b + 192 + part * 8 + 2 * k);
1645 raw.d = lk_u16(b + 208);
1646 } else if constexpr(F == 39){
1647 const unsigned char* b = r + (long)ks * 17;
1648 raw.e = b[0];
1649 #pragma unroll
1650 for(int i = 0; i < 4; ++i) raw.q[i] = (unsigned int)b[1 + 4 * i] | ((unsigned int)b[2 + 4 * i] << 8) | ((unsigned int)b[3 + 4 * i] << 16) | ((unsigned int)b[4 + 4 * i] << 24);
1651 } else if constexpr(F == 20){
1652 const unsigned char* b = r + (long)ks * 18;
1653 raw.d = lk_u16(b);
1654 #pragma unroll
1655 for(int i = 0; i < 4; ++i) raw.q[i] = (unsigned int)lk_w16(b + 2 + 4 * i);
1656 } else if constexpr(F == 23){
1657 const unsigned char* b = r + (long)(ks >> 3) * 136;
1658 raw.d = lk_u16(b); raw.sh = lk_u16(b + 2); raw.sl = b[4 + (j >> 1)];
1659 #pragma unroll
1660 for(int i = 0; i < 4; ++i) raw.q[i] = (unsigned int)lk_w32(b + 8 + 16 * j + 4 * i);
1661 } else static_assert(F != F, "a block format with no raw words");
1662}
1663__device__ __forceinline__ float lk_hf(unsigned int bits){ return __half2float(__ushort_as_half((unsigned short)(bits & 0xFFFF))); }
1664// The sub-block from its fetched words — `lk_sub4`'s levels, scales and offsets, operation for operation.
1665template<int F> __device__ __forceinline__ void lk_unpack(const lk_raw<F>& raw, int ks, int* w, float* d, float* c){
1666 int j = ks & 7, part = j >> 2, k = j & 3;
1667 if constexpr(F == 2 || F == 3){
1668 float dd, cc = 0.f;
1669 if constexpr(F == 2) dd = lk_hf(raw.d);
1670 else { dd = lk_hf(raw.dm); cc = lk_hf(raw.dm >> 16); }
1671 #pragma unroll
1672 for(int i = 0; i < 4; ++i){
1673 int q = (int)raw.q[i];
1674 w[i] = q & 0x0F0F0F0F; w[i + 4] = (q >> 4) & 0x0F0F0F0F;
1675 if(F == 2){ w[i] = __vsub4(w[i], 0x08080808); w[i + 4] = __vsub4(w[i + 4], 0x08080808); }
1676 }
1677 d[0] = d[1] = dd; c[0] = c[1] = cc;
1678 } else if constexpr(F == 6 || F == 7){
1679 float dd, cc = 0.f;
1680 if constexpr(F == 6) dd = lk_hf(raw.d);
1681 else { dd = lk_hf(raw.dm); cc = lk_hf(raw.dm >> 16); }
1682 unsigned int qh = raw.qh;
1683 #pragma unroll
1684 for(int i = 0; i < 4; ++i){
1685 int q = (int)raw.q[i];
1686 w[i] = (q & 0x0F0F0F0F) | lk_bit4((qh >> (4 * i)) & 0xF);
1687 w[i + 4] = ((q >> 4) & 0x0F0F0F0F) | lk_bit4((qh >> (16 + 4 * i)) & 0xF);
1688 if(F == 6){ w[i] = __vsub4(w[i], 0x10101010); w[i + 4] = __vsub4(w[i + 4], 0x10101010); }
1689 }
1690 d[0] = d[1] = dd; c[0] = c[1] = cc;
1691 } else if constexpr(F == 8){
1692 w[0] = raw.q0.x; w[1] = raw.q0.y; w[2] = raw.q0.z; w[3] = raw.q0.w; w[4] = raw.q1.x; w[5] = raw.q1.y; w[6] = raw.q1.z; w[7] = raw.q1.w;
1693 d[0] = d[1] = lk_hf(raw.d); c[0] = c[1] = 0.f;
1694 } else if constexpr(F == 10){
1695 float dd = lk_hf(raw.dm), dm = lk_hf(raw.dm >> 16);
1696 #pragma unroll
1697 for(int i = 0; i < 8; ++i) w[i] = ((int)raw.q[i] >> (2 * k)) & 0x03030303;
1698 #pragma unroll
1699 for(int h = 0; h < 2; ++h){ int sc = (raw.sc >> (8 * h)) & 0xFF; d[h] = dd * (float)(sc & 0xf); c[h] = -(dm * (float)(sc >> 4)); }
1700 } else if constexpr(F == 11){
1701 int bit = part * 4 + k;
1702 unsigned int a0 = raw.a0, a1 = raw.a1, t = raw.t;
1703 unsigned int aux[4] = {(a0 & 0x0f0f0f0fu) | ((t & 0x03030303u) << 4), (a1 & 0x0f0f0f0fu) | (((t >> 2) & 0x03030303u) << 4),
1704 ((a0 >> 4) & 0x0f0f0f0fu) | (((t >> 4) & 0x03030303u) << 4), ((a1 >> 4) & 0x0f0f0f0fu) | (((t >> 6) & 0x03030303u) << 4)};
1705 float dd = lk_hf(raw.d);
1706 #pragma unroll
1707 for(int i = 0; i < 8; ++i){
1708 int lo = ((int)raw.q[i] >> (2 * k)) & 0x03030303;
1709 int missing = (~((int)raw.hm[i] >> bit) & 0x01010101) << 2;
1710 w[i] = __vsub4(lo, missing);
1711 }
1712 #pragma unroll
1713 for(int h = 0; h < 2; ++h){
1714 int is = part * 8 + k * 2 + h;
1715 int sc = (signed char)((lk_pick4(aux, is >> 2) >> (8 * (is & 3))) & 0xff);
1716 d[h] = dd * (float)(sc - 32); c[h] = 0.f;
1717 }
1718 } else if constexpr(F == 12 || F == 13){
1719 int hi = j & 1, pp = j >> 1;
1720 // Scale byte `i` of the head's twelve.
1721 auto sb = [&](int i){ unsigned int wd = i < 4 ? raw.h.y : i < 8 ? raw.h.z : raw.h.w; return (int)((wd >> (8 * (i & 3))) & 0xFF); };
1722 int sc, m;
1723 if(j < 4){ sc = sb(j) & 63; m = sb(j + 4) & 63; }
1724 else { sc = (sb(j + 4) & 0xF) | ((sb(j - 4) >> 6) << 4); m = (sb(j + 4) >> 4) | ((sb(j) >> 6) << 4); }
1725 d[0] = d[1] = lk_hf(raw.h.x) * (float)sc; c[0] = c[1] = -lk_hf(raw.h.x >> 16) * (float)m;
1726 unsigned int q[8] = {raw.q0.x, raw.q0.y, raw.q0.z, raw.q0.w, raw.q1.x, raw.q1.y, raw.q1.z, raw.q1.w};
1727 #pragma unroll
1728 for(int i = 0; i < 8; ++i){
1729 int v = (int)((q[i] >> (4 * hi)) & 0x0F0F0F0Fu);
1730 if constexpr(F == 13){
1731 unsigned int qh[8] = {raw.qh0.x, raw.qh0.y, raw.qh0.z, raw.qh0.w, raw.qh1.x, raw.qh1.y, raw.qh1.z, raw.qh1.w};
1732 v |= (int)(((qh[i] >> (2 * pp + hi)) & 0x01010101u) << 4);
1733 }
1734 w[i] = v;
1735 }
1736 } else if constexpr(F == 14){
1737 float dd = lk_hf(raw.d);
1738 #pragma unroll
1739 for(int i = 0; i < 8; ++i){
1740 int lo = ((int)raw.ql[i] >> (4 * (k >> 1))) & 0x0F0F0F0F;
1741 int hi = (((int)raw.qh[i] >> (2 * k)) & 0x03030303) << 4;
1742 w[i] = __vsub4(lo | hi, 0x20202020);
1743 }
1744 d[0] = dd * (float)(signed char)(raw.sc & 0xFF); d[1] = dd * (float)(signed char)(raw.sc >> 8); c[0] = c[1] = 0.f;
1745 } else if constexpr(F == 39){
1746 #pragma unroll
1747 for(int i = 0; i < 4; ++i) lk_mxfp4_word(raw.q[i], w[i], w[i + 4]);
1748 d[0] = d[1] = lk_e8m0_half(raw.e); c[0] = c[1] = 0.f;
1749 } else if constexpr(F == 20 || F == 23){
1750 float dd;
1751 if constexpr(F == 20) dd = lk_hf(raw.d);
1752 else dd = lk_iq4xs_scale(lk_hf(raw.d), raw.sh, raw.sl, j);
1753 #pragma unroll
1754 for(int i = 0; i < 4; ++i) lk_iq4_word(raw.q[i], w[i], w[i + 4]);
1755 d[0] = d[1] = dd; c[0] = c[1] = 0.f;
1756 } else static_assert(F != F, "a block format with no raw words");
1757}
1758"#;
1759
1760const GEMV_BLK: &str = r#"
1765template<int F, int Q, int M, int W, int QO>
1766__device__ __forceinline__ void lk_gemv_blk_rows(lk_w w, const float* __restrict__ x, const signed char* __restrict__ xq,
1767 const signed char* __restrict__ xl, const float* __restrict__ xs,
1768 const float* __restrict__ bias, lk_out y, const int* __restrict__ yrow,
1769 long ypitch, int K, int N, signed char* __restrict__ yq, signed char* __restrict__ yl,
1770 float* __restrict__ ys){
1771 __shared__ float v[QO > 0 ? M : 1][32];
1772 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
1773 long o = (((long)blockIdx.x * blockDim.x + threadIdx.x) >> 5) * W;
1774 if(o < N){
1775 lk_out yo = y ? LK_ROW(y, yrow, ypitch) : lk_out();
1776 float d[W * M];
1777 lk_row_dot<F, Q, M, W>(w, o, K, x, xq, xl, xs, K, d);
1778 if(lane == 0){
1779 #pragma unroll
1780 for(int r = 0; r < W; ++r){
1781 #pragma unroll
1782 for(int t = 0; t < M; ++t){
1783 float val = bias ? d[r * M + t] + bias[o + r] : d[r * M + t];
1784 if(yo) yo[(long)t * N + o + r] = val;
1785 if(QO > 0) v[t][warp * W + r] = val;
1786 }
1787 }
1788 }
1789 }
1790 if(QO > 0){
1791 __syncthreads();
1792 if(warp < M){
1793 long at = (long)warp * N + (long)blockIdx.x * 32 + lane;
1794 lk_quant_lane<QO>(v[warp][lane], yq, yl, ys, at, at >> 5);
1795 }
1796 }
1797}
1798template<int F, int Q, int M, int W = 1, int QO = 0>
1799__global__ void lk_gemv_blk(lk_w w, const float* __restrict__ x, const signed char* __restrict__ xq,
1800 const signed char* __restrict__ xl, const float* __restrict__ xs,
1801 const float* __restrict__ bias, lk_out y, const int* __restrict__ yrow,
1802 long ypitch, int K, int N, signed char* __restrict__ yq, signed char* __restrict__ yl,
1803 float* __restrict__ ys){
1804 lk_gemv_blk_rows<F, Q, M, W, QO>(w, x, xq, xl, xs, bias, y, yrow, ypitch, K, N, yq, yl, ys);
1805}
1806"#;
1807
1808const GEMV_BLK_NORM: &str = r#"
1814template<int F, int Q, int W, bool ADD, bool SUM, bool OUTF, int QO, int PER, typename A, bool PRE, int VW>
1815__global__ void lk_gemv_blk_norm(lk_w w, const float* __restrict__ x, const signed char* __restrict__ xq,
1816 const signed char* __restrict__ xl, const float* __restrict__ xs,
1817 const float* __restrict__ bias, float* y, int K, int N, lk_norm_of nm, int vt,
1818 int* __restrict__ cnt){
1819 __shared__ A red[32];
1820 __shared__ int last;
1821 lk_gemv_blk_rows<F, Q, 1, W, 0>(w, x, xq, xl, xs, bias, y, nullptr, 0, K, N, nullptr, nullptr, nullptr);
1822 __threadfence();
1823 __syncthreads();
1824 if(threadIdx.x == 0) last = atomicAdd(cnt, 1) == gridDim.x - 1;
1825 __syncthreads();
1826 if(!last) return;
1827 __threadfence();
1828 lk_out yr = nm.y ? LK_ROW(nm.y, nm.yrow, nm.ypitch) : lk_out();
1829 lk_rmsnorm_row<ADD, SUM, OUTF, QO, PER, A, PRE, true, VW>(nm.x, nm.r, nm.s, nm.w, yr, nm.yq, nm.yl, nm.ys, N, nm.eps, 0, 0,
1830 nm.pw, nm.peps, red, vt);
1831 if(threadIdx.x == 0) *cnt = 0;
1832}
1833"#;
1834
1835const GEMV_PARTS: &str = r#"
1840template<int F, int Q, int M, int W = 1>
1841__global__ void lk_gemv_parts(const float* __restrict__ x, const signed char* __restrict__ xq, const signed char* __restrict__ xl,
1842 const float* __restrict__ xs, int K, int N, lk_parts parts){
1843 long o = (((long)blockIdx.x * blockDim.x + threadIdx.x) >> 5) * W;
1844 if(o >= N) return;
1845 int p = 0;
1846 while(p < LK_PARTS - 1 && o >= parts.end[p]) ++p;
1847 long start = p ? parts.end[p - 1] : 0, r = o - start;
1848 long n = parts.end[p] - start;
1849 float d[W * M];
1850 lk_gemv_dot<F, Q, M, W>(parts.w[p], r, K, x, xq, xl, xs, d);
1851 if((threadIdx.x & 31) == 0){
1852 lk_out yo = LK_ROW(lk_part_out(parts, p), parts.row[p], parts.pitch[p]);
1853 const float* b = parts.bias[p];
1854 #pragma unroll
1855 for(int i = 0; i < W; ++i)
1856 #pragma unroll
1857 for(int t = 0; t < M; ++t) yo[(long)t * n + r + i] = b ? d[i * M + t] + b[r + i] : d[i * M + t];
1858 }
1859}
1860"#;
1861
1862const GEMV_MMA: &str = r#"
1873template<int Q> __host__ __device__ constexpr int lk_mma_stages(){ return Q == 2 ? 2 : 3; }
1874
1875// A stage's row in shared memory: four blocks (128 values) and 16 bytes of padding, so the eight rows an `mma` reads
1876// fall on distinct banks.
1877#define LK_MP 144
1878// A stage: four blocks of each of the block's `8·NT` matrix rows and `16·MT` activation rows (both words' under two
1879// words), and their scales.
1880template<int Q, int MT, int NT> __host__ __device__ constexpr int lk_mma_stage_bytes(){
1881 return 8 * NT * LK_MP + (Q == 2 ? 2 : 1) * 16 * MT * LK_MP + 8 * NT * 4 * 2 + 16 * MT * 4 * 4;
1882}
1883template<int Q, int MT, int NT> __host__ __device__ constexpr int lk_mma_smem(){ return lk_mma_stages<Q>() * lk_mma_stage_bytes<Q, MT, NT>(); }
1884// One stage's four blocks (from block `blk0`, `nb` the row's), block `j` into partial `j` of `acc`: `b` the stage's
1885// buffer, `wld` this lane's address of its first n-tile's matrix rows for `ldmatrix` and `ald` of its first m-tile's
1886// activation rows, `wsr` its first n-tile's two outputs' scales, `row` its first activation row.
1887template<int Q, int MW, int NW, int AOFF, int LOFF, int XSOFF>
1888__device__ __forceinline__ void lk_mma_blocks(float (&acc)[MW][NW][4][4], const unsigned char* b, int wld, int ald, const __half* wsr,
1889 int row0, int blk0, int nb){
1890 #pragma unroll
1891 for(int j = 0; j < 4; ++j){
1892 if(blk0 + j < nb){
1893 int bw[NW][2];
1894 float w0[NW], w1[NW];
1895 #pragma unroll
1896 for(int n = 0; n < NW; ++n){
1897 lk_ldsm2_s8(bw[n], b + wld + n * 8 * LK_MP + 32 * j);
1898 w0[n] = __half2float(wsr[n * 32 + j]); w1[n] = __half2float(wsr[n * 32 + 4 + j]);
1899 }
1900 #pragma unroll
1901 for(int m = 0; m < MW; ++m){
1902 const int row = row0 + m * 16;
1903 int a[4], l[4];
1904 lk_ldsm_s8(a, b + AOFF + ald + m * 16 * LK_MP + 32 * j);
1905 if(Q == 2) lk_ldsm_s8(l, b + LOFF + ald + m * 16 * LK_MP + 32 * j);
1906 const float* xsr = (const float*)(b + XSOFF);
1907 const float s0 = xsr[row * 4 + j], s1 = xsr[(row + 8) * 4 + j];
1908 #pragma unroll
1909 for(int n = 0; n < NW; ++n){
1910 int c[4], cl[4] = {LK_DOT0, LK_DOT0, LK_DOT0, LK_DOT0};
1911 lk_mma_s8_from(c, a, bw[n], LK_DOT0);
1912 if(Q == 2) lk_mma_s8_from(cl, l, bw[n], LK_DOT0);
1913 float (&p)[4] = acc[m][n][j];
1914 p[0] = lk_q8_acc<Q>(p[0], w0[n], s0, lk_dot_f(c[0]), lk_dot_f(cl[0]));
1915 p[1] = lk_q8_acc<Q>(p[1], w1[n], s0, lk_dot_f(c[1]), lk_dot_f(cl[1]));
1916 p[2] = lk_q8_acc<Q>(p[2], w0[n], s1, lk_dot_f(c[2]), lk_dot_f(cl[2]));
1917 p[3] = lk_q8_acc<Q>(p[3], w1[n], s1, lk_dot_f(c[3]), lk_dot_f(cl[3]));
1918 }
1919 }
1920 }
1921 }
1922}
1923template<int Q, int MT, int NT, int MW = 1, int NW = 1>
1924__global__ void __launch_bounds__(32 * (NT / NW) * (MT / MW), MW == 1 && NW == 1 ? 512 / (32 * NT * MT) : 1) lk_gemv_mma(
1925 const signed char* __restrict__ xq, const signed char* __restrict__ xl, const float* __restrict__ xs, int K, int N, int M,
1926 lk_parts parts){
1927 constexpr int NB = 8 * NT, MS = MT / MW, ST = lk_mma_stages<Q>(), T = 32 * (NT / NW) * MS;
1928 constexpr int SB = lk_mma_stage_bytes<Q, MT, NT>(), RA = 16 * MT;
1929 constexpr int AOFF = NB * LK_MP, LOFF = AOFF + RA * LK_MP, WSOFF = AOFF + (Q == 2 ? 2 : 1) * RA * LK_MP, XSOFF = WSOFF + NB * 8;
1930 extern __shared__ __align__(16) unsigned char lk_ms[];
1931 const int tid = threadIdx.x, warp = tid >> 5, lane = tid & 31, g = lane >> 2, q4 = lane & 3;
1932 // This warp's first n-tile and first m-tile.
1933 const int ntile = (warp / MS) * NW, mbase = (warp % MS) * MW;
1934 const long o0 = (long)blockIdx.x * NB;
1935 const int nb = K >> 5, stages = (nb + 3) >> 2;
1936 auto part_of = [&](long o){ int p = 0; while(p < LK_PARTS - 1 && o >= parts.end[p]) ++p; return p; };
1937 auto row_in = [&](long o, int p){ return o - (p ? parts.end[p - 1] : 0); };
1938 // This thread's copies each stage: 16-byte chunks `tid + T·i` of the block's matrix rows (eight a row) and of its
1939 // activation rows; and its scales of the stage's, entries `tid + T·i` — matrix row `e / 4`'s block `e mod 4`, and
1940 // activation row `e / 4`'s.
1941 constexpr int WC = (NB * 8 + T - 1) / T, AC = (RA * 8 + T - 1) / T, WSC = (NB * 4 + T - 1) / T, XC = (RA * 4 + T - 1) / T;
1942 const signed char* wsrc[WC];
1943 bool win[WC];
1944 #pragma unroll
1945 for(int i = 0; i < WC; ++i){
1946 const int c = tid + T * i;
1947 const long o = o0 + (c >> 3);
1948 win[i] = c < NB * 8 && o < N;
1949 const int p = part_of(win[i] ? o : 0);
1950 wsrc[i] = parts.w[p].q + (win[i] ? row_in(o, p) : 0) * K + 16 * (c & 7);
1951 }
1952 bool sin_[WSC];
1953 const __half* wsc[WSC];
1954 #pragma unroll
1955 for(int i = 0; i < WSC; ++i){
1956 const int e = tid + T * i;
1957 const long o = o0 + (e >> 2);
1958 sin_[i] = e < NB * 4 && o < N;
1959 const int p = part_of(sin_[i] ? o : 0);
1960 wsc[i] = parts.w[p].s + (sin_[i] ? row_in(o, p) : 0) * nb + (e & 3);
1961 }
1962 bool xin[XC];
1963 const float* xsc[XC];
1964 #pragma unroll
1965 for(int i = 0; i < XC; ++i){
1966 const int e = tid + T * i;
1967 xin[i] = e < RA * 4 && (e >> 2) < M;
1968 xsc[i] = xs + (long)(xin[i] ? e >> 2 : 0) * nb + (e & 3);
1969 }
1970 auto copy = [&](int st){
1971 unsigned char* b = lk_ms + (st % ST) * SB;
1972 const int kb = st * 128;
1973 #pragma unroll
1974 for(int i = 0; i < WC; ++i){
1975 const int c = tid + T * i;
1976 if(c < NB * 8) lk_cp16(b + (c >> 3) * LK_MP + 16 * (c & 7), wsrc[i] + kb, win[i] && kb + 16 * (c & 7) < K);
1977 }
1978 #pragma unroll
1979 for(int i = 0; i < AC; ++i){
1980 const int c = tid + T * i;
1981 if(c < RA * 8){
1982 const int t = c >> 3;
1983 const bool in = t < M && kb + 16 * (c & 7) < K;
1984 const long off = (long)(in ? t : 0) * K + kb + 16 * (c & 7);
1985 lk_cp16(b + AOFF + t * LK_MP + 16 * (c & 7), xq + (in ? off : 0), in);
1986 if(Q == 2) lk_cp16(b + LOFF + t * LK_MP + 16 * (c & 7), xl + (in ? off : 0), in);
1987 }
1988 }
1989 };
1990 // A stage's scales by plain loads, a stage ahead of their use; set into the stage's buffer before its wait.
1991 __half wnext[WSC];
1992 float xnext[XC];
1993 auto load_scales = [&](int st){
1994 const int blk = st * 4 + (tid & 3);
1995 #pragma unroll
1996 for(int i = 0; i < WSC; ++i) if(tid + T * i < NB * 4) wnext[i] = sin_[i] && blk < nb ? wsc[i][st * 4] : __float2half(0.f);
1997 #pragma unroll
1998 for(int i = 0; i < XC; ++i) if(tid + T * i < RA * 4) xnext[i] = xin[i] && blk < nb ? xsc[i][st * 4] : 0.f;
1999 };
2000 auto set_scales = [&](int st){
2001 unsigned char* b = lk_ms + (st % ST) * SB;
2002 #pragma unroll
2003 for(int i = 0; i < WSC; ++i) if(tid + T * i < NB * 4) ((__half*)(b + WSOFF))[tid + T * i] = wnext[i];
2004 #pragma unroll
2005 for(int i = 0; i < XC; ++i) if(tid + T * i < RA * 4) ((float*)(b + XSOFF))[tid + T * i] = xnext[i];
2006 };
2007 float acc[MW][NW][4][4];
2008 #pragma unroll
2009 for(int m = 0; m < MW; ++m)
2010 #pragma unroll
2011 for(int n = 0; n < NW; ++n)
2012 #pragma unroll
2013 for(int p = 0; p < 4; ++p)
2014 #pragma unroll
2015 for(int e = 0; e < 4; ++e) acc[m][n][p][e] = 0.f;
2016 #pragma unroll
2017 for(int i = 0; i < ST - 1; ++i){ if(i < stages) copy(i); asm volatile("cp.async.commit_group;\n"); }
2018 if(stages > 0) load_scales(0);
2019 // Stage `st`: its scales set, its copies landed for every thread, the copy `ST − 1` stages on started into the buffer
2020 // every warp has finished reading; its buffer.
2021 auto ready = [&](int st){
2022 set_scales(st);
2023 asm volatile("cp.async.wait_group %0;\n" :: "n"(ST - 2));
2024 __syncthreads();
2025 if(st + ST - 1 < stages) copy(st + ST - 1);
2026 asm volatile("cp.async.commit_group;\n");
2027 if(st + 1 < stages) load_scales(st + 1);
2028 return lk_ms + (st % ST) * SB;
2029 };
2030 // `ldmatrix`'s row addresses: of the first n-tile's matrix rows, lane `l` row `l mod 8`'s bytes `16·(l / 8 mod 2)` on;
2031 // of an m-tile's activation rows, lane `l` row `l mod 8 + 8·(l / 8 mod 2)`'s bytes `16·(l / 16)` on.
2032 const int wld = (ntile * 8 + (lane & 7)) * LK_MP + 16 * ((lane >> 3) & 1);
2033 const int ald = (mbase * 16 + (lane & 7) + 8 * ((lane >> 3) & 1)) * LK_MP + 16 * (lane >> 4);
2034 const int row0 = mbase * 16 + g;
2035 const int wsoff = WSOFF + (ntile * 8 + 2 * q4) * 4 * 2;
2036 for(int st = 0; st < stages; ++st){
2037 const unsigned char* b = ready(st);
2038 lk_mma_blocks<Q, MW, NW, AOFF, LOFF, XSOFF>(acc, b, wld, ald, (const __half*)(b + wsoff), row0, 4 * st, nb);
2039 }
2040 asm volatile("cp.async.wait_group 0;\n");
2041 // Each element's four partials summed as `lk_q8_sum4` sums them, written as `lk_gemv_parts` writes it.
2042 #pragma unroll
2043 for(int m = 0; m < MW; ++m)
2044 #pragma unroll
2045 for(int n = 0; n < NW; ++n){
2046 float vs[4];
2047 #pragma unroll
2048 for(int e = 0; e < 4; ++e) vs[e] = lk_q8_sum4(acc[m][n][0][e], acc[m][n][1][e], acc[m][n][2][e], acc[m][n][3][e]);
2049 #pragma unroll
2050 for(int e = 0; e < 4; ++e){
2051 const float v = vs[e];
2052 const long o = o0 + (ntile + n) * 8 + 2 * q4 + (e & 1);
2053 const int t = (mbase + m) * 16 + g + 8 * (e >> 1);
2054 if(o < N && t < M){
2055 const int p = part_of(o);
2056 const long start = p ? parts.end[p - 1] : 0, rr = o - start, np = parts.end[p] - start;
2057 lk_out yo = LK_ROW(lk_part_out(parts, p), parts.row[p], parts.pitch[p]);
2058 const float* bias = parts.bias[p];
2059 yo[(long)t * np + rr] = bias ? v + bias[rr] : v;
2060 }
2061 }
2062 }
2063}
2064"#;
2065
2066const GATHER_BLK: &str = r#"
2069// Sub-block `sb` of row `r` of a table in block format `F`, its 32 values scaled into `o`.
2070template<int F>
2071__device__ __forceinline__ void lk_gather_sub(lk_w w, long r, int sb, float* __restrict__ o, float scale){
2072 signed char v[32]; float d[2], c[2];
2073 lk_sub<F>(w.b + r * w.rb, sb, v, d, c);
2074 #pragma unroll
2075 for(int i = 0; i < 32; ++i) o[i] = __fadd_rn(__fmul_rn(d[i >> 4], (float)v[i]), c[i >> 4]) * scale;
2076}
2077template<int F>
2078__global__ void lk_gather_blk(lk_w w, const int* __restrict__ idx, float* __restrict__ out, int cols, float scale){
2079 int sb = blockIdx.x * blockDim.x + threadIdx.x; if(sb >= (cols >> 5)) return;
2080 lk_gather_sub<F>(w, (long)(idx[blockIdx.y] - 1), sb, out + (long)blockIdx.y * cols + sb * 32, scale);
2081}
2082"#;
2083
2084const GATHER_Q8: &str = r#"
2087// Value `c` of row `r` of a Q8_0 table, scaled.
2088__device__ __forceinline__ float lk_gather_q8_at(const signed char* __restrict__ wq, const __half* __restrict__ ws, long r, int c,
2089 int cols, float scale){
2090 return (float)wq[r * cols + c] * __half2float(ws[r * (cols >> 5) + (c >> 5)]) * scale;
2091}
2092__global__ void lk_gather_q8(const signed char* __restrict__ wq, const __half* __restrict__ ws,
2093 const int* __restrict__ idx, float* __restrict__ out, int cols, float scale){
2094 int c = blockIdx.x * blockDim.x + threadIdx.x; if(c >= cols) return;
2095 out[(long)blockIdx.y * cols + c] = lk_gather_q8_at(wq, ws, (long)(idx[blockIdx.y] - 1), c, cols, scale);
2096}
2097"#;
2098
2099const GATHER_F32: &str = r#"
2102// Value `c` of row `r` of an `f32` table, scaled.
2103__device__ __forceinline__ float lk_gather_f32_at(const float* __restrict__ t, long r, int c, int cols, float scale){
2104 return t[r * cols + c] * scale;
2105}
2106__global__ void lk_gather_f32(const float* __restrict__ t, const int* __restrict__ idx, float* __restrict__ out, int cols,
2107 float scale){
2108 int c = blockIdx.x * blockDim.x + threadIdx.x; if(c >= cols) return;
2109 out[(long)blockIdx.y * cols + c] = lk_gather_f32_at(t, (long)(idx[blockIdx.y] - 1), c, cols, scale);
2110}
2111"#;
2112
2113const RMSNORM: &str = r#"
2123// The norm a fold runs over the row its first part finished: its operands `x` and `r` (one of them that result), the
2124// sum it writes, its weight and outputs (`y` at the row `yrow` names), the norm of its added operand first (`pw`,
2125// `peps`).
2126struct lk_norm_of {
2127 const float* x; const float* r; float* s; const float* w; lk_out y; const int* yrow; long ypitch;
2128 signed char* yq; signed char* yl; float* ys; float eps; const float* pw; float peps;
2129};
2130template<bool ADD, bool SUM, bool OUTF, int Q, int PER, typename A, bool PRE, bool L2, int VW = 1>
2131__device__ __forceinline__ void lk_rmsnorm_row(const float* x, const float* r, float* __restrict__ s,
2132 const float* __restrict__ w, lk_out yr, signed char* __restrict__ yq,
2133 signed char* __restrict__ yl, float* __restrict__ ys, int cols, float eps, long base, long xb,
2134 const float* __restrict__ pw, float peps, A* red, int vt){
2135 auto ld = [](const float* p){ return L2 ? __ldcg(p) : *p; };
2136 const int nw = blockDim.x >> 5, vw = vt >> 5, warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
2137 // The norm's thread this thread plays `j`-th, and whether its warp is one of the norm's.
2138 auto u = [&](int j){ return (warp + j * nw) * 32 + lane; };
2139 auto on = [&](int j){ return warp + j * nw < vw; };
2140 float v[VW][PER];
2141 // Under PRE the added operand is first normed here (by `pw`, `peps`), each value as a norm of its own computes it.
2142 float p[VW][PRE ? PER : 1];
2143 if(PRE){
2144 A pacc[VW];
2145 #pragma unroll
2146 for(int j = 0; j < VW; ++j){
2147 pacc[j] = 0;
2148 #pragma unroll
2149 for(int k = 0; k < PER; ++k){
2150 int i = u(j) + k * vt;
2151 p[j][k] = on(j) && i < cols ? ld(r + base + i) : 0.f;
2152 pacc[j] += (A)p[j][k] * (A)p[j][k];
2153 }
2154 }
2155 A pden = sqrt(lk_warps_sum<VW>(pacc, vw, red) / (A)cols + (A)peps);
2156 #pragma unroll
2157 for(int j = 0; j < VW; ++j){
2158 #pragma unroll
2159 for(int k = 0; k < PER; ++k){
2160 int i = u(j) + k * vt;
2161 if(on(j) && i < cols) p[j][k] = pw ? (float)((A)p[j][k] / pden * (A)pw[i]) : (float)((A)p[j][k] / pden);
2162 }
2163 }
2164 }
2165 A acc[VW];
2166 #pragma unroll
2167 for(int j = 0; j < VW; ++j){
2168 acc[j] = 0;
2169 #pragma unroll
2170 for(int k = 0; k < PER; ++k){
2171 int i = u(j) + k * vt;
2172 v[j][k] = 0.f;
2173 if(on(j) && i < cols){
2174 v[j][k] = ld(x + xb + i);
2175 if(ADD){ v[j][k] += PRE ? p[j][k] : ld(r + base + i); if(SUM) s[base + i] = v[j][k]; }
2176 }
2177 acc[j] += (A)v[j][k] * (A)v[j][k];
2178 }
2179 }
2180 A den = sqrt(lk_warps_sum<VW>(acc, vw, red) / (A)cols + (A)eps);
2181 #pragma unroll
2182 for(int j = 0; j < VW; ++j){
2183 if(!on(j)) continue;
2184 #pragma unroll
2185 for(int k = 0; k < PER; ++k){
2186 int i = u(j) + k * vt;
2187 // A warp holds a whole 32-value group: the quantizer's maximum is the warp's.
2188 if(Q == 0 ? i < cols : (i & ~31) < cols){
2189 float o = w ? (float)((A)v[j][k] / den * (A)w[i]) : (float)((A)v[j][k] / den);
2190 if(Q == 0 || OUTF) yr[i] = o;
2191 if(Q > 0) lk_quant_lane<Q>(o, yq, yl, ys, base + i, (base + i) >> 5);
2192 }
2193 }
2194 }
2195}
2196template<bool ADD, bool SUM, bool OUTF, int Q, int PER, typename A, bool PRE = false>
2197__global__ void __launch_bounds__(1024) lk_rmsnorm(const float* __restrict__ x, const float* __restrict__ r,
2198 float* __restrict__ s, const float* __restrict__ w, lk_out y,
2199 signed char* __restrict__ yq, signed char* __restrict__ yl, float* __restrict__ ys, int cols,
2200 float eps, long xp, long xo, const int* __restrict__ yrow, long ypitch, long yp, long yo,
2201 const float* __restrict__ pw = nullptr, float peps = 0.f){
2202 __shared__ A red[32];
2203 long base = (long)blockIdx.x * cols, xb = (long)blockIdx.x * xp + xo;
2204 lk_out yr = y ? LK_ROW(y, yrow, ypitch) + (long)blockIdx.x * yp + yo : lk_out();
2205 lk_rmsnorm_row<ADD, SUM, OUTF, Q, PER, A, PRE, false>(x, r, s, w, yr, yq, yl, ys, cols, eps, base, xb, pw, peps, red, blockDim.x);
2206}
2207"#;
2208
2209const GATHER_NORM: &str = r#"
2214template<int F, bool OUTF, int Q, int PER, typename A>
2215__global__ void __launch_bounds__(1024) lk_gather_norm(lk_w w, const float* __restrict__ t, const int* __restrict__ idx,
2216 float* __restrict__ out, int cols, float scale, const float* __restrict__ nw, lk_out y,
2217 signed char* __restrict__ yq, signed char* __restrict__ yl, float* __restrict__ ys, float eps,
2218 const int* __restrict__ yrow, long ypitch){
2219 __shared__ A red[32];
2220 const long r = (long)(idx[blockIdx.x] - 1), base = (long)blockIdx.x * cols;
2221 if constexpr(F == 0){
2222 for(int c = threadIdx.x; c < cols; c += blockDim.x) out[base + c] = lk_gather_f32_at(t, r, c, cols, scale);
2223 } else if constexpr(F == 8){
2224 for(int c = threadIdx.x; c < cols; c += blockDim.x) out[base + c] = lk_gather_q8_at(w.q, w.s, r, c, cols, scale);
2225 } else {
2226 for(int sb = threadIdx.x; sb < (cols >> 5); sb += blockDim.x) lk_gather_sub<F>(w, r, sb, out + base + sb * 32, scale);
2227 }
2228 __syncthreads();
2229 lk_out yr = y ? LK_ROW(y, yrow, ypitch) + base : lk_out();
2230 lk_rmsnorm_row<false, false, OUTF, Q, PER, A, false, false>(out, nullptr, nullptr, nw, yr, yq, yl, ys, cols, eps, base, base,
2231 nullptr, 0.f, red, blockDim.x);
2232}
2233"#;
2234
2235const GEMV_Q8_NORM: &str = r#"
2241template<int Q, int M, bool ADD, bool SUM, bool OUTF, int QO, int PER, typename A, bool PRE>
2242__global__ void __launch_bounds__(1024) lk_gemv_q8_norm(const signed char* __restrict__ wq, const __half* __restrict__ ws,
2243 const float* __restrict__ ax, const signed char* __restrict__ xq,
2244 const signed char* __restrict__ xl, const float* __restrict__ xs,
2245 const float* __restrict__ bias, float* y, int K, int N,
2246 const float* x, const float* r, float* __restrict__ s, const float* __restrict__ w, lk_out yo,
2247 const int* __restrict__ yrow, long ypitch, signed char* __restrict__ yq,
2248 signed char* __restrict__ yl, float* __restrict__ ys, float eps, int* __restrict__ cnt,
2249 const float* __restrict__ pw = nullptr, float peps = 0.f){
2250 __shared__ A red[32];
2251 __shared__ int last;
2252 int o = (blockIdx.x * blockDim.x + threadIdx.x) >> 5;
2253 if(o < N){
2254 float v[M];
2255 lk_q8_rows_dot<Q, M, 1>(wq, ws, o, K, ax, xq, xl, xs, v);
2256 if((threadIdx.x & 31) == 0){
2257 #pragma unroll
2258 for(int t = 0; t < M; ++t) y[(long)t * N + o] = bias ? v[t] + bias[o] : v[t];
2259 }
2260 }
2261 __threadfence();
2262 __syncthreads();
2263 if(threadIdx.x == 0) last = atomicAdd(cnt, 1) == gridDim.x - 1;
2264 __syncthreads();
2265 if(!last) return;
2266 __threadfence();
2267 lk_out yr = yo ? LK_ROW(yo, yrow, ypitch) : lk_out();
2268 for(int t = 0; t < M; ++t){
2269 long base = (long)t * N;
2270 lk_rmsnorm_row<ADD, SUM, OUTF, QO, PER, A, PRE, true>(x, r, s, w, yo ? yr + base : lk_out(), yq, yl, ys, N, eps, base, base, pw, peps, red, blockDim.x);
2271 }
2272 if(threadIdx.x == 0) *cnt = 0;
2273}
2274"#;
2275
2276const QUANT: &str = r#"
2278template<int Q>
2279__global__ void lk_quant(const float* __restrict__ x, signed char* __restrict__ xq, signed char* __restrict__ xl,
2280 float* __restrict__ xs, int n){
2281 long i = (long)blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
2282 lk_quant_lane<Q>(x[i], xq, xl, xs, i, i >> 5);
2283}
2284"#;
2285
2286const GEMV_Q8: &str = r#"
2290template<int Q, int M>
2291__global__ void lk_gemv_q8(const signed char* __restrict__ wq, const __half* __restrict__ ws,
2292 const float* __restrict__ x, const signed char* __restrict__ xq,
2293 const signed char* __restrict__ xl, const float* __restrict__ xs,
2294 const float* __restrict__ bias, lk_out y, const int* __restrict__ yrow,
2295 long ypitch, int K, int N){
2296 int o = (blockIdx.x * blockDim.x + threadIdx.x) >> 5; if(o >= N) return;
2297 lk_out yo = LK_ROW(y, yrow, ypitch);
2298 float v[M];
2299 lk_q8_rows_dot<Q, M, 1>(wq, ws, o, K, x, xq, xl, xs, v);
2300 if((threadIdx.x & 31) == 0){
2301 #pragma unroll
2302 for(int t = 0; t < M; ++t) yo[(long)t * N + o] = bias ? v[t] + bias[o] : v[t];
2303 }
2304}
2305"#;
2306
2307const MMA: &str = r#"
2310// An int32 `mma` sum begun at the bits of 1.5·2²³ ends as the float 1.5·2²³ + sum, exactly while |sum| < 2²² (a block's
2311// dot of 32 int8 products is under 2¹⁹), and one float subtraction reads the sum: the conversion it replaces runs at a
2312// quarter of the float rate, and an epilogue of one a product held the tensor cores to half their rate.
2313#define LK_DOT0 0x4B400000
2314__device__ __forceinline__ float lk_dot_f(int d){ return __int_as_float(d) - 12582912.f; }
2315__device__ __forceinline__ void lk_cp16(void* smem, const void* gmem, bool pred){
2316 unsigned s = (unsigned)__cvta_generic_to_shared(smem);
2317 int n = pred ? 16 : 0;
2318 asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" :: "r"(s), "l"(gmem), "r"(n));
2319}
2320// Four `f32` of a staged tile from four elements at `g` (zeros where `pred` is false): an `f32` array's by `cp.async`,
2321// landing with the copies in flight beside it; a binary16 array's widened as it is read, there at once.
2322__device__ __forceinline__ void lk_stage4(float* smem, const float* g, bool pred){ lk_cp16(smem, g, pred); }
2323__device__ __forceinline__ void lk_stage4(float* smem, const __half* g, bool pred){
2324 *(float4*)smem = pred ? lk_from4(g) : make_float4(0.f, 0.f, 0.f, 0.f);
2325}
2326__device__ __forceinline__ void lk_mma_s8(int* c, const int* a, const int* b){
2327 asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n"
2328 : "+r"(c[0]), "+r"(c[1]), "+r"(c[2]), "+r"(c[3])
2329 : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]));
2330}
2331__device__ __forceinline__ void lk_mma_s8_k16(int* c, const int* a, int b){
2332 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3}, {%4,%5}, {%6}, {%0,%1,%2,%3};\n"
2333 : "+r"(c[0]), "+r"(c[1]), "+r"(c[2]), "+r"(c[3]) : "r"(a[0]), "r"(a[1]), "r"(b));
2334}
2335// An int8 `mma` whose sums start at `c`: not volatile, so the scheduler interleaves it with the float work around it.
2336__device__ __forceinline__ void lk_mma_s8_from(int* d, const int* a, const int* b, int c){
2337 asm("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%10,%10,%10,%10};\n"
2338 : "=r"(d[0]), "=r"(d[1]), "=r"(d[2]), "=r"(d[3]) : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]), "r"(c));
2339}
2340// A `cp.async` of `B` bytes (4, 8 or 16) copying `n` of them and zero-filling the rest.
2341template<int B>
2342__device__ __forceinline__ void lk_cpn(void* smem, const void* gmem, int n){
2343 unsigned s = (unsigned)__cvta_generic_to_shared(smem);
2344 if constexpr(B == 16) asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" :: "r"(s), "l"(gmem), "r"(n));
2345 else asm volatile("cp.async.ca.shared.global [%0], [%1], %2, %3;\n" :: "r"(s), "l"(gmem), "n"(B), "r"(n));
2346}
2347// Four 8 × 8 matrices of 16-bit words from shared memory, a lane's row address each, as `mma` fragments.
2348__device__ __forceinline__ void lk_ldsm_s8(int* r, const void* p){
2349 unsigned s = (unsigned)__cvta_generic_to_shared(p);
2350 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n" : "=r"(r[0]), "=r"(r[1]), "=r"(r[2]), "=r"(r[3]) : "r"(s));
2351}
2352// Two, lanes 0 to 15 giving the rows.
2353__device__ __forceinline__ void lk_ldsm2_s8(int* r, const void* p){
2354 unsigned s = (unsigned)__cvta_generic_to_shared(p);
2355 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1}, [%2];\n" : "=r"(r[0]), "=r"(r[1]) : "r"(s));
2356}
2357"#;
2358
2359const GEMM_BLK: &str = r#"
2389#define LK_BN 128
2390template<int F> __host__ __device__ constexpr bool lk_halves_apart(){ return F == 10 || F == 11 || F == 14; }
2391// The formats whose sub-blocks carry an offset (a minimum): Q4_1, Q5_1, Q2_K, Q4_K, Q5_K.
2392template<int F> __host__ __device__ constexpr bool lk_has_offset(){ return F == 3 || F == 7 || F == 10 || F == 12 || F == 13; }
2393// A stage's activations, in bytes: the activation tile (and its low words) and each row's scale for each of the
2394// stage's four sub-blocks.
2395template<int Q, int BM> __host__ __device__ constexpr int lk_stage_acts(){ return (Q == 2 ? 2 : 1) * BM * 144 + BM * 4 * 4; }
2396// The rest of a stage, in bytes: the decoded weight tile, each weight sub-block's scales (two halves where they scale
2397// apart) and offsets, and the activation's half sums (16-bit, where the format has offsets).
2398template<int F, int Q, int BM> __host__ __device__ constexpr int lk_stage_rest(){
2399 return LK_BN * 144 + LK_BN * 4 * (lk_halves_apart<F>() ? 2 : 1) * 4
2400 + (lk_has_offset<F>() ? LK_BN * 4 * (lk_halves_apart<F>() ? 2 : 1) * 4 : 0)
2401 + (lk_has_offset<F>() ? (Q == 2 ? 2 : 1) * BM * 4 * 2 * 2 : 0);
2402}
2403// Whether the activations take two buffers — the next stage's landing while this one multiplies — within a block's
2404// 99 KB of shared memory.
2405template<int F, int Q, int BM> __host__ __device__ constexpr bool lk_stage_pipelined(){
2406 return 2 * lk_stage_acts<Q, BM>() + lk_stage_rest<F, Q, BM>() <= 101376;
2407}
2408// One stage's weights: the `SB` sub-blocks from `k0` of the `LK_BN` matrix rows from `wbase + n0`, their levels as int8
2409// words at `Bs` (rows `BP` bytes apart, zero past `N` and `K`) and each sub-block's scales (`DH`) and offsets (`CH`) at
2410// `Bd`, `Bc`: a thread a sub-block, the `U` of each of the `NT` threads from the words `raw` fetched for them.
2411// A paired tile's (`PAIR`) weight rows are its `LK_BN / 2` gate rows, then the same up rows.
2412template<bool PAIR> __device__ __forceinline__ int lk_tile_row(int r){ return PAIR ? r & (LK_BN / 2 - 1) : r; }
2413template<int F, int SB, int BP, int DH, int CH, int NT, int U, bool PAIR>
2414__device__ __forceinline__ void lk_stage_weights(const lk_raw<F> (&raw)[U], int n0, int N, int nb, int k0, unsigned char* Bs,
2415 float* Bd, float* Bc, int tid){
2416 #pragma unroll
2417 for(int u = 0; u < U; ++u){
2418 int c = tid + u * NT, r = c / SB, sb = c % SB, ks = (k0 >> 5) + sb;
2419 int wv[8]; float d[2], o[2];
2420 if(n0 + lk_tile_row<PAIR>(r) < N && ks < nb) lk_unpack<F>(raw[u], ks, wv, d, o);
2421 else { for(int i = 0; i < 8; ++i) wv[i] = 0; d[0] = d[1] = o[0] = o[1] = 0.f; }
2422 uint4* dst = (uint4*)(Bs + r * BP + sb * 32);
2423 dst[0] = make_uint4(wv[0], wv[1], wv[2], wv[3]); dst[1] = make_uint4(wv[4], wv[5], wv[6], wv[7]);
2424 #pragma unroll
2425 for(int h = 0; h < DH; ++h){
2426 Bd[(r * SB + sb) * DH + h] = d[h];
2427 if(CH) Bc[(r * SB + sb) * CH + h] = o[h];
2428 }
2429 }
2430}
2431// The next stage's raw weight words for this thread's `U` sub-blocks: of `w`, or of `w2` in a paired tile's up rows.
2432template<int F, int SB, int NT, int U, bool PAIR>
2433__device__ __forceinline__ void lk_stage_fetch(lk_raw<F> (&raw)[U], lk_w w, lk_w w2, long wbase, int n0, int N, int K, int nb, int k0,
2434 int tid){
2435 #pragma unroll
2436 for(int u = 0; u < U; ++u){
2437 int c = tid + u * NT, r = c / SB, ks = (k0 >> 5) + c % SB, row = lk_tile_row<PAIR>(r);
2438 if(n0 + row < N && ks < nb) lk_fetch<F>(raw[u], PAIR && r >= LK_BN / 2 ? w2 : w, wbase + n0 + row, K, nb, ks);
2439 }
2440}
2441// `BM` activation rows a block, `BM / 32 · 4` warps: 128 for a dense product, 64 for an expert's few rows. Paired (`G`
2442// 3), a block takes `LK_BN / 2` hidden columns of an expert's gate (`w`, `bias`) and up (`w2`, `bias2`) projections
2443// together and writes each hidden value through the gated activation `ACT` (`limit`, `slope`), as `lk_moe_glu` computes
2444// it, quantized as `lk_quant_lane` quantizes it (`hq`, `hl`, `hs`). A dense product with `part` given runs on a line
2445// of blocks: block `b < tail` the whole tile `b` (tiles row by row of column tiles), each later block one of `split`
2446// parts of a remaining tile's stages, its sums parked in `part`; the last part of a tile to arrive (`cnt`) sums the
2447// tile's parts in order and writes the tile.
2448#define LK_STREAM_STAGES 4
2449template<int Q, int BM> __host__ __device__ constexpr int lk_q8_stage(){ return (Q == 2 ? 2 : 1) * BM * 32 + LK_BN * 32; }
2450template<int BM> __host__ __device__ constexpr int lk_q8_group(){ return BM * 16 + LK_BN * 8; }
2451// Byte `16·c` of row `r` of a tile of 32-byte rows: the row's halves swapped on every other run of four rows.
2452__device__ __forceinline__ int lk_q8_swz(int r, int c){ return r * 32 + ((c ^ ((r >> 2) & 1)) << 4); }
2453__device__ __forceinline__ void lk_mbar_init(unsigned a, unsigned n){ asm volatile("mbarrier.init.shared.b64 [%0], %1;\n" :: "r"(a), "r"(n) : "memory"); }
2454__device__ __forceinline__ void lk_mbar_arrive(unsigned a){ asm volatile("{\n .reg .b64 st;\n mbarrier.arrive.shared.b64 st, [%0];\n}\n" :: "r"(a) : "memory"); }
2455// The barrier counts this thread's arrival once every `cp.async` it issued before has landed.
2456__device__ __forceinline__ void lk_mbar_copies(unsigned a){ asm volatile("cp.async.mbarrier.arrive.noinc.shared.b64 [%0];\n" :: "r"(a) : "memory"); }
2457__device__ __forceinline__ void lk_mbar_wait(unsigned a, unsigned parity){
2458 asm volatile("{\n .reg .pred p;\n LK_WAIT_%=:\n mbarrier.test_wait.parity.shared.b64 p, [%0], %1;\n @!p bra LK_WAIT_%=;\n}\n" :: "r"(a), "r"(parity) : "memory");
2459}
2460__device__ __forceinline__ void lk_cp16s(unsigned s, const void* g, bool pred){
2461 asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" :: "r"(s), "l"(g), "r"(pred ? 16 : 0));
2462}
2463__device__ __forceinline__ void lk_cp8s(unsigned s, const void* g, bool pred){
2464 asm volatile("cp.async.ca.shared.global [%0], [%1], 8, %2;\n" :: "r"(s), "l"(g), "r"(pred ? 8 : 0));
2465}
2466__device__ __forceinline__ void lk_ldsm_s(int* r, unsigned s){
2467 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n" : "=r"(r[0]), "=r"(r[1]), "=r"(r[2]), "=r"(r[3]) : "r"(s));
2468}
2469// Row `rw` of a tile's matrix rows: paired, row `rw` of warp `rw / 32`'s 32 is gate row `16·(rw / 32) + rw % 16` below
2470// 16 and the same up row above; otherwise row `rw`.
2471template<bool PAIR> __device__ __forceinline__ int lk_q8_row(int rw){ return PAIR ? (rw >> 5) * 16 + (rw & 15) : rw; }
2472// The streamed path of `lk_gemm_blk<8>` over a `K` of whole 128-value scale groups, each warp `WM` rows by 32 outputs
2473// of the tile: `lk_gemm_blk`'s arguments, its block's dynamic shared memory at `lk_sm`.
2474template<int Q, int G, int BM, int ACT, int WM>
2475__device__ __forceinline__ void lk_blk_streamed(unsigned char* lk_sm, lk_w w, const signed char* __restrict__ xq,
2476 const signed char* __restrict__ xl, const float* __restrict__ xs, const float* __restrict__ bias, lk_out y,
2477 const int* __restrict__ yrow, long ypitch, int K, int N, int M, const int* __restrict__ ecount,
2478 const int* __restrict__ eoff, const int* __restrict__ list, const float* __restrict__ wt,
2479 lk_w w2, const float* __restrict__ bias2, signed char* __restrict__ hq,
2480 signed char* __restrict__ hl, float* __restrict__ hs, float limit, float slope,
2481 int tail, int split, float* __restrict__ part, int* __restrict__ cnt, lk_parts parts){
2482 constexpr bool PAIR = G == 3 || G == 5, GROUPED = G >= 1 && G <= 3, DENSE = !GROUPED;
2483 constexpr int TW = PAIR ? LK_BN / 2 : LK_BN;
2484 constexpr int MT = WM / 16, NT = BM / WM * 128, AW = Q == 2 ? 2 : 1;
2485 constexpr int A_BYTES = BM * 32, STAGE = lk_q8_stage<Q, BM>(), GROUP = lk_q8_group<BM>(), RING = LK_STREAM_STAGES * STAGE;
2486 // The grid's blocks in launch order take the row tiles of a column tile one after another, so the blocks a column
2487 // tile's weights serve run together and read them once from memory.
2488 const int lin = blockIdx.x + blockIdx.y * gridDim.x;
2489 int m0 = (lin % gridDim.y) * BM, n0 = (lin / gridDim.y) * TW;
2490 // On a line of blocks: this block's tile, and the scale groups of its part of the tile (all of them for a whole tile).
2491 bool parted = DENSE && part && (int)blockIdx.x >= tail;
2492 if(DENSE && part){
2493 // The whole tiles are the first `tail` of the tiles row by row, and the cut ones the rest, as the block path cuts
2494 // them; the line takes the whole ones a column tile at a time — every column tile `rows` of them, the first `wide`
2495 // one more — so the blocks a column tile's weights serve run together.
2496 int b = blockIdx.x, nbx = (N + TW - 1) / TW, tile;
2497 if(b < tail){
2498 int rows = tail / nbx, wide = tail % nbx, c, r;
2499 if(b < (rows + 1) * wide){ c = b / (rows + 1); r = b % (rows + 1); }
2500 else { int e = b - (rows + 1) * wide; c = wide + e / rows; r = e % rows; }
2501 tile = r * nbx + c;
2502 } else tile = tail + (b - tail) / split;
2503 m0 = (tile / nbx) * BM; n0 = (tile % nbx) * TW;
2504 }
2505 if(G == 4){
2506 int p = 0;
2507 while(p < LK_PARTS - 1 && n0 >= parts.end[p]) ++p;
2508 int start = p ? parts.end[p - 1] : 0;
2509 w = parts.w[p]; y = lk_part_out(parts, p); bias = parts.bias[p]; yrow = parts.row[p]; ypitch = parts.pitch[p];
2510 N = parts.end[p] - start; n0 -= start;
2511 }
2512 long first = 0, wbase = 0;
2513 if(GROUPED){
2514 M = ecount[blockIdx.z];
2515 if(m0 >= M) return;
2516 first = eoff[blockIdx.z];
2517 wbase = (long)blockIdx.z * N;
2518 xq += first * K;
2519 if(Q == 2) xl += first * K;
2520 xs += first * (K >> 5);
2521 }
2522 const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
2523 const int nb = K >> 5;
2524 int g_lo = 0, g_hi = nb >> 2;
2525 if(parted){
2526 int piece = ((int)blockIdx.x - tail) % split, groups = nb >> 2;
2527 g_lo = piece * groups / split; g_hi = (piece + 1) * groups / split;
2528 }
2529 const int kb_lo = 4 * g_lo, n = 4 * (g_hi - g_lo);
2530 const unsigned base = (unsigned)__cvta_generic_to_shared(lk_sm);
2531 const unsigned full = base + RING + 2 * GROUP, empty = full + 8 * LK_STREAM_STAGES;
2532 if(tid == 0) for(int s = 0; s < LK_STREAM_STAGES; ++s){ lk_mbar_init(full + 8 * s, NT); lk_mbar_init(empty + 8 * s, NT / 32); }
2533 __syncthreads();
2534 // Each thread's copies, the same every stage: activation chunks, matrix chunks, and a row's scale group.
2535 constexpr int A_IT = (BM * 2 + NT - 1) / NT;
2536 const signed char* a_src[A_IT]; int a_dst[A_IT]; bool a_ok[A_IT], a_on[A_IT];
2537 #pragma unroll
2538 for(int it = 0; it < A_IT; ++it){
2539 int c = tid + it * NT, r = c >> 1, ch = c & 1;
2540 a_on[it] = c < BM * 2;
2541 a_ok[it] = a_on[it] && m0 + r < M;
2542 a_src[it] = xq + (long)(a_ok[it] ? m0 + r : 0) * K + ch * 16;
2543 a_dst[it] = lk_q8_swz(r, ch);
2544 }
2545 const long a_lo = Q == 2 ? (long)(xl - xq) : 0;
2546 // Two threads a matrix row: 256 copy the tile's rows, any beyond them none.
2547 const int w_r = tid >> 1, w_ch = tid & 1, w_row = lk_q8_row<PAIR>(w_r);
2548 const bool w_on = tid < 2 * LK_BN, w_up = PAIR && (w_r & 31) >= 16, w_ok = w_on && n0 + w_row < N;
2549 const signed char* w_src = (w_up ? w2.q : w.q) + (w_ok ? wbase + n0 + w_row : 0) * (long)K + w_ch * 16;
2550 const int w_dst = AW * A_BYTES + lk_q8_swz(w_r, w_ch);
2551 // Scale groups: threads below `BM` copy their activation row's four scales (16 bytes), the next 128 their matrix row's
2552 // four (8 bytes).
2553 const bool sa_on = tid < BM, s_on = tid < BM + LK_BN;
2554 const int s_r = sa_on ? tid : tid - BM, s_row = sa_on ? s_r : lk_q8_row<PAIR>(s_r);
2555 const bool s_up = PAIR && !sa_on && (s_r & 31) >= 16;
2556 const bool s_ok = s_on && (sa_on ? m0 + s_r < M : n0 + s_row < N);
2557 const char* s_src = sa_on ? (const char*)(xs + (long)(s_ok ? m0 + s_r : 0) * nb)
2558 : (const char*)((s_up ? w2.s : w.s) + (s_ok ? wbase + n0 + s_row : 0) * (long)nb);
2559 const int s_step = sa_on ? 4 : 2, s_dst = sa_on ? s_r * 16 : BM * 16 + s_r * 8;
2560 // Stage `i` into buffer `b` (`i`'s place in the ring), and with it scale group `i / 4` when `i` starts one.
2561 auto load = [&](int i, int b, bool group){
2562 const int kb = kb_lo + i;
2563 const unsigned st = base + b * STAGE;
2564 #pragma unroll
2565 for(int it = 0; it < A_IT; ++it){
2566 if(!a_on[it]) continue;
2567 lk_cp16s(st + a_dst[it], a_src[it] + kb * 32, a_ok[it]);
2568 if(Q == 2) lk_cp16s(st + A_BYTES + a_dst[it], a_src[it] + a_lo + kb * 32, a_ok[it]);
2569 }
2570 if(w_on) lk_cp16s(st + w_dst, w_src + kb * 32, w_ok);
2571 if(group && s_on){
2572 const unsigned sg = base + RING + ((kb >> 2) & 1) * GROUP + s_dst;
2573 if(sa_on) lk_cp16s(sg, s_src + kb * s_step, s_ok);
2574 else lk_cp8s(sg, s_src + kb * s_step, s_ok);
2575 }
2576 lk_mbar_copies(full + 8 * b);
2577 };
2578 const int wm = (warp >> 2) * WM, wn = (warp & 3) * 32;
2579 const int g = lane >> 2, t4 = lane & 3;
2580 float acc[MT][4][4];
2581 #pragma unroll
2582 for(int i = 0; i < MT; ++i)
2583 #pragma unroll
2584 for(int j = 0; j < 4; ++j)
2585 #pragma unroll
2586 for(int e = 0; e < 4; ++e) acc[i][j][e] = 0.f;
2587 // This lane's `ldmatrix` rows: the activation's 16 by its chunk, and the matrix's for two n-tiles at a time.
2588 const int a_row = wm + (lane & 15), a_ch = lane >> 4;
2589 const int b_row = wn + (lane >> 4) * 8 + (lane & 7), b_ch = (lane >> 3) & 1;
2590 const bool live = m0 + wm < M;
2591 // The stages run four at a time, a scale group each, so every stage's buffer, barrier phase and scale stage is fixed.
2592 if(n > 0){
2593 #pragma unroll
2594 for(int i = 0; i < LK_STREAM_STAGES - 1; ++i) load(i, i, i == 0);
2595 }
2596 for(int q = 0; q < n / 4; ++q){
2597 const unsigned char* ring = lk_sm + RING + ((g_lo + q) & 1) * GROUP;
2598 const float* sa = (const float*)ring;
2599 const __half* sw = (const __half*)(ring + BM * 16);
2600 #pragma unroll
2601 for(int j = 0; j < 4; ++j){
2602 const int i = 4 * q + j;
2603 lk_mbar_wait(full + 8 * j, q & 1);
2604 const unsigned st = base + j * STAGE;
2605 // A warp whose rows all lie past the tile's last row leaves the multiply to the others.
2606 if(live){
2607 int bf[4][2];
2608 #pragma unroll
2609 for(int p = 0; p < 2; ++p){
2610 int r4[4];
2611 lk_ldsm_s(r4, st + AW * A_BYTES + lk_q8_swz(b_row + p * 16, b_ch));
2612 bf[2 * p][0] = r4[0]; bf[2 * p][1] = r4[1]; bf[2 * p + 1][0] = r4[2]; bf[2 * p + 1][1] = r4[3];
2613 }
2614 float dcol[4][2];
2615 #pragma unroll
2616 for(int nt = 0; nt < 4; ++nt)
2617 #pragma unroll
2618 for(int cc = 0; cc < 2; ++cc) dcol[nt][cc] = __half2float(sw[(wn + nt * 8 + t4 * 2 + cc) * 4 + j]);
2619 #pragma unroll
2620 for(int mt = 0; mt < MT; ++mt){
2621 int af[4], al[4];
2622 lk_ldsm_s(af, st + lk_q8_swz(a_row + mt * 16, a_ch));
2623 if(Q == 2) lk_ldsm_s(al, st + A_BYTES + lk_q8_swz(a_row + mt * 16, a_ch));
2624 const float sa0 = sa[(wm + mt * 16 + g) * 4 + j], sa1 = sa[(wm + mt * 16 + g + 8) * 4 + j];
2625 #pragma unroll
2626 for(int nt = 0; nt < 4; ++nt){
2627 int dot[4], dl[4];
2628 lk_mma_s8_from(dot, af, bf[nt], LK_DOT0);
2629 if(Q == 2) lk_mma_s8_from(dl, al, bf[nt], LK_DOT0);
2630 #pragma unroll
2631 for(int e = 0; e < 4; ++e){
2632 const float s0 = e < 2 ? sa0 : sa1;
2633 acc[mt][nt][e] = Q == 2 ? fmaf(s0 * 0.0078125f * dcol[nt][e & 1], fmaf(lk_dot_f(dot[e]), 128.f, lk_dot_f(dl[e])), acc[mt][nt][e])
2634 : fmaf(s0 * dcol[nt][e & 1], lk_dot_f(dot[e]), acc[mt][nt][e]);
2635 }
2636 }
2637 }
2638 }
2639 __syncwarp();
2640 if(lane == 0) lk_mbar_arrive(empty + 8 * j);
2641 // The buffer the stage before this one read takes the stage three ahead, once every warp is past it; the stage
2642 // three ahead starts a scale group when this one is a group's second.
2643 if(i + LK_STREAM_STAGES - 1 < n){
2644 if(i >= 1) lk_mbar_wait(empty + 8 * ((j + 3) & 3), j == 0 ? (q - 1) & 1 : q & 1);
2645 load(i + LK_STREAM_STAGES - 1, (j + 3) & 3, j == 1);
2646 }
2647 }
2648 }
2649 asm volatile("cp.async.wait_all;\n" ::: "memory");
2650 __syncthreads();
2651 if(parted){
2652 // A part parks its sums; the tile's last part to arrive sums every part in order, its own read back too.
2653 __shared__ bool lk_last;
2654 int tile = tail + ((int)blockIdx.x - tail) / split;
2655 float4* mine = (float4*)(part + ((long)(blockIdx.x - tail) * NT + tid) * (MT * 16));
2656 #pragma unroll
2657 for(int mt = 0; mt < MT; ++mt)
2658 #pragma unroll
2659 for(int nt = 0; nt < 4; ++nt) mine[mt * 4 + nt] = make_float4(acc[mt][nt][0], acc[mt][nt][1], acc[mt][nt][2], acc[mt][nt][3]);
2660 __threadfence();
2661 __syncthreads();
2662 if(tid == 0) lk_last = atomicAdd(cnt + (tile - tail), 1) == split - 1;
2663 __syncthreads();
2664 if(!lk_last) return;
2665 #pragma unroll
2666 for(int mt = 0; mt < MT; ++mt)
2667 #pragma unroll
2668 for(int nt = 0; nt < 4; ++nt)
2669 #pragma unroll
2670 for(int e = 0; e < 4; ++e) acc[mt][nt][e] = 0.f;
2671 for(int pc = 0; pc < split; ++pc){
2672 const float4* src = (const float4*)(part + ((long)((tile - tail) * split + pc) * NT + tid) * (MT * 16));
2673 #pragma unroll
2674 for(int mt = 0; mt < MT; ++mt)
2675 #pragma unroll
2676 for(int nt = 0; nt < 4; ++nt){
2677 float4 v = __ldcg(src + mt * 4 + nt);
2678 acc[mt][nt][0] += v.x; acc[mt][nt][1] += v.y; acc[mt][nt][2] += v.z; acc[mt][nt][3] += v.w;
2679 }
2680 }
2681 if(tid == 0) cnt[tile - tail] = 0;
2682 }
2683 if(PAIR){
2684 // Each hidden value through the gated activation from its gate and up sums (plus their biases) in this thread,
2685 // staged by row, then quantized a 32-value block a warp at a time.
2686 float* H = (float*)lk_sm;
2687 #pragma unroll
2688 for(int mt = 0; mt < MT; ++mt)
2689 #pragma unroll
2690 for(int nt = 0; nt < 2; ++nt)
2691 #pragma unroll
2692 for(int e = 0; e < 4; ++e){
2693 int r = wm + mt * 16 + g + (e >= 2 ? 8 : 0), h = (wn >> 1) + nt * 8 + t4 * 2 + (e & 1), col = n0 + h;
2694 float gv = acc[mt][nt][e], uv = acc[mt][nt + 2][e];
2695 if(bias && col < N) gv = gv + bias[wbase + col];
2696 if(bias2 && col < N) uv = uv + bias2[wbase + col];
2697 float hv;
2698 if(G == 5) hv = ACT < 0 ? gv / (1.0f + expf(-gv)) * uv : uv * lk_act<(ACT < 0 ? 0 : ACT)>(gv);
2699 else if(ACT == 0) hv = gv / (1.0f + expf(-gv)) * uv;
2700 else { float gc = fminf(gv, limit); hv = gc / (1.0f + expf(-slope * gc)) * (fminf(fmaxf(uv, -limit), limit) + 1.0f); }
2701 H[r * TW + h] = hv;
2702 }
2703 __syncthreads();
2704 for(int r = warp; r < BM; r += NT / 32){
2705 if(m0 + r >= M) break;
2706 #pragma unroll
2707 for(int j = 0; j < TW / 32; ++j){
2708 int c = 32 * j + lane;
2709 if(n0 + 32 * j >= N) break;
2710 float hv = H[r * TW + c];
2711 long i = (first + m0 + r) * N + n0 + c;
2712 if(G == 5 && y) y[i] = hv;
2713 if(G == 3 || hq) lk_quant_lane<Q>(hv, hq, hl, hs, i, i >> 5);
2714 }
2715 }
2716 return;
2717 }
2718 lk_out yo = GROUPED ? y + first * N : LK_ROW(y, yrow, ypitch);
2719 #pragma unroll
2720 for(int mt = 0; mt < MT; ++mt)
2721 #pragma unroll
2722 for(int nt = 0; nt < 4; ++nt)
2723 #pragma unroll
2724 for(int e = 0; e < 4; ++e){
2725 int row = m0 + wm + mt * 16 + g + (e >= 2 ? 8 : 0);
2726 int col = n0 + wn + nt * 8 + t4 * 2 + (e & 1);
2727 if(row >= M || col >= N) continue;
2728 float v = bias ? acc[mt][nt][e] + bias[wbase + col] : acc[mt][nt][e];
2729 // G 2 weighs each choice's result by its weight, as the expert mixture's down projection does.
2730 yo[(long)row * N + col] = G == 2 ? v * wt[list[first + row]] : v;
2731 }
2732}
2733
2734template<int F, int Q, int G = 0, int BM = (G >= 1 && G <= 3 ? 64 : 128), int ACT = 0>
2735__global__ void __launch_bounds__(BM * 4, F == 8 && BM <= 64 ? 2 : 1) lk_gemm_blk(lk_w w, const signed char* __restrict__ xq,
2736 const signed char* __restrict__ xl, const float* __restrict__ xs, const float* __restrict__ bias, lk_out y,
2737 const int* __restrict__ yrow, long ypitch, int K, int N, int M, const int* __restrict__ ecount = nullptr,
2738 const int* __restrict__ eoff = nullptr, const int* __restrict__ list = nullptr, const float* __restrict__ wt = nullptr,
2739 lk_w w2 = {}, const float* __restrict__ bias2 = nullptr, signed char* __restrict__ hq = nullptr,
2740 signed char* __restrict__ hl = nullptr, float* __restrict__ hs = nullptr, float limit = 0.f, float slope = 0.f,
2741 int tail = 0, int split = 1, float* __restrict__ part = nullptr, int* __restrict__ cnt = nullptr, lk_parts parts = {}){
2742 extern __shared__ __align__(128) unsigned char lk_sm[];
2743 if constexpr(F == 8){
2744 if((K & 127) == 0){
2745 // Launched as eight warps over 128 rows, each warp takes 64 of them; otherwise 32.
2746 if constexpr(BM == 128){
2747 if(blockDim.x == 256){
2748 lk_blk_streamed<Q, G, BM, ACT, 64>(lk_sm, w, xq, xl, xs, bias, y, yrow, ypitch, K, N, M, ecount, eoff, list, wt, w2, bias2,
2749 hq, hl, hs, limit, slope, tail, split, part, cnt, parts);
2750 return;
2751 }
2752 }
2753 lk_blk_streamed<Q, G, BM, ACT, 32>(lk_sm, w, xq, xl, xs, bias, y, yrow, ypitch, K, N, M, ecount, eoff, list, wt, w2, bias2,
2754 hq, hl, hs, limit, slope, tail, split, part, cnt, parts);
2755 return;
2756 }
2757 }
2758 constexpr bool PAIR = G == 3 || G == 5, GROUPED = G >= 1 && G <= 3, DENSE = !GROUPED;
2759 constexpr int TW = PAIR ? LK_BN / 2 : LK_BN;
2760 constexpr int SB = 4, BK = 32 * SB, BP = BK + 16, AW = Q == 2 ? 2 : 1;
2761 constexpr int DH = lk_halves_apart<F>() ? 2 : 1, CH = lk_has_offset<F>() ? DH : 0;
2762 constexpr int A_BYTES = BM * BP;
2763 int m0 = blockIdx.y * BM, n0 = blockIdx.x * (PAIR ? LK_BN / 2 : LK_BN);
2764 // On a line of blocks: this block's tile, and the stages of its part of the tile (all of them for a whole tile).
2765 bool parted = DENSE && part && (int)blockIdx.x >= tail;
2766 if(DENSE && part){
2767 int b = blockIdx.x, nbx = (N + TW - 1) / TW, tile = b < tail ? b : tail + (b - tail) / split;
2768 m0 = (tile / nbx) * BM; n0 = (tile % nbx) * TW;
2769 }
2770 if(G == 4){
2771 // The part whose columns hold the tile: its matrix, outputs and bias, the tile's columns its own.
2772 int p = 0;
2773 while(p < LK_PARTS - 1 && n0 >= parts.end[p]) ++p;
2774 int start = p ? parts.end[p - 1] : 0;
2775 w = parts.w[p]; y = lk_part_out(parts, p); bias = parts.bias[p]; yrow = parts.row[p]; ypitch = parts.pitch[p];
2776 N = parts.end[p] - start; n0 -= start;
2777 }
2778 long first = 0, wbase = 0;
2779 if(GROUPED){
2780 // Grid layer `z` is an expert: its rows the gathered rows of its choices, its matrix rows `z·N …` of the stack.
2781 M = ecount[blockIdx.z];
2782 if(m0 >= M) return;
2783 first = eoff[blockIdx.z];
2784 wbase = (long)blockIdx.z * N;
2785 xq += first * K;
2786 if(Q == 2) xl += first * K;
2787 xs += first * (K >> 5);
2788 }
2789 int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
2790 int nb = K >> 5, stages = (K + BK - 1) / BK;
2791 int s_lo = 0, s_hi = stages;
2792 if(parted){
2793 int piece = ((int)blockIdx.x - tail) % split;
2794 s_lo = piece * stages / split; s_hi = (piece + 1) * stages / split;
2795 }
2796 // Each warp multiplies 32 × 32 of the tile.
2797 int wm = (warp >> 2) * 32, wn = (warp & 3) * 32;
2798 int g = lane >> 2, t4 = lane & 3;
2799 float acc[2][4][4];
2800 #pragma unroll
2801 for(int i = 0; i < 2; ++i)
2802 #pragma unroll
2803 for(int j = 0; j < 4; ++j)
2804 #pragma unroll
2805 for(int e = 0; e < 4; ++e) acc[i][j][e] = 0.f;
2806 constexpr bool PIPE = lk_stage_pipelined<F, Q, BM>();
2807 constexpr int ACTS = lk_stage_acts<Q, BM>();
2808 unsigned char* Bs = lk_sm + (PIPE ? 2 : 1) * ACTS;
2809 float* Bd = (float*)(Bs + LK_BN * BP);
2810 float* Bc = Bd + LK_BN * SB * DH;
2811 short* Xs = (short*)(Bc + LK_BN * SB * CH);
2812 // Stage `st`'s activation rows into buffer `b` by `cp.async`, and each row's scales: one 16-byte copy when the
2813 // stage is whole and the row's scales fall on 16 bytes.
2814 auto acts = [&](int st, int b){
2815 int k0 = st * BK;
2816 unsigned char* As = lk_sm + b * ACTS;
2817 float* Sa = (float*)(As + AW * A_BYTES);
2818 for(int c = tid; c < BM * SB * 2; c += (BM * 4)){
2819 int r = c / (SB * 2), h = c % (SB * 2);
2820 bool ok = m0 + r < M && k0 + h * 16 < K;
2821 long off = ok ? (long)(m0 + r) * K + k0 + h * 16 : 0;
2822 lk_cp16(As + r * BP + h * 16, xq + off, ok);
2823 if(Q == 2) lk_cp16(As + A_BYTES + r * BP + h * 16, xl + off, ok);
2824 if(h == 0){
2825 long base = (long)(m0 + r) * nb + (k0 >> 5);
2826 if((nb & 3) == 0 && m0 + r < M && (k0 >> 5) + SB <= nb) lk_cp16(Sa + r * SB, xs + base, true);
2827 else for(int j = 0; j < SB; ++j) Sa[r * SB + j] = m0 + r < M && (k0 >> 5) + j < nb ? xs[base + j] : 0.f;
2828 }
2829 }
2830 asm volatile("cp.async.commit_group;\n");
2831 };
2832 // Each thread's sub-blocks of a stage, their raw words fetched a stage ahead.
2833 constexpr int U = LK_BN * SB / (BM * 4);
2834 lk_raw<F> raw[U];
2835 lk_stage_fetch<F, SB, BM * 4, U, PAIR>(raw, w, w2, wbase, n0, N, K, nb, s_lo * BK, tid);
2836 // The first stage lands in the buffer its parity names, so each stage's buffer is `st & 1` wherever the part starts.
2837 acts(s_lo, PIPE ? s_lo & 1 : 0);
2838 for(int st = s_lo; st < s_hi; ++st){
2839 int k0 = st * BK, b = PIPE ? st & 1 : 0;
2840 const unsigned char* As = lk_sm + b * ACTS;
2841 const float* Sa = (const float*)(As + AW * A_BYTES);
2842 lk_stage_weights<F, SB, BP, DH, CH, BM * 4, U, PAIR>(raw, n0, N, nb, k0, Bs, Bd, Bc, tid);
2843 // The next stage's weights are fetched while this one multiplies.
2844 if(st + 1 < s_hi) lk_stage_fetch<F, SB, BM * 4, U, PAIR>(raw, w, w2, wbase, n0, N, K, nb, k0 + BK, tid);
2845 asm volatile("cp.async.wait_group 0;\n");
2846 __syncthreads();
2847 // The activation's sums over each half of each sub-block, for the offsets.
2848 if(lk_has_offset<F>()) for(int c = tid; c < AW * BM * SB * 2; c += (BM * 4)){
2849 int u = c / (BM * SB * 2), r = (c / (SB * 2)) % BM, h = c % (SB * 2);
2850 const int* a = (const int*)(As + u * A_BYTES + r * BP + h * 16);
2851 int sum = 0;
2852 #pragma unroll
2853 for(int i = 0; i < 4; ++i) sum = __dp4a(0x01010101, a[i], sum);
2854 Xs[(u * BM + r) * SB * 2 + h] = (short)sum;
2855 }
2856 __syncthreads();
2857 // The next stage's activations land in the other buffer while this one multiplies.
2858 if(PIPE && st + 1 < s_hi) acts(st + 1, b ^ 1);
2859 // A warp whose 32 rows all lie past the tile's last row (an expert with few rows) leaves the multiply to the others.
2860 #pragma unroll 1
2861 for(int sb = 0; sb < SB && m0 + wm < M; ++sb){
2862 if((k0 >> 5) + sb >= nb) break;
2863 int ko = sb * 32 + t4 * 4;
2864 int af[2][4], al[2][4], bf[4][2];
2865 // This lane's columns' scales and offsets (two columns an n-tile, a value a half where the halves scale apart),
2866 // and its rows' scales and their sums scaled by them (the low words' at a 128th), read once a sub-block.
2867 constexpr int OH = CH ? CH : 1;
2868 float dcol[4][2][DH], ocol[4][2][OH], sa[2][2], sx[2][2][DH], sxl[2][2][DH];
2869 #pragma unroll
2870 for(int nt = 0; nt < 4; ++nt)
2871 #pragma unroll
2872 for(int cc = 0; cc < 2; ++cc){
2873 int c = wn + nt * 8 + t4 * 2 + cc;
2874 #pragma unroll
2875 for(int h = 0; h < DH; ++h){
2876 dcol[nt][cc][h] = Bd[(c * SB + sb) * DH + h];
2877 if(CH) ocol[nt][cc][h] = Bc[(c * SB + sb) * CH + h];
2878 }
2879 }
2880 #pragma unroll
2881 for(int mt = 0; mt < 2; ++mt)
2882 #pragma unroll
2883 for(int hh = 0; hh < 2; ++hh){
2884 int r = wm + mt * 16 + g + hh * 8;
2885 float s0 = Sa[r * SB + sb];
2886 sa[mt][hh] = s0;
2887 if(lk_has_offset<F>()){
2888 const short* x = Xs + r * SB * 2 + sb * 2;
2889 if(DH == 2){ sx[mt][hh][0] = s0 * (float)x[0]; sx[mt][hh][DH - 1] = s0 * (float)x[1]; }
2890 else sx[mt][hh][0] = s0 * (float)(x[0] + x[1]);
2891 if(Q == 2){
2892 const short* l = Xs + (BM + r) * SB * 2 + sb * 2;
2893 float sl = s0 * 0.0078125f;
2894 if(DH == 2){ sxl[mt][hh][0] = sl * (float)l[0]; sxl[mt][hh][DH - 1] = sl * (float)l[1]; }
2895 else sxl[mt][hh][0] = sl * (float)(l[0] + l[1]);
2896 }
2897 }
2898 }
2899 #pragma unroll
2900 for(int mt = 0; mt < 2; ++mt){
2901 int r = wm + mt * 16 + g;
2902 af[mt][0] = *(const int*)(As + r * BP + ko);
2903 af[mt][1] = *(const int*)(As + (r + 8) * BP + ko);
2904 af[mt][2] = *(const int*)(As + r * BP + ko + 16);
2905 af[mt][3] = *(const int*)(As + (r + 8) * BP + ko + 16);
2906 if(Q == 2){
2907 al[mt][0] = *(const int*)(As + A_BYTES + r * BP + ko);
2908 al[mt][1] = *(const int*)(As + A_BYTES + (r + 8) * BP + ko);
2909 al[mt][2] = *(const int*)(As + A_BYTES + r * BP + ko + 16);
2910 al[mt][3] = *(const int*)(As + A_BYTES + (r + 8) * BP + ko + 16);
2911 }
2912 }
2913 #pragma unroll
2914 for(int nt = 0; nt < 4; ++nt){
2915 int c = wn + nt * 8 + g;
2916 bf[nt][0] = *(const int*)(Bs + c * BP + ko);
2917 bf[nt][1] = *(const int*)(Bs + c * BP + ko + 16);
2918 }
2919 #pragma unroll
2920 for(int mt = 0; mt < 2; ++mt)
2921 #pragma unroll
2922 for(int nt = 0; nt < 4; ++nt){
2923 // The accumulator's elements: rows g, g + 8 of the m-tile by columns 2·t4, 2·t4 + 1 of the n-tile.
2924 int dot[2][4], dl[2][4];
2925 #pragma unroll
2926 for(int h = 0; h < 2; ++h)
2927 #pragma unroll
2928 for(int e = 0; e < 4; ++e){ dot[h][e] = LK_DOT0; dl[h][e] = LK_DOT0; }
2929 if(lk_halves_apart<F>()){
2930 int a0[2] = {af[mt][0], af[mt][1]}, a1[2] = {af[mt][2], af[mt][3]};
2931 lk_mma_s8_k16(dot[0], a0, bf[nt][0]);
2932 lk_mma_s8_k16(dot[1], a1, bf[nt][1]);
2933 if(Q == 2){
2934 int l0[2] = {al[mt][0], al[mt][1]}, l1[2] = {al[mt][2], al[mt][3]};
2935 lk_mma_s8_k16(dl[0], l0, bf[nt][0]);
2936 lk_mma_s8_k16(dl[1], l1, bf[nt][1]);
2937 }
2938 } else {
2939 lk_mma_s8(dot[0], af[mt], bf[nt]);
2940 if(Q == 2) lk_mma_s8(dl[0], al[mt], bf[nt]);
2941 }
2942 #pragma unroll
2943 for(int e = 0; e < 4; ++e){
2944 int hh = e >> 1, cc = e & 1;
2945 float s0 = sa[mt][hh], a = acc[mt][nt][e];
2946 if constexpr(F == 8 && Q == 2){
2947 acc[mt][nt][e] = fmaf(s0 * 0.0078125f * dcol[nt][cc][0], fmaf(lk_dot_f(dot[0][e]), 128.f, lk_dot_f(dl[0][e])), a);
2948 continue;
2949 }
2950 #pragma unroll
2951 for(int h = 0; h < DH; ++h) a = fmaf(s0 * dcol[nt][cc][h], lk_dot_f(dot[h][e]), a);
2952 if(lk_has_offset<F>())
2953 #pragma unroll
2954 for(int h = 0; h < DH; ++h) a = fmaf(ocol[nt][cc][h], sx[mt][hh][h], a);
2955 if(Q == 2){
2956 float sl = s0 * 0.0078125f;
2957 #pragma unroll
2958 for(int h = 0; h < DH; ++h) a = fmaf(sl * dcol[nt][cc][h], lk_dot_f(dl[h][e]), a);
2959 if(lk_has_offset<F>())
2960 #pragma unroll
2961 for(int h = 0; h < DH; ++h) a = fmaf(ocol[nt][cc][h], sxl[mt][hh][h], a);
2962 }
2963 acc[mt][nt][e] = a;
2964 }
2965 }
2966 }
2967 __syncthreads();
2968 if(!PIPE && st + 1 < s_hi) acts(st + 1, 0);
2969 }
2970 if(parted){
2971 // A part parks its sums; the tile's last part to arrive sums every part in order, its own read back too.
2972 __shared__ bool lk_last;
2973 int tile = tail + ((int)blockIdx.x - tail) / split;
2974 float4* mine = (float4*)(part + ((long)(blockIdx.x - tail) * (BM * 4) + tid) * 32);
2975 #pragma unroll
2976 for(int mt = 0; mt < 2; ++mt)
2977 #pragma unroll
2978 for(int nt = 0; nt < 4; ++nt) mine[mt * 4 + nt] = make_float4(acc[mt][nt][0], acc[mt][nt][1], acc[mt][nt][2], acc[mt][nt][3]);
2979 __threadfence();
2980 __syncthreads();
2981 if(tid == 0) lk_last = atomicAdd(cnt + (tile - tail), 1) == split - 1;
2982 __syncthreads();
2983 if(!lk_last) return;
2984 #pragma unroll
2985 for(int mt = 0; mt < 2; ++mt)
2986 #pragma unroll
2987 for(int nt = 0; nt < 4; ++nt)
2988 #pragma unroll
2989 for(int e = 0; e < 4; ++e) acc[mt][nt][e] = 0.f;
2990 for(int pc = 0; pc < split; ++pc){
2991 const float4* src = (const float4*)(part + ((long)((tile - tail) * split + pc) * (BM * 4) + tid) * 32);
2992 #pragma unroll
2993 for(int mt = 0; mt < 2; ++mt)
2994 #pragma unroll
2995 for(int nt = 0; nt < 4; ++nt){
2996 float4 v = __ldcg(src + mt * 4 + nt);
2997 acc[mt][nt][0] += v.x; acc[mt][nt][1] += v.y; acc[mt][nt][2] += v.z; acc[mt][nt][3] += v.w;
2998 }
2999 }
3000 if(tid == 0) cnt[tile - tail] = 0;
3001 }
3002 if(PAIR){
3003 // Each hidden value's gate and up sums (plus their biases) staged by column, then through the gated activation
3004 // and quantized a 32-value block a warp at a time.
3005 float* E = (float*)lk_sm;
3006 #pragma unroll
3007 for(int mt = 0; mt < 2; ++mt)
3008 #pragma unroll
3009 for(int nt = 0; nt < 4; ++nt)
3010 #pragma unroll
3011 for(int e = 0; e < 4; ++e){
3012 int r = wm + mt * 16 + g + (e >= 2 ? 8 : 0), c = wn + nt * 8 + t4 * 2 + (e & 1), col = n0 + lk_tile_row<true>(c);
3013 const float* b = c < LK_BN / 2 ? bias : bias2;
3014 float v = acc[mt][nt][e];
3015 E[r * LK_BN + c] = b && col < N ? v + b[wbase + col] : v;
3016 }
3017 __syncthreads();
3018 for(int r = warp; r < BM; r += BM / 8){
3019 if(m0 + r >= M) break;
3020 #pragma unroll
3021 for(int j = 0; j < LK_BN / 64; ++j){
3022 int c = 32 * j + lane;
3023 if(n0 + 32 * j >= N) break;
3024 float gv = E[r * LK_BN + c], uv = E[r * LK_BN + LK_BN / 2 + c], hv;
3025 if(G == 5) hv = ACT < 0 ? gv / (1.0f + expf(-gv)) * uv : uv * lk_act<(ACT < 0 ? 0 : ACT)>(gv);
3026 else if(ACT == 0) hv = gv / (1.0f + expf(-gv)) * uv;
3027 else { float gc = fminf(gv, limit); hv = gc / (1.0f + expf(-slope * gc)) * (fminf(fmaxf(uv, -limit), limit) + 1.0f); }
3028 long i = (first + m0 + r) * N + n0 + c;
3029 if(G == 5 && y) y[i] = hv;
3030 if(G == 3 || hq) lk_quant_lane<Q>(hv, hq, hl, hs, i, i >> 5);
3031 }
3032 }
3033 return;
3034 }
3035 lk_out yo = GROUPED ? y + first * N : LK_ROW(y, yrow, ypitch);
3036 #pragma unroll
3037 for(int mt = 0; mt < 2; ++mt)
3038 #pragma unroll
3039 for(int nt = 0; nt < 4; ++nt)
3040 #pragma unroll
3041 for(int e = 0; e < 4; ++e){
3042 int row = m0 + wm + mt * 16 + g + (e >= 2 ? 8 : 0);
3043 int col = n0 + wn + nt * 8 + t4 * 2 + (e & 1);
3044 if(row >= M || col >= N) continue;
3045 float v = bias ? acc[mt][nt][e] + bias[wbase + col] : acc[mt][nt][e];
3046 // G 2 weighs each choice's result by its weight, as the expert mixture's down projection does.
3047 yo[(long)row * N + col] = G == 2 ? v * wt[list[first + row]] : v;
3048 }
3049}
3050"#;
3051
3052pub fn gemm_blk_smem_bytes(f: u32, two_pass: bool, bm: usize) -> usize {
3055 let block = gemm_blk_block_bytes(f, two_pass, bm);
3056 if f == 8 {
3057 block.max(gemm_blk_stream_bytes(two_pass, bm))
3058 } else {
3059 block
3060 }
3061}
3062
3063pub fn gemm_blk_launch_smem(f: u32, two_pass: bool, bm: usize, k: usize) -> usize {
3067 if f == 8 && k % 128 == 0 {
3068 gemm_blk_stream_bytes(two_pass, bm)
3069 } else {
3070 gemm_blk_block_bytes(f, two_pass, bm)
3071 }
3072}
3073
3074fn gemm_blk_block_bytes(f: u32, two_pass: bool, bm: usize) -> usize {
3080 let n = GEMM_TILE_N;
3081 let words = if two_pass { 2 } else { 1 };
3082 let halves = if matches!(f, 10 | 11 | 14) { 2 } else { 1 };
3083 let offset = matches!(f, 3 | 7 | 10 | 12 | 13);
3084 let acts = words * bm * 144 + bm * 4 * 4;
3085 let rest = n * 144
3086 + n * 4 * halves * 4
3087 + if offset { n * 4 * halves * 4 } else { 0 }
3088 + if offset { words * bm * 4 * 2 * 2 } else { 0 };
3089 let stages = if 2 * acts + rest <= 101_376 { 2 * acts + rest } else { acts + rest };
3091 stages.max(bm * n * 4)
3092}
3093
3094fn gemm_blk_stream_bytes(two_pass: bool, bm: usize) -> usize {
3099 let n = GEMM_TILE_N;
3100 let words = if two_pass { 2 } else { 1 };
3101 (STREAM_STAGES * (words * bm * 32 + n * 32) + 2 * (bm * 16 + n * 8) + 2 * STREAM_STAGES * 8).max(bm * n / 2 * 4)
3102}
3103
3104const STREAM_STAGES: usize = 4;
3106
3107pub fn gemm_blk_expert_rows(choices: usize, experts: usize) -> usize {
3111 if choices > 64 * experts {
3112 128
3113 } else {
3114 64
3115 }
3116}
3117
3118pub fn gemm_f32_splits(tiles: usize, sms: usize, chunks: usize) -> usize {
3121 (2 * sms / tiles.max(1)).clamp(1, chunks.max(1))
3122}
3123
3124pub fn gemm_blk_tail(tiles: usize, workers: usize, stages: usize) -> (usize, usize) {
3129 let rest = tiles % workers;
3130 let split = if rest == 0 { 1 } else { (workers / rest).min(stages) };
3131 if split < 2 {
3132 (tiles, 1)
3133 } else {
3134 (tiles - rest, split)
3135 }
3136}
3137
3138pub fn gemm_blk_threads(bm: usize) -> usize {
3140 bm * 4
3141}
3142
3143pub fn gemm_blk_launch_threads(f: u32, bm: usize, k: usize) -> usize {
3147 if f == 8 && k % 128 == 0 && bm == GEMM_BLK_TILE_M {
3148 256
3149 } else {
3150 gemm_blk_threads(bm)
3151 }
3152}
3153
3154pub const GEMM_BLK_TILE_M: usize = 128;
3156
3157pub fn gemm_blk_rows(f: u32, two_pass: bool) -> usize {
3161 if f == 8 && two_pass {
3162 64
3163 } else {
3164 GEMM_BLK_TILE_M
3165 }
3166}
3167
3168pub const GEMM_PARTS: usize = 6;
3173
3174pub const HEAD_TILE_M: usize = 128;
3176
3177pub const GEMM_TILE_N: usize = 128;
3179
3180const GEMV_F32: &str = r#"
3185#define LK_F32_ROWS 4
3186__global__ void __launch_bounds__(256) lk_gemv_f32(const float* __restrict__ w, const float* __restrict__ x,
3187 const float* __restrict__ bias, lk_out y, const int* __restrict__ yrow, long ypitch, int K, int N, int M,
3188 int parts = 1){
3189 __shared__ double run[LK_F32_ROWS][8];
3190 int warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
3191 int o = blockIdx.x * (8 / parts) + warp / parts, p = warp % parts;
3192 bool live = o < N;
3193 int span = (K + parts - 1) / parts, k0 = p * span, k1 = min(K, k0 + span);
3194 const float* wr = w + (long)(live ? o : 0) * K;
3195 lk_out yo = LK_ROW(y, yrow, ypitch);
3196 int rb = (M + gridDim.y - 1) / gridDim.y, t1 = min(M, (int)(blockIdx.y + 1) * rb);
3197 for(int t0 = blockIdx.y * rb; t0 < t1; t0 += LK_F32_ROWS){
3198 double acc[LK_F32_ROWS];
3199 #pragma unroll
3200 for(int r = 0; r < LK_F32_ROWS; ++r) acc[r] = 0.0;
3201 if(live)
3202 for(int k = k0 + lane; k < k1; k += 32){
3203 const double wv = (double)wr[k];
3204 #pragma unroll
3205 for(int r = 0; r < LK_F32_ROWS; ++r) if(t0 + r < t1) acc[r] += wv * (double)x[(long)(t0 + r) * K + k];
3206 }
3207 #pragma unroll
3208 for(int r = 0; r < LK_F32_ROWS; ++r) acc[r] = lk_warp_sum_d(acc[r]);
3209 if(parts == 1){
3210 #pragma unroll
3211 for(int r = 0; r < LK_F32_ROWS; ++r)
3212 if(live && lane == 0 && t0 + r < t1) yo[(long)(t0 + r) * N + o] = bias ? (float)acc[r] + bias[o] : (float)acc[r];
3213 continue;
3214 }
3215 if(lane == 0)
3216 #pragma unroll
3217 for(int r = 0; r < LK_F32_ROWS; ++r) run[r][warp] = acc[r];
3218 __syncthreads();
3219 if(live && p == 0 && lane == 0)
3220 #pragma unroll
3221 for(int r = 0; r < LK_F32_ROWS; ++r)
3222 if(t0 + r < t1){
3223 double s = 0.0;
3224 for(int i = 0; i < parts; ++i) s += run[r][warp + i];
3225 yo[(long)(t0 + r) * N + o] = bias ? (float)s + bias[o] : (float)s;
3226 }
3227 __syncthreads();
3228 }
3229}
3230"#;
3231
3232pub fn gemv_f32_parts(n: usize, k: usize, sms: usize) -> usize {
3235 [8, 4, 2].into_iter().find(|&p| k >= 128 * p && n * p <= 16 * sms).unwrap_or(1)
3236}
3237
3238const GEMM_F32: &str = r#"
3246__global__ void __launch_bounds__(256) lk_gemm_f32(const float* __restrict__ x, const float* __restrict__ w,
3247 const float* __restrict__ bias, lk_out y, int M, int N, int K,
3248 int split = 1, float* __restrict__ part = nullptr, int* __restrict__ cnt = nullptr){
3249 __shared__ __align__(16) float Xs[32][64];
3250 __shared__ __align__(16) float Ws[32][64];
3251 __shared__ bool lk_last;
3252 int m0 = blockIdx.y * 64, n0 = blockIdx.x * 64, tid = threadIdx.x, tr = tid >> 4, tn = tid & 15;
3253 int chunks = (K + 31) / 32, run = (chunks + split - 1) / split, k_lo = blockIdx.z * run * 32, k_hi = min(K, k_lo + run * 32);
3254 float acc[4][4];
3255 #pragma unroll
3256 for(int r = 0; r < 4; ++r)
3257 #pragma unroll
3258 for(int c = 0; c < 4; ++c) acc[r][c] = 0.f;
3259 for(int k0 = k_lo; k0 < k_hi; k0 += 32){
3260 for(int c = tid; c < 64 * 8; c += 256){
3261 int r = c >> 3, k = (c & 7) * 4;
3262 float4 v = m0 + r < M && k0 + k < k_hi ? *(const float4*)(x + (long)(m0 + r) * K + k0 + k) : make_float4(0.f, 0.f, 0.f, 0.f);
3263 Xs[k][r] = v.x; Xs[k + 1][r] = v.y; Xs[k + 2][r] = v.z; Xs[k + 3][r] = v.w;
3264 float4 u = n0 + r < N && k0 + k < k_hi ? *(const float4*)(w + (long)(n0 + r) * K + k0 + k) : make_float4(0.f, 0.f, 0.f, 0.f);
3265 Ws[k][r] = u.x; Ws[k + 1][r] = u.y; Ws[k + 2][r] = u.z; Ws[k + 3][r] = u.w;
3266 }
3267 __syncthreads();
3268 #pragma unroll 8
3269 for(int k = 0; k < 32; ++k){
3270 float4 a = *(const float4*)&Xs[k][4 * tr];
3271 float4 b = *(const float4*)&Ws[k][4 * tn];
3272 float ar[4] = {a.x, a.y, a.z, a.w}, br[4] = {b.x, b.y, b.z, b.w};
3273 #pragma unroll
3274 for(int r = 0; r < 4; ++r)
3275 #pragma unroll
3276 for(int c = 0; c < 4; ++c) acc[r][c] = fmaf(ar[r], br[c], acc[r][c]);
3277 }
3278 __syncthreads();
3279 }
3280 if(split > 1){
3281 int tile = blockIdx.y * gridDim.x + blockIdx.x;
3282 float4* mine = (float4*)(part + ((long)(tile * split + blockIdx.z) * 256 + tid) * 16);
3283 #pragma unroll
3284 for(int r = 0; r < 4; ++r) mine[r] = make_float4(acc[r][0], acc[r][1], acc[r][2], acc[r][3]);
3285 __threadfence();
3286 __syncthreads();
3287 if(tid == 0) lk_last = atomicAdd(cnt + tile, 1) == split - 1;
3288 __syncthreads();
3289 if(!lk_last) return;
3290 #pragma unroll
3291 for(int r = 0; r < 4; ++r)
3292 #pragma unroll
3293 for(int c = 0; c < 4; ++c) acc[r][c] = 0.f;
3294 for(int pc = 0; pc < split; ++pc){
3295 const float4* src = (const float4*)(part + ((long)(tile * split + pc) * 256 + tid) * 16);
3296 #pragma unroll
3297 for(int r = 0; r < 4; ++r){
3298 float4 v = __ldcg(src + r);
3299 acc[r][0] += v.x; acc[r][1] += v.y; acc[r][2] += v.z; acc[r][3] += v.w;
3300 }
3301 }
3302 if(tid == 0) cnt[tile] = 0;
3303 }
3304 #pragma unroll
3305 for(int r = 0; r < 4; ++r){
3306 int m = m0 + 4 * tr + r;
3307 if(m >= M) continue;
3308 #pragma unroll
3309 for(int c = 0; c < 4; ++c){
3310 int n = n0 + 4 * tn + c;
3311 if(n < N) y[(long)m * N + n] = bias ? acc[r][c] + bias[n] : acc[r][c];
3312 }
3313 }
3314}
3315"#;
3316
3317const BINARY: &str = r#"
3318template<int OP, int ACT, bool OUTF, int Q>
3319__global__ void lk_binary(const float* __restrict__ a, const float* __restrict__ b, float* __restrict__ c,
3320 signed char* __restrict__ cq, signed char* __restrict__ cl, float* __restrict__ cs, long n,
3321 long nb){
3322 long i = (long)blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
3323 float bv = ACT < 0 ? b[i % nb] : lk_act<ACT>(b[i % nb]);
3324 float v = OP == 0 ? a[i] + bv : a[i] * bv;
3325 if(OUTF) c[i] = v;
3326 if(Q > 0) lk_quant_lane<Q>(v, cq, cl, cs, i, i >> 5);
3327}
3328"#;
3329
3330const SCALE: &str = r#"
3332__global__ void lk_scale(const float* __restrict__ x, float* __restrict__ y, long n, float c){
3333 long i = (long)blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
3334 y[i] = x[i] * c;
3335}
3336"#;
3337
3338const SWIGLU: &str = r#"
3340template<bool OUTF, int Q>
3341__global__ void lk_swiglu(const float* __restrict__ g, const float* __restrict__ u, float* __restrict__ y,
3342 signed char* __restrict__ yq, signed char* __restrict__ yl, float* __restrict__ ys, long n){
3343 long i = (long)blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
3344 float gv = g[i];
3345 float v = gv / (1.0f + expf(-gv)) * u[i];
3346 if(OUTF) y[i] = v;
3347 if(Q > 0) lk_quant_lane<Q>(v, yq, yl, ys, i, i >> 5);
3348}
3349"#;
3350
3351const UNARY: &str = r#"
3353template<int ACT>
3354__global__ void lk_unary(const float* __restrict__ x, float* __restrict__ y, long n){
3355 long i = (long)blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
3356 y[i] = lk_act<ACT>(x[i]);
3357}
3358"#;
3359
3360const ROPE: &str = r#"
3368// One rotation: the arguments that differ between a layer's query and key rotations.
3369struct lk_rope_side { const float* x; const float* nw; lk_out y; const int* yrow; long ypitch; float eps; long xp, xo, yp, yo; int heads, per_row; long yz; };
3370// Head `h` of side `s`. A normed head's block is at least the head wide, so thread `t` takes its value `t` alone and
3371// the threads past the head add nothing: its sum of squares is the same tree whatever the block's width. `L2` reads the
3372// head past the L1, where other blocks of the launch wrote it. With `seqs`, a side's rows are a batch's sequences: row
3373// `r` turns from its own position, `pos[r]`, into its own table (`yz` values on from the last's) at its own row.
3374template<bool NORM, typename A, bool L2>
3375__device__ __forceinline__ void lk_rope_head(const lk_rope_side& s, int h, const float* __restrict__ cs,
3376 const float* __restrict__ sn, const int* __restrict__ pos, int hd, int half,
3377 int seqs, A* red, float* hv){
3378 auto ld = [](const float* p){ return L2 ? __ldcg(p) : *p; };
3379 const int r = h / s.per_row, t = threadIdx.x, rt = blockDim.x;
3380 const float* xh = s.x + (long)h * s.xp + s.xo;
3381 lk_out yh = seqs && s.yrow ? s.y + r * s.yz + (long)(s.yrow[r] - 1) * s.ypitch + (long)(h - r * s.per_row) * s.yp + s.yo
3382 : LK_ROW(s.y, s.yrow, s.ypitch) + (long)h * s.yp + s.yo;
3383 A den = 1;
3384 if(NORM){
3385 A acc = 0;
3386 for(int i = t; i < hd; i += rt) acc += (A)ld(xh + i) * (A)ld(xh + i);
3387 den = sqrt(lk_block_sum(acc, red) / (A)hd + (A)s.eps);
3388 }
3389 for(int i = t; i < hd; i += rt) hv[i] = NORM ? (float)((A)ld(xh + i) / den * (A)s.nw[i]) : ld(xh + i);
3390 __syncthreads();
3391 long row = (pos ? (long)((seqs ? pos[r] : *pos + r) - 1) : (long)r) * half;
3392 for(int i = t; i < half; i += rt){
3393 float c = cs[row + i], sv = sn[row + i], av = hv[i], bv = hv[i + half];
3394 yh[i] = av * c - bv * sv;
3395 yh[i + half] = av * sv + bv * c;
3396 }
3397 for(int i = 2 * half + t; i < hd; i += rt) yh[i] = hv[i];
3398}
3399template<bool NORM, typename A>
3400__global__ void lk_rope(const float* __restrict__ x, const float* __restrict__ nw, const float* __restrict__ cs,
3401 const float* __restrict__ sn, const int* __restrict__ pos, lk_out y,
3402 const int* __restrict__ yrow, long ypitch, int hd, int half, int per_row, float eps,
3403 long xp, long xo, long yp, long yo){
3404 __shared__ A red[32];
3405 __shared__ float hv[1024];
3406 const lk_rope_side s{x, nw, y, yrow, ypitch, eps, xp, xo, yp, yo, (int)gridDim.x, per_row, 0};
3407 lk_rope_head<NORM, A, false>(s, (int)blockIdx.x, cs, sn, pos, hd, half, 0, red, hv);
3408}
3409// Two rotations by one table at one position in one launch, `a`'s heads then `b`'s, each head as `lk_rope<NORM, A>`
3410// computes it.
3411template<bool NORM, typename A>
3412__global__ void lk_rope_parts(lk_rope_side a, lk_rope_side b, const float* __restrict__ cs, const float* __restrict__ sn,
3413 const int* __restrict__ pos, int hd, int half, int seqs = 0){
3414 __shared__ A red[32];
3415 __shared__ float hv[1024];
3416 // Each side by its own call, as `lk_gemv_parts_rope` turns them: a side chosen at run time puts the by-value
3417 // `lk_rope_side` in a local frame.
3418 if((int)blockIdx.x < a.heads) lk_rope_head<NORM, A, false>(a, (int)blockIdx.x, cs, sn, pos, hd, half, seqs, red, hv);
3419 else lk_rope_head<NORM, A, false>(b, (int)blockIdx.x - a.heads, cs, sn, pos, hd, half, seqs, red, hv);
3420}
3421"#;
3422
3423const GEMV_PARTS_ROPE: &str = r#"
3430template<int F, int Q, int M, int W, bool NORM, typename A>
3431__global__ void __launch_bounds__(1024) lk_gemv_parts_rope(const float* __restrict__ x, const signed char* __restrict__ xq,
3432 const signed char* __restrict__ xl, const float* __restrict__ xs, int K, int N,
3433 lk_parts parts, lk_rope_side a, lk_rope_side b, int pa, int pb,
3434 const float* __restrict__ cs, const float* __restrict__ sn, const int* __restrict__ pos,
3435 int hd, int half, int* __restrict__ cnt){
3436 __shared__ A red[32];
3437 __shared__ float hv[1024];
3438 __shared__ int done;
3439 const long rows = (long)(blockDim.x >> 5) * W, first = (long)blockIdx.x * rows, last = min(first + rows, (long)N);
3440 const long o = first + (long)(threadIdx.x >> 5) * W;
3441 if(o < N){
3442 int p = 0;
3443 while(p < LK_PARTS - 1 && o >= parts.end[p]) ++p;
3444 long start = p ? parts.end[p - 1] : 0, r = o - start;
3445 long n = parts.end[p] - start;
3446 float d[W * M];
3447 lk_gemv_dot<F, Q, M, W>(parts.w[p], r, K, x, xq, xl, xs, d);
3448 if((threadIdx.x & 31) == 0){
3449 lk_out yo = LK_ROW(lk_part_out(parts, p), parts.row[p], parts.pitch[p]);
3450 const float* b = parts.bias[p];
3451 #pragma unroll
3452 for(int i = 0; i < W; ++i)
3453 #pragma unroll
3454 for(int t = 0; t < M; ++t) yo[(long)t * n + r + i] = b ? d[i * M + t] + b[r + i] : d[i * M + t];
3455 }
3456 }
3457 __threadfence();
3458 __syncthreads();
3459 for(int side = 0; side < 2; ++side){
3460 const int pp = side ? pb : pa;
3461 if(pp < 0) continue;
3462 const long start = pp ? parts.end[pp - 1] : 0, lo = max(first, start), hi = min(last, (long)parts.end[pp]);
3463 for(long at = lo; at < hi;){
3464 const int h = (int)((at - start) / hd);
3465 const long to = min(hi, start + (long)(h + 1) * hd);
3466 const int head = (side ? a.heads : 0) + h, took = (int)(to - at);
3467 if(threadIdx.x == 0) done = atomicAdd(cnt + head, took) + took == hd;
3468 __syncthreads();
3469 if(done){
3470 __threadfence();
3471 // Each side by its own call: a side chosen at run time puts the by-value `lk_rope_side` in a local frame,
3472 // and every field the head's loops read from it goes to local memory.
3473 if(side) lk_rope_head<NORM, A, true>(b, h, cs, sn, pos, hd, half, 0, red, hv);
3474 else lk_rope_head<NORM, A, true>(a, h, cs, sn, pos, hd, half, 0, red, hv);
3475 if(threadIdx.x == 0) cnt[head] = 0;
3476 }
3477 __syncthreads();
3478 at = to;
3479 }
3480 }
3481}
3482"#;
3483
3484const SET_ROW: &str = r#"
3487__global__ void lk_set_row(lk_out t, const int* __restrict__ idx, int fixed, const float* __restrict__ src,
3488 long n, int cols, long tz = 0){
3489 long c = (long)blockIdx.x * blockDim.x + threadIdx.x; if(c >= n) return;
3490 // A batch's rows (`tz` > 0): row `r` of `src` into its own table, `tz` values on from the last's, at its own index.
3491 if(tz){ long r = c / cols; t[r * tz + (long)(idx[r] - 1) * cols + c % cols] = src[c]; return; }
3492 t[(long)((idx ? *idx : fixed) - 1) * cols + c] = src[c];
3493}
3494// The rows' quantized form, as `lk_set_row` sets their values: each value's words — its low word too under two words —
3495// and each block's scale, from its first value. Rows of whole 32-value blocks, so a block's place is its own.
3496__global__ void lk_set_row_q(signed char* __restrict__ tq, signed char* __restrict__ tl, float* __restrict__ ts,
3497 const int* __restrict__ idx, int fixed, const signed char* __restrict__ sq,
3498 const signed char* __restrict__ sl, const float* __restrict__ ss, long n, int cols, long tz = 0){
3499 long c = (long)blockIdx.x * blockDim.x + threadIdx.x; if(c >= n) return;
3500 long d = tz ? (c / cols) * tz + (long)(idx[c / cols] - 1) * cols + c % cols : (long)((idx ? *idx : fixed) - 1) * cols + c;
3501 tq[d] = sq[c];
3502 if(tl) tl[d] = sl[c];
3503 if((c & 31) == 0) ts[d >> 5] = ss[c >> 5];
3504}
3505"#;
3506
3507const ATTENTION: &str = r#"
3526#define LK_TILE 64
3527// The attention's sums in the accumulator type `A`: `float`, or `double` for the precise numerics.
3528template<typename A> struct lk_pair { A x, y; };
3529__device__ __forceinline__ float lk_fma(float a, float b, float c){ return __fmaf_rn(a, b, c); }
3530__device__ __forceinline__ double lk_fma(double a, double b, double c){ return __fma_rn(a, b, c); }
3531__device__ __forceinline__ float lk_add(float a, float b){ return __fadd_rn(a, b); }
3532__device__ __forceinline__ double lk_add(double a, double b){ return __dadd_rn(a, b); }
3533__device__ __forceinline__ float lk_exp(float v){ return expf(v); }
3534__device__ __forceinline__ double lk_exp(double v){ return exp(v); }
3535__device__ __forceinline__ float lk_wsum(float v){ return lk_warp_sum(v); }
3536__device__ __forceinline__ double lk_wsum(double v){ return lk_warp_sum_d(v); }
3537__device__ __forceinline__ float lk_wmax(float v){ return lk_warp_max(v); }
3538__device__ __forceinline__ double lk_wmax(double v){
3539 for(int o = 16; o > 0; o >>= 1) v = fmax(v, __shfl_down_sync(0xffffffffu, v, o));
3540 return __shfl_sync(0xffffffffu, v, 0);
3541}
3542// The `V` values (a power of two) each lane holds, each summed over a run of `D·2` lanes as `lk_wsum` sums a value over
3543// the warp — the lanes paired across `D`, then `D/2`, … then 1: while a lane holds more than one value it keeps one half
3544// (the upper where its bit `D` is set) and sends the other, then the one left is summed with its partner's. Value
3545// `base + i` lands at `v[i]`, `base` the halves a lane kept.
3546template<int D, int V, typename A, int N>
3547__device__ __forceinline__ void lk_tree_halves(A (&v)[N], int lane, int& base){
3548 if constexpr(D >= 1){
3549 if constexpr(V > 1){
3550 const bool up = lane & D;
3551 #pragma unroll
3552 for(int i = 0; i < V / 2; ++i){
3553 const A send = up ? v[i] : v[i + V / 2], keep = up ? v[i + V / 2] : v[i];
3554 v[i] = keep + __shfl_xor_sync(0xffffffffu, send, D);
3555 }
3556 if(up) base += V / 2;
3557 lk_tree_halves<D / 2, V / 2>(v, lane, base);
3558 } else {
3559 v[0] = v[0] + __shfl_xor_sync(0xffffffffu, v[0], D);
3560 lk_tree_halves<D / 2, 1>(v, lane, base);
3561 }
3562 }
3563}
3564template<int V> __host__ __device__ constexpr int lk_pow2_at_least(){ int p = 1; while(p < V) p *= 2; return p; }
3565// A tile's `n` keys scored for a block's `G` heads, run `R` lanes a key: a warp `KB` keys a run at once, `32 / R` runs a
3566// warp. A lane sums its share of the head (fours `lane mod R`, `+ R`, …), the run's lanes past the head zeros; the lanes
3567// past the run add as zeros, then each run sums its keys' dots as `lk_tree_halves` sums them — the operations
3568// `lk_wsum` takes over a key's dot with the head in lanes `0 … R − 1`. Key `j` reads cache row `row(j)` (−1: none); its
3569// head `g`'s sum goes to `emit(j, g, s)`, once. Which of a run's keys read a row is a mask (`ok`): the keys a lane
3570// emits are known only at run time, and an array indexed by them would live in local memory.
3571template<int G, typename A, int R, typename KE, typename Row, typename Emit>
3572__device__ __forceinline__ void lk_attn_scores_in(const float* qs, const KE* __restrict__ kc, int n, int hd, int kvw, long koff,
3573 Row row, Emit emit){
3574 constexpr int KB = G <= 2 ? 4 : (G <= 4 ? 2 : 1), S = KB * (32 / R), NV = KB * G, V = lk_pow2_at_least<NV>();
3575 constexpr int H = V < R ? V : R;
3576 const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5, h4 = hd >> 2;
3577 const int run = lane / R, at = lane % R;
3578 const float4* q4 = (const float4*)qs;
3579 for(int i = warp; i < n; i += nw * S){
3580 int rw[KB], ok = 0;
3581 #pragma unroll
3582 for(int b = 0; b < KB; ++b){ rw[b] = row(i + (run * KB + b) * nw); ok |= (rw[b] >= 0) << b; }
3583 A d[V];
3584 #pragma unroll
3585 for(int v = 0; v < V; ++v) d[v] = 0;
3586 for(int e = at; e < h4; e += R){
3587 float4 k[KB];
3588 #pragma unroll
3589 for(int b = 0; b < KB; ++b) k[b] = rw[b] >= 0 ? lk_from4(kc + (long)rw[b] * kvw + koff + 4 * e) : make_float4(0.f, 0.f, 0.f, 0.f);
3590 #pragma unroll
3591 for(int g = 0; g < G; ++g){
3592 float4 a = q4[g * h4 + e];
3593 #pragma unroll
3594 for(int b = 0; b < KB; ++b){
3595 A& s = d[b * G + g];
3596 s = lk_fma((A)a.x, (A)k[b].x, s); s = lk_fma((A)a.y, (A)k[b].y, s);
3597 s = lk_fma((A)a.z, (A)k[b].z, s); s = lk_fma((A)a.w, (A)k[b].w, s);
3598 }
3599 }
3600 }
3601 if constexpr(R < 32){
3602 #pragma unroll
3603 for(int v = 0; v < V; ++v) d[v] = lk_add(d[v], (A)0);
3604 }
3605 int base = 0;
3606 lk_tree_halves<R / 2, V>(d, lane, base);
3607 // A run's lanes below the last distance halved at hold every value; the first of them emits it.
3608 constexpr int LAST = R / H;
3609 if(at % LAST == 0){
3610 #pragma unroll
3611 for(int v = 0; v < V / H; ++v){
3612 const int idx = base + v;
3613 if(idx < NV){
3614 const int b = idx / G, g = idx - b * G;
3615 if(ok >> b & 1) emit(i + (run * KB + b) * nw, g, d[v]);
3616 }
3617 }
3618 }
3619 }
3620}
3621// The lanes a key's dot takes (`lk_attn_scores_in`'s `R`) for a head of `hd`: a half warp for a head of 9 to 16 fours
3622// — two keys a run at once — and the whole warp otherwise. A launch takes it once (`lk_attention`), so each run's body
3623// keeps its registers.
3624__host__ __device__ constexpr int lk_attn_run_width(int hd){ return hd / 4 > 8 && hd / 4 <= 16 ? 16 : 32; }
3625template<int G, typename A, typename KE, typename Row, typename Emit>
3626__device__ __forceinline__ void lk_attn_scores(const float* qs, const KE* __restrict__ kc, int n, int hd, int kvw, long koff,
3627 Row row, Emit emit){
3628 if(lk_attn_run_width(hd) == 16) lk_attn_scores_in<G, A, 16>(qs, kc, n, hd, kvw, koff, row, emit);
3629 else lk_attn_scores_in<G, A, 32>(qs, kc, n, hd, kvw, koff, row, emit);
3630}
3631// One chunk `[j0, j1)` of a block's `G` heads: each head's running maximum `mx` and sum `sum`, and with `VALUES` each
3632// thread's value slots weighed in `acc`.
3633template<int G, int VPT, typename A, bool VALUES, int R, typename KE, typename VE>
3634__device__ __forceinline__ void lk_attn_chunk(const float* qs, A* sc, A* mx, A* sum, A* alpha,
3635 const KE* __restrict__ kc, const VE* __restrict__ vc, int j0, int j1,
3636 int hd, int vd, int kvw, int vw, long koff, long vo, A scale,
3637 lk_pair<A> (&acc)[G * VPT]){
3638 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5;
3639 if(threadIdx.x < G){ mx[threadIdx.x] = -INFINITY; sum[threadIdx.x] = 0; }
3640 #pragma unroll
3641 for(int k = 0; k < G * VPT; ++k) acc[k] = lk_pair<A>{0, 0};
3642 __syncthreads();
3643 for(int jt = j0; jt < j1; jt += LK_TILE){
3644 int nt = min(LK_TILE, j1 - jt);
3645 lk_attn_scores_in<G, A, R>(qs, kc, nt, hd, kvw, koff, [&](int j){ return j < nt ? jt + j : -1; },
3646 [&](int j, int g, A s){ sc[g * LK_TILE + j] = s * scale; });
3647 __syncthreads();
3648 for(int g = warp; g < G; g += nw){
3649 A m = mx[g];
3650 for(int j = lane; j < nt; j += 32) m = fmax(m, sc[g * LK_TILE + j]);
3651 m = lk_wmax(m);
3652 A s = 0;
3653 for(int j = lane; j < nt; j += 32){ A p = lk_exp(sc[g * LK_TILE + j] - m); sc[g * LK_TILE + j] = p; s += p; }
3654 s = lk_wsum(s);
3655 if(lane == 0){ A a = lk_exp(mx[g] - m); alpha[g] = a; sum[g] = lk_fma(sum[g], a, s); mx[g] = m; }
3656 }
3657 __syncthreads();
3658 if(VALUES){
3659 const int ph = vd >> 1;
3660 #pragma unroll
3661 for(int k = 0; k < G * VPT; ++k){
3662 const int s = threadIdx.x + k * blockDim.x;
3663 if(s < G * ph){
3664 const int g = s / ph, e = s - g * ph;
3665 const A* w = sc + g * LK_TILE;
3666 lk_pair<A> a{acc[k].x * alpha[g], acc[k].y * alpha[g]};
3667 #pragma unroll 8
3668 for(int j = 0; j < nt; ++j){
3669 float2 v = lk_from2(vc + (long)(jt + j) * vw + vo + 2 * e);
3670 a.x = lk_fma(w[j], (A)v.x, a.x); a.y = lk_fma(w[j], (A)v.y, a.y);
3671 }
3672 acc[k] = a;
3673 }
3674 }
3675 }
3676 __syncthreads();
3677 }
3678}
3679// Chunk `u`'s heads' maxima and sums (thread `g` head `g`'s, the thread that resets them for a next chunk) and weighed
3680// values (each thread its value slots) into the block's partials `p0` — head `g`'s chunk `u` at `(g·splits + u)·stride`
3681// — as every form folds them.
3682template<int G, int VPT, typename A>
3683__device__ __forceinline__ void lk_attn_keep(A* __restrict__ p0, const A* mx, const A* sum, const lk_pair<A> (&acc)[G * VPT],
3684 int u, int splits, long stride, int vd){
3685 const int ph = vd >> 1;
3686 if(threadIdx.x < G){ A* p = p0 + (threadIdx.x * splits + u) * stride; p[0] = mx[threadIdx.x]; p[1] = sum[threadIdx.x]; }
3687 #pragma unroll
3688 for(int k = 0; k < G * VPT; ++k){
3689 const int s = threadIdx.x + k * blockDim.x;
3690 if(s < G * ph){
3691 const int g = s / ph, e = s - g * ph;
3692 A* p = p0 + (g * splits + u) * stride;
3693 p[2 + 2 * e] = acc[k].x; p[3 + 2 * e] = acc[k].y;
3694 }
3695 }
3696}
3697// Chunks `u0 … u0 + w − 1` of a row's span `[lo, end)`, cut every `chunk` positions, weighed side by side: step `t0`
3698// takes tile `t0` of every chunk at once — its keys scored, each (head, chunk)'s running maximum and sum taken over it,
3699// its values weighed — each chunk in exactly the operations `lk_attn_chunk` takes it in alone. Head `g`'s chunk `u`
3700// keeps its maximum, sum and last factor at `g·splits + u` of `mx`, `sum` and `alpha`, and its weighed values in the
3701// partials `p0` as `lk_attn_keep` keeps them; a thread's value slots run over the chunks, slot `s` chunk
3702// `s / (G·vd/2)`'s, so `w` chunks' slots fill at most the `G·VPT` a thread holds.
3703template<int G, int VPT, int R, typename A, typename KE, typename VE>
3704__device__ __forceinline__ void lk_attn_side(const float* qs, A* sc, A* mx, A* sum, A* alpha,
3705 const KE* __restrict__ kc, const VE* __restrict__ vc, int lo, int chunk,
3706 int end, int u0, int w, int splits, int hd, int vd, int kvw, int vw,
3707 long koff, long vo, A scale, A* __restrict__ p0, long stride){
3708 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5;
3709 const int ph = vd >> 1, slots = G * ph;
3710 // Chunk `u`'s first position and its keys in the tile from `t0`.
3711 auto first = [&](int u){ return min(lo + u * chunk, end); };
3712 auto in_tile = [&](int u, int t0){ int j0 = first(u); return max(0, min(LK_TILE, min(j0 + chunk, end) - j0 - t0)); };
3713 for(int i = threadIdx.x; i < G * w; i += blockDim.x){ int at = (i % G) * splits + u0 + i / G; mx[at] = -INFINITY; sum[at] = 0; }
3714 lk_pair<A> acc[G * VPT];
3715 #pragma unroll
3716 for(int k = 0; k < G * VPT; ++k) acc[k] = lk_pair<A>{0, 0};
3717 __syncthreads();
3718 const int longest = min(first(u0) + chunk, end) - first(u0);
3719 for(int t0 = 0; t0 < longest; t0 += LK_TILE){
3720 // The step's keys of every chunk, `full` a chunk (the last's maybe fewer), each scored as `lk_attn_chunk` scores it.
3721 const int full = min(LK_TILE, longest - t0);
3722 lk_attn_scores_in<G, A, R>(qs, kc, w * full, hd, kvw, koff,
3723 [&](int idx){ int ul = idx / full, j = idx - ul * full; return ul < w && j < in_tile(u0 + ul, t0) ? first(u0 + ul) + t0 + j : -1; },
3724 [&](int idx, int g, A s){ int ul = idx / full; sc[(ul * G + g) * LK_TILE + idx - ul * full] = s * scale; });
3725 __syncthreads();
3726 // Each (head, chunk)'s running maximum and sum over the step, a warp a pair.
3727 for(int pi = warp; pi < G * w; pi += nw){
3728 int g = pi % G, ul = pi / G, nt = in_tile(u0 + ul, t0), at = g * splits + u0 + ul;
3729 if(nt == 0) continue;
3730 A* srow = sc + (ul * G + g) * LK_TILE;
3731 A m = mx[at];
3732 for(int j = lane; j < nt; j += 32) m = fmax(m, srow[j]);
3733 m = lk_wmax(m);
3734 A s = 0;
3735 for(int j = lane; j < nt; j += 32){ A p = lk_exp(srow[j] - m); srow[j] = p; s += p; }
3736 s = lk_wsum(s);
3737 if(lane == 0){ A a = lk_exp(mx[at] - m); alpha[at] = a; sum[at] = lk_fma(sum[at], a, s); mx[at] = m; }
3738 }
3739 __syncthreads();
3740 // Every chunk's values weighed in, a thread its slots over the chunks.
3741 #pragma unroll
3742 for(int k = 0; k < G * VPT; ++k){
3743 const int s = threadIdx.x + k * blockDim.x;
3744 if(s < w * slots){
3745 const int ul = s / slots, rest = s - ul * slots, g = rest / ph, e = rest - g * ph, nt = in_tile(u0 + ul, t0);
3746 if(nt > 0){
3747 const A* wt = sc + (ul * G + g) * LK_TILE;
3748 const A al = alpha[g * splits + u0 + ul];
3749 const long j0 = first(u0 + ul) + t0;
3750 lk_pair<A> a{acc[k].x * al, acc[k].y * al};
3751 #pragma unroll 8
3752 for(int j = 0; j < nt; ++j){
3753 float2 v = lk_from2(vc + (j0 + j) * vw + vo + 2 * e);
3754 a.x = lk_fma(wt[j], (A)v.x, a.x); a.y = lk_fma(wt[j], (A)v.y, a.y);
3755 }
3756 acc[k] = a;
3757 }
3758 }
3759 }
3760 __syncthreads();
3761 }
3762 #pragma unroll
3763 for(int k = 0; k < G * VPT; ++k){
3764 const int s = threadIdx.x + k * blockDim.x;
3765 if(s < w * slots){
3766 const int ul = s / slots, rest = s - ul * slots, g = rest / ph, e = rest - g * ph;
3767 A* p = p0 + (g * splits + u0 + ul) * stride;
3768 p[2 + 2 * e] = acc[k].x; p[3 + 2 * e] = acc[k].y;
3769 }
3770 }
3771}
3772// A pair of a chunk's weighed values, read past the cache from the partials the other blocks wrote.
3773__device__ __forceinline__ lk_pair<float> lk_part_pair(const float* p){ float2 v = __ldcg((const float2*)p); return {v.x, v.y}; }
3774__device__ __forceinline__ lk_pair<double> lk_part_pair(const double* p){ double2 v = __ldcg((const double2*)p); return {v.x, v.y}; }
3775// Head `g`'s maximum (with its sink) and denominator over its `pe` chunks' maxima `ms` and sums `ls`, and each chunk's
3776// factor `ff` in the fold — a function of the chunks alone, the same wherever the chunks ran.
3777template<typename A>
3778__device__ __forceinline__ void lk_attn_fold(const A* ms, const A* ls, A* ff, A* fm, A* fl,
3779 const float* __restrict__ sinks, int h, int g, int pe, int splits){
3780 A sk = sinks ? (A)sinks[h] : (A)-INFINITY;
3781 A m = sk;
3782 for(int u = 0; u < pe; ++u) m = fmax(m, ms[g * splits + u]);
3783 A l = sinks ? lk_exp(sk - m) : (A)0;
3784 for(int u = 0; u < pe; ++u){
3785 A mu = ms[g * splits + u], f = mu > -INFINITY ? lk_exp(mu - m) : (A)0;
3786 ff[g * splits + u] = f;
3787 if(mu > -INFINITY) l = lk_fma(ls[g * splits + u], f, l);
3788 }
3789 fm[g] = m; fl[g] = l;
3790}
3791// The fold of a block's `G` heads' weighed values over their `pe` chunks' partials `p0` (as `lk_attn_keep` keeps them,
3792// in shared memory under `SHARED`, else read past the cache from what the other blocks wrote): each thread folds the
3793// value slots it weighed, chunk by chunk in order. Each head's values divided by its denominator `fl` into `out`.
3794template<int G, int VPT, typename A, bool SHARED = false, int QO = 0>
3795__device__ __forceinline__ void lk_attn_fold_values(const A* __restrict__ p0, const A* ms, const A* ff, const A* fl,
3796 float* __restrict__ out, long head0, int pe, int splits, long stride, int vd,
3797 lk_qout qo = {}){
3798 const int ph = vd >> 1;
3799 lk_pair<A> a[G * VPT];
3800 #pragma unroll
3801 for(int k = 0; k < G * VPT; ++k) a[k] = lk_pair<A>{0, 0};
3802 #pragma unroll 2
3803 for(int u = 0; u < pe; ++u){
3804 #pragma unroll
3805 for(int k = 0; k < G * VPT; ++k){
3806 const int s = threadIdx.x + k * blockDim.x;
3807 if(s < G * ph){
3808 const int g = s / ph, e = s - g * ph;
3809 if(ms[g * splits + u] > -INFINITY){
3810 const A f = ff[g * splits + u];
3811 const A* p = p0 + (g * splits + u) * stride + 2 + 2 * e;
3812 const lk_pair<A> v = SHARED ? lk_pair<A>{p[0], p[1]} : lk_part_pair(p);
3813 a[k].x = lk_fma(v.x, f, a[k].x); a[k].y = lk_fma(v.y, f, a[k].y);
3814 }
3815 }
3816 }
3817 }
3818 #pragma unroll
3819 for(int k = 0; k < G * VPT; ++k){
3820 const int s = threadIdx.x + k * blockDim.x;
3821 const bool on = s < G * ph;
3822 const int g = on ? s / ph : 0, e = s - g * ph;
3823 lk_attn_store<QO>(out, qo, (head0 + g) * vd + 2 * e, on, on ? (float)(a[k].x / fl[g]) : 0.f, on ? (float)(a[k].y / fl[g]) : 0.f);
3824 }
3825}
3826// The path an attention launch takes past a row of one chunk (`lk_attention`'s `PATH`), fixed where it launches: its
3827// chunks' partials folded by the last split block to arrive, held side by side by the one block of the row, or weighed
3828// one at a time by it; a launch of one chunk a row takes none. `LK_ATTN_ANY` takes whichever its grid and its
3829// arguments name.
3830enum lk_attn_path { LK_ATTN_ANY, LK_ATTN_SPLIT, LK_ATTN_HELD, LK_ATTN_CHUNKS, LK_ATTN_ONE };
3831// The attention's body at a key's run of `R` lanes (`lk_attn_run_width`), on path `PATH`, with the block's heads'
3832// running maxima, sums, factors and folded maxima and denominators, and the flag of the last split block to arrive.
3833template<int R, int G, int VPT, typename A, typename KE, typename VE, int QO, int PATH>
3834__device__ __forceinline__ void lk_attention_run(const float* __restrict__ q, const KE* __restrict__ kc,
3835 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
3836 A* __restrict__ part, int* __restrict__ cnt, int len, int win, int hd, int vd, int group, int heads, int kvw,
3837 int vw, int voff, float scale, int minc, int splits, long kz, int held, A* mx, A* sum, A* alpha, A* fm, A* fl,
3838 int& last, lk_qout qo){
3839 extern __shared__ __align__(16) unsigned char lk_smem[];
3840 float* qs = (float*)lk_smem;
3841 A* sc = (A*)(lk_smem + ((G * hd * 4 + 15) & ~15));
3842 A* ms = sc + G * LK_TILE * max(held, 1);
3843 A* ls = ms + G * splits;
3844 A* ff = ls + G * splits;
3845 A* hp = ff + G * splits;
3846 int hb = blockIdx.x, r = blockIdx.y, t = blockIdx.z;
3847 int h0 = hb * G, kvh = h0 / group;
3848 // A batch's rows (`kz` > 0) are its sequences: row `r` attends its own caches, `kz` values on from the last's, up
3849 // to its own position; a chunk's rows share the caches, at consecutive positions.
3850 if(kz){ kc += r * kz; vc += r * kz; }
3851 int end = pos ? (kz ? pos[r] : *pos + r) : len;
3852 int lo = win > 0 && end > win ? end - win : 0, n = end - lo;
3853 int pe = max(1, min(splits, (n + minc - 1) / minc)), chunk = (n + pe - 1) / pe;
3854 if(t >= pe) return;
3855 const float* qb = q + ((long)r * heads + h0) * hd;
3856 for(int i = threadIdx.x; i < G * hd; i += blockDim.x) qs[i] = qb[i];
3857 long koff = (long)kvh * hd, vo = voff + (long)kvh * vd, head0 = (long)r * heads + h0;
3858 const int ph = vd >> 1;
3859 lk_pair<A> acc[G * VPT];
3860 if(PATH == LK_ATTN_ONE || pe == 1){
3861 lk_attn_chunk<G, VPT, A, true, R>(qs, sc, mx, sum, alpha, kc, vc, lo, end, hd, vd, kvw, vw, koff, vo, (A)scale, acc);
3862 #pragma unroll
3863 for(int k = 0; k < G * VPT; ++k){
3864 const int s = threadIdx.x + k * blockDim.x;
3865 const bool on = s < G * ph;
3866 const int g = on ? s / ph : 0, e = s - g * ph;
3867 float x = 0.f, y = 0.f;
3868 if(on){
3869 A sk = sinks ? (A)sinks[h0 + g] : (A)-INFINITY;
3870 A m = fmax(sk, mx[g]), f = lk_exp(mx[g] - m);
3871 A l = lk_fma(sum[g], f, sinks ? lk_exp(sk - m) : (A)0);
3872 x = (float)(acc[k].x * f / l);
3873 y = (float)(acc[k].y * f / l);
3874 }
3875 lk_attn_store<QO>(out, qo, (head0 + g) * vd + 2 * e, on, x, y);
3876 }
3877 return;
3878 }
3879 long stride = (long)vd + 2;
3880 if(PATH == LK_ATTN_ANY ? gridDim.z > 1 : PATH == LK_ATTN_SPLIT){
3881 int j0 = min(lo + t * chunk, end), j1 = min(j0 + chunk, end);
3882 lk_attn_chunk<G, VPT, A, true, R>(qs, sc, mx, sum, alpha, kc, vc, j0, j1, hd, vd, kvw, vw, koff, vo, (A)scale, acc);
3883 lk_attn_keep<G, VPT>(part + head0 * splits * stride, mx, sum, acc, t, splits, stride, vd);
3884 __threadfence();
3885 __syncthreads();
3886 int* c = cnt + (long)r * gridDim.x + hb;
3887 if(threadIdx.x == 0) last = atomicAdd(c, 1) == pe - 1;
3888 __syncthreads();
3889 if(!last) return;
3890 __threadfence();
3891 for(int i = threadIdx.x; i < G * pe; i += blockDim.x){
3892 int g = i / pe, u = i - g * pe;
3893 const A* p = part + ((head0 + g) * splits + u) * stride;
3894 ms[g * splits + u] = __ldcg(p); ls[g * splits + u] = __ldcg(p + 1);
3895 }
3896 __syncthreads();
3897 if(threadIdx.x < G) lk_attn_fold(ms, ls, ff, fm, fl, sinks, h0 + threadIdx.x, threadIdx.x, pe, splits);
3898 __syncthreads();
3899 lk_attn_fold_values<G, VPT, A, false, QO>(part + head0 * splits * stride, ms, ff, fl, out, head0, pe, splits, stride, vd, qo);
3900 if(threadIdx.x == 0) *c = 0;
3901 return;
3902 }
3903 // One block takes every chunk of its row, in the arithmetic the split blocks use. With room for their partials
3904 // (`held`, after the fold's factors), each chunk once, `held` side by side, kept as a split block keeps it, then
3905 // folded alike; otherwise each chunk's maximum and sum first, then each chunk's values weighed in, chunk by chunk.
3906 if(PATH == LK_ATTN_ANY ? held != 0 : PATH == LK_ATTN_HELD){
3907 for(int u0 = 0; u0 < pe; u0 += held)
3908 lk_attn_side<G, VPT, R>(qs, sc, ms, ls, ff, kc, vc, lo, chunk, end, u0, min(held, pe - u0), splits, hd, vd, kvw, vw, koff, vo,
3909 (A)scale, hp, stride);
3910 __syncthreads();
3911 if(threadIdx.x < G) lk_attn_fold(ms, ls, ff, fm, fl, sinks, h0 + threadIdx.x, threadIdx.x, pe, splits);
3912 __syncthreads();
3913 lk_attn_fold_values<G, VPT, A, true, QO>(hp, ms, ff, fl, out, head0, pe, splits, stride, vd, qo);
3914 return;
3915 }
3916 for(int u = 0; u < pe; ++u){
3917 int j0 = min(lo + u * chunk, end), j1 = min(j0 + chunk, end);
3918 lk_attn_chunk<G, VPT, A, false, R>(qs, sc, mx, sum, alpha, kc, vc, j0, j1, hd, vd, kvw, vw, koff, vo, (A)scale, acc);
3919 if(threadIdx.x < G){ ms[threadIdx.x * splits + u] = mx[threadIdx.x]; ls[threadIdx.x * splits + u] = sum[threadIdx.x]; }
3920 }
3921 __syncthreads();
3922 if(threadIdx.x < G) lk_attn_fold(ms, ls, ff, fm, fl, sinks, h0 + threadIdx.x, threadIdx.x, pe, splits);
3923 lk_pair<A> a[G * VPT];
3924 #pragma unroll
3925 for(int k = 0; k < G * VPT; ++k) a[k] = lk_pair<A>{0, 0};
3926 for(int u = 0; u < pe; ++u){
3927 int j0 = min(lo + u * chunk, end), j1 = min(j0 + chunk, end);
3928 lk_attn_chunk<G, VPT, A, true, R>(qs, sc, mx, sum, alpha, kc, vc, j0, j1, hd, vd, kvw, vw, koff, vo, (A)scale, acc);
3929 #pragma unroll
3930 for(int k = 0; k < G * VPT; ++k){
3931 const int s = threadIdx.x + k * blockDim.x;
3932 if(s < G * ph){
3933 const int g = s / ph;
3934 if(ms[g * splits + u] > -INFINITY){
3935 a[k].x = lk_fma(acc[k].x, ff[g * splits + u], a[k].x);
3936 a[k].y = lk_fma(acc[k].y, ff[g * splits + u], a[k].y);
3937 }
3938 }
3939 }
3940 }
3941 #pragma unroll
3942 for(int k = 0; k < G * VPT; ++k){
3943 const int s = threadIdx.x + k * blockDim.x;
3944 const bool on = s < G * ph;
3945 const int g = on ? s / ph : 0, e = s - g * ph;
3946 lk_attn_store<QO>(out, qo, (head0 + g) * vd + 2 * e, on, on ? (float)(a[k].x / fl[g]) : 0.f, on ? (float)(a[k].y / fl[g]) : 0.f);
3947 }
3948}
3949template<int G, int VPT, typename A, int MB = 2, typename KE = float, typename VE = float, int QO = 0, int PATH = LK_ATTN_ANY,
3950 int RW = 0>
3951__global__ void __launch_bounds__(256, MB) lk_attention(const float* __restrict__ q, const KE* __restrict__ kc,
3952 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
3953 A* __restrict__ part, int* __restrict__ cnt, int len, int win, int hd, int vd, int group, int heads, int kvw,
3954 int vw, int voff, float scale, int minc, int splits, long kz = 0, int held = 0, lk_qout qo = {}){
3955 __shared__ A mx[G], sum[G], alpha[G], fm[G], fl[G];
3956 __shared__ int last;
3957 if(RW == 0 ? lk_attn_run_width(hd) == 16 : RW == 16)
3958 lk_attention_run<16, G, VPT, A, KE, VE, QO, PATH>(q, kc, vc, pos, sinks, out, part, cnt, len, win, hd, vd, group, heads, kvw, vw, voff,
3959 scale, minc, splits, kz, held, mx, sum, alpha, fm, fl, last, qo);
3960 else
3961 lk_attention_run<32, G, VPT, A, KE, VE, QO, PATH>(q, kc, vc, pos, sinks, out, part, cnt, len, win, hd, vd, group, heads, kvw, vw, voff,
3962 scale, minc, splits, kz, held, mx, sum, alpha, fm, fl, last, qo);
3963}
3964"#;
3965
3966const ATTENTION_SHARED: &str = r#"
3980#define LK_ST 16
3981// The chunks a row's `n` positions are cut into: at most `splits`, each at least `minc` positions, every one but the
3982// last `chunk` positions — a whole number of `LK_ST`-row steps.
3983__device__ __forceinline__ int lk_shared_chunks(int n, int splits, int minc, int& chunk){
3984 int pe = max(1, min(splits, (n + minc - 1) / minc));
3985 chunk = ((n + pe - 1) / pe + LK_ST - 1) / LK_ST * LK_ST;
3986 return max(1, (n + chunk - 1) / chunk);
3987}
3988template<int G, int VPT, int H4, bool VK, typename KE = float, typename VE = float, int QO = 0>
3989__global__ void __launch_bounds__(256) lk_attention_shared(const float* __restrict__ q, const KE* __restrict__ kc,
3990 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
3991 float* __restrict__ part, int len, int win, int vd, int heads, int kvw, int vw, int voff, float scale, int minc,
3992 int splits, long kz = 0, lk_qout qo = {}){
3993 constexpr int HD = 4 * H4, HQ = G <= 4 ? 1 : (G <= 8 ? 2 : 4), GP = 4 * HQ, TN = LK_ST * HQ / 8, NV = 4 * TN;
3994 constexpr int M2 = (2 * H4 + 31) / 32, RF = (LK_ST * H4 + 255) / 256;
3995 extern __shared__ __align__(16) float lk_ss[];
3996 float* ks = lk_ss; // [2][LK_ST][HD]: two steps' rows
3997 float* sc = ks + 2 * LK_ST * HD; // [LK_ST][GP]: a step's scores, then weights, a key's heads together
3998 __shared__ float mx[G], sum[G], alpha[G];
3999 int hb = blockIdx.x, r = blockIdx.y, t = blockIdx.z, tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
4000 // A batch's rows (`kz` > 0) are its sequences, as `lk_attention` takes them.
4001 if(kz){ kc += r * kz; vc += r * kz; }
4002 int end = pos ? (kz ? pos[r] : *pos + r) : len;
4003 int lo = win > 0 && end > win ? end - win : 0, chunk;
4004 int pe = lk_shared_chunks(end - lo, splits, minc, chunk);
4005 if(t >= pe) return;
4006 int j0 = lo + t * chunk, j1 = min(j0 + chunk, end);
4007 const KE* kb = kc + (long)hb * HD;
4008 const VE* vb = vc + (long)hb * vd + voff;
4009 long head0 = (long)r * heads + (long)hb * G;
4010 // A warp's four heads and keys; a lane's part of the width: float pairs `lane + 32·m`.
4011 int hq = warp % HQ, kg = warp / HQ;
4012 float2 qr[4][M2];
4013 #pragma unroll
4014 for(int h = 0; h < 4; ++h)
4015 #pragma unroll
4016 for(int m = 0; m < M2; ++m){
4017 int c = lane + 32 * m, hh = 4 * hq + h;
4018 qr[h][m] = hh < G && c < 2 * H4 ? ((const float2*)(q + (head0 + hh) * HD))[c] : make_float2(0.f, 0.f);
4019 }
4020 if(tid < G){ mx[tid] = -INFINITY; sum[tid] = 0.f; }
4021 float2 acc[G][VPT];
4022 #pragma unroll
4023 for(int g = 0; g < G; ++g)
4024 #pragma unroll
4025 for(int k = 0; k < VPT; ++k) acc[g][k] = make_float2(0.f, 0.f);
4026 // Step `s`'s rows into buffer `s & 1`, the rows past the chunk zero; every step a commit group.
4027 auto issue = [&](int s){
4028 int js = j0 + s * LK_ST;
4029 if(js < j1){
4030 float* dst = ks + (s & 1) * LK_ST * HD;
4031 #pragma unroll
4032 for(int u = 0; u < RF; ++u){
4033 int c = tid + u * 256;
4034 if(c < LK_ST * H4){
4035 int j = c / H4, e = c - j * H4;
4036 bool in = js + j < j1;
4037 lk_stage4(dst + j * HD + 4 * e, kb + (long)(in ? js + j : js) * kvw + 4 * e, in);
4038 }
4039 }
4040 }
4041 asm volatile("cp.async.commit_group;\n");
4042 };
4043 int steps = (j1 - j0 + LK_ST - 1) / LK_ST;
4044 issue(0);
4045 issue(1);
4046 for(int s = 0; s < steps; ++s){
4047 asm volatile("cp.async.wait_group 1;\n");
4048 __syncthreads();
4049 const float* kst = ks + (s & 1) * LK_ST * HD;
4050 int nt = min(LK_ST, j1 - (j0 + s * LK_ST));
4051 float a[NV];
4052 #pragma unroll
4053 for(int i = 0; i < NV; ++i) a[i] = 0.f;
4054 #pragma unroll
4055 for(int m = 0; m < M2; ++m){
4056 int c = lane + 32 * m;
4057 if(c < 2 * H4){
4058 #pragma unroll
4059 for(int n = 0; n < TN; ++n){
4060 float2 k = ((const float2*)(kst + (kg * TN + n) * HD))[c];
4061 #pragma unroll
4062 for(int h = 0; h < 4; ++h){
4063 a[h * TN + n] = fmaf(qr[h][m].x, k.x, a[h * TN + n]);
4064 a[h * TN + n] = fmaf(qr[h][m].y, k.y, a[h * TN + n]);
4065 }
4066 }
4067 }
4068 }
4069 float tot = lk_lane_sums<NV>(a);
4070 {
4071 int i = lane & (NV - 1), hh = 4 * hq + i / TN, j = kg * TN + i % TN;
4072 if(lane < NV && hh < G) sc[j * GP + hh] = j < nt ? tot * scale : -INFINITY;
4073 }
4074 __syncthreads();
4075 // Each head's running maximum and sum over the step: a half-warp a head, a lane a key.
4076 {
4077 int g = warp + 8 * (lane >> 4), j = lane & 15;
4078 bool live = g < G;
4079 float s0 = live ? sc[j * GP + g] : -INFINITY, mo = live ? mx[g] : -INFINITY, m = s0;
4080 #pragma unroll
4081 for(int o = 8; o > 0; o >>= 1) m = fmaxf(m, __shfl_xor_sync(0xffffffffu, m, o));
4082 m = fmaxf(m, mo);
4083 float pv = s0 == -INFINITY ? 0.f : expf(s0 - m), ss = pv;
4084 if(live) sc[j * GP + g] = pv;
4085 #pragma unroll
4086 for(int o = 8; o > 0; o >>= 1) ss += __shfl_xor_sync(0xffffffffu, ss, o);
4087 if(live && j == 0){ float al = mo == -INFINITY ? 0.f : expf(mo - m); alpha[g] = al; sum[g] = fmaf(sum[g], al, ss); mx[g] = m; }
4088 }
4089 __syncthreads();
4090 // Every head's values weighed in, `VPT` pairs a thread, each value read once from the step's rows.
4091 #pragma unroll
4092 for(int k = 0; k < VPT; ++k){
4093 int e = tid + k * 256;
4094 if(2 * e < vd){
4095 #pragma unroll
4096 for(int g = 0; g < G; ++g){ acc[g][k].x *= alpha[g]; acc[g][k].y *= alpha[g]; }
4097 for(int j = 0; j < nt; ++j){
4098 float2 v = VK ? *(const float2*)(kst + j * HD + voff + 2 * e) : lk_from2(vb + (long)(j0 + s * LK_ST + j) * vw + 2 * e);
4099 #pragma unroll
4100 for(int c4 = 0; c4 < HQ; ++c4){
4101 float4 w = ((const float4*)(sc + j * GP))[c4];
4102 float wv[4] = {w.x, w.y, w.z, w.w};
4103 #pragma unroll
4104 for(int i = 0; i < 4; ++i)
4105 if(4 * c4 + i < G){
4106 acc[4 * c4 + i][k].x = fmaf(wv[i], v.x, acc[4 * c4 + i][k].x);
4107 acc[4 * c4 + i][k].y = fmaf(wv[i], v.y, acc[4 * c4 + i][k].y);
4108 }
4109 }
4110 }
4111 }
4112 }
4113 __syncthreads();
4114 issue(s + 2);
4115 }
4116 if(pe == 1){
4117 #pragma unroll
4118 for(int g = 0; g < G; ++g){
4119 float sk = sinks ? sinks[hb * G + g] : -INFINITY, m = fmaxf(sk, mx[g]), f = expf(mx[g] - m);
4120 float l = fmaf(sum[g], f, sinks ? expf(sk - m) : 0.f);
4121 #pragma unroll
4122 for(int k = 0; k < VPT; ++k){
4123 int e = tid + k * 256;
4124 lk_attn_store<QO>(out, qo, (head0 + g) * vd + 2 * e, 2 * e < vd, acc[g][k].x * f / l, acc[g][k].y * f / l);
4125 }
4126 }
4127 return;
4128 }
4129 long stride = (long)vd + 2;
4130 #pragma unroll
4131 for(int g = 0; g < G; ++g){
4132 float* pp = part + ((head0 + g) * splits + t) * stride;
4133 if(tid == 0){ pp[0] = mx[g]; pp[1] = sum[g]; }
4134 #pragma unroll
4135 for(int k = 0; k < VPT; ++k){
4136 int e = tid + k * 256;
4137 if(2 * e < vd) ((float2*)(pp + 2))[e] = acc[g][k];
4138 }
4139 }
4140}
4141// The fold of `lk_attention_shared`'s chunks: a block a head of a row and 128 of its values, each thread's pair of
4142// values summed over the chunks in order, sixteen chunks' pairs read at once — the first sixteen while a warp takes
4143// the chunks' factors (each chunk's maximum against the head's, sink included) and the denominator.
4144template<int QO = 0>
4145__global__ void __launch_bounds__(64) lk_attention_fold(const float* __restrict__ part, const int* __restrict__ pos,
4146 const float* __restrict__ sinks, float* __restrict__ out, int len, int win, int vd, int heads, int minc, int splits,
4147 int seqs = 0, lk_qout qo = {}){
4148 extern __shared__ float lk_ff[];
4149 __shared__ float fl;
4150 int h = blockIdx.x, r = blockIdx.y, tid = threadIdx.x, lane = tid & 31;
4151 int end = pos ? (seqs ? pos[r] : *pos + r) : len;
4152 int lo = win > 0 && end > win ? end - win : 0, chunk;
4153 int pe = lk_shared_chunks(end - lo, splits, minc, chunk);
4154 if(pe == 1) return;
4155 long head = (long)r * heads + h, stride = (long)vd + 2;
4156 const float* hp = part + head * splits * stride;
4157 int e = blockIdx.z * 64 + tid;
4158 bool in = 2 * e < vd;
4159 float2 v[16];
4160 #pragma unroll
4161 for(int i = 0; i < 16; ++i) v[i] = in && i < pe ? __ldcg((const float2*)(hp + i * stride + 2) + e) : make_float2(0.f, 0.f);
4162 if(tid < 32){
4163 float m = sinks ? sinks[h] : -INFINITY;
4164 for(int u = lane; u < pe; u += 32) m = fmaxf(m, __ldcg(hp + u * stride));
4165 m = lk_warp_max(m);
4166 float l = 0.f;
4167 for(int u = lane; u < pe; u += 32){
4168 float mu = __ldcg(hp + u * stride), f = mu > -INFINITY ? expf(mu - m) : 0.f;
4169 lk_ff[u] = f;
4170 l = fmaf(__ldcg(hp + u * stride + 1), f, l);
4171 }
4172 l = lk_warp_sum(l);
4173 if(lane == 0) fl = sinks ? l + expf(sinks[h] - m) : l;
4174 }
4175 __syncthreads();
4176 float2 a = make_float2(0.f, 0.f);
4177 for(int u0 = 0; u0 < pe; u0 += 16){
4178 if(u0 > 0)
4179 #pragma unroll
4180 for(int i = 0; i < 16; ++i) v[i] = in && u0 + i < pe ? __ldcg((const float2*)(hp + (u0 + i) * stride + 2) + e) : make_float2(0.f, 0.f);
4181 #pragma unroll
4182 for(int i = 0; i < 16; ++i)
4183 if(u0 + i < pe){ a.x = fmaf(v[i].x, lk_ff[u0 + i], a.x); a.y = fmaf(v[i].y, lk_ff[u0 + i], a.y); }
4184 }
4185 lk_attn_store<QO>(out, qo, head * vd + 2 * e, in, a.x / fl, a.y / fl);
4186}
4187"#;
4188
4189pub const ATTN_SHARED_HEADS: usize = 16;
4191
4192pub const ATTN_SHARED_STEP: usize = 16;
4194
4195pub const ATTN_FOLD_PAIRS: usize = 64;
4197
4198pub fn attn_shared_splits(span: usize, sms: usize) -> usize {
4202 sms.clamp(1, span.div_ceil(ATTN_MIN_SPLIT).max(1))
4203}
4204
4205pub fn attn_shared_padded_heads(g: usize) -> usize {
4207 4 * if g <= 4 { 1 } else if g <= 8 { 2 } else { 4 }
4208}
4209
4210pub fn attn_shared_smem_bytes(g: usize, hd: usize) -> usize {
4213 (2 * ATTN_SHARED_STEP * hd + ATTN_SHARED_STEP * attn_shared_padded_heads(g)) * 4
4214}
4215
4216const ATTENTION_TILED: &str = r#"
4229#define LK_FQ 32
4230#define LK_FK 64
4231#define LK_FD 32
4232#define LK_FV 8
4233template<int HG, int VI, typename KE = float, typename VE = float, typename TE = float>
4234__global__ void __launch_bounds__(256) lk_attention_tiled(const float* __restrict__ q, const KE* __restrict__ kc,
4235 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
4236 int len, int win, int hd, int vd, int group, int heads, int kvw, int vw, int voff, float scale, int rows,
4237 const float* __restrict__ qt = nullptr, long qp = 0, long qtp = 0, const TE* __restrict__ kt = nullptr, long ktw = 0,
4238 long kto = 0, int kh = 0){
4239 constexpr int BR = LK_FQ / HG;
4240 extern __shared__ __align__(16) float lk_fa[];
4241 float* qs = lk_fa; // [LK_FD][LK_FQ]: a chunk of the queries
4242 float* ks = qs + LK_FD * LK_FQ; // [LK_FD][LK_FK]: the same chunk of the tile's keys
4243 float* ps = ks + LK_FD * LK_FK; // [LK_FK][LK_FQ]: the tile's scores, then its weights
4244 float* vs = ps + LK_FK * LK_FQ; // [LK_FV][vd]: eight keys' values
4245 __shared__ float mq[LK_FQ], lq[LK_FQ], aq[LK_FQ];
4246 __shared__ int loq[LK_FQ], endq[LK_FQ], span[2];
4247 // Scores: lane `q8` a group of 4 queries, lane `k4` and the warp a pair of keys. Values: lane `q8` the same 4
4248 // queries, lane `k4` and the warp a group of 4 values, `VI` groups 128 apart.
4249 int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, q8 = lane & 7, k4 = lane >> 3, kp = 4 * warp + k4;
4250 int r0 = blockIdx.x * BR, h0 = blockIdx.y * HG;
4251 long koff = (long)(h0 / group) * (kt ? kh : hd), vo = voff + (long)(h0 / group) * vd;
4252 if(tid < LK_FQ){
4253 int r = r0 + tid / HG;
4254 int end = r < rows ? (pos ? *pos + r : len) : 0;
4255 endq[tid] = end; loq[tid] = win > 0 && end > win ? end - win : 0;
4256 mq[tid] = -INFINITY; lq[tid] = 0.f;
4257 }
4258 __syncthreads();
4259 if(tid == 0){
4260 int lo = 0x7fffffff, hi = 0;
4261 for(int i = 0; i < LK_FQ; ++i) if(endq[i] > 0){ lo = min(lo, loq[i]); hi = max(hi, endq[i]); }
4262 span[0] = hi > 0 ? lo : 0; span[1] = hi;
4263 }
4264 __syncthreads();
4265 int jlo = span[0], jhi = span[1];
4266 float o[4][VI][4];
4267 #pragma unroll
4268 for(int a = 0; a < 4; ++a)
4269 #pragma unroll
4270 for(int i = 0; i < VI; ++i)
4271 #pragma unroll
4272 for(int c = 0; c < 4; ++c) o[a][i][c] = 0.f;
4273 for(int jt = jlo; jt < jhi; jt += LK_FK){
4274 // The tile's scores: queries 4·q8 … 4·q8 + 3 against keys 2·kp, 2·kp + 1.
4275 float s[4][2];
4276 #pragma unroll
4277 for(int a = 0; a < 4; ++a){ s[a][0] = 0.f; s[a][1] = 0.f; }
4278 // A chunk of the queries' and keys' values, four a thread (two fours of keys): fetched a chunk ahead, so their loads
4279 // are in flight while the chunk before multiplies.
4280 auto fetch_q = [&](int d0){
4281 int qi = tid >> 3, dd = (tid & 7) * 4, r = r0 + qi / HG, h = h0 + qi % HG, d = d0 + dd;
4282 long u = (long)r * heads + h;
4283 if(r >= rows || d >= hd) return make_float4(0.f, 0.f, 0.f, 0.f);
4284 return !kt ? *(const float4*)(q + u * hd + d) : d < kh ? *(const float4*)(q + u * qp + d) : *(const float4*)(qt + u * qtp + (d - kh));
4285 };
4286 auto fetch_k = [&](int d0, int part){
4287 int c = tid + part * 256, kj = c >> 3, d = d0 + (c & 7) * 4, j = jt + kj;
4288 if(j >= jhi || d >= hd) return make_float4(0.f, 0.f, 0.f, 0.f);
4289 return kt && d >= kh ? lk_from4(kt + (long)j * ktw + kto + (d - kh)) : lk_from4(kc + (long)j * kvw + koff + d);
4290 };
4291 float4 nq = fetch_q(0), nk0 = fetch_k(0, 0), nk1 = fetch_k(0, 1);
4292 for(int d0 = 0; d0 < hd; d0 += LK_FD){
4293 {
4294 int qi = tid >> 3, dd = (tid & 7) * 4;
4295 qs[(dd + 0) * LK_FQ + qi] = nq.x; qs[(dd + 1) * LK_FQ + qi] = nq.y;
4296 qs[(dd + 2) * LK_FQ + qi] = nq.z; qs[(dd + 3) * LK_FQ + qi] = nq.w;
4297 }
4298 #pragma unroll
4299 for(int u = 0; u < 2; ++u){
4300 int c = tid + u * 256, kj = c >> 3, dd = (c & 7) * 4;
4301 float4 v = u == 0 ? nk0 : nk1;
4302 ks[(dd + 0) * LK_FK + kj] = v.x; ks[(dd + 1) * LK_FK + kj] = v.y;
4303 ks[(dd + 2) * LK_FK + kj] = v.z; ks[(dd + 3) * LK_FK + kj] = v.w;
4304 }
4305 __syncthreads();
4306 if(d0 + LK_FD < hd){ nq = fetch_q(d0 + LK_FD); nk0 = fetch_k(d0 + LK_FD, 0); nk1 = fetch_k(d0 + LK_FD, 1); }
4307 #pragma unroll 8
4308 for(int dd = 0; dd < LK_FD; ++dd){
4309 float4 a4 = *(const float4*)(qs + dd * LK_FQ + 4 * q8);
4310 float2 b2 = *(const float2*)(ks + dd * LK_FK + 2 * kp);
4311 s[0][0] = fmaf(a4.x, b2.x, s[0][0]); s[0][1] = fmaf(a4.x, b2.y, s[0][1]);
4312 s[1][0] = fmaf(a4.y, b2.x, s[1][0]); s[1][1] = fmaf(a4.y, b2.y, s[1][1]);
4313 s[2][0] = fmaf(a4.z, b2.x, s[2][0]); s[2][1] = fmaf(a4.z, b2.y, s[2][1]);
4314 s[3][0] = fmaf(a4.w, b2.x, s[3][0]); s[3][1] = fmaf(a4.w, b2.y, s[3][1]);
4315 }
4316 __syncthreads();
4317 }
4318 #pragma unroll
4319 for(int a = 0; a < 4; ++a)
4320 #pragma unroll
4321 for(int b = 0; b < 2; ++b){
4322 int qi = 4 * q8 + a, j = jt + 2 * kp + b;
4323 ps[(2 * kp + b) * LK_FQ + qi] = j >= loq[qi] && j < endq[qi] ? s[a][b] * scale : -INFINITY;
4324 }
4325 __syncthreads();
4326 // Each query's running maximum and sum, eight threads a query.
4327 {
4328 int qi = tid >> 3, part = tid & 7;
4329 float m = -INFINITY;
4330 #pragma unroll
4331 for(int k = 0; k < 8; ++k) m = fmaxf(m, ps[(part * 8 + k) * LK_FQ + qi]);
4332 #pragma unroll
4333 for(int x = 1; x < 8; x <<= 1) m = fmaxf(m, __shfl_xor_sync(0xffffffffu, m, x));
4334 float mo = mq[qi], mn = fmaxf(mo, m), sum = 0.f;
4335 #pragma unroll
4336 for(int k = 0; k < 8; ++k){
4337 float* sp = ps + (part * 8 + k) * LK_FQ + qi;
4338 float p = *sp == -INFINITY ? 0.f : expf(*sp - mn);
4339 *sp = p; sum += p;
4340 }
4341 #pragma unroll
4342 for(int x = 1; x < 8; x <<= 1) sum += __shfl_xor_sync(0xffffffffu, sum, x);
4343 // Every lane's read of the query's maximum before the first lane writes it.
4344 __syncwarp();
4345 if(part == 0){
4346 float al = mo == -INFINITY ? 0.f : expf(mo - mn);
4347 aq[qi] = al; lq[qi] = fmaf(lq[qi], al, sum); mq[qi] = mn;
4348 }
4349 }
4350 __syncthreads();
4351 // The tile's weighed values: queries 4·q8 … 4·q8 + 3, values 4·kp + 128·i.
4352 #pragma unroll
4353 for(int a = 0; a < 4; ++a){
4354 float al = aq[4 * q8 + a];
4355 #pragma unroll
4356 for(int i = 0; i < VI; ++i)
4357 #pragma unroll
4358 for(int c = 0; c < 4; ++c) o[a][i][c] *= al;
4359 }
4360 int nk = min(LK_FK, jhi - jt), v4 = vd >> 2;
4361 // Eight keys' values, `VI` fours a thread, fetched a group ahead.
4362 float4 nv[VI];
4363 auto fetch_v = [&](int k0){
4364 #pragma unroll
4365 for(int i = 0; i < VI; ++i){
4366 int c = tid + i * 256, kk = c / v4, e = c - kk * v4, j = jt + k0 + kk;
4367 nv[i] = c < LK_FV * v4 && j < jhi ? lk_from4(vc + (long)j * vw + vo + 4 * e) : make_float4(0.f, 0.f, 0.f, 0.f);
4368 }
4369 };
4370 fetch_v(0);
4371 for(int k0 = 0; k0 < nk; k0 += LK_FV){
4372 #pragma unroll
4373 for(int i = 0; i < VI; ++i){
4374 int c = tid + i * 256, kk = c / v4, e = c - kk * v4;
4375 if(c < LK_FV * v4) *(float4*)(vs + kk * vd + 4 * e) = nv[i];
4376 }
4377 __syncthreads();
4378 if(k0 + LK_FV < nk) fetch_v(k0 + LK_FV);
4379 #pragma unroll
4380 for(int kk = 0; kk < LK_FV; ++kk){
4381 float4 p = *(const float4*)(ps + (k0 + kk) * LK_FQ + 4 * q8);
4382 #pragma unroll
4383 for(int i = 0; i < VI; ++i){
4384 int col = 4 * kp + 128 * i;
4385 if(col < vd){
4386 float4 v = *(const float4*)(vs + kk * vd + col);
4387 o[0][i][0] = fmaf(p.x, v.x, o[0][i][0]); o[0][i][1] = fmaf(p.x, v.y, o[0][i][1]); o[0][i][2] = fmaf(p.x, v.z, o[0][i][2]); o[0][i][3] = fmaf(p.x, v.w, o[0][i][3]);
4388 o[1][i][0] = fmaf(p.y, v.x, o[1][i][0]); o[1][i][1] = fmaf(p.y, v.y, o[1][i][1]); o[1][i][2] = fmaf(p.y, v.z, o[1][i][2]); o[1][i][3] = fmaf(p.y, v.w, o[1][i][3]);
4389 o[2][i][0] = fmaf(p.z, v.x, o[2][i][0]); o[2][i][1] = fmaf(p.z, v.y, o[2][i][1]); o[2][i][2] = fmaf(p.z, v.z, o[2][i][2]); o[2][i][3] = fmaf(p.z, v.w, o[2][i][3]);
4390 o[3][i][0] = fmaf(p.w, v.x, o[3][i][0]); o[3][i][1] = fmaf(p.w, v.y, o[3][i][1]); o[3][i][2] = fmaf(p.w, v.z, o[3][i][2]); o[3][i][3] = fmaf(p.w, v.w, o[3][i][3]);
4391 }
4392 }
4393 }
4394 __syncthreads();
4395 }
4396 }
4397 #pragma unroll
4398 for(int a = 0; a < 4; ++a){
4399 int qi = 4 * q8 + a, r = r0 + qi / HG, h = h0 + qi % HG;
4400 if(r >= rows) continue;
4401 float m = mq[qi], sk = sinks ? sinks[h] : -INFINITY, mf = fmaxf(sk, m);
4402 float f = m == -INFINITY ? 0.f : expf(m - mf), l = fmaf(lq[qi], f, sinks ? expf(sk - mf) : 0.f);
4403 float* op = out + ((long)r * heads + h) * vd;
4404 #pragma unroll
4405 for(int i = 0; i < VI; ++i){
4406 int col = 4 * kp + 128 * i;
4407 if(col < vd) *(float4*)(op + col) = make_float4(o[a][i][0] * f / l, o[a][i][1] * f / l, o[a][i][2] * f / l, o[a][i][3] * f / l);
4408 }
4409 }
4410}
4411"#;
4412
4413const ATTENTION_FLASH: &str = r#"
4424#define LK_FB 64
4425#define LK_KD 32
4426#define LK_VK 16
4427template<int HG, int HD, int VD, int BQ, int NT = 128, typename KE = float, typename VE = float, typename TE = float>
4428__global__ void __launch_bounds__(NT) lk_attention_flash(const float* __restrict__ q, const KE* __restrict__ kc,
4429 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
4430 int len, int win, int group, int heads, int kvw, int vw, int voff, float scale, int rows,
4431 const float* __restrict__ qt = nullptr, long qp = 0, long qtp = 0, const TE* __restrict__ kt = nullptr, long ktw = 0,
4432 long kto = 0, int kh = 0){
4433 constexpr int TQ = NT / 16, QI = BQ / TQ, QP = HD + 4, KP = LK_KD + 4, VP = VD + 4, PP = BQ + 4, VG = VD / 64 > 0 ? VD / 64 : 1;
4434 constexpr int BR = BQ / HG;
4435 extern __shared__ __align__(16) float lk_fl[];
4436 float* qs = lk_fl; // [BQ][QP]: the block's queries
4437 float* ks = qs + BQ * QP; // [2][LK_FB][KP]: two stages of a chunk of the tile's keys
4438 float* vs = ks + 2 * LK_FB * KP; // [2][LK_VK][VP]: two stages of the tile's values
4439 float* ps = vs + 2 * LK_VK * VP; // [LK_FB][PP]: the tile's weights, a thread's queries together
4440 __shared__ int loq[BQ], endq[BQ], span[2];
4441 int tid = threadIdx.x, tq = tid >> 4, tk = tid & 15;
4442 int r0 = blockIdx.x * BR, h0 = blockIdx.y * HG;
4443 long koff = (long)(h0 / group) * (kt ? kh : HD), vo = voff + (long)(h0 / group) * VD;
4444 // Query `qi` of the block: head `h0 + qi % HG` of row `r0 + qi / HG`.
4445 for(int qi = tid; qi < BQ; qi += NT){
4446 int r = r0 + qi / HG;
4447 int end = r < rows ? (pos ? *pos + r : len) : 0;
4448 endq[qi] = end; loq[qi] = win > 0 && end > win ? end - win : 0;
4449 }
4450 for(int c = tid; c < BQ * (HD / 4); c += NT){
4451 int qi = c / (HD / 4), d = 4 * (c % (HD / 4)), r = r0 + qi / HG, h = h0 + qi % HG;
4452 long u = (long)r * heads + h;
4453 float4 v = make_float4(0.f, 0.f, 0.f, 0.f);
4454 if(r < rows) v = !kt ? *(const float4*)(q + u * HD + d) : d < kh ? *(const float4*)(q + u * qp + d) : *(const float4*)(qt + u * qtp + (d - kh));
4455 *(float4*)(qs + qi * QP + d) = v;
4456 }
4457 __syncthreads();
4458 if(tid == 0){
4459 int lo = 0x7fffffff, hi = 0;
4460 for(int i = 0; i < BQ; ++i) if(endq[i] > 0){ lo = min(lo, loq[i]); hi = max(hi, endq[i]); }
4461 span[0] = hi > 0 ? lo : 0; span[1] = hi;
4462 }
4463 __syncthreads();
4464 int jlo = span[0], jhi = span[1];
4465 float m[QI], l[QI], o[QI][VG][4];
4466 #pragma unroll
4467 for(int i = 0; i < QI; ++i){
4468 m[i] = -INFINITY; l[i] = 0.f;
4469 #pragma unroll
4470 for(int g = 0; g < VG; ++g)
4471 #pragma unroll
4472 for(int c = 0; c < 4; ++c) o[i][g][c] = 0.f;
4473 }
4474 // A chunk `d0` of tile `jt`'s keys into stage `st`; a step `k0` of its values into stage `st`.
4475 auto stage_k = [&](int jt, int d0, int st){
4476 float* dst = ks + st * LK_FB * KP;
4477 for(int c = tid; c < LK_FB * (LK_KD / 4); c += NT){
4478 int kj = c / (LK_KD / 4), d = d0 + 4 * (c % (LK_KD / 4)), j = jt + kj;
4479 bool in = j < jhi && d < HD;
4480 if(kt && d >= kh) lk_stage4(dst + kj * KP + (d - d0), kt + (long)(in ? j : jlo) * ktw + kto + (d - kh), in);
4481 else lk_stage4(dst + kj * KP + (d - d0), kc + (long)(in ? j : jlo) * kvw + koff + (in ? d : 0), in);
4482 }
4483 asm volatile("cp.async.commit_group;\n");
4484 };
4485 auto stage_v = [&](int jt, int k0, int st){
4486 float* dst = vs + st * LK_VK * VP;
4487 for(int c = tid; c < LK_VK * (VD / 4); c += NT){
4488 int kk = c / (VD / 4), e = 4 * (c % (VD / 4)), j = jt + k0 + kk;
4489 bool in = j < jhi;
4490 lk_stage4(dst + kk * VP + e, vc + (long)(in ? j : jlo) * vw + vo + e, in);
4491 }
4492 asm volatile("cp.async.commit_group;\n");
4493 };
4494 constexpr int DS = (HD + LK_KD - 1) / LK_KD, VS = LK_FB / LK_VK;
4495 for(int jt = jlo; jt < jhi; jt += LK_FB){
4496 // The tile's scores: queries tq + 8·i against keys tk + 16·j, over the head a chunk at a time.
4497 float s[QI][4];
4498 #pragma unroll
4499 for(int i = 0; i < QI; ++i)
4500 #pragma unroll
4501 for(int j = 0; j < 4; ++j) s[i][j] = 0.f;
4502 stage_k(jt, 0, 0);
4503 for(int dc = 0; dc < DS; ++dc){
4504 if(dc + 1 < DS){ stage_k(jt, (dc + 1) * LK_KD, (dc + 1) & 1); asm volatile("cp.async.wait_group 1;\n"); }
4505 else asm volatile("cp.async.wait_group 0;\n");
4506 __syncthreads();
4507 const float* kst = ks + (dc & 1) * LK_FB * KP;
4508 #pragma unroll
4509 for(int d4 = 0; d4 < LK_KD; d4 += 4){
4510 if(dc * LK_KD + d4 >= HD) break;
4511 float4 kv[4];
4512 #pragma unroll
4513 for(int j = 0; j < 4; ++j) kv[j] = *(const float4*)(kst + (tk + 16 * j) * KP + d4);
4514 #pragma unroll
4515 for(int i = 0; i < QI; ++i){
4516 float4 qv = *(const float4*)(qs + (tq + TQ * i) * QP + dc * LK_KD + d4);
4517 #pragma unroll
4518 for(int j = 0; j < 4; ++j){
4519 s[i][j] = fmaf(qv.x, kv[j].x, s[i][j]); s[i][j] = fmaf(qv.y, kv[j].y, s[i][j]);
4520 s[i][j] = fmaf(qv.z, kv[j].z, s[i][j]); s[i][j] = fmaf(qv.w, kv[j].w, s[i][j]);
4521 }
4522 }
4523 }
4524 __syncthreads();
4525 }
4526 stage_v(jt, 0, 0);
4527 // Each query's running maximum and sum across its 16 lanes; its weights into shared memory, a thread's together.
4528 #pragma unroll
4529 for(int i = 0; i < QI; ++i){
4530 int qi = tq + TQ * i;
4531 float mt = -INFINITY;
4532 #pragma unroll
4533 for(int j = 0; j < 4; ++j){
4534 int kj = jt + tk + 16 * j;
4535 s[i][j] = kj >= loq[qi] && kj < endq[qi] ? s[i][j] * scale : -INFINITY;
4536 mt = fmaxf(mt, s[i][j]);
4537 }
4538 #pragma unroll
4539 for(int x = 1; x < 16; x <<= 1) mt = fmaxf(mt, __shfl_xor_sync(0xffffffffu, mt, x));
4540 float mn = fmaxf(m[i], mt), sum = 0.f;
4541 #pragma unroll
4542 for(int j = 0; j < 4; ++j){
4543 float pv = s[i][j] == -INFINITY ? 0.f : expf(s[i][j] - mn);
4544 ps[(tk + 16 * j) * PP + QI * tq + i] = pv;
4545 sum += pv;
4546 }
4547 #pragma unroll
4548 for(int x = 1; x < 16; x <<= 1) sum += __shfl_xor_sync(0xffffffffu, sum, x);
4549 float al = m[i] == -INFINITY ? 0.f : expf(m[i] - mn);
4550 l[i] = fmaf(l[i], al, sum); m[i] = mn;
4551 #pragma unroll
4552 for(int g = 0; g < VG; ++g)
4553 #pragma unroll
4554 for(int c = 0; c < 4; ++c) o[i][g][c] *= al;
4555 }
4556 // The tile's weighed values, `LK_VK` keys a step: queries tq + 8·i, values 4·(tk + 16·g).
4557 int nk = min(LK_FB, jhi - jt);
4558 for(int vstep = 0; vstep < VS; ++vstep){
4559 int k0 = vstep * LK_VK;
4560 if(k0 >= nk){ asm volatile("cp.async.wait_group 0;\n"); break; }
4561 if(vstep + 1 < VS && k0 + LK_VK < nk){ stage_v(jt, k0 + LK_VK, (vstep + 1) & 1); asm volatile("cp.async.wait_group 1;\n"); }
4562 else asm volatile("cp.async.wait_group 0;\n");
4563 __syncthreads();
4564 const float* vst = vs + (vstep & 1) * LK_VK * VP;
4565 #pragma unroll 4
4566 for(int kk = 0; kk < LK_VK; ++kk){
4567 const float4* pr = (const float4*)(ps + (k0 + kk) * PP + QI * tq);
4568 float pw[QI];
4569 if constexpr(QI % 4 == 0){
4570 #pragma unroll
4571 for(int i4 = 0; i4 < QI / 4; ++i4){ float4 t = pr[i4]; pw[4 * i4] = t.x; pw[4 * i4 + 1] = t.y; pw[4 * i4 + 2] = t.z; pw[4 * i4 + 3] = t.w; }
4572 } else {
4573 #pragma unroll
4574 for(int i2 = 0; i2 < QI / 2; ++i2){ float2 t = ((const float2*)pr)[i2]; pw[2 * i2] = t.x; pw[2 * i2 + 1] = t.y; }
4575 }
4576 #pragma unroll
4577 for(int g = 0; g < VG; ++g){
4578 int col = 4 * (tk + 16 * g);
4579 if(col < VD){
4580 float4 v = *(const float4*)(vst + kk * VP + col);
4581 #pragma unroll
4582 for(int i = 0; i < QI; ++i){
4583 o[i][g][0] = fmaf(pw[i], v.x, o[i][g][0]); o[i][g][1] = fmaf(pw[i], v.y, o[i][g][1]);
4584 o[i][g][2] = fmaf(pw[i], v.z, o[i][g][2]); o[i][g][3] = fmaf(pw[i], v.w, o[i][g][3]);
4585 }
4586 }
4587 }
4588 }
4589 __syncthreads();
4590 }
4591 }
4592 #pragma unroll
4593 for(int i = 0; i < QI; ++i){
4594 int qi = tq + TQ * i, r = r0 + qi / HG, h = h0 + qi % HG;
4595 if(r >= rows) continue;
4596 float sk = sinks ? sinks[h] : -INFINITY, mf = fmaxf(sk, m[i]);
4597 float f = m[i] == -INFINITY ? 0.f : expf(m[i] - mf), lf = fmaf(l[i], f, sinks ? expf(sk - mf) : 0.f);
4598 float* op = out + ((long)r * heads + h) * VD;
4599 #pragma unroll
4600 for(int g = 0; g < VG; ++g){
4601 int col = 4 * (tk + 16 * g);
4602 if(col < VD) *(float4*)(op + col) = make_float4(o[i][g][0] * f / lf, o[i][g][1] * f / lf, o[i][g][2] * f / lf, o[i][g][3] * f / lf);
4603 }
4604 }
4605}
4606"#;
4607
4608const ATTENTION_MMA: &str = r#"
4619__device__ __forceinline__ void lk_mma_h16(float* c, const unsigned* a, const unsigned* b){
4620 asm volatile("mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n"
4621 : "+f"(c[0]), "+f"(c[1]), "+f"(c[2]), "+f"(c[3]) : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]));
4622}
4623__device__ __forceinline__ void lk_ldsm4(unsigned* r, const void* p){
4624 unsigned s = (unsigned)__cvta_generic_to_shared(p);
4625 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n" : "=r"(r[0]), "=r"(r[1]), "=r"(r[2]), "=r"(r[3]) : "r"(s));
4626}
4627__device__ __forceinline__ void lk_ldsm4_t(unsigned* r, const void* p){
4628 unsigned s = (unsigned)__cvta_generic_to_shared(p);
4629 asm volatile("ldmatrix.sync.aligned.m8n8.x4.trans.shared.b16 {%0,%1,%2,%3}, [%4];\n" : "=r"(r[0]), "=r"(r[1]), "=r"(r[2]), "=r"(r[3]) : "r"(s));
4630}
4631__device__ __forceinline__ unsigned lk_h2(float a, float b){
4632 __half2 h = __floats2half2_rn(a, b);
4633 return *(unsigned*)&h;
4634}
4635template<int HG, int HD, int VD, typename KE = float, typename VE = float, typename TE = float>
4636__global__ void __launch_bounds__(128) lk_attention_mma(const float* __restrict__ q, const KE* __restrict__ kc,
4637 const VE* __restrict__ vc, const int* __restrict__ pos, const float* __restrict__ sinks, float* __restrict__ out,
4638 int len, int win, int group, int heads, int kvw, int vw, int voff, float scale, int rows,
4639 const float* __restrict__ qt = nullptr, long qp = 0, long qtp = 0, const TE* __restrict__ kt = nullptr, long ktw = 0,
4640 long kto = 0, int kh = 0){
4641 constexpr int BQ = 64, BR = BQ / HG, KS = HD / 16, NV = VD / 8, KP = HD + 8, VP = VD + 8;
4642 // The keys a tile: 64, or 32 where heads or values pass 192 wide, so the values' sums fit a warp's registers.
4643 constexpr int KT = HD > 192 || VD > 192 ? 32 : 64;
4644 extern __shared__ __align__(16) __half lk_hm[];
4645 __half* ks = lk_hm; // [KT][KP]: the tile's keys
4646 __half* vs = ks + KT * KP; // [KT][VP]: its values
4647 __shared__ int loq[BQ], endq[BQ], span[2];
4648 int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t4 = lane & 3;
4649 int r0 = blockIdx.x * BR, h0 = blockIdx.y * HG;
4650 long koff = (long)(h0 / group) * (kt ? kh : HD), vo = voff + (long)(h0 / group) * VD;
4651 if(tid < BQ){
4652 int r = r0 + tid / HG;
4653 int end = r < rows ? (pos ? *pos + r : len) : 0;
4654 endq[tid] = end; loq[tid] = win > 0 && end > win ? end - win : 0;
4655 }
4656 __syncthreads();
4657 if(tid == 0){
4658 int lo = 0x7fffffff, hi = 0;
4659 for(int i = 0; i < BQ; ++i) if(endq[i] > 0){ lo = min(lo, loq[i]); hi = max(hi, endq[i]); }
4660 span[0] = hi > 0 ? lo : 0; span[1] = hi;
4661 }
4662 // The warp's queries: rows g and g + 8 of its 16, as A fragments of every 16 values of the head.
4663 int qa = 16 * warp + g, qb = qa + 8;
4664 auto qrow = [&](int qi, int d) -> const float* {
4665 int r = r0 + qi / HG, h = h0 + qi % HG;
4666 if(r >= rows) return nullptr;
4667 long u = (long)r * heads + h;
4668 return !kt ? q + u * HD + d : d < kh ? q + u * qp + d : qt + u * qtp + (d - kh);
4669 };
4670 unsigned qf[KS][4];
4671 #pragma unroll
4672 for(int k = 0; k < KS; ++k){
4673 int d0 = 16 * k + 2 * t4, d1 = d0 + 8;
4674 const float *a0 = qrow(qa, d0), *a1 = qrow(qb, d0), *a2 = qrow(qa, d1), *a3 = qrow(qb, d1);
4675 float2 v0 = a0 ? *(const float2*)a0 : make_float2(0.f, 0.f), v1 = a1 ? *(const float2*)a1 : make_float2(0.f, 0.f);
4676 float2 v2 = a2 ? *(const float2*)a2 : make_float2(0.f, 0.f), v3 = a3 ? *(const float2*)a3 : make_float2(0.f, 0.f);
4677 qf[k][0] = lk_h2(v0.x, v0.y); qf[k][1] = lk_h2(v1.x, v1.y); qf[k][2] = lk_h2(v2.x, v2.y); qf[k][3] = lk_h2(v3.x, v3.y);
4678 }
4679 __syncthreads();
4680 int jlo = span[0], jhi = span[1];
4681 int loa = loq[qa], enda = endq[qa], lob = loq[qb], endb = endq[qb];
4682 float o[NV][4], ma = -INFINITY, mb = -INFINITY, la = 0.f, lb = 0.f;
4683 #pragma unroll
4684 for(int n = 0; n < NV; ++n)
4685 #pragma unroll
4686 for(int c = 0; c < 4; ++c) o[n][c] = 0.f;
4687 for(int jt = jlo; jt < jhi; jt += KT){
4688 // The tile's keys and values, as f16, rows past the span zero.
4689 for(int c = tid; c < KT * (HD / 4); c += 128){
4690 int kj = c / (HD / 4), d = 4 * (c % (HD / 4)), j = jt + kj;
4691 float4 v = make_float4(0.f, 0.f, 0.f, 0.f);
4692 if(j < jhi) v = kt && d >= kh ? lk_from4(kt + (long)j * ktw + kto + (d - kh)) : lk_from4(kc + (long)j * kvw + koff + d);
4693 unsigned* dst = (unsigned*)(ks + kj * KP + d);
4694 dst[0] = lk_h2(v.x, v.y); dst[1] = lk_h2(v.z, v.w);
4695 }
4696 for(int c = tid; c < KT * (VD / 4); c += 128){
4697 int kj = c / (VD / 4), e = 4 * (c % (VD / 4)), j = jt + kj;
4698 float4 v = j < jhi ? lk_from4(vc + (long)j * vw + vo + e) : make_float4(0.f, 0.f, 0.f, 0.f);
4699 unsigned* dst = (unsigned*)(vs + kj * VP + e);
4700 dst[0] = lk_h2(v.x, v.y); dst[1] = lk_h2(v.z, v.w);
4701 }
4702 __syncthreads();
4703 // Scores: the warp's 16 queries against the tile's keys, tiles of eight.
4704 float s[KT / 8][4];
4705 #pragma unroll
4706 for(int n = 0; n < KT / 8; ++n)
4707 #pragma unroll
4708 for(int c = 0; c < 4; ++c) s[n][c] = 0.f;
4709 int mi = lane >> 3, ri = lane & 7;
4710 #pragma unroll
4711 for(int k = 0; k < KS; ++k)
4712 #pragma unroll
4713 for(int np = 0; np < KT / 16; ++np){
4714 unsigned b[4];
4715 lk_ldsm4(b, ks + (16 * np + (mi >= 2 ? 8 : 0) + ri) * KP + 16 * k + ((mi & 1) ? 8 : 0));
4716 lk_mma_h16(s[2 * np], qf[k], b);
4717 lk_mma_h16(s[2 * np + 1], qf[k], b + 2);
4718 }
4719 // Each row's running maximum and sum across its four lanes; the weights rounded to f16 as A fragments.
4720 float xa = -INFINITY, xb = -INFINITY;
4721 #pragma unroll
4722 for(int n = 0; n < KT / 8; ++n)
4723 #pragma unroll
4724 for(int c = 0; c < 2; ++c){
4725 int j = jt + 8 * n + 2 * t4 + c;
4726 s[n][c] = j >= loa && j < enda ? s[n][c] * scale : -INFINITY;
4727 s[n][c + 2] = j >= lob && j < endb ? s[n][c + 2] * scale : -INFINITY;
4728 xa = fmaxf(xa, s[n][c]); xb = fmaxf(xb, s[n][c + 2]);
4729 }
4730 #pragma unroll
4731 for(int x = 1; x < 4; x <<= 1){ xa = fmaxf(xa, __shfl_xor_sync(0xffffffffu, xa, x)); xb = fmaxf(xb, __shfl_xor_sync(0xffffffffu, xb, x)); }
4732 float na = fmaxf(ma, xa), nb = fmaxf(mb, xb), sa = 0.f, sb = 0.f;
4733 #pragma unroll
4734 for(int n = 0; n < KT / 8; ++n)
4735 #pragma unroll
4736 for(int c = 0; c < 2; ++c){
4737 s[n][c] = s[n][c] == -INFINITY ? 0.f : expf(s[n][c] - na);
4738 s[n][c + 2] = s[n][c + 2] == -INFINITY ? 0.f : expf(s[n][c + 2] - nb);
4739 sa += s[n][c]; sb += s[n][c + 2];
4740 }
4741 #pragma unroll
4742 for(int x = 1; x < 4; x <<= 1){ sa += __shfl_xor_sync(0xffffffffu, sa, x); sb += __shfl_xor_sync(0xffffffffu, sb, x); }
4743 float aa = ma == -INFINITY ? 0.f : expf(ma - na), ab = mb == -INFINITY ? 0.f : expf(mb - nb);
4744 la = fmaf(la, aa, sa); lb = fmaf(lb, ab, sb); ma = na; mb = nb;
4745 #pragma unroll
4746 for(int n = 0; n < NV; ++n){ o[n][0] *= aa; o[n][1] *= aa; o[n][2] *= ab; o[n][3] *= ab; }
4747 // The weighed values: 16 keys a step, two tiles of eight values a load.
4748 #pragma unroll
4749 for(int k = 0; k < KT / 16; ++k){
4750 unsigned pa[4] = {lk_h2(s[2 * k][0], s[2 * k][1]), lk_h2(s[2 * k][2], s[2 * k][3]),
4751 lk_h2(s[2 * k + 1][0], s[2 * k + 1][1]), lk_h2(s[2 * k + 1][2], s[2 * k + 1][3])};
4752 #pragma unroll
4753 for(int np = 0; np < NV / 2; ++np){
4754 unsigned b[4];
4755 lk_ldsm4_t(b, vs + (16 * k + ((mi & 1) ? 8 : 0) + ri) * VP + 16 * np + (mi >= 2 ? 8 : 0));
4756 lk_mma_h16(o[2 * np], pa, b);
4757 lk_mma_h16(o[2 * np + 1], pa, b + 2);
4758 }
4759 }
4760 __syncthreads();
4761 }
4762 // Each row's outputs, its sink in the denominator.
4763 #pragma unroll
4764 for(int half = 0; half < 2; ++half){
4765 int qi = half ? qb : qa, r = r0 + qi / HG, h = h0 + qi % HG;
4766 if(r >= rows) continue;
4767 float m = half ? mb : ma, l = half ? lb : la;
4768 float sk = sinks ? sinks[h] : -INFINITY, mf = fmaxf(sk, m);
4769 float f = m == -INFINITY ? 0.f : expf(m - mf), lf = fmaf(l, f, sinks ? expf(sk - mf) : 0.f);
4770 float* op = out + ((long)r * heads + h) * VD;
4771 #pragma unroll
4772 for(int n = 0; n < NV; ++n)
4773 *(float2*)(op + 8 * n + 2 * t4) = make_float2(o[n][2 * half] * f / lf, o[n][2 * half + 1] * f / lf);
4774 }
4775}
4776"#;
4777
4778pub const ATTN_MMA_QUERIES: usize = 64;
4780
4781pub fn attn_mma_keys(hd: usize, vd: usize) -> usize {
4783 if hd > 192 || vd > 192 {
4784 32
4785 } else {
4786 64
4787 }
4788}
4789
4790pub fn attn_mma_smem_bytes(hd: usize, vd: usize) -> usize {
4793 attn_mma_keys(hd, vd) * ((hd + 8) * 2 + (vd + 8) * 2)
4794}
4795
4796const ATTENTION_I8: &str = r#"
4816template<int HD, int VD, typename KE = float, typename VE = float, typename TE = float>
4817__global__ void __launch_bounds__(256) lk_kv_two_words(const KE* __restrict__ kc, const VE* __restrict__ vc,
4818 const int* __restrict__ pos, int rows, int len, int kvw, int vw, int voff, int lpad,
4819 signed char* __restrict__ k8h, signed char* __restrict__ k8l, float* __restrict__ ksc,
4820 signed char* __restrict__ v8h, signed char* __restrict__ v8l, float* __restrict__ vsc,
4821 const TE* __restrict__ kt = nullptr, long ktw = 0, long kto = 0, int kh = 0){
4822 __shared__ __align__(16) int th[32][VD / 4], tl[32][VD / 4]; // the run's value rows' words, a key a row
4823 int hi = pos ? min(len, *pos + rows - 1) : len;
4824 int j0 = 32 * blockIdx.x, kv = blockIdx.y;
4825 if(j0 >= hi) return;
4826 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
4827 long koff = (long)kv * (kt ? kh : HD), vo = voff + (long)kv * VD, row = (long)kv * lpad;
4828 auto amax4 = [](float m, float4 v){ return fmaxf(fmaxf(m, fmaxf(fabsf(v.x), fabsf(v.y))), fmaxf(fabsf(v.z), fabsf(v.w))); };
4829 constexpr int KI = (HD / 4 + 31) / 32, VI = (VD / 4 + 31) / 32;
4830 for(int kk = warp; kk < 32; kk += 8){
4831 int j = j0 + kk;
4832 bool in = j < hi;
4833 float4 v[KI > VI ? KI : VI];
4834 float a = 0.f;
4835 #pragma unroll
4836 for(int i = 0; i < KI; ++i){
4837 int c = lane + 32 * i, d = 4 * c;
4838 v[i] = !(in && c < HD / 4) ? make_float4(0.f, 0.f, 0.f, 0.f)
4839 : kt && d >= kh ? lk_from4(kt + (long)j * ktw + kto + (d - kh)) : lk_from4(kc + (long)j * kvw + koff + d);
4840 a = amax4(a, v[i]);
4841 }
4842 #pragma unroll
4843 for(int x = 16; x > 0; x >>= 1) a = fmaxf(a, __shfl_xor_sync(0xffffffffu, a, x));
4844 float s = lk_two_scale(a), rs = 1.f / s;
4845 #pragma unroll
4846 for(int i = 0; i < KI; ++i){
4847 int c = lane + 32 * i;
4848 if(c >= HD / 4) break;
4849 int h[4], l[4];
4850 lk_two_words_r(v[i].x, s, rs, h[0], l[0]); lk_two_words_r(v[i].y, s, rs, h[1], l[1]); lk_two_words_r(v[i].z, s, rs, h[2], l[2]); lk_two_words_r(v[i].w, s, rs, h[3], l[3]);
4851 *(int*)(k8h + (row + j) * HD + 4 * c) = lk_int8x4(h[0], h[1], h[2], h[3]);
4852 *(int*)(k8l + (row + j) * HD + 4 * c) = lk_int8x4(l[0], l[1], l[2], l[3]);
4853 }
4854 if(lane == 0) ksc[row + j] = in ? s : 0.f;
4855 a = 0.f;
4856 #pragma unroll
4857 for(int i = 0; i < VI; ++i){
4858 int c = lane + 32 * i;
4859 v[i] = in && c < VD / 4 ? lk_from4(vc + (long)j * vw + vo + 4 * c) : make_float4(0.f, 0.f, 0.f, 0.f);
4860 a = amax4(a, v[i]);
4861 }
4862 #pragma unroll
4863 for(int x = 16; x > 0; x >>= 1) a = fmaxf(a, __shfl_xor_sync(0xffffffffu, a, x));
4864 s = lk_two_scale(a); rs = 1.f / s;
4865 #pragma unroll
4866 for(int i = 0; i < VI; ++i){
4867 int c = lane + 32 * i;
4868 if(c >= VD / 4) break;
4869 int h[4], l[4];
4870 lk_two_words_r(v[i].x, s, rs, h[0], l[0]); lk_two_words_r(v[i].y, s, rs, h[1], l[1]); lk_two_words_r(v[i].z, s, rs, h[2], l[2]); lk_two_words_r(v[i].w, s, rs, h[3], l[3]);
4871 th[kk][c] = lk_int8x4(h[0], h[1], h[2], h[3]); tl[kk][c] = lk_int8x4(l[0], l[1], l[2], l[3]);
4872 }
4873 if(lane == 0) vsc[row + j] = in ? s : 0.f;
4874 }
4875 __syncthreads();
4876 long run = ((long)kv * (lpad / 32) + blockIdx.x) * VD * 32;
4877 // Each value column's 32 keys in the weights' order, 16 bytes a thread.
4878 const unsigned char* bh = (const unsigned char*)th;
4879 const unsigned char* bl = (const unsigned char*)tl;
4880 for(int c = threadIdx.x; c < VD * 2; c += 256){
4881 int d = c >> 1, half = c & 1;
4882 unsigned wh[4], wl[4];
4883 #pragma unroll
4884 for(int w = 0; w < 4; ++w){
4885 wh[w] = 0u; wl[w] = 0u;
4886 #pragma unroll
4887 for(int x = 0; x < 4; ++x){
4888 int kk = 16 * half + 2 * w + (x & 1) + 8 * (x >> 1);
4889 wh[w] |= (unsigned)bh[kk * VD + d] << (8 * x); wl[w] |= (unsigned)bl[kk * VD + d] << (8 * x);
4890 }
4891 }
4892 *(int4*)(v8h + run + d * 32 + 16 * half) = make_int4((int)wh[0], (int)wh[1], (int)wh[2], (int)wh[3]);
4893 *(int4*)(v8l + run + d * 32 + 16 * half) = make_int4((int)wl[0], (int)wl[1], (int)wl[2], (int)wl[3]);
4894 }
4895}
4896template<int HG, int HD, int VD>
4897__global__ void __launch_bounds__(128) lk_attention_i8(const float* __restrict__ q, const signed char* __restrict__ k8h,
4898 const signed char* __restrict__ k8l, const float* __restrict__ ksg, const signed char* __restrict__ v8h,
4899 const signed char* __restrict__ v8l, const float* __restrict__ vsg, int lpad, const int* __restrict__ pos,
4900 const float* __restrict__ sinks, float* __restrict__ out, int len, int win, int group, int heads, float scale, int rows,
4901 const float* __restrict__ qt = nullptr, long qp = 0, long qtp = 0, int kh = 0){
4902 constexpr int BQ = 64, BR = BQ / HG, KS = HD / 32;
4903 constexpr int KT = HD > 128 || VD > 128 ? 32 : 64;
4904 constexpr int KP = HD + 16, VP = KT + 16;
4905 extern __shared__ __align__(16) signed char lk_im[];
4906 signed char* kh8 = lk_im; // [KT][KP]: the tile's keys, high words
4907 signed char* kl8 = kh8 + KT * KP; // [KT][KP]: their low words
4908 signed char* vh8 = kl8 + KT * KP; // [VD][VP]: the tile's values a column a row, the keys in the weights' order
4909 signed char* vl8 = vh8 + VD * VP; // [VD][VP]: their low words
4910 float* ksc2 = (float*)(vl8 + VD * VP); // [2][KT]: each key's scale, a buffer a tile's parity
4911 float* vsc2 = ksc2 + 2 * KT; // [2][KT]: each value row's scale
4912 __shared__ int loq[BQ], endq[BQ], span[2];
4913 int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, g = lane >> 2, t4 = lane & 3;
4914 int r0 = blockIdx.x * BR, h0 = blockIdx.y * HG;
4915 long row = (long)(h0 / group) * lpad;
4916 if(tid < BQ){
4917 int r = r0 + tid / HG;
4918 int end = r < rows ? (pos ? *pos + r : len) : 0;
4919 endq[tid] = end; loq[tid] = win > 0 && end > win ? end - win : 0;
4920 }
4921 __syncthreads();
4922 if(tid == 0){
4923 int lo = 0x7fffffff, hi = 0;
4924 for(int i = 0; i < BQ; ++i) if(endq[i] > 0){ lo = min(lo, loq[i]); hi = max(hi, endq[i]); }
4925 span[0] = hi > 0 ? lo : 0; span[1] = hi;
4926 }
4927 // The warp's queries: rows g and g + 8 of its 16, each its two words under its largest magnitude's scale, as A
4928 // fragments of every 32 values of the head.
4929 int qa = 16 * warp + g, qb = qa + 8;
4930 auto qload = [&](int qi, int d) -> float4 {
4931 int r = r0 + qi / HG, h = h0 + qi % HG;
4932 if(r >= rows) return make_float4(0.f, 0.f, 0.f, 0.f);
4933 long u = (long)r * heads + h;
4934 return *(const float4*)(!qt ? q + u * HD + d : d < kh ? q + u * qp + d : qt + u * qtp + (d - kh));
4935 };
4936 auto amax4 = [](float m, float4 v){ return fmaxf(fmaxf(m, fmaxf(fabsf(v.x), fabsf(v.y))), fmaxf(fabsf(v.z), fabsf(v.w))); };
4937 float xa = 0.f, xb = 0.f;
4938 #pragma unroll
4939 for(int k = 0; k < KS; ++k)
4940 #pragma unroll
4941 for(int half = 0; half < 2; ++half){
4942 int d = 32 * k + 16 * half + 4 * t4;
4943 xa = amax4(xa, qload(qa, d)); xb = amax4(xb, qload(qb, d));
4944 }
4945 #pragma unroll
4946 for(int x = 1; x < 4; x <<= 1){ xa = fmaxf(xa, __shfl_xor_sync(0xffffffffu, xa, x)); xb = fmaxf(xb, __shfl_xor_sync(0xffffffffu, xb, x)); }
4947 const float sqa = lk_two_scale(xa), sqb = lk_two_scale(xb);
4948 int qh[KS][4], ql[KS][4];
4949 #pragma unroll
4950 for(int k = 0; k < KS; ++k)
4951 #pragma unroll
4952 for(int f = 0; f < 4; ++f){
4953 float4 v = qload(f & 1 ? qb : qa, 32 * k + 16 * (f >> 1) + 4 * t4);
4954 float s = f & 1 ? sqb : sqa;
4955 int h0_, l0_, h1_, l1_, h2_, l2_, h3_, l3_;
4956 lk_two_words(v.x, s, h0_, l0_); lk_two_words(v.y, s, h1_, l1_); lk_two_words(v.z, s, h2_, l2_); lk_two_words(v.w, s, h3_, l3_);
4957 qh[k][f] = lk_int8x4(h0_, h1_, h2_, h3_); ql[k][f] = lk_int8x4(l0_, l1_, l2_, l3_);
4958 }
4959 __syncthreads();
4960 // Tiles from a whole tile below the span's start, so each is whole runs of the values' order and whole scale chunks.
4961 int jlo = span[0] / KT * KT, jhi = span[1];
4962 int loa = loq[qa], enda = endq[qa], lob = loq[qb], endb = endq[qb];
4963 const float fqa = sqa * scale * 0.0078125f, fqb = sqb * scale * 0.0078125f;
4964 float o[VD / 8][4], ma = -INFINITY, mb = -INFINITY, la = 0.f, lb = 0.f;
4965 #pragma unroll
4966 for(int n = 0; n < VD / 8; ++n)
4967 #pragma unroll
4968 for(int c = 0; c < 4; ++c) o[n][c] = 0.f;
4969 // A tile's keys' two words and both its scales, into parity `pb`'s scale buffers; its value runs' two words. Past the
4970 // span zero. Each a `cp.async` group.
4971 auto load_k = [&](int jt){
4972 int pb = ((jt - jlo) / KT) & 1;
4973 for(int c = tid; c < KT * HD / 16; c += 128){
4974 int kj = c / (HD / 16), o16 = 16 * (c % (HD / 16));
4975 bool in = jt + kj < jhi;
4976 long at = (row + (in ? jt + kj : 0)) * HD + o16;
4977 lk_cp16(kh8 + kj * KP + o16, k8h + at, in); lk_cp16(kl8 + kj * KP + o16, k8l + at, in);
4978 }
4979 for(int c = tid; c < KT / 4; c += 128){
4980 bool in = jt + 4 * c < jhi;
4981 lk_cp16(ksc2 + KT * pb + 4 * c, ksg + row + (in ? jt + 4 * c : 0), in); lk_cp16(vsc2 + KT * pb + 4 * c, vsg + row + (in ? jt + 4 * c : 0), in);
4982 }
4983 asm volatile("cp.async.commit_group;\n");
4984 };
4985 auto load_v = [&](int jt){
4986 for(int c = tid; c < VD * KT / 16; c += 128){
4987 int d = c / (KT / 16), o16 = 16 * (c % (KT / 16)), r = (jt + o16) >> 5;
4988 bool in = 32 * r < jhi;
4989 long at = ((row / 32 + (in ? r : 0)) * VD + d) * 32 + (o16 & 31);
4990 lk_cp16(vh8 + d * VP + o16, v8h + at, in); lk_cp16(vl8 + d * VP + o16, v8l + at, in);
4991 }
4992 asm volatile("cp.async.commit_group;\n");
4993 };
4994 if(jlo < jhi){ load_k(jlo); load_v(jlo); }
4995 for(int jt = jlo; jt < jhi; jt += KT){
4996 asm volatile("cp.async.wait_group 1;\n");
4997 __syncthreads();
4998 const float* ksc = ksc2 + KT * (((jt - jlo) / KT) & 1);
4999 const float* vsc = vsc2 + KT * (((jt - jlo) / KT) & 1);
5000 // Scores: the warp's 16 queries against the tile's keys, every tile of eight keys at once; a score's high dot and its
5001 // two cross dots fold into one exact integer, 128·qₕ·kₕ + qₕ·kₗ + qₗ·kₕ, its low dot qₗ·kₗ, 128 times finer, into
5002 // another.
5003 float s[KT / 8][4];
5004 int mi = lane >> 3, ri = lane & 7;
5005 {
5006 int c[KT / 8][4];
5007 #pragma unroll
5008 for(int n = 0; n < KT / 8; ++n)
5009 #pragma unroll
5010 for(int i = 0; i < 4; ++i) c[n][i] = 0;
5011 #pragma unroll
5012 for(int k = 0; k < KS; ++k)
5013 #pragma unroll
5014 for(int np = 0; np < KT / 16; ++np){
5015 int bh[4];
5016 lk_ldsm_s8(bh, kh8 + (16 * np + (mi >= 2 ? 8 : 0) + ri) * KP + 32 * k + ((mi & 1) ? 16 : 0));
5017 lk_mma_s8(c[2 * np], qh[k], bh); lk_mma_s8(c[2 * np + 1], qh[k], bh + 2);
5018 }
5019 #pragma unroll
5020 for(int n = 0; n < KT / 8; ++n)
5021 #pragma unroll
5022 for(int i = 0; i < 4; ++i) c[n][i] *= 128;
5023 #pragma unroll
5024 for(int k = 0; k < KS; ++k){
5025 #pragma unroll
5026 for(int np = 0; np < KT / 16; ++np){
5027 int bl[4];
5028 lk_ldsm_s8(bl, kl8 + (16 * np + (mi >= 2 ? 8 : 0) + ri) * KP + 32 * k + ((mi & 1) ? 16 : 0));
5029 lk_mma_s8(c[2 * np], qh[k], bl); lk_mma_s8(c[2 * np + 1], qh[k], bl + 2);
5030 }
5031 #pragma unroll
5032 for(int np = 0; np < KT / 16; ++np){
5033 int bh[4];
5034 lk_ldsm_s8(bh, kh8 + (16 * np + (mi >= 2 ? 8 : 0) + ri) * KP + 32 * k + ((mi & 1) ? 16 : 0));
5035 lk_mma_s8(c[2 * np], ql[k], bh); lk_mma_s8(c[2 * np + 1], ql[k], bh + 2);
5036 }
5037 }
5038 int cl[KT / 8][4];
5039 #pragma unroll
5040 for(int n = 0; n < KT / 8; ++n)
5041 #pragma unroll
5042 for(int i = 0; i < 4; ++i) cl[n][i] = 0;
5043 #pragma unroll
5044 for(int k = 0; k < KS; ++k)
5045 #pragma unroll
5046 for(int np = 0; np < KT / 16; ++np){
5047 int bl[4];
5048 lk_ldsm_s8(bl, kl8 + (16 * np + (mi >= 2 ? 8 : 0) + ri) * KP + 32 * k + ((mi & 1) ? 16 : 0));
5049 lk_mma_s8(cl[2 * np], ql[k], bl); lk_mma_s8(cl[2 * np + 1], ql[k], bl + 2);
5050 }
5051 #pragma unroll
5052 for(int n = 0; n < KT / 8; ++n)
5053 #pragma unroll
5054 for(int i = 0; i < 4; ++i)
5055 s[n][i] = ((float)c[n][i] + (float)cl[n][i] * 0.0078125f) * (i < 2 ? fqa : fqb) * ksc[8 * n + 2 * t4 + (i & 1)];
5056 }
5057 __syncthreads();
5058 if(jt + KT < jhi) load_k(jt + KT);
5059 else asm volatile("cp.async.commit_group;\n");
5060 // Each row's running maximum and sum across its four lanes.
5061 float ya = -INFINITY, yb = -INFINITY;
5062 #pragma unroll
5063 for(int n = 0; n < KT / 8; ++n)
5064 #pragma unroll
5065 for(int c = 0; c < 2; ++c){
5066 int j = jt + 8 * n + 2 * t4 + c;
5067 if(!(j >= loa && j < enda)) s[n][c] = -INFINITY;
5068 if(!(j >= lob && j < endb)) s[n][c + 2] = -INFINITY;
5069 ya = fmaxf(ya, s[n][c]); yb = fmaxf(yb, s[n][c + 2]);
5070 }
5071 #pragma unroll
5072 for(int x = 1; x < 4; x <<= 1){ ya = fmaxf(ya, __shfl_xor_sync(0xffffffffu, ya, x)); yb = fmaxf(yb, __shfl_xor_sync(0xffffffffu, yb, x)); }
5073 float na = fmaxf(ma, ya), nb = fmaxf(mb, yb), sa = 0.f, sb = 0.f;
5074 #pragma unroll
5075 for(int n = 0; n < KT / 8; ++n)
5076 #pragma unroll
5077 for(int c = 0; c < 2; ++c){
5078 s[n][c] = s[n][c] == -INFINITY ? 0.f : expf(s[n][c] - na);
5079 s[n][c + 2] = s[n][c + 2] == -INFINITY ? 0.f : expf(s[n][c + 2] - nb);
5080 sa += s[n][c]; sb += s[n][c + 2];
5081 }
5082 #pragma unroll
5083 for(int x = 1; x < 4; x <<= 1){ sa += __shfl_xor_sync(0xffffffffu, sa, x); sb += __shfl_xor_sync(0xffffffffu, sb, x); }
5084 float aa = ma == -INFINITY ? 0.f : expf(ma - na), ab = mb == -INFINITY ? 0.f : expf(mb - nb);
5085 la = fmaf(la, aa, sa); lb = fmaf(lb, ab, sb); ma = na; mb = nb;
5086 #pragma unroll
5087 for(int n = 0; n < VD / 8; ++n){ o[n][0] *= aa; o[n][1] *= aa; o[n][2] *= ab; o[n][3] *= ab; }
5088 // The weights times each value row's scale, each row's three words under its largest's scale — so the weights move
5089 // the outputs a hundredth of what the operands' two words do — as A fragments of every 32 keys: positions 4·t4 … of
5090 // a run its tiles 4k and 4k + 1, 16 + 4·t4 … its tiles 4k + 2 and 4k + 3.
5091 float wa = 0.f, wb = 0.f;
5092 #pragma unroll
5093 for(int n = 0; n < KT / 8; ++n)
5094 #pragma unroll
5095 for(int c = 0; c < 2; ++c){
5096 float sv = vsc[8 * n + 2 * t4 + c];
5097 s[n][c] *= sv; s[n][c + 2] *= sv;
5098 wa = fmaxf(wa, s[n][c]); wb = fmaxf(wb, s[n][c + 2]);
5099 }
5100 #pragma unroll
5101 for(int x = 1; x < 4; x <<= 1){ wa = fmaxf(wa, __shfl_xor_sync(0xffffffffu, wa, x)); wb = fmaxf(wb, __shfl_xor_sync(0xffffffffu, wb, x)); }
5102 const float swa = lk_two_scale(wa), swb = lk_two_scale(wb);
5103 const float ra = 1.f / swa, rb = 1.f / swb;
5104 int ah[KT / 32][4], al[KT / 32][4], aq[KT / 32][4];
5105 #pragma unroll
5106 for(int k = 0; k < KT / 32; ++k)
5107 #pragma unroll
5108 for(int f = 0; f < 4; ++f){
5109 int n = 4 * k + 2 * (f >> 1), c = 2 * (f & 1);
5110 float sw = f & 1 ? swb : swa, rw = f & 1 ? rb : ra;
5111 float e[4] = {s[n][c], s[n][c + 1], s[n + 1][c], s[n + 1][c + 1]};
5112 int h[4], l[4], q[4];
5113 #pragma unroll
5114 for(int i = 0; i < 4; ++i){
5115 h[i] = __float2int_rn(e[i] * rw);
5116 float r1 = __fmaf_rn(-(float)h[i], sw, e[i]);
5117 l[i] = __float2int_rn(r1 * rw * 128.f);
5118 q[i] = __float2int_rn(__fmaf_rn(-(float)l[i], sw * 0.0078125f, r1) * rw * 16384.f);
5119 }
5120 ah[k][f] = lk_int8x4(h[0], h[1], h[2], h[3]); al[k][f] = lk_int8x4(l[0], l[1], l[2], l[3]); aq[k][f] = lk_int8x4(q[0], q[1], q[2], q[3]);
5121 }
5122 asm volatile("cp.async.wait_group 1;\n");
5123 __syncthreads();
5124 // The weighed values: four tiles of eight value columns at a time, each sum folded as the scores' are — the weights'
5125 // and values' leading products, 128·wₕ·vₕ + wₕ·vₗ + wₗ·vₕ, in one exact integer, the next, wₗ·vₗ + wₗₗ·vₕ, 128 times
5126 // finer, in another.
5127 const float fwa = swa * 0.0078125f, fwb = swb * 0.0078125f;
5128 constexpr int NG = VD / 32, NPG = VD / 16 / NG;
5129 #pragma unroll
5130 for(int gq = 0; gq < NG; ++gq){
5131 int c[2 * NPG][4];
5132 #pragma unroll
5133 for(int n = 0; n < 2 * NPG; ++n)
5134 #pragma unroll
5135 for(int i = 0; i < 4; ++i) c[n][i] = 0;
5136 #pragma unroll
5137 for(int k = 0; k < KT / 32; ++k)
5138 #pragma unroll
5139 for(int np = 0; np < NPG; ++np){
5140 int bh[4];
5141 lk_ldsm_s8(bh, vh8 + (16 * (gq * NPG + np) + (mi >= 2 ? 8 : 0) + ri) * VP + 32 * k + ((mi & 1) ? 16 : 0));
5142 lk_mma_s8(c[2 * np], ah[k], bh); lk_mma_s8(c[2 * np + 1], ah[k], bh + 2);
5143 }
5144 #pragma unroll
5145 for(int n = 0; n < 2 * NPG; ++n)
5146 #pragma unroll
5147 for(int i = 0; i < 4; ++i) c[n][i] *= 128;
5148 #pragma unroll
5149 for(int k = 0; k < KT / 32; ++k){
5150 #pragma unroll
5151 for(int np = 0; np < NPG; ++np){
5152 int bl[4];
5153 lk_ldsm_s8(bl, vl8 + (16 * (gq * NPG + np) + (mi >= 2 ? 8 : 0) + ri) * VP + 32 * k + ((mi & 1) ? 16 : 0));
5154 lk_mma_s8(c[2 * np], ah[k], bl); lk_mma_s8(c[2 * np + 1], ah[k], bl + 2);
5155 }
5156 #pragma unroll
5157 for(int np = 0; np < NPG; ++np){
5158 int bh[4];
5159 lk_ldsm_s8(bh, vh8 + (16 * (gq * NPG + np) + (mi >= 2 ? 8 : 0) + ri) * VP + 32 * k + ((mi & 1) ? 16 : 0));
5160 lk_mma_s8(c[2 * np], al[k], bh); lk_mma_s8(c[2 * np + 1], al[k], bh + 2);
5161 }
5162 }
5163 int cl[2 * NPG][4];
5164 #pragma unroll
5165 for(int n = 0; n < 2 * NPG; ++n)
5166 #pragma unroll
5167 for(int i = 0; i < 4; ++i) cl[n][i] = 0;
5168 #pragma unroll
5169 for(int k = 0; k < KT / 32; ++k){
5170 #pragma unroll
5171 for(int np = 0; np < NPG; ++np){
5172 int bl[4];
5173 lk_ldsm_s8(bl, vl8 + (16 * (gq * NPG + np) + (mi >= 2 ? 8 : 0) + ri) * VP + 32 * k + ((mi & 1) ? 16 : 0));
5174 lk_mma_s8(cl[2 * np], al[k], bl); lk_mma_s8(cl[2 * np + 1], al[k], bl + 2);
5175 }
5176 #pragma unroll
5177 for(int np = 0; np < NPG; ++np){
5178 int bh[4];
5179 lk_ldsm_s8(bh, vh8 + (16 * (gq * NPG + np) + (mi >= 2 ? 8 : 0) + ri) * VP + 32 * k + ((mi & 1) ? 16 : 0));
5180 lk_mma_s8(cl[2 * np], aq[k], bh); lk_mma_s8(cl[2 * np + 1], aq[k], bh + 2);
5181 }
5182 }
5183 #pragma unroll
5184 for(int n = 0; n < 2 * NPG; ++n)
5185 #pragma unroll
5186 for(int i = 0; i < 4; ++i)
5187 o[2 * gq * NPG + n][i] = fmaf((float)c[n][i] + (float)cl[n][i] * 0.0078125f, i < 2 ? fwa : fwb, o[2 * gq * NPG + n][i]);
5188 }
5189 __syncthreads();
5190 if(jt + KT < jhi) load_v(jt + KT);
5191 else asm volatile("cp.async.commit_group;\n");
5192 }
5193 asm volatile("cp.async.wait_group 0;\n");
5194 // Each row's outputs, its sink in the denominator.
5195 #pragma unroll
5196 for(int half = 0; half < 2; ++half){
5197 int qi = half ? qb : qa, r = r0 + qi / HG, h = h0 + qi % HG;
5198 if(r >= rows) continue;
5199 float m = half ? mb : ma, l = half ? lb : la;
5200 float sk = sinks ? sinks[h] : -INFINITY, mf = fmaxf(sk, m);
5201 float f = m == -INFINITY ? 0.f : expf(m - mf), lf = fmaf(l, f, sinks ? expf(sk - mf) : 0.f);
5202 float* op = out + ((long)r * heads + h) * VD;
5203 #pragma unroll
5204 for(int n = 0; n < VD / 8; ++n)
5205 *(float2*)(op + 8 * n + 2 * t4) = make_float2(o[n][2 * half] * f / lf, o[n][2 * half + 1] * f / lf);
5206 }
5207}
5208"#;
5209
5210pub const ATTN_I8_QUERIES: usize = 64;
5212
5213pub const fn attn_i8_keys(hd: usize, vd: usize) -> usize {
5215 if hd > 128 || vd > 128 {
5216 32
5217 } else {
5218 64
5219 }
5220}
5221
5222pub const fn attn_i8_smem_bytes(hd: usize, vd: usize) -> usize {
5225 let kt = attn_i8_keys(hd, vd);
5226 2 * kt * (hd + 16) + 2 * vd * (kt + 16) + 16 * kt
5227}
5228
5229const _: () = assert!(
5230 attn_i8_smem_bytes(128, 128) <= 48 * 1024 && attn_i8_smem_bytes(256, 256) <= 48 * 1024,
5231 "the two-word attention keeps the default shared memory at every width it takes"
5232);
5233
5234pub fn attn_i8_rows(len: usize) -> usize {
5236 len.next_multiple_of(64)
5237}
5238
5239pub fn attn_i8_scratch(len: usize, groups: usize, hd: usize, vd: usize) -> [usize; 3] {
5242 let rows = groups * attn_i8_rows(len);
5243 [2 * rows * hd, 2 * rows * vd, 2 * rows * 4]
5244}
5245
5246const SLICE: &str = r#"
5247template<typename XE>
5248__global__ void lk_slice(const XE* __restrict__ x, float* __restrict__ y, long n, int every, int from, int width){
5249 long o = (long)blockIdx.x * blockDim.x + threadIdx.x; if(o >= n) return;
5250 long g = o / width;
5251 y[o] = lk_from(x[g * every + from + (o - g * width)]);
5252}
5253"#;
5254
5255const JOIN: &str = r#"
5258__global__ void lk_join(const float* __restrict__ a, const float* __restrict__ b, lk_out y,
5259 const int* __restrict__ yrow, long ypitch, long n, int ha, int hb, int heads = 1, long yz = 0){
5260 long o = (long)blockIdx.x * blockDim.x + threadIdx.x; if(o >= n) return;
5261 long u = o / (ha + hb); int i = (int)(o - u * (ha + hb));
5262 float v = i < ha ? a[u * ha + i] : b[u * hb + (i - ha)];
5263 if(yz){ long r = u / heads; y[r * yz + (long)(yrow[r] - 1) * ypitch + (o - r * heads * (ha + hb))] = v; return; }
5264 LK_ROW(y, yrow, ypitch)[o] = v;
5265}
5266"#;
5267
5268const HEADS: &str = r#"
5283template<int F, int Q, int M>
5284__global__ void lk_head_gemv(lk_w w, const float* __restrict__ x, const signed char* __restrict__ xq,
5285 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_out y,
5286 int H, int a, int b, long xp, long xo, const int* __restrict__ yrow, long ypitch, long yp, long yo){
5287 int o = (blockIdx.x * blockDim.x + threadIdx.x) >> 5; if(o >= H * b) return;
5288 long off = (long)(o / b) * xp + xo, ld = (long)H * xp;
5289 float v[M];
5290 constexpr bool X = lk_f32_acts<Q>();
5291 lk_dot<F, Q, M>(w, o, a, X ? x + off : nullptr, X ? nullptr : xq + off, Q == 2 ? xl + off : nullptr,
5292 X ? nullptr : xs + (off >> 5), ld, v);
5293 if((threadIdx.x & 31) == 0){
5294 lk_out yr = LK_ROW(y, yrow, ypitch) + (long)(o / b) * yp + yo + o % b;
5295 #pragma unroll
5296 for(int t = 0; t < M; ++t) yr[(long)t * H * yp] = v[t];
5297 }
5298}
5299// The bytes of a format's smallest unit decoded alone and the sub-blocks it holds: a K-quant super-block (eight), a
5300// legacy block (one); a Q8_0 block is staged as its 32 values and, after a row's values, its scale.
5301template<int F> __host__ __device__ constexpr int lk_unit_bytes(){
5302 return F == 2 ? 18 : F == 3 ? 20 : F == 6 ? 22 : F == 7 ? 24 : F == 39 ? 17 : F == 20 ? 18 : F == 10 ? 84 : F == 11 ? 110 :
5303 F == 12 ? 144 : F == 13 ? 176 : F == 14 ? 210 : F == 23 ? 136 : 34;
5304}
5305template<int F> __host__ __device__ constexpr int lk_unit_subs(){ return F == 10 || F == 11 || F == 12 || F == 13 || F == 14 || F == 23 ? 8 : 1; }
5306// A staged row: the units of `W` sub-blocks (and a unit more, should they straddle two), 15 bytes for the start's
5307// alignment, rounded to an odd number of words so the lanes' rows fall on distinct banks.
5308template<int F, int W> __host__ __device__ constexpr int lk_axpy_pitch(){
5309 return F == 8 ? ((W * 34 + 3) / 4 % 2 == 0 ? (W * 34 + 3) / 4 * 4 + 4 : (W * 34 + 3) / 4 * 4)
5310 : ((((W + lk_unit_subs<F>() - 1) / lk_unit_subs<F>() + 1) * lk_unit_bytes<F>() + 15 + 3) / 4 % 2 == 0
5311 ? (((W + lk_unit_subs<F>() - 1) / lk_unit_subs<F>() + 1) * lk_unit_bytes<F>() + 15 + 3) / 4 * 4 + 4
5312 : (((W + lk_unit_subs<F>() - 1) / lk_unit_subs<F>() + 1) * lk_unit_bytes<F>() + 15 + 3) / 4 * 4);
5313}
5314template<int O, typename T>
5315__device__ __forceinline__ void lk_fly_step(T* acc, int lane){
5316 bool up = lane & O;
5317 #pragma unroll
5318 for(int i = 0; i < O; ++i){
5319 T send = up ? acc[i] : acc[i + O];
5320 T keep = up ? acc[i + O] : acc[i];
5321 acc[i] = keep + __shfl_xor_sync(0xffffffffu, send, O);
5322 }
5323}
5324// The sums over a warp's lanes of each lane's 32 values, lane `l` ending with sum `l` in `acc[0]`.
5325template<typename T>
5326__device__ __forceinline__ void lk_fly(T* acc, int lane){
5327 lk_fly_step<16>(acc, lane); lk_fly_step<8>(acc, lane); lk_fly_step<4>(acc, lane); lk_fly_step<2>(acc, lane); lk_fly_step<1>(acc, lane);
5328}
5329template<int F, int Q, int M, int W, int R>
5330__global__ void __launch_bounds__(32 * W * R) lk_head_axpy_staged(lk_w w, const float* __restrict__ x,
5331 const signed char* __restrict__ xq, const signed char* __restrict__ xl, const float* __restrict__ xs,
5332 lk_out y, int H, int a, int b, long xp, long xo, const int* __restrict__ yrow, long ypitch, long yp,
5333 long yo, int tile){
5334 typedef typename lk_acc<Q>::t acc_t;
5335 constexpr int US = lk_unit_subs<F>(), UB = lk_unit_bytes<F>(), PITCH = lk_axpy_pitch<F, W>();
5336 extern __shared__ __align__(16) unsigned char rows[];
5337 __shared__ acc_t red[R][W][32];
5338 int nsb = b >> 5, h = blockIdx.y, s0 = blockIdx.x * W, u0 = s0 / US;
5339 int units = min((s0 + W - 1) / US - u0 + 1, (nsb + US - 1) / US - u0);
5340 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, wsb = warp % W, grp = warp / W, sb = s0 + wsb;
5341 long ld = (long)H * xp;
5342 for(int t = 0; t < M; ++t){
5343 acc_t acc[32];
5344 #pragma unroll
5345 for(int i = 0; i < 32; ++i) acc[i] = 0;
5346 for(int e0 = 0; e0 < a; e0 += tile){
5347 int here = min(tile, a - e0);
5348 if(t == 0 || tile < a){
5349 __syncthreads();
5350 if(F == 8){
5351 int nb = b >> 5;
5352 for(int c = threadIdx.x; c < here * units * 2; c += blockDim.x){
5353 int e = c / (units * 2), u = (c % (units * 2)) >> 1, half = c & 1;
5354 long row = (long)h * a + e0 + e;
5355 uint4 v = __ldg((const uint4*)(w.q + row * b + (long)(u0 + u) * 32) + half);
5356 unsigned int* d = (unsigned int*)(rows + e * PITCH + u * 32 + half * 16);
5357 d[0] = v.x; d[1] = v.y; d[2] = v.z; d[3] = v.w;
5358 if(half == 0) *(__half*)(rows + e * PITCH + W * 32 + u * 2) = w.s[row * nb + u0 + u];
5359 }
5360 } else {
5361 int chunks = (units * UB + 30) / 16;
5362 for(int c = threadIdx.x; c < here * chunks; c += blockDim.x){
5363 int e = c / chunks, k = c % chunks;
5364 const unsigned char* start = w.b + ((long)h * a + e0 + e) * w.rb + (long)u0 * UB;
5365 uint4 v = __ldg((const uint4*)((unsigned long)start & ~15ul) + k);
5366 unsigned int* d = (unsigned int*)(rows + e * PITCH + k * 16);
5367 d[0] = v.x; d[1] = v.y; d[2] = v.z; d[3] = v.w;
5368 }
5369 }
5370 __syncthreads();
5371 }
5372 if(sb < nsb){
5373 for(int e = grp * 32 + lane; e < here; e += 32 * R){
5374 long k = (long)h * xp + xo + e0 + e;
5375 float xv;
5376 if(lk_f32_acts<Q>()) xv = x[t * ld + k];
5377 else {
5378 float s = xs[t * (ld >> 5) + (k >> 5)];
5379 xv = Q == 1 ? s * (float)xq[t * ld + k] : s * ((float)xq[t * ld + k] + 0.0078125f * (float)xl[t * ld + k]);
5380 }
5381 if(F == 8){
5382 const signed char* q = (const signed char*)(rows + e * PITCH) + (sb - u0) * 32;
5383 float d = __half2float(*(const __half*)(rows + e * PITCH + W * 32 + (sb - u0) * 2));
5384 #pragma unroll
5385 for(int i = 0; i < 32; ++i) acc[i] += (acc_t)(d * (float)q[i]) * (acc_t)xv;
5386 } else {
5387 int delta = (int)(((unsigned long)(w.b + ((long)h * a + e0 + e) * w.rb + (long)u0 * UB)) & 15ul);
5388 signed char v[32]; float d[2], c[2];
5389 lk_sub<F>(rows + e * PITCH + delta, sb - u0 * US, v, d, c);
5390 #pragma unroll
5391 for(int i = 0; i < 32; ++i) acc[i] += (acc_t)__fadd_rn(__fmul_rn(d[i >> 4], (float)v[i]), c[i >> 4]) * (acc_t)xv;
5392 }
5393 }
5394 }
5395 }
5396 lk_fly(acc, lane);
5397 red[grp][wsb][lane] = acc[0];
5398 __syncthreads();
5399 if(grp == 0 && sb < nsb){
5400 acc_t sum = red[0][wsb][lane];
5401 #pragma unroll
5402 for(int r = 1; r < R; ++r) sum += red[r][wsb][lane];
5403 LK_ROW(y, yrow, ypitch)[((long)t * H + h) * yp + yo + sb * 32 + lane] = (float)sum;
5404 }
5405 __syncthreads();
5406 }
5407}
5408template<int F, int Q, int M>
5409__global__ void lk_head_axpy(lk_w w, const float* __restrict__ x, const signed char* __restrict__ xq,
5410 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_out y,
5411 int H, int a, int b, long xp, long xo, const int* __restrict__ yrow, long ypitch, long yp, long yo){
5412 typedef typename lk_acc<Q>::t acc_t;
5413 __shared__ acc_t red[8][M][32];
5414 int j = blockIdx.x, h = blockIdx.y, lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
5415 long ld = (long)H * xp;
5416 acc_t acc[M];
5417 #pragma unroll
5418 for(int t = 0; t < M; ++t) acc[t] = 0;
5419 for(int e = warp; e < a; e += 8){
5420 long row = (long)h * a + e, k = (long)h * xp + xo + e;
5421 float wv;
5422 if(F == 8) wv = __half2float(w.s[row * (b >> 5) + j]) * (float)w.q[row * b + j * 32 + lane];
5423 else {
5424 float d, c;
5425 int lv = lk_level<F>(w.b + row * w.rb, j, lane, d, c);
5426 wv = __fadd_rn(__fmul_rn(d, (float)lv), c);
5427 }
5428 #pragma unroll
5429 for(int t = 0; t < M; ++t){
5430 float xv;
5431 if(lk_f32_acts<Q>()) xv = x[t * ld + k];
5432 else {
5433 float s = xs[t * (ld >> 5) + (k >> 5)];
5434 xv = Q == 1 ? s * (float)xq[t * ld + k] : s * ((float)xq[t * ld + k] + 0.0078125f * (float)xl[t * ld + k]);
5435 }
5436 acc[t] += (acc_t)wv * (acc_t)xv;
5437 }
5438 }
5439 #pragma unroll
5440 for(int t = 0; t < M; ++t) red[warp][t][lane] = acc[t];
5441 __syncthreads();
5442 if(warp == 0){
5443 #pragma unroll
5444 for(int t = 0; t < M; ++t){
5445 acc_t sum = 0;
5446 #pragma unroll
5447 for(int u = 0; u < 8; ++u) sum += red[u][t][lane];
5448 LK_ROW(y, yrow, ypitch)[((long)t * H + h) * yp + yo + j * 32 + lane] = (float)sum;
5449 }
5450 }
5451}
5452"#;
5453
5454const HEAD_TILED: &str = r#"
5466template<int F, int Q, bool OUT, typename XE = float>
5467__global__ void __launch_bounds__(256) lk_head_tiled(lk_w w, const signed char* __restrict__ xq,
5468 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_out y, int H, int a, int b, long xp,
5469 long xo, const int* __restrict__ yrow, long ypitch, long yp, long yo, int M, const XE* __restrict__ x = nullptr,
5470 long xr = 0, const int* __restrict__ upto = nullptr, int after = 0){
5471 __shared__ __align__(16) float Xs[32][128]; // [input][row]
5472 __shared__ __align__(16) float Ws[32][64]; // [input][output]
5473 int h = blockIdx.z, m0 = blockIdx.y * 128, o0 = blockIdx.x * 64, tid = threadIdx.x, tr = tid >> 4, to = tid & 15;
5474 if(upto) M = min(M, *upto + after);
5475 if(m0 >= M) return;
5476 long ld = xr ? xr : (long)H * xp;
5477 // A stage's 32 inputs by the block's 64 outputs are 64 sub-blocks: each output row's sub-block of the stage when the
5478 // rows are the outputs, each input row's two sub-blocks of the outputs otherwise. A thread takes eight values `8·sq`
5479 // … `+ 7` of one, its raw words fetched a stage ahead: a warp's 32 output rows at one `sq` (each store its own bank),
5480 // or eight threads to an input row (two 16-byte stores each, the row's outputs in order).
5481 int sq = OUT ? tid >> 6 : tid & 3;
5482 int srow = OUT ? tid & 63 : tid >> 3, shalf = OUT ? 0 : (tid >> 2) & 1;
5483 int K = OUT ? a : b, nb = K >> 5;
5484 long wrow = OUT ? (long)h * b + o0 + srow : (long)h * a + srow;
5485 bool wout = OUT ? o0 + srow < b : o0 + 32 * shalf < b;
5486 lk_raw<F> raw;
5487 auto fetch = [&](int i0){
5488 int ks = OUT ? i0 >> 5 : (o0 >> 5) + shalf;
5489 if(wout && (OUT || i0 + srow < a)) lk_fetch<F>(raw, w, wrow + (OUT ? 0 : i0), K, nb, ks);
5490 };
5491 float acc[8][4];
5492 #pragma unroll
5493 for(int r = 0; r < 8; ++r)
5494 #pragma unroll
5495 for(int o = 0; o < 4; ++o) acc[r][o] = 0.f;
5496 fetch(0);
5497 for(int i0 = 0; i0 < a; i0 += 32){
5498 // The rows' inputs, four a thread at a time, a warp's 32 rows at one input so each store lands on its own bank: as
5499 // computed under Q 3, as the inputs' quantized blocks hold them otherwise.
5500 #pragma unroll
5501 for(int u2 = 0; u2 < 4; ++u2){
5502 int c = tid + u2 * 256, r = c & 127, i = (c >> 7) * 4, m = m0 + r;
5503 float v[4] = {0.f, 0.f, 0.f, 0.f};
5504 if(m < M && i0 + i < a){
5505 long k = (long)h * xp + xo + i0 + i;
5506 if(Q == 3){
5507 float4 f = lk_from4(x + m * ld + k);
5508 v[0] = f.x; v[1] = f.y; v[2] = f.z; v[3] = f.w;
5509 } else {
5510 float sc = xs[m * (ld >> 5) + (k >> 5)];
5511 int hi = *(const int*)(xq + m * ld + k), lo = Q == 2 ? *(const int*)(xl + m * ld + k) : 0;
5512 #pragma unroll
5513 for(int u = 0; u < 4; ++u){
5514 float qv = (float)(signed char)(hi >> (8 * u));
5515 v[u] = Q == 1 ? sc * qv : sc * (qv + 0.0078125f * (float)(signed char)(lo >> (8 * u)));
5516 }
5517 }
5518 }
5519 #pragma unroll
5520 for(int u = 0; u < 4; ++u) Xs[i + u][r] = v[u];
5521 }
5522 // The stage's weights from their raw words: values 8·sq … 8·sq + 7 of this thread's sub-block, each the
5523 // dequantizer's `d·level + c`.
5524 {
5525 int ks = OUT ? i0 >> 5 : (o0 >> 5) + shalf;
5526 int lv[8]; float d[2], c[2];
5527 bool live = wout && (OUT || i0 + srow < a);
5528 if(live) lk_unpack<F>(raw, ks, lv, d, c);
5529 float wv[8];
5530 #pragma unroll
5531 for(int e = 0; e < 8; ++e){
5532 int l = 8 * sq + e;
5533 wv[e] = live ? __fadd_rn(__fmul_rn(d[l >> 4], (float)(signed char)(lv[l >> 2] >> (8 * (l & 3)))), c[l >> 4]) : 0.f;
5534 }
5535 if(OUT){
5536 #pragma unroll
5537 for(int e = 0; e < 8; ++e) Ws[8 * sq + e][srow] = wv[e];
5538 } else {
5539 float4* dst = (float4*)&Ws[srow][32 * shalf + 8 * sq];
5540 dst[0] = make_float4(wv[0], wv[1], wv[2], wv[3]); dst[1] = make_float4(wv[4], wv[5], wv[6], wv[7]);
5541 }
5542 }
5543 // The next stage's weights are fetched while this one multiplies.
5544 if(i0 + 32 < a) fetch(i0 + 32);
5545 __syncthreads();
5546 #pragma unroll 8
5547 for(int i = 0; i < 32; ++i){
5548 float4 x0 = *(const float4*)&Xs[i][8 * tr], x1 = *(const float4*)&Xs[i][8 * tr + 4];
5549 float4 wv = *(const float4*)&Ws[i][4 * to];
5550 float xa[8] = {x0.x, x0.y, x0.z, x0.w, x1.x, x1.y, x1.z, x1.w}, wa[4] = {wv.x, wv.y, wv.z, wv.w};
5551 #pragma unroll
5552 for(int r = 0; r < 8; ++r)
5553 #pragma unroll
5554 for(int o = 0; o < 4; ++o) acc[r][o] = fmaf(xa[r], wa[o], acc[r][o]);
5555 }
5556 __syncthreads();
5557 }
5558 #pragma unroll
5559 for(int r = 0; r < 8; ++r){
5560 int m = m0 + 8 * tr + r;
5561 if(m >= M) continue;
5562 lk_out yr = LK_ROW(y, yrow, ypitch) + ((long)m * H + h) * yp + yo + o0 + 4 * to;
5563 #pragma unroll
5564 for(int o = 0; o < 4; ++o) if(o0 + 4 * to + o < b) yr[o] = acc[r][o];
5565 }
5566}
5567"#;
5568
5569const ARGMAX: &str = r#"
5570// The first maximum of a block's values (`best`, at `at`; `at` −1 for none), every thread's own first, reduced so the
5571// earlier index wins a tie.
5572__device__ __forceinline__ void lk_first_max(float& best, int& at, float* bv, int* bi){
5573 for(int o = 16; o > 0; o >>= 1){
5574 float ov = __shfl_down_sync(0xffffffffu, best, o); int oi = __shfl_down_sync(0xffffffffu, at, o);
5575 if(oi >= 0 && (at < 0 || ov > best || (ov == best && oi < at))){ best = ov; at = oi; }
5576 }
5577 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
5578 __syncthreads();
5579 if(lane == 0){ bv[warp] = best; bi[warp] = at; }
5580 __syncthreads();
5581 if(threadIdx.x == 0){
5582 for(int w = 1; w < nw; ++w)
5583 if(bi[w] >= 0 && (at < 0 || bv[w] > best || (bv[w] == best && bi[w] < at))){ best = bv[w]; at = bi[w]; }
5584 }
5585}
5586template<bool ADV>
5587__global__ void __launch_bounds__(256) lk_argmax(const float* __restrict__ x, int n, int* __restrict__ out, int* __restrict__ tok,
5588 int* __restrict__ pos, int step, int* __restrict__ gen, int* __restrict__ count,
5589 float* __restrict__ pv, int* __restrict__ pi, int* __restrict__ cnt, int seqs = 0, int genz = 0){
5590 __shared__ float bv[32]; __shared__ int bi[32];
5591 __shared__ int last;
5592 // With `seqs`, grid row `y` is a batch's sequence: its logits, result, token, position, count and run its own, the
5593 // run `genz` tokens on from the last sequence's.
5594 if(seqs){
5595 long z = blockIdx.y;
5596 x += z * n; out += z; pv += z * gridDim.x; pi += z * gridDim.x; cnt += z;
5597 if(tok){ tok += z; pos += z; gen += z * genz; count += z; }
5598 }
5599 int run = (n + gridDim.x - 1) / gridDim.x, from = blockIdx.x * run, to = min(n, from + run);
5600 float best = -INFINITY; int at = -1;
5601 for(int i = from + threadIdx.x; i < to; i += blockDim.x){ float v = x[i]; if(at < 0 || v > best){ best = v; at = i; } }
5602 lk_first_max(best, at, bv, bi);
5603 if(threadIdx.x == 0){ pv[blockIdx.x] = best; pi[blockIdx.x] = at; }
5604 __threadfence();
5605 __syncthreads();
5606 if(threadIdx.x == 0) last = atomicAdd(cnt, 1) == gridDim.x - 1;
5607 __syncthreads();
5608 if(!last) return;
5609 __threadfence();
5610 best = -INFINITY; at = -1;
5611 // The blocks' runs are in order: a thread's blocks, then the threads, keep the first maximum.
5612 for(int k = threadIdx.x; k < gridDim.x; k += blockDim.x){
5613 float v = __ldcg(pv + k); int i = __ldcg(pi + k);
5614 if(i >= 0 && (at < 0 || v > best)){ best = v; at = i; }
5615 }
5616 lk_first_max(best, at, bv, bi);
5617 if(threadIdx.x == 0){
5618 *out = at + 1;
5619 if(ADV){ *tok = at + 1; *pos = *pos + step; gen[*count] = at + 1; *count = *count + 1; }
5620 *cnt = 0;
5621 }
5622}
5623"#;
5624
5625
5626const CONV: &str = r#"
5635// Position `p`'s slot (from 0, positions before the first too) of a ring of `T` rows.
5636__device__ __forceinline__ int lk_slot(int p, int T){ return ((p % T) + T) % T; }
5637template<bool SILU, bool L2, typename A>
5638__global__ void lk_conv(const float* __restrict__ x, const float* __restrict__ wc, float* __restrict__ cs,
5639 float* __restrict__ y, int C, int T, int R, int nl2, float eps, const int* __restrict__ pos){
5640 __shared__ A red[32];
5641 int c = blockIdx.x * blockDim.x + threadIdx.x;
5642 const int q0 = *pos - 1;
5643 for(int r = 0; r < R; ++r){
5644 float v = 0.f;
5645 if(c < C){
5646 const int q = q0 + r;
5647 const float cur = x[(long)r * C + c];
5648 cs[(long)lk_slot(q, T) * C + c] = cur;
5649 A sum = 0;
5650 for(int t = 0; t < T - 1; ++t) sum += (A)cs[(long)lk_slot(q - (T - 1) + t, T) * C + c] * (A)wc[(long)t * C + c];
5651 sum += (A)cur * (A)wc[(long)(T - 1) * C + c];
5652 v = (float)sum;
5653 if(SILU) v = v / (1.0f + expf(-v));
5654 }
5655 if(L2 && blockIdx.x < nl2){
5656 A den = sqrt(lk_block_sum(c < C ? (A)v * (A)v : (A)0, red) + (A)eps);
5657 v = (float)((A)v / den);
5658 }
5659 if(c < C) y[(long)r * C + c] = v;
5660 }
5661}
5662template<bool SILU, bool L2, typename A>
5663__global__ void lk_conv_rows(const float* __restrict__ x, const float* __restrict__ wc, const float* __restrict__ cs,
5664 float* __restrict__ y, int C, int T, int R, int nl2, float eps, const int* __restrict__ pos){
5665 __shared__ A red[32];
5666 int c = blockIdx.x * blockDim.x + threadIdx.x, r = blockIdx.y;
5667 const int q0 = *pos - 1;
5668 float v = 0.f;
5669 if(c < C){
5670 A sum = 0;
5671 for(int t = 0; t < T; ++t){
5672 int j = r - (T - 1) + t;
5673 sum += (A)(j >= 0 ? x[(long)j * C + c] : cs[(long)lk_slot(q0 + j, T) * C + c]) * (A)wc[(long)t * C + c];
5674 }
5675 v = (float)sum;
5676 if(SILU) v = v / (1.0f + expf(-v));
5677 }
5678 if(L2 && blockIdx.x < nl2){
5679 A den = sqrt(lk_block_sum(c < C ? (A)v * (A)v : (A)0, red) + (A)eps);
5680 v = (float)((A)v / den);
5681 }
5682 if(c < C) y[(long)r * C + c] = v;
5683}
5684// One causal convolution of `lk_conv_parts`: its input row, weights, ring and output, its channels `C`, its blocks
5685// `nb`, and whether each head of its output is normed.
5686struct lk_conv_part { const float* x; const float* wc; float* cs; float* y; int C; int nb; int l2; };
5687// With `seqs`, grid row `y` is a batch's sequence: each part's input row, ring and output its part of one array of them,
5688// its position `pos[y]`.
5689struct lk_convs { lk_conv_part p[4]; };
5690// One row of `np` causal convolutions through the SiLU in one launch, their blocks one after another: block `b` the
5691// part whose blocks hold it, each channel's sum — and a normed part's head of `blockDim.x` channels — as `lk_conv<true,
5692// L2>` computes them over one row.
5693template<typename A>
5694__global__ void lk_conv_parts(lk_convs ps, int np, int T, float eps, const int* __restrict__ pos, int seqs = 0){
5695 __shared__ A red[32];
5696 int b = blockIdx.x, p = 0;
5697 while(p + 1 < np && b >= ps.p[p].nb){ b -= ps.p[p].nb; ++p; }
5698 int C = ps.p[p].C, c = b * blockDim.x + threadIdx.x;
5699 long z = seqs ? blockIdx.y : 0;
5700 const int q = pos[z] - 1;
5701 const float* __restrict__ x = ps.p[p].x + z * C;
5702 const float* __restrict__ wc = ps.p[p].wc;
5703 float* __restrict__ cs = ps.p[p].cs + z * T * C;
5704 float* __restrict__ y = ps.p[p].y + z * C;
5705 float v = 0.f;
5706 if(c < C){
5707 const float cur = x[c];
5708 cs[(long)lk_slot(q, T) * C + c] = cur;
5709 A sum = 0;
5710 for(int t = 0; t < T - 1; ++t) sum += (A)cs[(long)lk_slot(q - (T - 1) + t, T) * C + c] * (A)wc[(long)t * C + c];
5711 sum += (A)cur * (A)wc[(long)(T - 1) * C + c];
5712 v = (float)sum;
5713 v = v / (1.0f + expf(-v));
5714 }
5715 if(ps.p[p].l2){
5716 A den = sqrt(lk_block_sum(c < C ? (A)v * (A)v : (A)0, red) + (A)eps);
5717 v = (float)((A)v / den);
5718 }
5719 if(c < C) y[c] = v;
5720}
5721// A block of `R` rows from position `pos`: its last rows, up to `T`, each into its slot.
5722__global__ void lk_conv_carry(const float* __restrict__ x, float* __restrict__ cs, int C, int T, int R, const int* __restrict__ pos){
5723 int c = blockIdx.x * blockDim.x + threadIdx.x; if(c >= C) return;
5724 const int q0 = *pos - 1;
5725 for(int j = max(0, R - T); j < R; ++j) cs[(long)lk_slot(q0 + j, T) * C + c] = x[(long)j * C + c];
5726}
5727"#;
5728
5729const L2NORM: &str = r#"
5731template<typename A>
5732__global__ void lk_l2norm(const float* __restrict__ x, float* __restrict__ y, int cols, float eps){
5733 __shared__ A red[32];
5734 long base = (long)blockIdx.x * cols;
5735 A acc = 0;
5736 for(int i = threadIdx.x; i < cols; i += blockDim.x) acc += (A)x[base + i] * (A)x[base + i];
5737 A den = sqrt(lk_block_sum(acc, red) + (A)eps);
5738 for(int i = threadIdx.x; i < cols; i += blockDim.x) y[base + i] = (float)((A)x[base + i] / den);
5739}
5740"#;
5741
5742const DECAY: &str = r#"
5744__global__ void lk_decay(const float* __restrict__ ar, const float* __restrict__ dt, const float* __restrict__ al,
5745 float* __restrict__ y, int n, int heads){
5746 int i = blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
5747 int h = i % heads;
5748 y[i] = powf(1.0f + expf(ar[i] + dt[h]), al[h]);
5749}
5750"#;
5751
5752const GATES: &str = r#"
5757__global__ void lk_gates(const float* __restrict__ br, const float* __restrict__ ar, const float* __restrict__ dt,
5758 const float* __restrict__ al, float* __restrict__ beta, float* __restrict__ decay, int n, int heads){
5759 int i = blockIdx.x * blockDim.x + threadIdx.x; if(i >= n) return;
5760 int h = i % heads;
5761 beta[i] = 1.0f / (1.0f + expf(-br[i]));
5762 decay[i] = powf(1.0f + expf(ar[i] + dt[h]), al[h]);
5763}
5764__global__ void __launch_bounds__(256) lk_gate_proj(const float* __restrict__ wb, const float* __restrict__ wa,
5765 const float* __restrict__ x, const float* __restrict__ dt, const float* __restrict__ al, float* __restrict__ beta,
5766 float* __restrict__ decay, int K, int H, int parts){
5767 __shared__ double run[2][8];
5768 int warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
5769 int o = blockIdx.x * (8 / parts) + warp / parts, p = warp % parts;
5770 bool live = o < H;
5771 int span = (K + parts - 1) / parts, k0 = p * span, k1 = min(K, k0 + span);
5772 const float* rb = wb + (long)(live ? o : 0) * K;
5773 const float* ra = wa + (long)(live ? o : 0) * K;
5774 double sb = 0.0, sa = 0.0;
5775 if(live) for(int k = k0 + lane; k < k1; k += 32) sb += (double)rb[k] * (double)x[k];
5776 if(live) for(int k = k0 + lane; k < k1; k += 32) sa += (double)ra[k] * (double)x[k];
5777 sb = lk_warp_sum_d(sb);
5778 sa = lk_warp_sum_d(sa);
5779 if(parts > 1){
5780 if(lane == 0){ run[0][warp] = sb; run[1][warp] = sa; }
5781 __syncthreads();
5782 if(p == 0){
5783 sb = 0.0; sa = 0.0;
5784 for(int i = 0; i < parts; ++i){ sb += run[0][warp + i]; sa += run[1][warp + i]; }
5785 }
5786 }
5787 if(live && p == 0 && lane == 0){
5788 float b = (float)sb, a = (float)sa;
5789 beta[o] = 1.0f / (1.0f + expf(-b));
5790 decay[o] = powf(1.0f + expf(a + dt[o]), al[o]);
5791 }
5792}
5793"#;
5794
5795const DELTA_STEP: &str = r#"
5800template<typename A, int DW, typename SS>
5801__device__ __forceinline__ void lk_delta_rows(const float* __restrict__ q, const float* __restrict__ k, const float* __restrict__ v,
5802 const float* __restrict__ bet, const float* __restrict__ alp, SS* __restrict__ S,
5803 float* __restrict__ o, int D, int NK, int HV, int R, int seqs){
5804 // With `seqs`, grid layer `z` is a batch's sequence: its one row, its gates and its own memory, each the sequence's
5805 // part of one array of them all.
5806 if(seqs){
5807 long z = blockIdx.z;
5808 q += z * NK * D; k += z * NK * D; v += z * HV * D; bet += z * HV; alp += z * HV; S += z * HV * D * D; o += z * HV * D;
5809 }
5810 int h = blockIdx.x, lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
5811 int a = blockIdx.y * (blockDim.x >> 5) + warp;
5812 if(a >= D) return;
5813 long base = (long)h * D * D + (long)a * D;
5814 float sreg[DW];
5815 #pragma unroll
5816 for(int i = 0; i < DW; ++i) if(lane + 32 * i < D) sreg[i] = lk_from(S[base + lane + 32 * i]);
5817 for(int r = 0; r < R; ++r){
5818 const float* kk = k + ((long)r * NK + h % NK) * D;
5819 const float* qq = q + ((long)r * NK + h % NK) * D;
5820 A su = 0, sw = 0, skq = 0;
5821 #pragma unroll
5822 for(int i = 0; i < DW; ++i){
5823 const int b = lane + 32 * i;
5824 if(b < D){
5825 A sv = sreg[i], kb = kk[b], qb = qq[b];
5826 su += sv * kb; sw += sv * qb; skq += kb * qb;
5827 }
5828 }
5829 float u = (float)lk_warp_sum(su), w = (float)lk_warp_sum(sw), kq = (float)lk_warp_sum(skq);
5830 long hr = (long)r * HV + h;
5831 float alpha = alp[hr], coef = bet[hr] * (v[hr * D + a] - alpha * u);
5832 if(lane == 0) o[hr * D + a] = (alpha * w + coef * kq) * (1.0f / sqrtf((float)D));
5833 #pragma unroll
5834 for(int i = 0; i < DW; ++i) if(lane + 32 * i < D) sreg[i] = lk_kept<SS>(alpha * sreg[i] + coef * kk[lane + 32 * i]);
5835 }
5836 #pragma unroll
5837 for(int i = 0; i < DW; ++i) if(lane + 32 * i < D) S[base + lane + 32 * i] = lk_to<SS>(sreg[i]);
5838}
5839template<typename A, int DW = 32, typename SS = float>
5840__global__ void lk_delta_step(const float* __restrict__ q, const float* __restrict__ k, const float* __restrict__ v,
5841 const float* __restrict__ bet, const float* __restrict__ alp, SS* __restrict__ S,
5842 float* __restrict__ o, int D, int NK, int HV, int R, int seqs = 0){
5843 lk_delta_rows<A, DW, SS>(q, k, v, bet, alp, S, o, D, NK, HV, R, seqs);
5844}
5845"#;
5846
5847const DELTA_CHUNKS: &str = r#"
5863#define LK_DC 64
5864template<int D>
5865__global__ void __launch_bounds__(256) lk_delta_prep(const float* __restrict__ q, const float* __restrict__ k,
5866 const float* __restrict__ bet, const float* __restrict__ alp, float* __restrict__ Kg, float* __restrict__ Qg,
5867 float* __restrict__ Tb, float* __restrict__ P, float* __restrict__ gl, int NK, int HV, int R){
5868 constexpr int CP = LK_DC + 1, KT = 32, KP = KT + 1;
5869 static_assert(D % KT == 0, "the width in whole runs of 32");
5870 extern __shared__ __align__(16) float lk_dp[];
5871 float* kt = lk_dp; // [LK_DC][KP]: a run of 32 of the chunk's keys' values
5872 float* qt = kt + LK_DC * KP; // [LK_DC][KP]: and of its queries'
5873 float* ts = lk_dp; // [LK_DC][CP]: T = (I + A)⁻¹, once every run is summed
5874 float* as = lk_dp + 2 * LK_DC * KP; // [LK_DC][CP]: A below the diagonal
5875 float* xs = as + LK_DC * CP; // [3][16][16]: the sums T's lower blocks take
5876 __shared__ float lg[LK_DC], bs[LK_DC], eg[LK_DC];
5877 int h = blockIdx.x, c = blockIdx.y, tid = threadIdx.x, kh = h % NK, tq = tid >> 4, tr = tid & 15;
5878 int r0 = c * LK_DC, n = min(LK_DC, R - r0);
5879 long base = (long)h * gridDim.y + c;
5880 if(tid == 0){
5881 float acc = 0.f;
5882 for(int t = 0; t < LK_DC; ++t){
5883 if(t < n) acc += fmaxf(logf(alp[(long)(r0 + t) * HV + h]), -80.f);
5884 lg[t] = acc;
5885 }
5886 }
5887 if(tid < LK_DC) bs[tid] = tid < n ? bet[(long)(r0 + tid) * HV + h] : 0.f;
5888 __syncthreads();
5889 if(tid < LK_DC) eg[tid] = tid < n ? expf(lg[tid]) : 0.f;
5890 // k·k and q·k over rows t = tq + 16·i and s = tr + 16·j, a 4 × 4 tile a thread, the width a run of 32 at a time; and
5891 // the decayed keys and queries diag(γ)·K and diag(γ)·Q, a value of the width at a time.
5892 float kk[4][4], qk[4][4];
5893 #pragma unroll
5894 for(int i = 0; i < 4; ++i)
5895 #pragma unroll
5896 for(int j = 0; j < 4; ++j){ kk[i][j] = 0.f; qk[i][j] = 0.f; }
5897 for(int d0 = 0; d0 < D; d0 += KT){
5898 __syncthreads();
5899 for(int i = tid; i < LK_DC * KT; i += 256){
5900 int t = i / KT, dd = i % KT;
5901 long at = ((long)(r0 + t) * NK + kh) * D + d0 + dd;
5902 kt[t * KP + dd] = t < n ? k[at] : 0.f;
5903 qt[t * KP + dd] = t < n ? q[at] : 0.f;
5904 }
5905 __syncthreads();
5906 for(int i = tid; i < KT * LK_DC; i += 256){
5907 int dd = i / LK_DC, t = i % LK_DC;
5908 long at = (base * D + d0 + dd) * LK_DC + t;
5909 Kg[at] = eg[t] * kt[t * KP + dd];
5910 Qg[at] = eg[t] * qt[t * KP + dd];
5911 }
5912 #pragma unroll 8
5913 for(int dd = 0; dd < KT; ++dd){
5914 float kr[4], qr[4], kc[4];
5915 #pragma unroll
5916 for(int i = 0; i < 4; ++i){ kr[i] = kt[(tq + 16 * i) * KP + dd]; qr[i] = qt[(tq + 16 * i) * KP + dd]; kc[i] = kt[(tr + 16 * i) * KP + dd]; }
5917 #pragma unroll
5918 for(int i = 0; i < 4; ++i)
5919 #pragma unroll
5920 for(int j = 0; j < 4; ++j){ kk[i][j] = fmaf(kr[i], kc[j], kk[i][j]); qk[i][j] = fmaf(qr[i], kc[j], qk[i][j]); }
5921 }
5922 }
5923 // A below the diagonal; the masked scores on and below it, a column of the chunk's rows at a time.
5924 #pragma unroll
5925 for(int i = 0; i < 4; ++i)
5926 #pragma unroll
5927 for(int j = 0; j < 4; ++j){
5928 int t = tq + 16 * i, s = tr + 16 * j;
5929 float g = expf(lg[t] - lg[s]);
5930 as[t * CP + s] = s < t && t < n ? bs[t] * g * kk[i][j] : 0.f;
5931 P[base * LK_DC * LK_DC + s * LK_DC + t] = s <= t && t < n ? g * qk[i][j] : 0.f;
5932 }
5933 __syncthreads();
5934 // T = (I + A)⁻¹ by 16 × 16 blocks: each diagonal block's inverse by forward substitution, a column a thread held in
5935 // registers, the blocks above the diagonal zero; then the blocks below it a diagonal at a time,
5936 // T_ij = −T_ii·Σ_{j≤k<i} A_ik·T_kj — its sums first, then T_ii times them.
5937 if(tid < LK_DC){
5938 int o = 16 * (tid >> 4), jj = tid & 15;
5939 float x[16];
5940 #pragma unroll
5941 for(int tt = 0; tt < 16; ++tt){
5942 float acc = tt == jj ? 1.f : 0.f;
5943 #pragma unroll
5944 for(int s = 0; s < tt; ++s) acc = fmaf(-as[(o + tt) * CP + o + s], x[s], acc);
5945 x[tt] = tt >= jj ? acc : 0.f;
5946 }
5947 #pragma unroll
5948 for(int tt = 0; tt < 16; ++tt) ts[(o + tt) * CP + o + jj] = x[tt];
5949 } else {
5950 for(int i = tid - LK_DC; i < LK_DC * LK_DC; i += 256 - LK_DC)
5951 if((i % LK_DC) >> 4 > (i / LK_DC) >> 4) ts[(i / LK_DC) * CP + i % LK_DC] = 0.f;
5952 }
5953 __syncthreads();
5954 for(int dg = 1; dg < 4; ++dg){
5955 for(int e = tid; e < (4 - dg) * 256; e += 256){
5956 int j = e >> 8, i = j + dg, r = (e >> 4) & 15, cc = e & 15;
5957 float acc = 0.f;
5958 for(int m = 16 * j; m < 16 * i; ++m) acc = fmaf(as[(16 * i + r) * CP + m], ts[m * CP + 16 * j + cc], acc);
5959 xs[e] = acc;
5960 }
5961 __syncthreads();
5962 for(int e = tid; e < (4 - dg) * 256; e += 256){
5963 int j = e >> 8, i = j + dg, r = (e >> 4) & 15, cc = e & 15;
5964 float acc = 0.f;
5965 #pragma unroll
5966 for(int m = 0; m < 16; ++m) acc = fmaf(ts[(16 * i + r) * CP + 16 * i + m], xs[(j << 8) + m * 16 + cc], acc);
5967 ts[(16 * i + r) * CP + 16 * j + cc] = -acc;
5968 }
5969 __syncthreads();
5970 }
5971 // T·diag(β), a column of the chunk's rows at a time; each row's decay to the chunk's end, and the chunk's own.
5972 for(int i = tid; i < LK_DC * LK_DC; i += 256){
5973 int s = i / LK_DC, t = i % LK_DC;
5974 Tb[base * LK_DC * LK_DC + i] = ts[t * CP + s] * bs[s];
5975 }
5976 if(tid <= LK_DC) gl[base * (LK_DC + 1) + tid] = tid < LK_DC ? (tid < n ? expf(lg[n - 1] - lg[tid]) : 0.f) : expf(lg[n - 1]);
5977}
5978// `B` values from `p`, sixteen bytes at a time where `B` is a multiple of four.
5979template<int B>
5980__device__ __forceinline__ void lk_load(const float* __restrict__ p, float (&r)[B]){
5981 if constexpr(B % 4 == 0){
5982 #pragma unroll
5983 for(int i = 0; i < B / 4; ++i){
5984 float4 v = __ldg((const float4*)p + i);
5985 r[4 * i] = v.x; r[4 * i + 1] = v.y; r[4 * i + 2] = v.z; r[4 * i + 3] = v.w;
5986 }
5987 } else {
5988 #pragma unroll
5989 for(int i = 0; i < B; ++i) r[i] = __ldg(p + i);
5990 }
5991}
5992template<int D, int RV, typename SS = float>
5993__global__ void __launch_bounds__(64) lk_delta_state(const float* __restrict__ k, const float* __restrict__ v,
5994 const float* __restrict__ Kg, const float* __restrict__ Qg, const float* __restrict__ Tb, const float* __restrict__ P,
5995 const float* __restrict__ gl, SS* __restrict__ S, float* __restrict__ o, int NK, int HV, int R){
5996 constexpr int RB = RV, B = D / 32;
5997 static_assert(RV == 16 && D % 32 == 0, "two warps, each 32 of the chunk's rows and 8 of the memory's; a lane D / 32 of the width");
5998 __shared__ __align__(16) float sm[D * RB]; // the block's rows of the memory, a value of the width at a time
5999 __shared__ __align__(16) float rd[LK_DC * RB]; // V − diag(γ)·K·S₀ᵀ over them, then the corrections Δ
6000 int h = blockIdx.x, lane = threadIdx.x & 31, warp = threadIdx.x >> 5, kh = h % NK, a0 = blockIdx.y * RB, b0 = lane * B;
6001 int t = 32 * warp + lane, aw = 8 * warp;
6002 int chunks = (R + LK_DC - 1) / LK_DC;
6003 const float sc = rsqrtf((float)D);
6004 float s[8][B];
6005 #pragma unroll
6006 for(int a = 0; a < 8; ++a){
6007 if constexpr(sizeof(SS) == 4) lk_load<B>((const float*)S + ((long)h * D + a0 + aw + a) * D + b0, s[a]);
6008 else
6009 #pragma unroll
6010 for(int i = 0; i < B; ++i) s[a][i] = lk_from(S[((long)h * D + a0 + aw + a) * D + b0 + i]);
6011 }
6012 for(int c = 0; c < chunks; ++c){
6013 int r0 = c * LK_DC, n = min(LK_DC, R - r0);
6014 long base = (long)h * chunks + c;
6015 const float *Kc = Kg + base * LK_DC * D, *Qc = Qg + base * LK_DC * D;
6016 const float *Tc = Tb + base * LK_DC * LK_DC, *Pc = P + base * LK_DC * LK_DC, *g = gl + base * (LK_DC + 1);
6017 #pragma unroll
6018 for(int i = 0; i < B; ++i)
6019 #pragma unroll
6020 for(int a = 0; a < 8; ++a) sm[(b0 + i) * RB + aw + a] = s[a][i];
6021 __syncthreads();
6022 float xr[RB], xo[RB];
6023 #pragma unroll
6024 for(int a = 0; a < RB; ++a){ xr[a] = 0.f; xo[a] = 0.f; }
6025 #pragma unroll 4
6026 for(int b = 0; b < D; ++b){
6027 float kv = __ldg(Kc + b * LK_DC + t), qv = __ldg(Qc + b * LK_DC + t);
6028 const float4* sp = (const float4*)(sm + b * RB);
6029 #pragma unroll
6030 for(int q4 = 0; q4 < RB / 4; ++q4){
6031 float4 sv = sp[q4];
6032 xr[4 * q4] = fmaf(kv, sv.x, xr[4 * q4]); xr[4 * q4 + 1] = fmaf(kv, sv.y, xr[4 * q4 + 1]);
6033 xr[4 * q4 + 2] = fmaf(kv, sv.z, xr[4 * q4 + 2]); xr[4 * q4 + 3] = fmaf(kv, sv.w, xr[4 * q4 + 3]);
6034 xo[4 * q4] = fmaf(qv, sv.x, xo[4 * q4]); xo[4 * q4 + 1] = fmaf(qv, sv.y, xo[4 * q4 + 1]);
6035 xo[4 * q4 + 2] = fmaf(qv, sv.z, xo[4 * q4 + 2]); xo[4 * q4 + 3] = fmaf(qv, sv.w, xo[4 * q4 + 3]);
6036 }
6037 }
6038 {
6039 float vv[RB];
6040 if(t < n) lk_load<RB>(v + ((long)(r0 + t) * HV + h) * D + a0, vv);
6041 else for(int a = 0; a < RB; ++a) vv[a] = 0.f;
6042 #pragma unroll
6043 for(int a = 0; a < RB; ++a) rd[t * RB + a] = vv[a] - xr[a];
6044 }
6045 __syncthreads();
6046 #pragma unroll
6047 for(int a = 0; a < RB; ++a) xr[a] = 0.f;
6048 #pragma unroll 8
6049 for(int sr = 0; sr < LK_DC; ++sr){
6050 float tv = __ldg(Tc + sr * LK_DC + t);
6051 const float4* rp = (const float4*)(rd + sr * RB);
6052 #pragma unroll
6053 for(int q4 = 0; q4 < RB / 4; ++q4){
6054 float4 rv = rp[q4];
6055 xr[4 * q4] = fmaf(tv, rv.x, xr[4 * q4]); xr[4 * q4 + 1] = fmaf(tv, rv.y, xr[4 * q4 + 1]);
6056 xr[4 * q4 + 2] = fmaf(tv, rv.z, xr[4 * q4 + 2]); xr[4 * q4 + 3] = fmaf(tv, rv.w, xr[4 * q4 + 3]);
6057 }
6058 }
6059 __syncthreads();
6060 #pragma unroll
6061 for(int a = 0; a < RB; ++a) rd[t * RB + a] = xr[a];
6062 __syncthreads();
6063 #pragma unroll 8
6064 for(int sr = 0; sr < LK_DC; ++sr){
6065 float pv = __ldg(Pc + sr * LK_DC + t);
6066 const float4* dp = (const float4*)(rd + sr * RB);
6067 #pragma unroll
6068 for(int q4 = 0; q4 < RB / 4; ++q4){
6069 float4 dv = dp[q4];
6070 xo[4 * q4] = fmaf(pv, dv.x, xo[4 * q4]); xo[4 * q4 + 1] = fmaf(pv, dv.y, xo[4 * q4 + 1]);
6071 xo[4 * q4 + 2] = fmaf(pv, dv.z, xo[4 * q4 + 2]); xo[4 * q4 + 3] = fmaf(pv, dv.w, xo[4 * q4 + 3]);
6072 }
6073 }
6074 if(t < n){
6075 float4* op = (float4*)(o + ((long)(r0 + t) * HV + h) * D + a0);
6076 #pragma unroll
6077 for(int q4 = 0; q4 < RB / 4; ++q4) op[q4] = make_float4(xo[4 * q4] * sc, xo[4 * q4 + 1] * sc, xo[4 * q4 + 2] * sc, xo[4 * q4 + 3] * sc);
6078 }
6079 float gn = g[LK_DC];
6080 #pragma unroll
6081 for(int a = 0; a < 8; ++a)
6082 #pragma unroll
6083 for(int i = 0; i < B; ++i) s[a][i] *= gn;
6084 #pragma unroll 8
6085 for(int tt = 0; tt < n; ++tt){
6086 float kv[B];
6087 lk_load<B>(k + ((long)(r0 + tt) * NK + kh) * D + b0, kv);
6088 float gt = g[tt];
6089 float4 d0 = *(const float4*)(rd + tt * RB + aw), d1 = *(const float4*)(rd + tt * RB + aw + 4);
6090 float dv[8] = {d0.x, d0.y, d0.z, d0.w, d1.x, d1.y, d1.z, d1.w};
6091 #pragma unroll
6092 for(int a = 0; a < 8; ++a){
6093 float da = gt * dv[a];
6094 #pragma unroll
6095 for(int i = 0; i < B; ++i) s[a][i] = fmaf(da, kv[i], s[a][i]);
6096 }
6097 }
6098 // The memory as its storage keeps it, at the chunk's end.
6099 #pragma unroll
6100 for(int a = 0; a < 8; ++a)
6101 #pragma unroll
6102 for(int i = 0; i < B; ++i) s[a][i] = lk_kept<SS>(s[a][i]);
6103 __syncthreads();
6104 }
6105 #pragma unroll
6106 for(int a = 0; a < 8; ++a)
6107 #pragma unroll
6108 for(int i = 0; i < B; ++i) S[((long)h * D + a0 + aw + a) * D + b0 + i] = lk_to<SS>(s[a][i]);
6109}
6110"#;
6111
6112pub const DELTA_CHUNK: usize = 64;
6115pub const DELTA_STATE_ROWS: usize = 16;
6116pub const DELTA_STATE_THREADS: usize = 64;
6117
6118pub const DELTA_PREP_SMEM: usize = (2 * DELTA_CHUNK * 33 + DELTA_CHUNK * (DELTA_CHUNK + 1) + 3 * 256) * 4;
6121
6122const _: () = assert!(DELTA_PREP_SMEM <= 48 * 1024, "the chunked delta rule's preparation keeps the default shared memory");
6123const _: () = assert!(DELTA_CHUNK * 33 * 2 >= DELTA_CHUNK * (DELTA_CHUNK + 1), "T fits where the runs of keys and queries were");
6124
6125pub fn delta_chunks_take(d: usize) -> bool {
6128 d <= 128 && d % 32 == 0
6129}
6130
6131pub fn delta_chunk_scratch(chunks: usize, hv: usize, d: usize) -> [usize; 5] {
6135 let per = chunks * hv;
6136 [per * DELTA_CHUNK * d, per * DELTA_CHUNK * d, per * DELTA_CHUNK * DELTA_CHUNK, per * DELTA_CHUNK * DELTA_CHUNK, per * (DELTA_CHUNK + 1)]
6137}
6138
6139const DELTA_NORM: &str = r#"
6143// The head of `D` values at `i0`, a value a thread of the norm's own block: warp `w` of the block's plays the norm's
6144// warps `w`, `w + nw`, … (`VW` of them), each value read past the L1 under `L2`.
6145template<bool OUTF, int Q, typename A, int VW, bool L2>
6146__device__ __forceinline__ void lk_delta_norm_row(const float* __restrict__ z, const float* __restrict__ gn, const float* o,
6147 float* __restrict__ out, signed char* __restrict__ oq, signed char* __restrict__ ol,
6148 float* __restrict__ os, long i0, int D, float eps, A* red){
6149 const int nw = blockDim.x >> 5, vw = (D + 31) >> 5, warp = threadIdx.x >> 5, lane = threadIdx.x & 31;
6150 float val[VW];
6151 A acc[VW];
6152 #pragma unroll
6153 for(int j = 0; j < VW; ++j){
6154 const int u = (warp + j * nw) * 32 + lane;
6155 val[j] = warp + j * nw < vw && u < D ? (L2 ? __ldcg(o + i0 + u) : o[i0 + u]) : 0.f;
6156 acc[j] = (A)val[j] * (A)val[j];
6157 }
6158 A den = sqrt(lk_warps_sum<VW>(acc, vw, red) / (A)D + (A)eps);
6159 #pragma unroll
6160 for(int j = 0; j < VW; ++j){
6161 const int u = (warp + j * nw) * 32 + lane;
6162 if(warp + j * nw >= vw || u >= D) continue;
6163 const long i = i0 + u;
6164 float zz = z[i];
6165 float r = (float)((A)gn[u] * (A)val[j] / den) * (zz / (1.0f + expf(-zz)));
6166 if(OUTF) out[i] = r;
6167 if(Q > 0) lk_quant_lane<Q>(r, oq, ol, os, i, i >> 5);
6168 }
6169}
6170template<bool OUTF, int Q, typename A>
6171__global__ void lk_delta_norm(const float* __restrict__ z, const float* __restrict__ gn, const float* __restrict__ o,
6172 float* __restrict__ out, signed char* __restrict__ oq, signed char* __restrict__ ol,
6173 float* __restrict__ os, int D, float eps){
6174 __shared__ A red[32];
6175 lk_delta_norm_row<OUTF, Q, A, 1, false>(z, gn, o, out, oq, ol, os, (long)blockIdx.x * D, D, eps, red);
6176}
6177"#;
6178
6179const DELTA_STEP_NORM: &str = r#"
6185template<typename A, int DW, bool OUTF, int Q, int VW, typename SS>
6186__global__ void lk_delta_step(const float* __restrict__ q, const float* __restrict__ k, const float* __restrict__ v,
6187 const float* __restrict__ bet, const float* __restrict__ alp, SS* __restrict__ S,
6188 float* o, int D, int NK, int HV, int R, int seqs, const float* __restrict__ z,
6189 const float* __restrict__ gn, float* __restrict__ out, signed char* __restrict__ oq,
6190 signed char* __restrict__ ol, float* __restrict__ os, float eps, int* __restrict__ cnt){
6191 __shared__ A red[32];
6192 __shared__ int last;
6193 lk_delta_rows<A, DW, SS>(q, k, v, bet, alp, S, o, D, NK, HV, R, seqs);
6194 __threadfence();
6195 __syncthreads();
6196 const long zs = seqs ? blockIdx.z : 0, h = blockIdx.x;
6197 if(threadIdx.x == 0) last = atomicAdd(cnt + zs * HV + h, 1) == gridDim.y - 1;
6198 __syncthreads();
6199 if(!last) return;
6200 __threadfence();
6201 for(int r = 0; r < R; ++r) lk_delta_norm_row<OUTF, Q, A, VW, true>(z, gn, o, out, oq, ol, os, ((zs * R + r) * HV + h) * D, D, eps, red);
6202 if(threadIdx.x == 0) cnt[zs * HV + h] = 0;
6203}
6204"#;
6205
6206const MOE: &str = r#"
6216// The router's logits (an expert's row of a router in format `FR`, or with `RF` of an f32 one read against the f32
6217// input, plus the router's bias `rb` when there is one) across the grid's blocks into `logits`: a warp an expert, or an
6218// f32 router's row split over `WPE` warps reading four values a lane at a time, their sums added in order; the last
6219// block to arrive (`cnt`, reset by it) takes their softmax and the top `K` — each pick a warp-wide arg-max, the first
6220// expert of the largest weight winning as a scan in expert order would — renormalized with `norm`, each its softmax
6221// over every expert without. Grid row `y` routes row `y` of the input into its own logits, choices and count.
6222// A row's softmax over its `E` experts' logits and its top `K` — each pick a warp-wide arg-max, the first expert of the
6223// largest weight winning as a scan in expert order would — renormalized with `norm`, each its softmax over every expert
6224// without: a block's threads, `p` `E` floats of shared memory, `red` 32. Lane `r` of the first warp keeps pick `r`'s
6225// weight for the renormalization (`K` at most 32), every lane summing the picks in order.
6226template<int Q>
6227__device__ __forceinline__ void lk_moe_pick_row(const float* logits, float* p, float* red, int E, int K, int norm,
6228 int* __restrict__ idx, float* __restrict__ wt){
6229 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
6230 for(int i = threadIdx.x; i < E; i += blockDim.x) p[i] = __ldcg(logits + i);
6231 __syncthreads();
6232 float m = -INFINITY;
6233 for(int i = threadIdx.x; i < E; i += blockDim.x) m = fmaxf(m, p[i]);
6234 m = lk_block_max(m, red);
6235 typedef typename lk_acc<Q>::t A;
6236 __shared__ A sums[32];
6237 A s = 0;
6238 for(int i = threadIdx.x; i < E; i += blockDim.x){ float v = expf(p[i] - m); p[i] = v; s += (A)v; }
6239 s = lk_block_sum(s, sums);
6240 __syncthreads();
6241 if(warp == 0){
6242 A total = 0, mine = 0;
6243 for(int r = 0; r < K; ++r){
6244 float bv = -1.0f; int bi = -1;
6245 for(int i = lane; i < E; i += 32) if(p[i] > bv){ bv = p[i]; bi = i; }
6246 for(int o = 16; o > 0; o >>= 1){
6247 float ov = __shfl_down_sync(0xffffffffu, bv, o);
6248 int oi = __shfl_down_sync(0xffffffffu, bi, o);
6249 if(ov > bv || (ov == bv && oi >= 0 && (bi < 0 || oi < bi))){ bv = ov; bi = oi; }
6250 }
6251 bv = __shfl_sync(0xffffffffu, bv, 0);
6252 bi = __shfl_sync(0xffffffffu, bi, 0);
6253 const A pick = (A)bv / s;
6254 if(lane == r) mine = pick;
6255 if(lane == 0){ idx[r] = bi; p[bi] = -2.0f; if(!norm) wt[r] = (float)pick; }
6256 total += pick;
6257 __syncwarp();
6258 }
6259 if(norm && lane < K) wt[lane] = (float)(mine / total);
6260 }
6261}
6262template<int Q, int RF, int FR, int WPE = 1>
6263__global__ void __launch_bounds__(256) lk_moe_route(const float* __restrict__ x, const signed char* __restrict__ xq,
6264 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_w wr,
6265 const float* __restrict__ wf, const float* __restrict__ rb, int d, int E, int K, int norm,
6266 int* __restrict__ idx, float* __restrict__ wt, float* __restrict__ logits, int* __restrict__ cnt){
6267 extern __shared__ float p[];
6268 __shared__ float red[32];
6269 __shared__ double split[8];
6270 __shared__ int last;
6271 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5, e = (blockIdx.x * nw + warp) / WPE;
6272 {
6273 long row = blockIdx.y;
6274 if(x) x += row * d;
6275 if(xq) xq += row * d;
6276 if(xl) xl += row * d;
6277 if(xs) xs += row * (d >> 5);
6278 idx += row * K; wt += row * K; logits += row * E; cnt += row;
6279 }
6280 if(RF && WPE > 1){
6281 // Warp `q` of an expert's `WPE` sums its `q`-th part of the row, four values a lane at a time.
6282 int q = warp % WPE, part = d / WPE;
6283 double sum = 0.0;
6284 if(e < E){
6285 const float4* we = (const float4*)(wf + (long)e * d + (long)q * part);
6286 const float4* xv = (const float4*)(x + (long)q * part);
6287 if(Q == 0){
6288 double a = 0.0;
6289 for(int t = lane; t < (part >> 2); t += 32){
6290 float4 w = we[t], v = xv[t];
6291 a += (double)w.x * (double)v.x + (double)w.y * (double)v.y + (double)w.z * (double)v.z + (double)w.w * (double)v.w;
6292 }
6293 sum = lk_warp_sum_d(a);
6294 } else {
6295 float a = 0.f;
6296 for(int t = lane; t < (part >> 2); t += 32){
6297 float4 w = we[t], v = xv[t];
6298 a += w.x * v.x + w.y * v.y + w.z * v.z + w.w * v.w;
6299 }
6300 sum = lk_warp_sum(a);
6301 }
6302 }
6303 if(lane == 0) split[warp] = sum;
6304 __syncthreads();
6305 if(lane == 0 && q == 0 && e < E){
6306 float v;
6307 if(Q == 0){ double t = split[warp]; for(int i = 1; i < WPE; ++i) t += split[warp + i]; v = (float)t; }
6308 else { float t = (float)split[warp]; for(int i = 1; i < WPE; ++i) t += (float)split[warp + i]; v = t; }
6309 logits[e] = rb ? v + rb[e] : v;
6310 }
6311 } else if(e < E){
6312 float v;
6313 if(RF){
6314 const float* we = wf + (long)e * d;
6315 if(Q == 0){ double a = 0.0; for(int t = lane; t < d; t += 32) a += (double)we[t] * (double)x[t]; v = (float)lk_warp_sum_d(a); }
6316 else { float a = 0.f; for(int t = lane; t < d; t += 32) a += we[t] * x[t]; v = lk_warp_sum(a); }
6317 } else {
6318 lk_gemv_dot<FR, Q, 1>(wr, e, d, x, xq, xl, xs, &v);
6319 }
6320 if(lane == 0) logits[e] = rb ? v + rb[e] : v;
6321 }
6322 __threadfence();
6323 __syncthreads();
6324 if(threadIdx.x == 0) last = atomicAdd(cnt, 1) == gridDim.x - 1;
6325 __syncthreads();
6326 if(!last) return;
6327 __threadfence();
6328 lk_moe_pick_row<Q>(logits, p, red, E, K, norm, idx, wt);
6329 if(threadIdx.x == 0) *cnt = 0;
6330}
6331// Row `blockIdx.x`'s choices and weights from its logits (`lk_moe_pick_row`), as `lk_moe_route` takes them: a wide
6332// chunk's router, its logits a GEMM's.
6333template<int Q>
6334__global__ void __launch_bounds__(256) lk_moe_pick(const float* __restrict__ logits, int E, int K, int norm,
6335 int* __restrict__ idx, float* __restrict__ wt){
6336 extern __shared__ float p[];
6337 __shared__ float red[32];
6338 long row = blockIdx.x;
6339 lk_moe_pick_row<Q>(logits + row * E, p, red, E, K, norm, idx + row * K, wt + row * K);
6340}
6341// The rows' choices grouped by expert, in row order then choice order: `ecount[e]` choices of expert `e` from `eoff[e]`
6342// in `list` (each a choice `r·K + k`), and each choice's place in `list` (`pos`). One block: the counts by shared
6343// atomics and their offsets by one thread, then the choices a block-width at a time, each placed after its expert's
6344// choices before it — in earlier block-widths (`run`), in earlier warps of its own (`wc`), in earlier lanes of its
6345// warp (`__match_any_sync`). Shared memory: `run`, then `wc`, (1 + warps) · `E` ints.
6346__global__ void lk_moe_group(const int* __restrict__ idx, int n, int E, int* __restrict__ ecount, int* __restrict__ eoff,
6347 int* __restrict__ list, int* __restrict__ pos){
6348 extern __shared__ int cur[];
6349 int *run = cur, *wc = cur + E;
6350 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = blockDim.x >> 5;
6351 for(int e = threadIdx.x; e < E; e += blockDim.x) run[e] = 0;
6352 for(int j = threadIdx.x; j < nw * E; j += blockDim.x) wc[j] = 0;
6353 __syncthreads();
6354 for(int i = threadIdx.x; i < n; i += blockDim.x) atomicAdd(&run[idx[i]], 1);
6355 __syncthreads();
6356 if(threadIdx.x == 0){
6357 int at = 0;
6358 for(int e = 0; e < E; ++e){ int c = run[e]; ecount[e] = c; eoff[e] = at; run[e] = at; at += c; }
6359 eoff[E] = at;
6360 }
6361 __syncthreads();
6362 for(int base = 0; base < n; base += blockDim.x){
6363 int i = base + threadIdx.x, e = i < n ? idx[i] : -1;
6364 unsigned peers = __match_any_sync(0xffffffffu, e);
6365 int rank = __popc(peers & ((1u << lane) - 1u));
6366 if(e >= 0 && rank == 0) wc[warp * E + e] = __popc(peers);
6367 __syncthreads();
6368 if(e >= 0){
6369 int p = run[e] + rank;
6370 for(int u = 0; u < warp; ++u) p += wc[u * E + e];
6371 list[p] = i; pos[i] = p;
6372 }
6373 __syncthreads();
6374 for(int x = threadIdx.x; x < E; x += blockDim.x){
6375 int sum = 0;
6376 for(int u = 0; u < nw; ++u){ sum += wc[u * E + x]; wc[u * E + x] = 0; }
6377 run[x] += sum;
6378 }
6379 __syncthreads();
6380 }
6381}
6382// Each choice's activation row, in `list` order: the row of `x` (f32 under Q 0, its int8 words and scales otherwise)
6383// of the choice's token.
6384template<int Q>
6385__global__ void lk_moe_gather(const float* __restrict__ x, const signed char* __restrict__ xq, const signed char* __restrict__ xl,
6386 const float* __restrict__ xs, const int* __restrict__ list, int K, int d, float* __restrict__ gx,
6387 signed char* __restrict__ gq, signed char* __restrict__ gl, float* __restrict__ gs){
6388 long p = blockIdx.x, r = list[p] / K;
6389 for(int i = threadIdx.x; i < d; i += blockDim.x){
6390 if(Q == 0) gx[p * d + i] = x[r * d + i];
6391 else { gq[p * d + i] = xq[r * d + i]; if(Q == 2) gl[p * d + i] = xl[r * d + i]; }
6392 }
6393 if(Q > 0) for(int b = threadIdx.x; b < (d >> 5); b += blockDim.x) gs[p * (d >> 5) + b] = xs[r * (d >> 5) + b];
6394}
6395// A choice count in slabs of 16, then of 8, 4, 2 and 1 rows — every row in exactly one slab, each row's dots those of
6396// one row (`lk_gemv_dot` sums a row alike whatever its slab), `slab<M>(first)` called for each.
6397template<typename S>
6398__device__ __forceinline__ void lk_slabs(int n, S slab){
6399 int s0 = 0;
6400 for(; n - s0 >= 16; s0 += 16) slab.template run<16>(s0);
6401 if(n - s0 >= 8){ slab.template run<8>(s0); s0 += 8; }
6402 if(n - s0 >= 4){ slab.template run<4>(s0); s0 += 4; }
6403 if(n - s0 >= 2){ slab.template run<2>(s0); s0 += 2; }
6404 if(n - s0 >= 1) slab.template run<1>(s0);
6405}
6406template<int Q, int ACT, int FG, int FU>
6407struct lk_up_slab {
6408 const float* x; const signed char* xq; const signed char* xl; const float* xs; lk_w wg, wu;
6409 const float* bg; const float* bu; float* h; long wrow, off, j; int d, dff; float limit, slope;
6410 template<int M> __device__ __forceinline__ void run(int s0) const {
6411 long at = (off + s0) * d;
6412 float g[M], u[M];
6413 lk_gemv_dot<FG, Q, M>(wg, wrow, d, Q ? nullptr : x + at, Q ? xq + at : nullptr, Q == 2 ? xl + at : nullptr,
6414 Q ? xs + (at >> 5) : nullptr, g);
6415 lk_gemv_dot<FU, Q, M>(wu, wrow, d, Q ? nullptr : x + at, Q ? xq + at : nullptr, Q == 2 ? xl + at : nullptr,
6416 Q ? xs + (at >> 5) : nullptr, u);
6417 if((threadIdx.x & 31) == 0){
6418 #pragma unroll
6419 for(int t = 0; t < M; ++t){
6420 float gv = bg ? g[t] + bg[wrow] : g[t], uv = bu ? u[t] + bu[wrow] : u[t];
6421 float o;
6422 if(ACT == 0) o = gv / (1.0f + expf(-gv)) * uv;
6423 else { float gc = fminf(gv, limit); o = gc / (1.0f + expf(-slope * gc)) * (fminf(fmaxf(uv, -limit), limit) + 1.0f); }
6424 h[(off + s0 + t) * dff + j] = o;
6425 }
6426 }
6427 }
6428};
6429// Each expert's gate and up rows over the activation rows of the choices of it (`n` from `off` of the gathered rows),
6430// in slabs (`lk_slabs`), each row's dots as one row's (`lk_gemv_dot`), through the gated activation of `lk_moe_up`: a
6431// warp a row of the expert's, a grid row an expert.
6432template<int Q, int ACT, int FG, int FU>
6433__global__ void lk_moe_up_rows(const float* __restrict__ x, const signed char* __restrict__ xq,
6434 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_w wg, lk_w wu,
6435 const float* __restrict__ bg, const float* __restrict__ bu, const int* __restrict__ ecount,
6436 const int* __restrict__ eoff, float* __restrict__ h, int d, int dff, float limit, float slope){
6437 int e = blockIdx.y, n = ecount[e];
6438 long j = ((long)blockIdx.x * blockDim.x + threadIdx.x) >> 5;
6439 if(n == 0 || j >= dff) return;
6440 lk_slabs(n, lk_up_slab<Q, ACT, FG, FU>{x, xq, xl, xs, wg, wu, bg, bu, h, (long)e * dff + j, eoff[e], j, d, dff, limit, slope});
6441}
6442// Each expert's down rows over the hidden rows of the choices of it, in slabs (`lk_slabs`), each choice's result
6443// weighed by its weight into `part` (its place in `list`), as `lk_moe_down` weighs it: a warp an output, a grid row an
6444// expert.
6445template<int Q, int FD>
6446struct lk_down_slab {
6447 const float* h; const signed char* hq; const signed char* hl; const float* hs; lk_w wd; const float* bd;
6448 const int* list; const float* wt; typename lk_acc<Q>::t* part; long wrow, off, o; int d, dff;
6449 template<int M> __device__ __forceinline__ void run(int s0) const {
6450 typedef typename lk_acc<Q>::t A;
6451 long at = (off + s0) * dff;
6452 float v[M];
6453 lk_gemv_dot<FD, Q, M>(wd, wrow, dff, Q ? nullptr : h + at, Q ? hq + at : nullptr, Q == 2 ? hl + at : nullptr,
6454 Q ? hs + (at >> 5) : nullptr, v);
6455 if((threadIdx.x & 31) == 0){
6456 #pragma unroll
6457 for(int t = 0; t < M; ++t) part[(off + s0 + t) * d + o] = (A)(bd ? v[t] + bd[wrow] : v[t]) * (A)wt[list[off + s0 + t]];
6458 }
6459 }
6460};
6461template<int Q, int FD>
6462__global__ void lk_moe_down_rows(const float* __restrict__ h, const signed char* __restrict__ hq,
6463 const signed char* __restrict__ hl, const float* __restrict__ hs, lk_w wd,
6464 const float* __restrict__ bd, const int* __restrict__ ecount, const int* __restrict__ eoff,
6465 const int* __restrict__ list, const float* __restrict__ wt,
6466 typename lk_acc<Q>::t* __restrict__ part, int d, int dff){
6467 int e = blockIdx.y, n = ecount[e];
6468 long o = ((long)blockIdx.x * blockDim.x + threadIdx.x) >> 5;
6469 if(n == 0 || o >= d) return;
6470 lk_slabs(n, lk_down_slab<Q, FD>{h, hq, hl, hs, wd, bd, list, wt, part, (long)e * d + o, eoff[e], o, d, dff});
6471}
6472// Each choice's hidden row from its expert's gate and up rows over it (`g`, `u`, in `list` order), plus the expert's
6473// biases, through the gated activation of `lk_moe_up`.
6474template<int ACT>
6475__global__ void lk_moe_glu(const float* __restrict__ g, const float* __restrict__ u, const float* __restrict__ bg,
6476 const float* __restrict__ bu, const int* __restrict__ idx, const int* __restrict__ list,
6477 float* __restrict__ h, long n, int dff, float limit, float slope){
6478 long i = (long)blockIdx.x * blockDim.x + threadIdx.x;
6479 if(i >= n) return;
6480 long wrow = (long)idx[list[i / dff]] * dff + i % dff;
6481 float gv = bg ? g[i] + bg[wrow] : g[i], uv = bu ? u[i] + bu[wrow] : u[i];
6482 if(ACT == 0) h[i] = gv / (1.0f + expf(-gv)) * uv;
6483 else { float gc = fminf(gv, limit); h[i] = gc / (1.0f + expf(-slope * gc)) * (fminf(fmaxf(uv, -limit), limit) + 1.0f); }
6484}
6485// Output `o` of row `r`: its choices' weighed results summed in choice order, as `lk_moe_down` sums them.
6486template<int Q>
6487__device__ __forceinline__ float lk_moe_combined(const typename lk_acc<Q>::t* __restrict__ part, const int* __restrict__ pos,
6488 int K, int d, long r, long o){
6489 typedef typename lk_acc<Q>::t A;
6490 A s = 0;
6491 for(int k = 0; k < K; ++k) s += part[(long)pos[r * K + k] * d + o];
6492 return (float)s;
6493}
6494// Each row's output (`lk_moe_combined`).
6495template<int Q>
6496__global__ void lk_moe_combine(const typename lk_acc<Q>::t* __restrict__ part, const int* __restrict__ pos, int K, int d,
6497 float* __restrict__ y){
6498 long r = blockIdx.y, o = (long)blockIdx.x * blockDim.x + threadIdx.x;
6499 if(o >= d) return;
6500 y[r * d + o] = lk_moe_combined<Q>(part, pos, K, d, r, o);
6501}
6502// Each chosen expert's gate and up rows (formats `FG`, `FU`, plus their biases `bg`, `bu`) through the gated activation:
6503// SwiGLU, or with `ACT` 1 the gate clamped to at most `limit`, the up to `[−limit, limit]`, the sigmoid sharpened by
6504// `slope`; then the shared expert's (formats `FSG`, `FSU`, `sdff` rows) through SwiGLU, after the kept experts' rows.
6505// A warp takes `W` consecutive rows, all of one expert (`W` divides `dff` and `sdff`).
6506template<int Q, int ACT, int FG, int FU, int FSG, int FSU, int W = 1>
6507__global__ void lk_moe_up(const float* __restrict__ x, const signed char* __restrict__ xq,
6508 const signed char* __restrict__ xl, const float* __restrict__ xs, lk_w wg, lk_w wu,
6509 lk_w sg, lk_w su, const float* __restrict__ bg, const float* __restrict__ bu,
6510 const int* __restrict__ idx, float* __restrict__ h, int d, int dff, int K, int sdff,
6511 float limit, float slope){
6512 long row = (((long)blockIdx.x * blockDim.x + threadIdx.x) >> 5) * W, routed = (long)K * dff;
6513 if(row >= routed + sdff) return;
6514 float gv[W], uv[W];
6515 if(row < routed){
6516 int r = row / dff, j = row % dff;
6517 long wrow = (long)idx[r] * dff + j;
6518 lk_gemv_dot<FG, Q, 1, W>(wg, wrow, d, x, xq, xl, xs, gv);
6519 lk_gemv_dot<FU, Q, 1, W>(wu, wrow, d, x, xq, xl, xs, uv);
6520 #pragma unroll
6521 for(int i = 0; i < W; ++i){
6522 if(bg) gv[i] += bg[wrow + i];
6523 if(bu) uv[i] += bu[wrow + i];
6524 }
6525 } else {
6526 lk_gemv_dot<FSG, Q, 1, W>(sg, row - routed, d, x, xq, xl, xs, gv);
6527 lk_gemv_dot<FSU, Q, 1, W>(su, row - routed, d, x, xq, xl, xs, uv);
6528 }
6529 if((threadIdx.x & 31) == 0){
6530 #pragma unroll
6531 for(int i = 0; i < W; ++i){
6532 if(ACT == 0 || row >= routed) h[row + i] = gv[i] / (1.0f + expf(-gv[i])) * uv[i];
6533 else {
6534 float gc = fminf(gv[i], limit);
6535 h[row + i] = gc / (1.0f + expf(-slope * gc)) * (fminf(fmaxf(uv[i], -limit), limit) + 1.0f);
6536 }
6537 }
6538 }
6539}
6540// Each chosen expert's down row (format `FD`, plus its bias `bd`), summed by the expert's weight; then the shared
6541// expert's down row (format `FSD`) over the hidden rows past the kept experts', added to the sum. A block takes `W`
6542// consecutive outputs: a warp each kept expert's `W` rows, and `SW` warps (`SW` dividing `W`) the shared expert's, `W /
6543// SW` rows each — every output's dots and sums as a block of one output computes them. `plus`, when given, is added
6544// to each output, as an add of the two arrays adds it.
6545template<int Q, int FD, int FSD, int W, int SW>
6546__device__ __forceinline__ void lk_moe_down_block(const float* __restrict__ h, const signed char* __restrict__ hq,
6547 const signed char* __restrict__ hl, const float* __restrict__ hs, lk_w wd, lk_w sd,
6548 const float* __restrict__ bd, const int* __restrict__ idx, const float* __restrict__ wt,
6549 float* __restrict__ y, int d, int dff, int K, int sdff, const float* __restrict__ plus){
6550 typedef typename lk_acc<Q>::t A;
6551 constexpr int SR = W / SW;
6552 __shared__ A part[32][W];
6553 int o0 = blockIdx.x * W, lane = threadIdx.x & 31, r = threadIdx.x >> 5;
6554 if(r < K){
6555 long wrow = (long)idx[r] * d + o0;
6556 long hoff = (long)r * dff;
6557 float v[W];
6558 lk_gemv_dot<FD, Q, 1, W>(wd, wrow, dff, h + hoff, Q ? hq + hoff : nullptr, Q == 2 ? hl + hoff : nullptr,
6559 Q ? hs + (hoff >> 5) : nullptr, v);
6560 if(lane == 0)
6561 #pragma unroll
6562 for(int i = 0; i < W; ++i) part[r][i] = (A)(bd ? v[i] + bd[wrow + i] : v[i]) * (A)wt[r];
6563 } else {
6564 int i0 = (r - K) * SR;
6565 long hoff = (long)K * dff;
6566 float v[SR];
6567 lk_gemv_dot<FSD, Q, 1, SR>(sd, o0 + i0, sdff, h + hoff, Q ? hq + hoff : nullptr, Q == 2 ? hl + hoff : nullptr,
6568 Q ? hs + (hoff >> 5) : nullptr, v);
6569 if(lane == 0)
6570 #pragma unroll
6571 for(int i = 0; i < SR; ++i) part[K][i0 + i] = (A)v[i];
6572 }
6573 __syncthreads();
6574 if(threadIdx.x < W){
6575 int i = threadIdx.x;
6576 A s = 0;
6577 for(int e = 0; e < K; ++e) s += part[e][i];
6578 // The shared expert's row adds to the experts' sum as an `f32` sum of the two results.
6579 float v = sdff ? (float)s + (float)part[K][i] : (float)s;
6580 y[o0 + i] = plus ? v + plus[o0 + i] : v;
6581 }
6582}
6583template<int Q, int FD, int FSD, int W = 1, int SW = 1>
6584__global__ void lk_moe_down(const float* __restrict__ h, const signed char* __restrict__ hq,
6585 const signed char* __restrict__ hl, const float* __restrict__ hs, lk_w wd, lk_w sd,
6586 const float* __restrict__ bd, const int* __restrict__ idx, const float* __restrict__ wt,
6587 float* __restrict__ y, int d, int dff, int K, int sdff){
6588 lk_moe_down_block<Q, FD, FSD, W, SW>(h, hq, hl, hs, wd, sd, bd, idx, wt, y, d, dff, K, sdff, nullptr);
6589}
6590"#;
6591
6592const MOE_NORM: &str = r#"
6599template<int Q, int FD, int FSD, int W, int SW, bool ADD, bool SUM, bool OUTF, int QO, int PER, typename A, bool PRE, int VW>
6600__global__ void lk_moe_down_norm(const float* __restrict__ h, const signed char* __restrict__ hq,
6601 const signed char* __restrict__ hl, const float* __restrict__ hs, lk_w wd, lk_w sd,
6602 const float* __restrict__ bd, const int* __restrict__ idx, const float* __restrict__ wt,
6603 float* y, int d, int dff, int K, int sdff, const float* __restrict__ plus, lk_norm_of nm,
6604 int vt, int* __restrict__ cnt){
6605 __shared__ A red[32];
6606 __shared__ int last;
6607 lk_moe_down_block<Q, FD, FSD, W, SW>(h, hq, hl, hs, wd, sd, bd, idx, wt, y, d, dff, K, sdff, plus);
6608 __threadfence();
6609 __syncthreads();
6610 if(threadIdx.x == 0) last = atomicAdd(cnt, 1) == gridDim.x - 1;
6611 __syncthreads();
6612 if(!last) return;
6613 __threadfence();
6614 lk_out yr = nm.y ? LK_ROW(nm.y, nm.yrow, nm.ypitch) : lk_out();
6615 lk_rmsnorm_row<ADD, SUM, OUTF, QO, PER, A, PRE, true, VW>(nm.x, nm.r, nm.s, nm.w, yr, nm.yq, nm.yl, nm.ys, d, nm.eps, 0, 0,
6616 nm.pw, nm.peps, red, vt);
6617 if(threadIdx.x == 0) *cnt = 0;
6618}
6619template<int Q, bool ADD, bool SUM, bool OUTF, int QO, int PER, typename A, bool PRE>
6620__global__ void __launch_bounds__(1024) lk_moe_combine_norm(const typename lk_acc<Q>::t* __restrict__ part,
6621 const int* __restrict__ pos, int K, int d, const float* __restrict__ plus, float* y,
6622 lk_norm_of nm){
6623 __shared__ A red[32];
6624 long r = blockIdx.x, base = r * d;
6625 for(int o = threadIdx.x; o < d; o += blockDim.x){
6626 float v = lk_moe_combined<Q>(part, pos, K, d, r, o);
6627 y[base + o] = plus ? v + plus[base + o] : v;
6628 }
6629 __syncthreads();
6630 lk_out yr = nm.y ? LK_ROW(nm.y, nm.yrow, nm.ypitch) + base : lk_out();
6631 lk_rmsnorm_row<ADD, SUM, OUTF, QO, PER, A, PRE, false>(nm.x, nm.r, nm.s, nm.w, yr, nm.yq, nm.yl, nm.ys, d, nm.eps, base,
6632 base, nm.pw, nm.peps, red, blockDim.x);
6633}
6634"#;