ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
graph_ir_cuda.cu
Go to the documentation of this file.
1// CUDA kernels for the IR interpreter's device path (see graph_ir_cuda.h).
2#include "graph_ir_cuda.h"
3
4#include <stdexcept>
5#include <string>
6
7namespace ember_ir {
8namespace {
9
10constexpr int kThreads = 256;
11
12void check_launch(const char* what) {
14 if (e != cudaSuccess) throw std::runtime_error(std::string("IR CUDA kernel ") + what + ": " + cudaGetErrorString(e));
15}
16
17unsigned blocks_for(size_t n) { return static_cast<unsigned>((n + kThreads - 1) / kThreads); }
18
19__global__ void embedding_kernel(const float* __restrict__ table, const int* __restrict__ tokens,
20 float* __restrict__ out, int d) {
21 const int t = blockIdx.x;
22 const float* src = table + (size_t)tokens[t] * d;
23 for (int i = threadIdx.x; i < d; i += blockDim.x) out[(size_t)t * d + i] = src[i];
24}
25
26__global__ void pos_embedding_kernel(const float* __restrict__ x, const float* __restrict__ P, float* __restrict__ out,
27 int d, int start_pos, const int* __restrict__ dpos) {
28 const int t = blockIdx.x;
29 if (dpos) start_pos = *dpos;
30 const float* p = P + (size_t)(start_pos + t) * d;
31 for (int i = threadIdx.x; i < d; i += blockDim.x) out[(size_t)t * d + i] = __fadd_rn(x[(size_t)t * d + i], p[i]);
32}
33
34// One block per row; block-wide double sums in the CPU kernel's order of
35// operations (mean first, then the centred squares).
36__device__ double block_sum(double v, double* red) {
37 for (int o = 16; o > 0; o >>= 1) v += __shfl_xor_sync(0xffffffff, v, o);
38 const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
39 if (lane == 0) red[warp] = v;
41 double s = 0.0;
42 for (int w = 0; w < (int)(blockDim.x >> 5); ++w) s += red[w];
44 return s;
45}
46
47__global__ void layernorm_kernel(const float* __restrict__ x, const float* __restrict__ w, const float* __restrict__ b,
48 float* __restrict__ out, int d, float eps) {
49 __shared__ double red[kThreads / 32];
50 const float* r = x + (size_t)blockIdx.x * d;
51 float* o = out + (size_t)blockIdx.x * d;
52 double s = 0.0;
53 for (int i = threadIdx.x; i < d; i += blockDim.x) s += r[i];
54 const double mean = block_sum(s, red) / d;
55 double v = 0.0;
56 for (int i = threadIdx.x; i < d; i += blockDim.x) {
57 const double c = r[i] - mean;
58 v += c * c;
59 }
60 const double var = block_sum(v, red) / d;
61 const float inv = static_cast<float>(1.0 / sqrt(var + eps));
62 const float m = static_cast<float>(mean);
63 for (int i = threadIdx.x; i < d; i += blockDim.x)
64 o[i] = __fadd_rn(__fmul_rn(__fmul_rn(__fsub_rn(r[i], m), inv), w[i]), b[i]);
65}
66
67__global__ void bias_add_kernel(float* __restrict__ y, const float* __restrict__ b, size_t n, int O) {
68 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
69 if (i < n) y[i] = __fadd_rn(y[i], b[i % O]);
70}
71
72__global__ void binary_kernel(const float* __restrict__ a, const float* __restrict__ b, float* __restrict__ out,
73 size_t n, int op) {
74 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
75 if (i < n) out[i] = op == 0 ? __fadd_rn(a[i], b[i]) : op == 1 ? __fmul_rn(a[i], b[i]) : __fsub_rn(a[i], b[i]);
76}
77
78// op 0 relu, 1 sigmoid, 2 affine.
79__global__ void unary_kernel(const float* __restrict__ x, float* __restrict__ out, size_t n, int op, float scale,
80 float shift) {
81 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
82 if (i >= n) return;
83 const float v = x[i];
84 if (op == 0)
85 out[i] = 0.0f < v ? v : 0.0f; // std::max(0.0f, v)
86 else if (op == 1)
87 out[i] = __fdiv_rn(1.0f, __fadd_rn(1.0f, expf(-v)));
88 else
90}
91
92__global__ void mul_col_kernel(const float* __restrict__ x, const float* __restrict__ c, float* __restrict__ out,
93 size_t n, int w) {
94 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
95 if (i < n) out[i] = __fmul_rn(x[i], c[i / w]);
96}
97
98__global__ void scale_kernel(const float* __restrict__ x, const float* __restrict__ g, const float* __restrict__ b,
99 float* __restrict__ out, size_t n, int w) {
100 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
101 if (i >= n) return;
102 const int j = (int)(i % w);
103 const float p = __fmul_rn(x[i], g[j]);
104 out[i] = b ? __fadd_rn(p, b[j]) : p;
105}
106
107// One thread per row: the CPU's ascending sum.
108__global__ void rms_row_kernel(const float* __restrict__ x, float* __restrict__ out, int T, int w, float eps) {
109 const int t = blockIdx.x * blockDim.x + threadIdx.x;
110 if (t >= T) return;
111 const float* r = x + (size_t)t * w;
112 float ss = 0.0f;
113 for (int j = 0; j < w; ++j) ss = __fadd_rn(ss, __fmul_rn(r[j], r[j]));
114 out[t] = __fsqrt_rn(__fadd_rn(__fdiv_rn(ss, (float)w), eps));
115}
116
117struct DepthSources {
118 const float* p[kIrDepthSourcesMax];
119};
120
121// One block per row. Thread i forms source i's logit (unit-weight rmsnorm,
122// then the dot with w, both in ascending j); thread 0 the softmax in
123// ascending i; then the threads over j the mixture in ascending i.
124__global__ void depth_attend_kernel(DepthSources src, int S, const float* __restrict__ w, float* __restrict__ out,
125 int d, float eps) {
127 const size_t row = (size_t)blockIdx.x * d;
128 for (int i = threadIdx.x; i < S; i += blockDim.x) {
129 const float* s = src.p[i] + row;
130 float ss = 0.0f;
131 for (int j = 0; j < d; ++j) ss = __fadd_rn(ss, __fmul_rn(s[j], s[j]));
132 ss = __fdiv_rn(ss, (float)d);
133 const float r = __fdiv_rn(1.0f, __fsqrt_rn(__fadd_rn(ss, eps)));
134 float l = 0.0f;
135 for (int j = 0; j < d; ++j) l = __fadd_rn(l, __fmul_rn(__fmul_rn(s[j], r), w[j]));
136 a[i] = l;
137 }
139 if (threadIdx.x == 0) {
140 float mx = -INFINITY;
141 for (int i = 0; i < S; ++i) mx = mx < a[i] ? a[i] : mx; // std::max(mx, a[i])
142 float z = 0.0f;
143 for (int i = 0; i < S; ++i) {
144 a[i] = expf(__fsub_rn(a[i], mx));
145 z = __fadd_rn(z, a[i]);
146 }
147 for (int i = 0; i < S; ++i) a[i] = __fdiv_rn(a[i], z);
148 }
150 for (int j = threadIdx.x; j < d; j += blockDim.x) {
151 float o = __fmul_rn(src.p[0][row + j], a[0]);
152 for (int i = 1; i < S; ++i) o = __fadd_rn(o, __fmul_rn(src.p[i][row + j], a[i]));
153 out[row + j] = o;
154 }
155}
156
157// elu(x) + 1 as graph_ir.cpp elu_plus_one: x + 1 for x > 0, else
158// (exp(x) - 1) + 1, each step rounded.
159__device__ float elu_plus_one(float x) {
160 float r = x;
161 if (r <= 0.f) r = __fsub_rn(expf(r), 1.f);
162 return __fadd_rn(r, 1.f);
163}
164
165// One thread per channel, rows in order (linear_attention_row_cpu per row).
166__global__ void linear_attention_kernel(const float* __restrict__ q, const float* __restrict__ k,
167 const float* __restrict__ v, float* __restrict__ num,
168 float* __restrict__ den, float* __restrict__ out, int T, int w,
169 float den_floor) {
170 const int c = blockIdx.x * blockDim.x + threadIdx.x;
171 if (c >= w) return;
172 float nm = num[c], dn = den[c];
173 for (int t = 0; t < T; ++t) {
174 const size_t i = (size_t)t * w + c;
175 const float pk = elu_plus_one(k[i]);
176 nm = __fadd_rn(nm, __fmul_rn(pk, v[i]));
177 dn = __fadd_rn(dn, pk);
178 out[i] = __fdiv_rn(__fmul_rn(elu_plus_one(q[i]), nm), dn < den_floor ? den_floor : dn); // std::max
179 }
180 num[c] = nm;
181 den[c] = dn;
182}
183
184// One block; s and the next state in shared memory (ssm_scan_row_cpu per row).
185__global__ void ssm_scan_kernel(const float* __restrict__ u, const float* __restrict__ A, const float* __restrict__ B,
186 const float* __restrict__ C, const float* __restrict__ D, float* __restrict__ s_io,
187 float* __restrict__ y, int T, int n) {
188 extern __shared__ float sm[];
189 float* s = sm; // [n]
190 float* ns = sm + n; // [n]
191 for (int i = threadIdx.x; i < n; i += blockDim.x) s[i] = s_io[i];
192 const float dd = D[0];
194 for (int t = 0; t < T; ++t) {
195 const float* ut = u + (size_t)t * n;
196 for (int i = threadIdx.x; i < n; i += blockDim.x) {
197 const float* a = A + (size_t)i * n;
198 float acc = 0.0f;
199 for (int j = 0; j < n; ++j) acc = __fadd_rn(acc, __fmul_rn(a[j], s[j]));
200 ns[i] = __fadd_rn(acc, __fmul_rn(B[i], ut[i]));
201 }
203 for (int i = threadIdx.x; i < n; i += blockDim.x) {
204 s[i] = ns[i];
205 y[(size_t)t * n + i] = __fadd_rn(__fmul_rn(C[i], ns[i]), __fmul_rn(dd, ut[i]));
206 }
208 }
209 for (int i = threadIdx.x; i < n; i += blockDim.x) s_io[i] = s[i];
210}
211
212__global__ void kv_write_kernel(float* __restrict__ K, float* __restrict__ V, const float* __restrict__ k,
213 const float* __restrict__ v, size_t n, int row, int start_pos,
214 const int* __restrict__ dpos) {
215 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
216 if (i >= n) return;
217 const size_t off = (size_t)(dpos ? *dpos : start_pos) * row + i;
218 K[off] = k[i];
219 V[off] = v[i];
220}
221
222__global__ void silu_kernel(const float* __restrict__ x, float* __restrict__ out, size_t n) {
223 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
224 if (i < n) {
225 const float v = x[i];
226 out[i] = __fmul_rn(v, __fdiv_rn(1.0f, __fadd_rn(1.0f, expf(-v))));
227 }
228}
229
230__global__ void gelu_kernel(const float* __restrict__ x, float* __restrict__ out, size_t n, int tanh_form) {
231 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
232 if (i >= n) return;
233 const float v = x[i];
234 const float half_v = __fmul_rn(0.5f, v);
235 if (tanh_form) {
236 const float c = sqrtf(2.0f / 3.14159265358979323846f);
237 const float cube = __fmul_rn(__fmul_rn(__fmul_rn(0.044715f, v), v), v);
239 } else {
240 out[i] = __fmul_rn(half_v, __fadd_rn(1.0f, erff(__fdiv_rn(v, sqrtf(2.0f)))));
241 }
242}
243
244// One block per token, threads over (head, pair) of q then k.
245__global__ void rope_kernel(float* __restrict__ q, float* __restrict__ k, int hq, int hk, int hd, int start_pos,
246 const float* __restrict__ freqs, const int* __restrict__ dpos) {
247 const int t = blockIdx.x, half = hd / 2;
248 if (dpos) start_pos = *dpos;
249 const float* f = freqs + (size_t)(start_pos + t) * half * 2;
250 const int nq = hq * half, total = (hq + hk) * half;
251 for (int j = threadIdx.x; j < total; j += blockDim.x) {
252 const bool is_q = j < nq;
253 const int jj = is_q ? j : j - nq;
254 const int h = jj / half, i = jj % half;
255 float* x = (is_q ? q + (size_t)t * hq * hd : k + (size_t)t * hk * hd) + (size_t)h * hd + 2 * i;
256 const float c = f[2 * i], s = f[2 * i + 1];
257 const float x0 = x[0], x1 = x[1];
258 x[0] = __fsub_rn(__fmul_rn(x0, c), __fmul_rn(x1, s));
259 x[1] = __fadd_rn(__fmul_rn(x0, s), __fmul_rn(x1, c));
260 }
261}
262
263// One block per token, threads over (head, pair) (rope_rows_cpu).
264__global__ void rope_rows_kernel(float* __restrict__ x, int heads, int hd, int n_rot, int neox, int start_pos,
265 const float* __restrict__ freqs, const int* __restrict__ dpos) {
266 const int t = blockIdx.x, half = n_rot / 2;
267 if (dpos) start_pos = *dpos;
268 const float* f = freqs + (size_t)(start_pos + t) * half * 2;
269 for (int j = threadIdx.x; j < heads * half; j += blockDim.x) {
270 const int h = j / half, i = j % half;
271 float* r = x + ((size_t)t * heads + h) * hd;
272 const int i0 = neox ? i : 2 * i, i1 = neox ? i + half : 2 * i + 1;
273 const float c = f[2 * i], s = f[2 * i + 1];
274 const float v0 = r[i0], v1 = r[i1];
275 r[i0] = __fsub_rn(__fmul_rn(v0, c), __fmul_rn(v1, s));
276 r[i1] = __fadd_rn(__fmul_rn(v0, s), __fmul_rn(v1, c));
277 }
278}
279
280__global__ void conv1d_causal_kernel(const float* __restrict__ x, const float* __restrict__ w,
281 const float* __restrict__ bias, float* __restrict__ state, float* __restrict__ out,
282 int T, int C, int K) {
283 const int c = blockIdx.x * blockDim.x + threadIdx.x;
284 if (c >= C) return;
285 for (int t = 0; t < T; ++t) {
286 const float xc = x[(size_t)t * C + c];
287 float acc = 0.0f;
288 for (int k = 0; k < K; ++k) {
289 const float v = k < K - 1 ? state[(size_t)k * C + c] : xc;
290 acc = __fadd_rn(acc, __fmul_rn(w[(size_t)c * K + k], v));
291 }
292 out[(size_t)t * C + c] = bias ? __fadd_rn(acc, bias[c]) : acc;
293 if (K > 1) { // the channel's state becomes the last K - 1 entries of [state; x]
294 for (int k = 0; k + 2 < K; ++k) state[(size_t)k * C + c] = state[(size_t)(k + 1) * C + c];
295 state[(size_t)(K - 2) * C + c] = xc;
296 }
297 }
298}
299
300__global__ void selective_scan_kernel(const float* __restrict__ x, const float* __restrict__ dt,
301 const float* __restrict__ B, const float* __restrict__ Cm,
302 const float* __restrict__ A, const float* __restrict__ d, float* __restrict__ s,
303 float* __restrict__ y, int T, int D, int N, int softplus) {
304 const int c = blockIdx.x * blockDim.x + threadIdx.x;
305 if (c >= D) return;
306 float* sc = s + (size_t)c * N;
307 const float* ac = A + (size_t)c * N;
308 for (int t = 0; t < T; ++t) {
309 const float dtc = dt[(size_t)t * D + c], xc = x[(size_t)t * D + c];
310 const float delta = softplus ? (dtc <= 20.0f ? log1pf(expf(dtc)) : dtc) : dtc;
311 const float xd = __fmul_rn(xc, delta);
312 const float* bt = B + (size_t)t * N;
313 const float* ct = Cm + (size_t)t * N;
314 float acc = 0.0f;
315 for (int n = 0; n < N; ++n) {
316 const float decay = __fmul_rn(sc[n], expf(__fmul_rn(delta, ac[n])));
317 const float v = __fadd_rn(decay, __fmul_rn(bt[n], xd));
318 sc[n] = v;
319 acc = __fadd_rn(acc, __fmul_rn(v, ct[n]));
320 }
321 y[(size_t)t * D + c] = d ? __fadd_rn(acc, __fmul_rn(d[c], xc)) : acc;
322 }
323}
324
325struct ExprCode {
326 int n;
327 expr::Instr code[expr::kMaxCode];
328};
329struct ExprPtrs {
331 const float* col[expr::kMaxColumns];
332};
333
334// One thread per element: the stage's stack program (graph_ir_expr.cpp
335// unary_op / binary_op, each primitive rounded on its own).
336__global__ void expr_kernel(ExprCode code, ExprPtrs p, float* __restrict__ out, size_t n, int w) {
337 const size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;
338 if (i >= n) return;
339 const size_t t = i / w;
340 float st[expr::kMaxStack];
341 int sp = 0;
342 for (int k = 0; k < code.n; ++k) {
343 const expr::Instr ins = code.code[k];
344 switch (ins.op) {
345 case expr::Op::CONST: st[sp++] = ins.c; break;
346 case expr::Op::INPUT: st[sp++] = p.in[ins.idx][i]; break;
347 case expr::Op::COL: st[sp++] = p.col[ins.idx][t]; break;
348 case expr::Op::WIDTH: st[sp++] = (float)w; break;
349 case expr::Op::NEG: st[sp - 1] = -st[sp - 1]; break;
350 case expr::Op::EXP: st[sp - 1] = expf(st[sp - 1]); break;
351 case expr::Op::LOG: st[sp - 1] = logf(st[sp - 1]); break;
352 case expr::Op::SQRT: st[sp - 1] = __fsqrt_rn(st[sp - 1]); break;
353 case expr::Op::TANH: st[sp - 1] = tanhf(st[sp - 1]); break;
354 case expr::Op::SIGMOID: st[sp - 1] = __fdiv_rn(1.0f, __fadd_rn(1.0f, expf(-st[sp - 1]))); break;
355 case expr::Op::ERF: st[sp - 1] = erff(st[sp - 1]); break;
356 case expr::Op::ABS: st[sp - 1] = fabsf(st[sp - 1]); break;
357 case expr::Op::RELU: st[sp - 1] = 0.0f < st[sp - 1] ? st[sp - 1] : 0.0f; break; // std::max(0.0f, a)
358 case expr::Op::WHERE: {
359 const float b = st[--sp], a = st[--sp];
360 st[sp - 1] = st[sp - 1] > 0.0f ? a : b;
361 break;
362 }
363 default: {
364 const float b = st[--sp], a = st[sp - 1];
365 float r;
366 switch (ins.op) {
367 case expr::Op::ADD: r = __fadd_rn(a, b); break;
368 case expr::Op::SUB: r = __fsub_rn(a, b); break;
369 case expr::Op::MUL: r = __fmul_rn(a, b); break;
370 case expr::Op::DIV: r = __fdiv_rn(a, b); break;
371 case expr::Op::POW: r = powf(a, b); break;
372 case expr::Op::MAX: r = a >= b ? a : b; break;
373 default: r = a <= b ? a : b; break; // MIN
374 }
375 st[sp - 1] = r;
376 }
377 }
378 }
379 out[i] = st[0];
380}
381
382// One thread per row, ascending columns: 0 + r0 + r1 + ... (rowsum), or the
383// first largest entry (rowmax).
384__global__ void row_reduce_kernel(const float* __restrict__ x, float* __restrict__ out, int T, int w, int is_max) {
385 const int t = blockIdx.x * blockDim.x + threadIdx.x;
386 if (t >= T) return;
387 const float* r = x + (size_t)t * w;
388 float acc;
389 if (is_max) {
390 acc = r[0];
391 for (int j = 1; j < w; ++j) acc = r[j] > acc ? r[j] : acc;
392 } else {
393 acc = 0.0f;
394 for (int j = 0; j < w; ++j) acc = __fadd_rn(acc, r[j]);
395 }
396 out[t] = acc;
397}
398
399// Block-wide float max / sum (every thread gets the result).
400__device__ float block_reduce(float v, bool is_max, float* red) {
401 for (int o = 16; o > 0; o >>= 1) {
402 const float u = __shfl_xor_sync(0xffffffff, v, o);
403 v = is_max ? fmaxf(v, u) : v + u;
404 }
405 const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
406 if (lane == 0) red[warp] = v;
408 float r = red[0];
409 for (int w = 1; w < (int)(blockDim.x >> 5); ++w) r = is_max ? fmaxf(r, red[w]) : r + red[w];
411 return r;
412}
413
414constexpr int kWinChunk = 1024; // visible keys scored per pass (shared memory)
415constexpr int kWinDimsPerThread = 4; // head_dim <= kThreads * 4
416
417// Sliding-window attention with sinks, one block per (token, q head). The
418// visible keys of query position p (cpu_attention.cpp swa_visible_ranges) are
419// walked in ascending order, sinks then window, kWinChunk at a time: scores
420// into shared memory (a warp per key), then an online softmax (running max,
421// sum and output rescaled when a later chunk raises the max). With n <=
422// kWinChunk visible keys this is the CPU form's single max / exp / sum pass.
423// Masked keys are never read, so they contribute exactly zero.
424__global__ void window_attention_kernel(const float* __restrict__ Q, const float* __restrict__ K,
425 const float* __restrict__ V, float* __restrict__ out, int hq, int hkv,
426 int hd, int start_pos, float scale, int window, int sinks,
427 const int* __restrict__ dpos, float softcap) {
428 const int t = blockIdx.x, h = blockIdx.y;
429 const int pos = (dpos ? *dpos : start_pos) + t;
430 const int win_lo = pos + 1 > window ? pos + 1 - window : 0;
431 const int sink_hi = min(min(sinks, pos + 1), win_lo);
432 const int n = sink_hi + (pos + 1 - win_lo);
433 const int kvh = h / (hq / hkv);
434 extern __shared__ float sm[];
435 float* q = sm; // [hd]
436 float* s = sm + hd; // [kWinChunk]
437 __shared__ float red[kThreads / 32];
438 for (int d = threadIdx.x; d < hd; d += blockDim.x) q[d] = Q[((size_t)t * hq + h) * hd + d];
440 const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nwarps = blockDim.x >> 5;
441 float acc[kWinDimsPerThread] = {0.0f, 0.0f, 0.0f, 0.0f};
442 float M = -INFINITY, L = 0.0f;
443 for (int c0 = 0; c0 < n; c0 += kWinChunk) {
444 const int cn = min(kWinChunk, n - c0);
445 for (int i = warp; i < cn; i += nwarps) {
446 const int j = c0 + i;
447 const int key = j < sink_hi ? j : win_lo + (j - sink_hi);
448 const float* kr = K + ((size_t)key * hkv + kvh) * hd;
449 float dot = 0.0f;
450 for (int d = lane; d < hd; d += 32) dot += q[d] * kr[d];
451 for (int o = 16; o > 0; o >>= 1) dot += __shfl_xor_sync(0xffffffff, dot, o);
452 if (lane == 0) {
453 float sc = dot * scale;
454 if (softcap > 0.0f) sc = softcap * tanhf(sc / softcap); // IR_SPEC 5.15 softcap (revision 1.5)
455 s[i] = sc;
456 }
457 }
459 float mx = -INFINITY;
460 for (int i = threadIdx.x; i < cn; i += blockDim.x) mx = fmaxf(mx, s[i]);
461 const float newM = fmaxf(M, block_reduce(mx, true, red));
462 const float rescale = expf(M - newM); // 0 on the first chunk (M = -inf)
463 float sum = 0.0f;
464 for (int i = threadIdx.x; i < cn; i += blockDim.x) {
465 const float e = expf(s[i] - newM);
466 s[i] = e;
467 sum += e;
468 }
469 L = L * rescale + block_reduce(sum, false, red); // block_reduce syncs: s[] is complete
470 for (int r = 0; r < kWinDimsPerThread; ++r) {
471 const int d = threadIdx.x + r * blockDim.x;
472 if (d >= hd) break;
473 float a = 0.0f;
474 for (int i = 0; i < cn; ++i) {
475 const int j = c0 + i;
476 const int key = j < sink_hi ? j : win_lo + (j - sink_hi);
477 a += s[i] * V[((size_t)key * hkv + kvh) * hd + d];
478 }
479 acc[r] = acc[r] * rescale + a;
480 }
481 M = newM;
482 __syncthreads(); // s[] is rewritten by the next chunk
483 }
484 const float inv = 1.0f / (L + 1e-9f); // softmax_vector_cpu's normaliser
485 for (int r = 0; r < kWinDimsPerThread; ++r) {
486 const int d = threadIdx.x + r * blockDim.x;
487 if (d >= hd) break;
488 out[((size_t)t * hq + h) * hd + d] = acc[r] * inv;
489 }
490}
491
492// Attention mask turn_state (IR_SPEC 6.8.2) over a compact kv cache, one block per (token, q head).
493// codes[r] = 2 s + c for the position row r holds (s its segment, c = 1 at a state position); the
494// call's token t is at row row0 + t (row0 read from *drow when given: the captured decode step), and
495// sees row j <= row0 + t iff s(j) == s(t) or c(j) == 1. The rows are walked in ascending order,
496// kWinChunk at a time, as window_attention_kernel walks its keys: visible rows scored into shared
497// memory (a warp per row), masked rows -inf (never read), an online softmax over the chunks. A chunk
498// with no visible row is skipped; the query's own row is always visible, so the last chunk has one.
499__global__ void turn_state_attention_kernel(const float* __restrict__ Q, const float* __restrict__ K,
500 const float* __restrict__ V, float* __restrict__ out, int hq, int hkv,
501 int hd, int row0, const int* __restrict__ codes, float scale,
502 const int* __restrict__ drow) {
503 const int t = blockIdx.x, h = blockIdx.y;
504 if (drow) row0 = *drow;
505 const int n = row0 + t + 1;
506 const int qs = codes[row0 + t] >> 1;
507 const int kvh = h / (hq / hkv);
508 extern __shared__ float sm[];
509 float* q = sm; // [hd]
510 float* s = sm + hd; // [kWinChunk]
511 __shared__ float red[kThreads / 32];
512 for (int d = threadIdx.x; d < hd; d += blockDim.x) q[d] = Q[((size_t)t * hq + h) * hd + d];
514 const int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nwarps = blockDim.x >> 5;
515 float acc[kWinDimsPerThread] = {0.0f, 0.0f, 0.0f, 0.0f};
516 float M = -INFINITY, L = 0.0f;
517 for (int c0 = 0; c0 < n; c0 += kWinChunk) {
518 const int cn = min(kWinChunk, n - c0);
519 for (int i = warp; i < cn; i += nwarps) {
520 const int code = codes[c0 + i];
521 if ((code >> 1) != qs && !(code & 1)) { // masked (uniform over the warp)
522 if (lane == 0) s[i] = -INFINITY;
523 continue;
524 }
525 const float* kr = K + ((size_t)(c0 + i) * hkv + kvh) * hd;
526 float dot = 0.0f;
527 for (int d = lane; d < hd; d += 32) dot += q[d] * kr[d];
528 for (int o = 16; o > 0; o >>= 1) dot += __shfl_xor_sync(0xffffffff, dot, o);
529 if (lane == 0) s[i] = dot * scale;
530 }
532 float mx = -INFINITY;
533 for (int i = threadIdx.x; i < cn; i += blockDim.x) mx = fmaxf(mx, s[i]);
534 const float cm = block_reduce(mx, true, red);
535 if (cm == -INFINITY) { // every row of the chunk is masked (uniform over the block)
537 continue;
538 }
539 const float newM = fmaxf(M, cm);
540 const float rescale = expf(M - newM); // 0 on the first visible chunk (M = -inf)
541 float sum = 0.0f;
542 for (int i = threadIdx.x; i < cn; i += blockDim.x) {
543 const float e = expf(s[i] - newM); // exactly 0 for a masked row
544 s[i] = e;
545 sum += e;
546 }
547 L = L * rescale + block_reduce(sum, false, red); // block_reduce syncs: s[] is complete
548 for (int r = 0; r < kWinDimsPerThread; ++r) {
549 const int d = threadIdx.x + r * blockDim.x;
550 if (d >= hd) break;
551 float a = 0.0f;
552 for (int i = 0; i < cn; ++i)
553 if (s[i] != 0.0f) a += s[i] * V[((size_t)(c0 + i) * hkv + kvh) * hd + d];
554 acc[r] = acc[r] * rescale + a;
555 }
556 M = newM;
557 __syncthreads(); // s[] is rewritten by the next chunk
558 }
559 const float inv = 1.0f / (L + 1e-9f); // softmax_vector_cpu's normaliser
560 for (int r = 0; r < kWinDimsPerThread; ++r) {
561 const int d = threadIdx.x + r * blockDim.x;
562 if (d >= hd) break;
563 out[((size_t)t * hq + h) * hd + d] = acc[r] * inv;
564 }
565}
566
567// Row w of K and V takes row idx[w], for w = w0 .. n-1, in ascending order, in place: idx ascends and
568// idx[w] >= w, so a row is read before any later step overwrites it (one block, a barrier per row).
569__global__ void kv_compact_kernel(float* __restrict__ K, float* __restrict__ V, const int* __restrict__ idx, int w0,
570 int n, int row) {
571 for (int w = w0; w < n; ++w) {
572 const int r = idx[w];
573 if (r != w)
574 for (int i = threadIdx.x; i < row; i += blockDim.x) {
575 K[(size_t)w * row + i] = K[(size_t)r * row + i];
576 V[(size_t)w * row + i] = V[(size_t)r * row + i];
577 }
579 }
580}
581
582// out[t] = table[id] for a text id, state[id - vocab] for a state id (IR_SPEC 6.8.1). EXACT (a copy).
583__global__ void embedding_state_kernel(const float* __restrict__ table, const float* __restrict__ state, int vocab,
584 const int* __restrict__ tokens, float* __restrict__ out, int d) {
585 const int t = blockIdx.x;
586 const int id = tokens[t];
587 const float* src = id >= vocab ? state + (size_t)(id - vocab) * d : table + (size_t)id * d;
588 for (int i = threadIdx.x; i < d; i += blockDim.x) out[(size_t)t * d + i] = src[i];
589}
590
591// Mixture-of-Recursions (graph_ir_cuda.h): one block per compact row.
592__global__ void rows_gather_kernel(const float* __restrict__ src, const int* __restrict__ idx, float* __restrict__ dst,
593 int w) {
594 const int a = blockIdx.x;
595 const float* s = src + (size_t)idx[a] * w;
596 for (int i = threadIdx.x; i < w; i += blockDim.x) dst[(size_t)a * w + i] = s[i];
597}
598
599__global__ void rows_scatter_kernel(const float* __restrict__ src, const int* __restrict__ idx, float* __restrict__ dst,
600 int w) {
601 const int a = blockIdx.x;
602 float* d = dst + (size_t)idx[a] * w;
603 for (int i = threadIdx.x; i < w; i += blockDim.x) d[i] = src[(size_t)a * w + i];
604}
605
606__global__ void mor_gate_kernel(const float* __restrict__ x_in, float* __restrict__ x, const float* __restrict__ g,
607 int w) {
608 const int a = blockIdx.x;
609 const float ga = g[a];
610 for (int i = threadIdx.x; i < w; i += blockDim.x) {
611 const size_t j = (size_t)a * w + i;
612 const float diff = __fsub_rn(x[j], x_in[j]);
613 const float p = __fmul_rn(ga, diff);
614 x[j] = __fadd_rn(x_in[j], p);
615 }
616}
617
618// rope_kernel and rope_rows_kernel with row t at position pos[t].
619__global__ void rope_at_kernel(float* __restrict__ q, float* __restrict__ k, int hq, int hk, int hd,
620 const int* __restrict__ pos, const float* __restrict__ freqs) {
621 const int t = blockIdx.x, half = hd / 2;
622 const float* f = freqs + (size_t)pos[t] * half * 2;
623 const int nq = hq * half, total = (hq + hk) * half;
624 for (int j = threadIdx.x; j < total; j += blockDim.x) {
625 const bool is_q = j < nq;
626 const int jj = is_q ? j : j - nq;
627 const int h = jj / half, i = jj % half;
628 float* x = (is_q ? q + (size_t)t * hq * hd : k + (size_t)t * hk * hd) + (size_t)h * hd + 2 * i;
629 const float c = f[2 * i], s = f[2 * i + 1];
630 const float x0 = x[0], x1 = x[1];
631 x[0] = __fsub_rn(__fmul_rn(x0, c), __fmul_rn(x1, s));
632 x[1] = __fadd_rn(__fmul_rn(x0, s), __fmul_rn(x1, c));
633 }
634}
635
636__global__ void rope_rows_at_kernel(float* __restrict__ x, int heads, int hd, int n_rot, int neox,
637 const int* __restrict__ pos, const float* __restrict__ freqs) {
638 const int t = blockIdx.x, half = n_rot / 2;
639 const float* f = freqs + (size_t)pos[t] * half * 2;
640 for (int j = threadIdx.x; j < heads * half; j += blockDim.x) {
641 const int h = j / half, i = j % half;
642 float* r = x + ((size_t)t * heads + h) * hd;
643 const int i0 = neox ? i : 2 * i, i1 = neox ? i + half : 2 * i + 1;
644 const float c = f[2 * i], s = f[2 * i + 1];
645 const float v0 = r[i0], v1 = r[i1];
646 r[i0] = __fsub_rn(__fmul_rn(v0, c), __fmul_rn(v1, s));
647 r[i1] = __fadd_rn(__fmul_rn(v0, s), __fmul_rn(v1, c));
648 }
649}
650
651} // namespace
652
653void ir_rows_gather_cuda(const float* src, const int* idx, float* dst, int n, int w, cudaStream_t stream) {
654 if (n <= 0) return;
656 check_launch("rows gather");
657}
658
659void ir_rows_scatter_cuda(const float* src, const int* idx, float* dst, int n, int w, cudaStream_t stream) {
660 if (n <= 0) return;
662 check_launch("rows scatter");
663}
664
665void ir_mor_gate_cuda(const float* x_in, float* x, const float* g, int n, int w, cudaStream_t stream) {
666 if (n <= 0) return;
668 check_launch("mor gate");
669}
670
671void ir_rope_at_cuda(float* q, float* k, int T, int hq, int hk, int hd, const int* pos, const float* freqs,
674 check_launch("rope (positions)");
675}
676
677void ir_rope_rows_at_cuda(float* x, int T, int heads, int hd, int n_rot, bool neox, const int* pos, const float* freqs,
680 throw std::runtime_error("IR CUDA rope: n_rot " + std::to_string(n_rot) + " / head_dim " + std::to_string(hd));
682 check_launch("rope (rows, positions)");
683}
684
685void ir_turn_state_attention_cuda(const float* q, const float* K, const float* V, float* out, int T, int row0,
686 const int* codes, int hq, int hkv, int hd, float scale, cudaStream_t stream,
687 const int* drow) {
689 throw std::runtime_error("IR CUDA turn_state attention: unsupported shape (head_dim " + std::to_string(hd) + ")");
690 const size_t shm = (size_t)(hd + kWinChunk) * sizeof(float);
692 drow);
693 check_launch("turn_state attention");
694}
695
696void ir_kv_compact_cuda(float* K, float* V, const int* idx, int w0, int n, int row, cudaStream_t stream) {
697 if (w0 >= n) return;
699 check_launch("kv compact");
700}
701
702void ir_embedding_state_cuda(const float* table, const float* state, int vocab, const int* tokens, float* out, int T,
703 int d, cudaStream_t stream) {
705 check_launch("embedding (state rows)");
706}
707
708void ir_window_attention_cuda(const float* q, const float* K, const float* V, float* out, int T, int start_pos,
709 int hq, int hkv, int hd, float scale, int window, int sinks, cudaStream_t stream,
710 const int* dpos, float softcap) {
712 throw std::runtime_error("IR CUDA window attention: unsupported shape (window " + std::to_string(window) +
713 ", head_dim " + std::to_string(hd) + ")");
714 const size_t shm = (size_t)(hd + kWinChunk) * sizeof(float);
716 sinks, dpos, softcap);
717 check_launch("window attention");
718}
719
720void ir_kv_write_cuda(float* K, float* V, const float* k, const float* v, int T, int row, int start_pos,
721 cudaStream_t stream, const int* dpos) {
722 const size_t n = (size_t)T * row;
724 check_launch("kv write");
725}
726
727void ir_relu_cuda(const float* x, float* out, size_t n, cudaStream_t stream) {
728 unary_kernel<<<blocks_for(n), kThreads, 0, stream>>>(x, out, n, 0, 0.0f, 0.0f);
729 check_launch("relu");
730}
731
732void ir_sigmoid_cuda(const float* x, float* out, size_t n, cudaStream_t stream) {
733 unary_kernel<<<blocks_for(n), kThreads, 0, stream>>>(x, out, n, 1, 0.0f, 0.0f);
734 check_launch("sigmoid");
735}
736
737void ir_affine_cuda(const float* x, float* out, size_t n, float scale, float shift, cudaStream_t stream) {
739 check_launch("affine");
740}
741
742void ir_mul_col_cuda(const float* x, const float* c, float* out, int T, int w, cudaStream_t stream) {
743 const size_t n = (size_t)T * w;
744 mul_col_kernel<<<blocks_for(n), kThreads, 0, stream>>>(x, c, out, n, w);
745 check_launch("mul_col");
746}
747
748void ir_scale_cuda(const float* x, const float* g, const float* b, float* out, int T, int w, cudaStream_t stream) {
749 const size_t n = (size_t)T * w;
750 scale_kernel<<<blocks_for(n), kThreads, 0, stream>>>(x, g, b, out, n, w);
751 check_launch("scale");
752}
753
754void ir_rms_row_cuda(const float* x, float* out, int T, int w, float eps, cudaStream_t stream) {
755 rms_row_kernel<<<(T + 63) / 64, 64, 0, stream>>>(x, out, T, w, eps);
756 check_launch("rms_row");
757}
758
759void ir_depth_attend_cuda(const float* const* sources, int S, const float* w, float* out, int T, int d, float eps,
762 throw std::runtime_error("IR CUDA depth_attend: " + std::to_string(S) + " sources (2.." +
763 std::to_string(kIrDepthSourcesMax) + " supported)");
764 DepthSources src{};
765 for (int i = 0; i < S; ++i) src.p[i] = sources[i];
767 check_launch("depth_attend");
768}
769
770void ir_linear_attention_cuda(const float* q, const float* k, const float* v, float* num, float* den, float* out,
771 int T, int width, float den_floor, cudaStream_t stream) {
772 linear_attention_kernel<<<(width + 127) / 128, 128, 0, stream>>>(q, k, v, num, den, out, T, width, den_floor);
773 check_launch("linear_attention");
774}
775
776void ir_ssm_scan_cuda(const float* u, const float* A, const float* B, const float* C, const float* D, float* s,
777 float* y, int T, int n, cudaStream_t stream) {
779 throw std::runtime_error("IR CUDA ssm_scan: n " + std::to_string(n) + " (at most " + std::to_string(kIrSsmMax) +
780 ")");
781 const int threads = n < kThreads ? (n + 31) / 32 * 32 : kThreads;
782 ssm_scan_kernel<<<1, threads, (size_t)2 * n * sizeof(float), stream>>>(u, A, B, C, D, s, y, T, n);
783 check_launch("ssm_scan");
784}
785
786void ir_embedding_cuda(const float* table, const int* tokens, float* out, int T, int d, cudaStream_t stream) {
788 check_launch("embedding");
789}
790
791void ir_pos_embedding_cuda(const float* x, const float* P, float* out, int T, int d, int start_pos,
792 cudaStream_t stream, const int* dpos) {
794 check_launch("pos_embedding");
795}
796
797void ir_layernorm_cuda(const float* x, const float* w, const float* b, float* out, int T, int d, float eps,
800 check_launch("layernorm");
801}
802
803void ir_bias_add_cuda(float* y, const float* b, int T, int O, cudaStream_t stream) {
804 const size_t n = (size_t)T * O;
806 check_launch("bias");
807}
808
809void ir_binary_cuda(const float* a, const float* b, float* out, size_t n, int op, cudaStream_t stream) {
810 binary_kernel<<<blocks_for(n), kThreads, 0, stream>>>(a, b, out, n, op);
811 check_launch(op == 0 ? "add" : op == 1 ? "mul" : "sub");
812}
813
814void ir_silu_cuda(const float* x, float* out, size_t n, cudaStream_t stream) {
816 check_launch("silu");
817}
818
819void ir_gelu_cuda(const float* x, float* out, size_t n, bool tanh_form, cudaStream_t stream) {
820 gelu_kernel<<<blocks_for(n), kThreads, 0, stream>>>(x, out, n, tanh_form ? 1 : 0);
821 check_launch("gelu");
822}
823
824void ir_rope_cuda(float* q, float* k, int T, int hq, int hk, int hd, int start_pos, const float* freqs,
825 cudaStream_t stream, const int* dpos) {
827 check_launch("rope");
828}
829
830void ir_rope_rows_cuda(float* x, int T, int heads, int hd, int n_rot, bool neox, int start_pos, const float* freqs,
831 cudaStream_t stream, const int* dpos) {
833 throw std::runtime_error("IR CUDA rope: n_rot " + std::to_string(n_rot) + " / head_dim " + std::to_string(hd));
835 check_launch("rope (rows)");
836}
837
838void ir_conv1d_causal_cuda(const float* x, const float* w, const float* bias, float* state, float* out, int T, int C,
839 int K, cudaStream_t stream) {
840 if (K < 1 || C <= 0) throw std::runtime_error("IR CUDA conv1d_causal: kernel " + std::to_string(K));
841 conv1d_causal_kernel<<<(C + 127) / 128, 128, 0, stream>>>(x, w, bias, state, out, T, C, K);
842 check_launch("conv1d_causal");
843}
844
845void ir_selective_scan_cuda(const float* x, const float* dt, const float* B, const float* C, const float* A,
846 const float* d, float* s, float* y, int T, int D, int N, bool softplus,
848 selective_scan_kernel<<<(D + 127) / 128, 128, 0, stream>>>(x, dt, B, C, A, d, s, y, T, D, N, softplus ? 1 : 0);
849 check_launch("selective_scan");
850}
851
852size_t ir_expr_scratch_floats(const std::vector<expr::Stage>& stages, int T, int w) {
853 if (stages.size() <= 1) return 0;
854 return (size_t)T * w + (stages.size() - 1) * (size_t)T;
855}
856
857void ir_expr_cuda(const std::vector<expr::Stage>& stages, const float* const* ins, int n_ins, float* scratch,
858 float* out, int T, int w, cudaStream_t stream) {
860 throw std::runtime_error("IR CUDA expr: " + std::to_string(n_ins) + " inputs, " + std::to_string(stages.size()) +
861 " stages");
862 ExprPtrs p{};
863 for (int k = 0; k < n_ins; ++k) p.in[k] = ins[k];
864 float* full = scratch; // a reduction's argument, [T, w], reused stage by stage
865 for (size_t k = 0; k + 1 < stages.size(); ++k) p.col[k] = scratch + (size_t)T * w + k * (size_t)T;
866 const size_t n = (size_t)T * w;
867 for (size_t k = 0; k < stages.size(); ++k) {
868 const expr::Stage& s = stages[k];
869 if (s.code.empty() || s.code.size() > expr::kMaxCode) throw std::runtime_error("IR CUDA expr: a stage's code");
870 ExprCode code{};
871 code.n = (int)s.code.size();
872 for (size_t i = 0; i < s.code.size(); ++i) code.code[i] = s.code[i];
873 const bool last = k + 1 == stages.size();
874 expr_kernel<<<blocks_for(n), kThreads, 0, stream>>>(code, p, last ? out : full, n, w);
875 check_launch("expr");
876 if (last) break;
877 row_reduce_kernel<<<(T + 63) / 64, 64, 0, stream>>>(full, const_cast<float*>(p.col[k]), T, w, s.reduce == 2);
878 check_launch("expr row reduction");
879 }
880}
881
882namespace {
883
884__global__ void mla_kv_write_kernel(float* __restrict__ KV, const float* __restrict__ c, const float* __restrict__ kr,
885 int T, int r, int dr, int start_pos, const int* __restrict__ dpos) {
886 if (dpos) start_pos = *dpos;
887 const int w = r + dr;
888 const size_t n = (size_t)T * w;
889 for (size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (size_t)gridDim.x * blockDim.x) {
890 const int t = (int)(i / w), e = (int)(i % w);
891 KV[((size_t)start_pos + t) * w + e] = e < r ? c[(size_t)t * r + e] : kr[(size_t)t * dr + (e - r)];
892 }
893}
894
895// One block per (row t, head h). Shared: qa [r], qr_h [dr], u [r], the chunk's scores [kMlaChunk].
896__global__ void mla_attention_kernel(const float* __restrict__ qn, const float* __restrict__ qr,
897 const float* __restrict__ KV, const float* __restrict__ Kb,
898 const float* __restrict__ Vb, float* __restrict__ out, int H, int r, int dn, int dr,
899 int dv, float scale, int window, int sinks, int start_pos,
900 const int* __restrict__ dpos) {
901 extern __shared__ float sm[];
902 float* qa = sm;
903 float* q2 = qa + r;
904 float* u = q2 + dr;
905 float* sc = u + r;
906 __shared__ float m_s, z_s, alpha_s;
907 const int t = blockIdx.x / H, h = blockIdx.x % H;
908 if (dpos) start_pos = *dpos;
909 const int p = start_pos + t;
910 const int w = r + dr;
911 const float* K = Kb + (size_t)h * r * dn;
912 const float* V = Vb + (size_t)h * dv * r;
913 const float* qnh = qn + ((size_t)t * H + h) * dn;
914 const float* qrh = qr + ((size_t)t * H + h) * dr;
915 for (int i = threadIdx.x; i < r; i += blockDim.x) {
916 float acc = 0.0f;
917 for (int k = 0; k < dn; ++k) acc = __fadd_rn(acc, __fmul_rn(K[(size_t)i * dn + k], qnh[k]));
918 qa[i] = acc;
919 u[i] = 0.0f;
920 }
921 for (int e = threadIdx.x; e < dr; e += blockDim.x) q2[e] = qrh[e];
922 // section 5.15: [0, sink_hi) then [win_lo, p] (cpu_attention.cpp swa_visible_ranges)
923 int win_lo = 0, sink_hi = 0;
924 if (window > 0) {
925 win_lo = p + 1 > window ? p + 1 - window : 0;
926 sink_hi = min(min(sinks, p + 1), win_lo);
927 }
928 const int nvis = sink_hi + (p + 1 - win_lo);
929 const bool one_chunk = nvis <= kMlaChunk;
930 if (threadIdx.x == 0) {
931 m_s = -INFINITY;
932 z_s = 0.0f;
933 }
935 for (int c0 = 0; c0 < nvis; c0 += kMlaChunk) {
936 const int cn = min(kMlaChunk, nvis - c0);
937 for (int j = threadIdx.x; j < cn; j += blockDim.x) {
938 const int v = c0 + j;
939 const int key = v < sink_hi ? v : win_lo + (v - sink_hi);
940 const float* row = KV + (size_t)key * w;
941 float a = 0.0f, b = 0.0f;
942 for (int i = 0; i < r; ++i) a = __fadd_rn(a, __fmul_rn(qa[i], row[i]));
943 for (int e = 0; e < dr; ++e) b = __fadd_rn(b, __fmul_rn(q2[e], row[r + e]));
944 sc[j] = __fmul_rn(__fadd_rn(a, b), scale);
945 }
947 if (threadIdx.x == 0) { // max, then e_j and z in ascending j (the CPU's order within a chunk)
948 float cm = m_s;
949 for (int j = 0; j < cn; ++j) cm = fmaxf(cm, sc[j]);
950 const float alpha = m_s == -INFINITY ? 0.0f : expf(__fsub_rn(m_s, cm));
951 float zc = 0.0f;
952 for (int j = 0; j < cn; ++j) {
953 sc[j] = expf(__fsub_rn(sc[j], cm));
954 zc = __fadd_rn(zc, sc[j]);
955 }
957 m_s = cm;
958 alpha_s = alpha;
959 }
961 const float alpha = alpha_s, z = z_s;
962 for (int i = threadIdx.x; i < r; i += blockDim.x) {
963 float acc = one_chunk ? 0.0f : __fmul_rn(u[i], alpha);
964 for (int j = 0; j < cn; ++j) {
965 const int v = c0 + j;
966 const int key = v < sink_hi ? v : win_lo + (v - sink_hi);
967 const float a = one_chunk ? __fdiv_rn(sc[j], z) : sc[j];
968 acc = __fadd_rn(acc, __fmul_rn(a, KV[(size_t)key * w + i]));
969 }
970 u[i] = acc;
971 }
973 }
974 if (!one_chunk) {
975 for (int i = threadIdx.x; i < r; i += blockDim.x) u[i] = __fdiv_rn(u[i], z_s);
977 }
978 float* o = out + ((size_t)t * H + h) * dv;
979 for (int k = threadIdx.x; k < dv; k += blockDim.x) {
980 float acc = 0.0f;
981 for (int i = 0; i < r; ++i) acc = __fadd_rn(acc, __fmul_rn(V[(size_t)k * r + i], u[i]));
982 o[k] = acc;
983 }
984}
985
986} // namespace
987
988void ir_mla_kv_write_cuda(float* KV, const float* c, const float* kr, int T, int r, int dr, int start_pos,
989 cudaStream_t stream, const int* dpos) {
990 const size_t n = (size_t)T * (r + dr);
992 check_launch("mla kv write");
993}
994
995void ir_mla_attention_cuda(const float* qn, const float* qr, const float* KV, const float* Kb, const float* Vb,
996 float* out, int T, int start_pos, int H, int r, int dn, int dr, int dv, float scale,
997 int window, int sinks, cudaStream_t stream, const int* dpos) {
998 const size_t smem = (static_cast<size_t>(r) * 2 + dr + kMlaChunk) * sizeof(float);
999 if (smem > 48 * 1024)
1000 throw std::runtime_error("IR mla_attention on CUDA: kv_rank " + std::to_string(r) + " needs " + std::to_string(smem) +
1001 " bytes of shared memory (at most 48 KiB)");
1004 check_launch("mla attention");
1005}
1006
1007} // namespace ember_ir
static constexpr size_t kMaxInputs
__global__ void rope_kernel(float *x, int num_heads, int head_dim, const float *all_freqs_cis_base, int pos, bool use_adjacent_pairing)
__global__ void silu_kernel(const float *x, float *out, int n)
const float * in[expr::Expr::kMaxInputs]
const float * p[kIrDepthSourcesMax]
expr::Instr code[expr::kMaxCode]
const float * col[expr::kMaxColumns]
int n
std::vector< float > v
float c
std::vector< E > a
node out
node scale
size_t j
constexpr size_t kMaxCode
constexpr size_t kMaxColumns
constexpr size_t kMaxStack
Definition api.h:20
void ir_linear_attention_cuda(const float *q, const float *k, const float *v, float *num, float *den, float *out, int T, int width, float den_floor, cudaStream_t stream)
void ir_conv1d_causal_cuda(const float *x, const float *w, const float *bias, float *state, float *out, int T, int C, int K, cudaStream_t stream)
void ir_turn_state_attention_cuda(const float *q, const float *K, const float *V, float *out, int T, int row0, const int *codes, int hq, int hkv, int hd, float scale, cudaStream_t stream, const int *drow)
void ir_binary_cuda(const float *a, const float *b, float *out, size_t n, int op, cudaStream_t stream)
void ir_bias_add_cuda(float *y, const float *b, int T, int O, cudaStream_t stream)
void ir_rope_rows_at_cuda(float *x, int T, int heads, int hd, int n_rot, bool neox, const int *pos, const float *freqs, cudaStream_t stream)
void ir_rows_gather_cuda(const float *src, const int *idx, float *dst, int n, int w, cudaStream_t stream)
void ir_rows_scatter_cuda(const float *src, const int *idx, float *dst, int n, int w, cudaStream_t stream)
void ir_kv_write_cuda(float *K, float *V, const float *k, const float *v, int T, int row, int start_pos, cudaStream_t stream, const int *dpos)
size_t ir_expr_scratch_floats(const std::vector< expr::Stage > &stages, int T, int w)
void ir_embedding_cuda(const float *table, const int *tokens, float *out, int T, int d, cudaStream_t stream)
void ir_gelu_cuda(const float *x, float *out, size_t n, bool tanh_form, cudaStream_t stream)
void ir_silu_cuda(const float *x, float *out, size_t n, cudaStream_t stream)
void ir_depth_attend_cuda(const float *const *sources, int S, const float *w, float *out, int T, int d, float eps, cudaStream_t stream)
void ir_rope_rows_cuda(float *x, int T, int heads, int hd, int n_rot, bool neox, int start_pos, const float *freqs, cudaStream_t stream, const int *dpos)
void ir_kv_compact_cuda(float *K, float *V, const int *idx, int w0, int n, int row, cudaStream_t stream)
void ir_pos_embedding_cuda(const float *x, const float *P, float *out, int T, int d, int start_pos, cudaStream_t stream, const int *dpos)
void ir_window_attention_cuda(const float *q, const float *K, const float *V, float *out, int T, int start_pos, int hq, int hkv, int hd, float scale, int window, int sinks, cudaStream_t stream, const int *dpos, float softcap)
void ir_scale_cuda(const float *x, const float *g, const float *b, float *out, int T, int w, cudaStream_t stream)
void ir_expr_cuda(const std::vector< expr::Stage > &stages, const float *const *ins, int n_ins, float *scratch, float *out, int T, int w, cudaStream_t stream)
void ir_rope_at_cuda(float *q, float *k, int T, int hq, int hk, int hd, const int *pos, const float *freqs, cudaStream_t stream)
void ir_rms_row_cuda(const float *x, float *out, int T, int w, float eps, cudaStream_t stream)
void ir_layernorm_cuda(const float *x, const float *w, const float *b, float *out, int T, int d, float eps, cudaStream_t stream)
void ir_relu_cuda(const float *x, float *out, size_t n, cudaStream_t stream)
void ir_affine_cuda(const float *x, float *out, size_t n, float scale, float shift, cudaStream_t stream)
void ir_selective_scan_cuda(const float *x, const float *dt, const float *B, const float *C, const float *A, const float *d, float *s, float *y, int T, int D, int N, bool softplus, cudaStream_t stream)
void ir_mla_attention_cuda(const float *qn, const float *qr, const float *KV, const float *Kb, const float *Vb, float *out, int T, int start_pos, int H, int r, int dn, int dr, int dv, float scale, int window, int sinks, cudaStream_t stream, const int *dpos)
void ir_mul_col_cuda(const float *x, const float *c, float *out, int T, int w, cudaStream_t stream)
void ir_embedding_state_cuda(const float *table, const float *state, int vocab, const int *tokens, float *out, int T, int d, cudaStream_t stream)
void ir_mor_gate_cuda(const float *x_in, float *x, const float *g, int n, int w, cudaStream_t stream)
void ir_mla_kv_write_cuda(float *KV, const float *c, const float *kr, int T, int r, int dr, int start_pos, cudaStream_t stream, const int *dpos)
void ir_rope_cuda(float *q, float *k, int T, int hq, int hk, int hd, int start_pos, const float *freqs, cudaStream_t stream, const int *dpos)
void ir_ssm_scan_cuda(const float *u, const float *A, const float *B, const float *C, const float *D, float *s, float *y, int T, int n, cudaStream_t stream)
void ir_sigmoid_cuda(const float *x, float *out, size_t n, cudaStream_t stream)