phanerozoic commited on
Commit
62ec8be
·
verified ·
1 Parent(s): 35adf41

model-as-a-kernel v1 source

Browse files
mak_cuda/mak_kernels.h CHANGED
@@ -8,9 +8,9 @@
8
  int mak_grid_blocks_impl(int smem_bytes);
9
 
10
  cudaError_t mak_launch_mega(const long long* prog, int n_phases, int pos0,
11
- int steps, int slot0, int prompt_len,
12
- int* bar_cnt, int* bar_sense, int grid,
13
- int smem_bytes, cudaStream_t stream);
14
 
15
  cudaError_t mak_launch_phase(const long long* prog, int phase, int pos,
16
  int step_slot, int grid, int smem_bytes,
 
8
  int mak_grid_blocks_impl(int smem_bytes);
9
 
10
  cudaError_t mak_launch_mega(const long long* prog, int n_phases, int pos0,
11
+ int total, int slot0, int prompt_len,
12
+ int chunk_m, int* bar_cnt, int* bar_sense,
13
+ int grid, int smem_bytes, cudaStream_t stream);
14
 
15
  cudaError_t mak_launch_phase(const long long* prog, int phase, int pos,
16
  int step_slot, int grid, int smem_bytes,
mak_cuda/megakernel.cu CHANGED
@@ -37,6 +37,7 @@ enum PhaseOp : int {
37
  OP_ATTN = 3,
38
  OP_ARGMAX_PART = 4,
39
  OP_ARGMAX_FIN = 5,
 
40
  };
41
 
42
  enum InputTransform : int {
@@ -130,7 +131,7 @@ __device__ unsigned long long block_max_u64(unsigned long long v,
130
  // and any block with nothing to do simply falls through to the barrier.
131
  // ---------------------------------------------------------------------------
132
 
133
- __device__ void ph_embed(const long long* P, int pos, int prompt_len) {
134
  const bf16* tab = reinterpret_cast<const bf16*>(P[2]);
135
  bf16* out = reinterpret_cast<bf16*>(P[3]);
136
  const int* prompt = reinterpret_cast<const int*>(P[5]);
@@ -138,13 +139,16 @@ __device__ void ph_embed(const long long* P, int pos, int prompt_len) {
138
  const long long K = P[8];
139
  // Positions inside the prompt read from the staged prompt buffer; beyond
140
  // it, from the feedback token written by the previous step's argmax.
141
- const long long t = (pos < prompt_len && prompt != nullptr)
142
- ? (long long)prompt[pos]
143
- : (long long)tok[0];
144
- const bf16* row = tab + t * K;
145
- for (long long k = (long long)blockIdx.x * blockDim.x + threadIdx.x; k < K;
146
- k += (long long)gridDim.x * blockDim.x)
147
- out[k] = row[k];
 
 
 
148
  }
149
 
150
  __device__ __forceinline__ unsigned long long amax_key(float v, unsigned idx) {
@@ -157,10 +161,10 @@ __device__ __forceinline__ unsigned long long amax_key(float v, unsigned idx) {
157
  // Finalize one element of split-S attention: combine per-chunk partials
158
  // (max, sum, weighted V) into the bf16 attention output, matching eager's
159
  // bf16 rounding of the attention output before the O projection.
160
- __device__ __forceinline__ float attn_finalize(const float* part, int h,
161
  int d, int D, int maxch,
162
  int nch) {
163
- const float* base = part + ((long long)h * maxch) * (D + 2);
164
  float M = -INFINITY;
165
  for (int c = 0; c < nch; ++c)
166
  M = fmaxf(M, base[(long long)c * (D + 2) + D]);
@@ -197,78 +201,72 @@ __device__ __forceinline__ float mk_load_x(const bf16* x, const bf16* gamma,
197
  // Reduced (or plain) bf16 input element k for a consumer phase: sum the
198
  // producer's fp32 k-slice partials in fixed order, add the residual, round
199
  // to bf16, optionally write the materialized vector back.
200
- __device__ __forceinline__ bf16 consume_x(const long long* P, long long k,
201
- long long K, int itflags) {
202
- if (itflags & ITF_REDUCE) {
203
- const float* pp = reinterpret_cast<const float*>(P[1]);
204
- float s = pp[k];
205
- #pragma unroll
206
- for (int sl = 1; sl < NSLICE; ++sl) s += pp[(long long)sl * K + k];
207
- const bf16* resid = reinterpret_cast<const bf16*>(P[5]);
208
- if (resid != nullptr) s = bf2f(resid[k]) + s;
209
- const bf16 v = f2bf(s);
210
- if (itflags & ITF_WRITEBACK) reinterpret_cast<bf16*>(P[6])[k] = v;
211
- return v;
212
- }
213
- return reinterpret_cast<const bf16*>(P[1])[k];
214
- }
215
-
216
- __device__ void ph_gemv(const long long* P, int pos) {
217
  const bf16* W = reinterpret_cast<const bf16*>(P[2]);
218
  const bf16* gamma = reinterpret_cast<const bf16*>(P[4]);
219
  const bf16* resid = reinterpret_cast<const bf16*>(P[5]);
220
  const long long N = P[7], K = P[8];
221
- const int itflags = (int)P[9];
222
- const int itrans = itflags & ITF_BASE_MASK;
223
  const int epi = (int)P[10];
 
224
  const float eps = bits2f(P[14]);
225
  const float* part = reinterpret_cast<const float*>(P[1]);
 
226
  const int D = (int)P[13];
227
  const int maxch = (int)P[15];
228
- const int nch = (pos + ATTN_CHUNK) / ATTN_CHUNK; // ceil((pos+1)/CHUNK)
229
-
230
- // For reduce inputs, the vector length that carries the reduction is the
231
- // producer's row count: K for most transforms, 2K for swiglu pairs.
232
- const long long KR = (itrans == IT_SWIGLU) ? 2 * K : K;
233
-
234
- // Stage the transformed input once per block: every transform value is a
235
- // bf16-rounded quantity by semantics, so bf16 staging is exact. This turns
236
- // per-row-visit transform work (swiglu exp, attention finalize) into
237
- // once-per-block work and makes the dot loop pure shared-memory reads.
238
- // RMSNORM stages in two passes (raw materialized value first, then the
239
- // norm applied in place) so reduce+resid+writeback consumption happens
240
- // exactly once per element.
241
  extern __shared__ bf16 xs[];
242
  __shared__ float s_red[32];
243
  __shared__ float s_scale;
244
- if (itrans == IT_RMSNORM) {
245
- float ss = 0.f;
246
- for (long long k = threadIdx.x; k < K; k += blockDim.x) {
247
- const bf16 xv = consume_x(P, k, KR, itflags);
248
- xs[k] = xv;
249
- const float v = bf2f(xv);
250
- ss += v * v;
251
- }
252
- ss = block_sum(ss, s_red);
253
- if (threadIdx.x == 0) s_scale = rsqrtf(ss / (float)K + eps);
254
- __syncthreads();
255
- const float inv = s_scale;
256
- for (long long k = threadIdx.x; k < K; k += blockDim.x)
257
- xs[k] = f2bf(bf2f(gamma[k]) * bf2f(f2bf(bf2f(xs[k]) * inv)));
258
- } else {
259
- for (long long k = threadIdx.x; k < K; k += blockDim.x) {
260
- float v;
261
- if (itrans == IT_SWIGLU) {
262
- const float g = bf2f(consume_x(P, k, KR, itflags));
 
 
 
263
  const float sig = 1.f / (1.f + expf(-g));
264
  const float sg = bf2f(f2bf(g * sig));
265
- v = bf2f(f2bf(sg * bf2f(consume_x(P, K + k, KR, itflags))));
266
- } else if (itrans == IT_ATTNFIN) {
267
- v = attn_finalize(part, (int)(k / D), (int)(k % D), D, maxch, nch);
268
- } else {
269
- v = bf2f(consume_x(P, k, KR, itflags));
270
  }
271
- xs[k] = f2bf(v);
 
 
 
 
 
 
 
 
 
 
 
272
  }
273
  }
274
  __syncthreads();
@@ -277,57 +275,61 @@ __device__ void ph_gemv(const long long* P, int pos) {
277
  const long long warp =
278
  ((long long)blockIdx.x * blockDim.x + threadIdx.x) >> 5;
279
  const long long nwarps = ((long long)gridDim.x * blockDim.x) >> 5;
280
- __shared__ unsigned long long s64[32];
281
  unsigned long long best = amax_key(-INFINITY, 0xFFFFFFFFu);
282
- // EP_PARTIAL producers split each row across NSLICE k-slices handled by
283
- // independent warps: NSLICE times the parallelism and chains a quarter as
284
- // long, with the fixed-order reduction done by the consumer's staging.
285
- const bool partial = (epi == EP_PARTIAL);
286
- const long long V = partial ? N * NSLICE : N;
287
- const long long kslice = K / NSLICE;
288
- for (long long v = warp; v < V; v += nwarps) {
289
- const long long row = partial ? v / NSLICE : v;
290
- const int slice = partial ? (int)(v % NSLICE) : 0;
291
- const long long klo = partial ? (long long)slice * kslice : 0;
292
- const long long khi = partial ? klo + kslice : K;
293
  const bf16* wr = W + row * K;
294
- float acc = 0.f;
295
- // 16-byte loads, next tile prefetched so the weight fetch for iteration
296
- // i+1 overlaps the fma chain of iteration i.
297
- long long k = klo + (long long)lane * 8;
298
- if (k + 7 < khi) {
 
 
299
  int4 wv4 = *reinterpret_cast<const int4*>(wr + k);
300
- for (; k + 7 < khi; k += 256) {
301
  int4 nxt;
302
- const bool more = (k + 256 + 7 < khi);
303
  if (more) nxt = *reinterpret_cast<const int4*>(wr + k + 256);
304
  const __nv_bfloat162* wp =
305
  reinterpret_cast<const __nv_bfloat162*>(&wv4);
306
- const __nv_bfloat162* xp =
307
- reinterpret_cast<const __nv_bfloat162*>(xs + k);
308
  #pragma unroll
309
- for (int j = 0; j < 4; ++j) {
310
- acc = fmaf(__low2float(wp[j]), __low2float(xp[j]), acc);
311
- acc = fmaf(__high2float(wp[j]), __high2float(xp[j]), acc);
 
 
 
 
 
 
 
 
 
312
  }
313
  if (more) wv4 = nxt;
314
  }
315
  }
316
- acc = warp_sum(acc);
317
- if (lane == 0) {
318
- if (partial)
319
- reinterpret_cast<float*>(P[3])[(long long)slice * N + row] = acc;
320
- else if (epi == EP_RESID)
321
- reinterpret_cast<bf16*>(P[3])[row] = f2bf(bf2f(resid[row]) + acc);
322
- else if (epi == EP_F32)
323
- reinterpret_cast<float*>(P[3])[row] = acc;
324
- else if (epi == EP_F32_AMAX) {
325
- reinterpret_cast<float*>(P[3])[row] = acc;
326
- const unsigned long long kk = amax_key(bf2f(f2bf(acc)),
327
- (unsigned)row);
328
- best = (kk > best) ? kk : best;
329
- } else {
330
- reinterpret_cast<bf16*>(P[3])[row] = f2bf(acc);
 
 
 
 
 
 
331
  }
332
  }
333
  }
@@ -339,16 +341,58 @@ __device__ void ph_gemv(const long long* P, int pos) {
339
  }
340
  }
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- // Attention with rope fused in and cached positions split across chunks.
344
- // Slots: p1 qkv, p2 kcache, p3 vcache, p4 fp32 partials
345
- // [Hq, maxch, D+2] (weighted V, chunk max, chunk sum), p5 q-norm w,
346
- // p6 k-norm w, 7 max_seq, 8 qk flag, 9 eps bits, 10 theta bits, 11 Hq,
347
- // 12 Hkv, 13 D, 14 scale, 15 maxch. Each (head, chunk) block ropes its own
348
- // copy of q (identical bits across chunks); the chunk owning `pos` also
349
  // norms/ropes K and appends K/V for its group (duplicate identical writes
350
- // across GQA members are benign). The O-projection finalizes the partials.
351
- __device__ void ph_attn(const long long* P, int pos) {
 
 
352
  const long long max_seq = P[7];
353
  const int qk_flag = (int)P[8];
354
  const float eps = bits2f(P[9]);
@@ -356,29 +400,11 @@ __device__ void ph_attn(const long long* P, int pos) {
356
  const int Hq = (int)P[11], Hkv = (int)P[12], D = (int)P[13];
357
  const float scale = bits2f(P[14]);
358
  const int maxch = (int)P[15];
359
- const int S = pos + 1;
360
- const int nch = (S + ATTN_CHUNK - 1) / ATTN_CHUNK;
361
- const int own_ch = pos / ATTN_CHUNK;
362
  const int rep = Hq / Hkv;
 
363
 
364
  const bf16* qnw = reinterpret_cast<const bf16*>(P[5]);
365
  const bf16* knw = reinterpret_cast<const bf16*>(P[6]);
366
- const bool red = (qk_flag & 2) != 0;
367
- const long long KT = (long long)(Hq + 2 * Hkv) * D;
368
- const float* qp = reinterpret_cast<const float*>(P[1]);
369
- const bf16* qb = reinterpret_cast<const bf16*>(P[1]);
370
-
371
- // qkv element: reduce the producer's fp32 k-slice partials (fixed order)
372
- // and round to bf16, reproducing the monolithic store's value semantics.
373
- auto qkv_at = [&](long long idx) -> float {
374
- if (red) {
375
- float s = qp[idx];
376
- #pragma unroll
377
- for (int sl = 1; sl < NSLICE; ++sl) s += qp[(long long)sl * KT + idx];
378
- return bf2f(f2bf(s));
379
- }
380
- return bf2f(qb[idx]);
381
- };
382
 
383
  __shared__ float qs[MAX_HEAD_DIM];
384
  __shared__ float hbuf[MAX_HEAD_DIM];
@@ -386,137 +412,150 @@ __device__ void ph_attn(const long long* P, int pos) {
386
  __shared__ float s_red[32];
387
  __shared__ float s_inv, s_m;
388
 
389
- const int total = Hq * nch;
390
- for (int v = blockIdx.x; v < total; v += gridDim.x) {
391
- const int head = v / nch;
392
- const int ch = v % nch;
393
- const int kvh = head / rep;
394
- bf16* kc = reinterpret_cast<bf16*>(P[2]) + (long long)kvh * max_seq * D;
395
- bf16* vc = reinterpret_cast<bf16*>(P[3]) + (long long)kvh * max_seq * D;
396
- float* part = reinterpret_cast<float*>(P[4]) +
397
- ((long long)head * maxch + ch) * (D + 2);
398
- const int half = D >> 1;
399
-
400
- // stage rope'd q (bf16-rounded values) into shared
401
- {
402
- const long long qbase = (long long)head * D;
403
- if (qk_flag & 1) {
404
- float ss = 0.f;
405
- for (int d = threadIdx.x; d < D; d += blockDim.x) {
406
- const float x = qkv_at(qbase + d);
407
- ss += x * x;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  }
409
- ss = block_sum(ss, s_red);
410
- if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)D + eps);
411
  __syncthreads();
412
- for (int d = threadIdx.x; d < D; d += blockDim.x)
413
- hbuf[d] = bf2f(f2bf(bf2f(qnw[d]) *
414
- bf2f(f2bf(qkv_at(qbase + d) * s_inv))));
415
- } else {
416
- for (int d = threadIdx.x; d < D; d += blockDim.x)
417
- hbuf[d] = qkv_at(qbase + d);
418
  }
419
- __syncthreads();
420
- for (int d = threadIdx.x; d < half; d += blockDim.x) {
421
- const float inv_freq = 1.f / powf(theta, (float)(2 * d) / (float)D);
422
- const float ang = (float)pos * inv_freq;
423
- const float c = bf2f(f2bf(cosf(ang)));
424
- const float s = bf2f(f2bf(sinf(ang)));
425
- const float x1 = hbuf[d], x2 = hbuf[d + half];
426
- qs[d] = bf2f(f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s))));
427
- qs[d + half] = bf2f(f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s))));
428
  }
429
- __syncthreads();
430
- }
431
 
432
- // the chunk owning `pos` norms/ropes K and appends K/V for its group
433
- if (ch == own_ch) {
434
- const long long kbase = (long long)(Hq + kvh) * D;
435
- const long long vbase = (long long)(Hq + Hkv + kvh) * D;
436
- if (qk_flag & 1) {
437
- float ss = 0.f;
438
- for (int d = threadIdx.x; d < D; d += blockDim.x) {
439
- const float x = qkv_at(kbase + d);
440
- ss += x * x;
 
 
 
 
 
 
441
  }
442
- ss = block_sum(ss, s_red);
443
- if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)D + eps);
444
- __syncthreads();
445
- for (int d = threadIdx.x; d < D; d += blockDim.x)
446
- hbuf[d] = bf2f(f2bf(bf2f(knw[d]) *
447
- bf2f(f2bf(qkv_at(kbase + d) * s_inv))));
448
- } else {
449
- for (int d = threadIdx.x; d < D; d += blockDim.x)
450
- hbuf[d] = qkv_at(kbase + d);
451
  }
452
  __syncthreads();
453
- bf16* krow = kc + (long long)pos * D;
454
- bf16* vrow = vc + (long long)pos * D;
455
- for (int d = threadIdx.x; d < half; d += blockDim.x) {
456
- const float inv_freq = 1.f / powf(theta, (float)(2 * d) / (float)D);
457
- const float ang = (float)pos * inv_freq;
458
- const float c = bf2f(f2bf(cosf(ang)));
459
- const float s = bf2f(f2bf(sinf(ang)));
460
- const float x1 = hbuf[d], x2 = hbuf[d + half];
461
- krow[d] = f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s)));
462
- krow[d + half] = f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s)));
 
 
 
463
  }
464
- for (int d = threadIdx.x; d < D; d += blockDim.x)
465
- vrow[d] = f2bf(qkv_at(vbase + d));
466
- __threadfence_block();
467
  __syncthreads();
468
- }
469
 
470
- // score this chunk: eager rounds QK^T to bf16, scales as a bf16 op,
471
- // upcasts to fp32 for the softmax
472
- const int s0 = ch * ATTN_CHUNK;
473
- const int s1 = (s0 + ATTN_CHUNK < S) ? s0 + ATTN_CHUNK : S;
474
- const int cn = s1 - s0;
475
- const int wid = threadIdx.x >> 5, lane = threadIdx.x & 31;
476
- const int nw = blockDim.x >> 5;
477
- for (int si = wid; si < cn; si += nw) {
478
- const bf16* kr = kc + (long long)(s0 + si) * D;
479
- float acc = 0.f;
480
- for (int d = lane; d < D; d += 32) acc = fmaf(qs[d], bf2f(kr[d]), acc);
481
- acc = warp_sum(acc);
482
- if (lane == 0) {
483
- float x = bf2f(f2bf(acc));
484
- sc[si] = bf2f(f2bf(x * scale));
485
  }
 
486
  }
487
- __syncthreads();
 
488
 
489
- float mx = -INFINITY;
490
- for (int si = threadIdx.x; si < cn; si += blockDim.x)
491
- mx = fmaxf(mx, sc[si]);
492
- mx = block_max(mx, s_red);
493
- if (threadIdx.x == 0) s_m = mx;
494
- __syncthreads();
 
 
 
 
 
495
 
496
- float lsum = 0.f;
497
- for (int si = threadIdx.x; si < cn; si += blockDim.x) {
498
- const float p = expf(sc[si] - s_m);
499
- sc[si] = p;
500
- lsum += p;
501
- }
502
- lsum = block_sum(lsum, s_red);
503
- __syncthreads();
504
 
505
- if (threadIdx.x < D) {
506
- const int d = threadIdx.x;
507
- float acc = 0.f;
508
- for (int si = 0; si < cn; ++si)
509
- acc = fmaf(sc[si], bf2f(vc[(long long)(s0 + si) * D + d]), acc);
510
- part[d] = acc;
511
- }
512
- if (threadIdx.x == 0) {
513
- part[D] = s_m;
514
- part[D + 1] = lsum;
515
- }
516
- __syncthreads();
517
  }
518
  }
519
-
520
  __device__ void ph_argmax_part(const long long* P) {
521
  const float* lg = reinterpret_cast<const float*>(P[1]);
522
  unsigned long long* parts = reinterpret_cast<unsigned long long*>(P[3]);
@@ -555,13 +594,15 @@ __device__ void ph_argmax_fin(const long long* P, int step_slot) {
555
  }
556
 
557
  __device__ __forceinline__ void dispatch_phase(const long long* P, int pos,
558
- int step_slot, int prompt_len) {
 
559
  switch ((int)P[0]) {
560
- case OP_EMBED: ph_embed(P, pos, prompt_len); break;
561
- case OP_GEMV: ph_gemv(P, pos); break;
562
- case OP_ATTN: ph_attn(P, pos); break;
563
  case OP_ARGMAX_PART: ph_argmax_part(P); break;
564
  case OP_ARGMAX_FIN: ph_argmax_fin(P, step_slot); break;
 
565
  default: break;
566
  }
567
  }
@@ -592,29 +633,44 @@ __device__ void grid_barrier(int* cnt, int* sense, int* local_sense) {
592
  __syncthreads();
593
  }
594
 
595
- // One launch runs `steps` decode steps; the grid barrier carries across the
596
- // step boundary, so the argmax of step i feeds the embedding read of step
597
- // i+1 without leaving the kernel. steps=1 is a single decode step.
 
 
 
598
  __global__ void mega_kernel(const long long* __restrict__ prog, int n_phases,
599
- int pos0, int steps, int slot0, int prompt_len,
600
- int* bar_cnt, int* bar_sense) {
601
  __shared__ int s_sense;
602
  if (threadIdx.x == 0) s_sense = atomicAdd(bar_sense, 0);
603
  __syncthreads();
604
- for (int st = 0; st < steps; ++st) {
605
- const int pos = pos0 + st;
606
- const int slot = slot0 + st;
 
 
 
 
 
 
 
607
  for (int p = 0; p < n_phases; ++p) {
608
- dispatch_phase(prog + (long long)p * 16, pos, slot, prompt_len);
609
- const bool last = (st + 1 == steps) && (p + 1 == n_phases);
 
 
610
  if (!last) grid_barrier(bar_cnt, bar_sense, &s_sense);
611
  }
 
612
  }
613
  }
614
 
615
  __global__ void phase_kernel(const long long* __restrict__ prog, int p,
616
  int pos, int step_slot, int prompt_len) {
617
- dispatch_phase(prog + (long long)p * 16, pos, step_slot, prompt_len);
 
 
618
  }
619
 
620
  } // namespace mak
@@ -648,11 +704,12 @@ int mak_grid_blocks_impl(int smem_bytes) {
648
  }
649
 
650
  cudaError_t mak_launch_mega(const long long* prog, int n_phases, int pos0,
651
- int steps, int slot0, int prompt_len,
652
- int* bar_cnt, int* bar_sense, int grid,
653
- int smem_bytes, cudaStream_t stream) {
654
  mak::mega_kernel<<<grid, BLOCK_THREADS, smem_bytes, stream>>>(
655
- prog, n_phases, pos0, steps, slot0, prompt_len, bar_cnt, bar_sense);
 
656
  return cudaGetLastError();
657
  }
658
 
 
37
  OP_ATTN = 3,
38
  OP_ARGMAX_PART = 4,
39
  OP_ARGMAX_FIN = 5,
40
+ OP_KV_APPEND = 6, // prefill chunks only: rope+append K/V for all m
41
  };
42
 
43
  enum InputTransform : int {
 
131
  // and any block with nothing to do simply falls through to the barrier.
132
  // ---------------------------------------------------------------------------
133
 
134
+ __device__ void ph_embed(const long long* P, int pos, int M, int prompt_len) {
135
  const bf16* tab = reinterpret_cast<const bf16*>(P[2]);
136
  bf16* out = reinterpret_cast<bf16*>(P[3]);
137
  const int* prompt = reinterpret_cast<const int*>(P[5]);
 
139
  const long long K = P[8];
140
  // Positions inside the prompt read from the staged prompt buffer; beyond
141
  // it, from the feedback token written by the previous step's argmax.
142
+ for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;
143
+ i < (long long)M * K; i += (long long)gridDim.x * blockDim.x) {
144
+ const int m = (int)(i / K);
145
+ const long long k = i % K;
146
+ const int pm = pos + m;
147
+ const long long t = (pm < prompt_len && prompt != nullptr)
148
+ ? (long long)prompt[pm]
149
+ : (long long)tok[0];
150
+ out[i] = tab[t * K + k];
151
+ }
152
  }
153
 
154
  __device__ __forceinline__ unsigned long long amax_key(float v, unsigned idx) {
 
161
  // Finalize one element of split-S attention: combine per-chunk partials
162
  // (max, sum, weighted V) into the bf16 attention output, matching eager's
163
  // bf16 rounding of the attention output before the O projection.
164
+ __device__ __forceinline__ float attn_finalize(const float* mpart, int h,
165
  int d, int D, int maxch,
166
  int nch) {
167
+ const float* base = mpart + ((long long)h * maxch) * (D + 2);
168
  float M = -INFINITY;
169
  for (int c = 0; c < nch; ++c)
170
  M = fmaxf(M, base[(long long)c * (D + 2) + D]);
 
201
  // Reduced (or plain) bf16 input element k for a consumer phase: sum the
202
  // producer's fp32 k-slice partials in fixed order, add the residual, round
203
  // to bf16, optionally write the materialized vector back.
204
+ // GEMV over an M-token chunk. Weights are read once per chunk: each warp's
205
+ // 16-byte weight tile feeds M accumulators against M staged input rows, so
206
+ // prefill weight traffic is amortized by the chunk size. mmode (slot 12)
207
+ // restricts the phase to the chunk's last row (LM head). Buffers are laid
208
+ // out [M][len]; the residual stream and outputs address row m.
209
+ __device__ void ph_gemv(const long long* P, int pos, int M) {
 
 
 
 
 
 
 
 
 
 
 
210
  const bf16* W = reinterpret_cast<const bf16*>(P[2]);
211
  const bf16* gamma = reinterpret_cast<const bf16*>(P[4]);
212
  const bf16* resid = reinterpret_cast<const bf16*>(P[5]);
213
  const long long N = P[7], K = P[8];
214
+ const int itrans = ((int)P[9]) & ITF_BASE_MASK;
 
215
  const int epi = (int)P[10];
216
+ const int mmode = (int)P[12];
217
  const float eps = bits2f(P[14]);
218
  const float* part = reinterpret_cast<const float*>(P[1]);
219
+ const bf16* xin = reinterpret_cast<const bf16*>(P[1]);
220
  const int D = (int)P[13];
221
  const int maxch = (int)P[15];
222
+
223
+ const int m0 = mmode ? (M - 1) : 0;
224
+ const int Ma = M - m0; // staged rows
225
+
226
+ // Stage transformed inputs for all chunk rows: [Ma][K] in shared, bf16
227
+ // (every transform value is bf16-rounded by semantics, so staging is
228
+ // exact). RMSNORM computes one inverse rms per row.
 
 
 
 
 
 
229
  extern __shared__ bf16 xs[];
230
  __shared__ float s_red[32];
231
  __shared__ float s_scale;
232
+ __shared__ unsigned long long s64[32];
233
+ for (int m = 0; m < Ma; ++m) {
234
+ const int srcm = m0 + m;
235
+ if (itrans == IT_RMSNORM) {
236
+ const bf16* xr = xin + (long long)srcm * K;
237
+ float ss = 0.f;
238
+ for (long long k = threadIdx.x; k < K; k += blockDim.x) {
239
+ const float v = bf2f(xr[k]);
240
+ ss += v * v;
241
+ }
242
+ ss = block_sum(ss, s_red);
243
+ if (threadIdx.x == 0) s_scale = rsqrtf(ss / (float)K + eps);
244
+ __syncthreads();
245
+ const float inv = s_scale;
246
+ for (long long k = threadIdx.x; k < K; k += blockDim.x)
247
+ xs[(long long)m * K + k] =
248
+ f2bf(bf2f(gamma[k]) * bf2f(f2bf(bf2f(xr[k]) * inv)));
249
+ __syncthreads();
250
+ } else if (itrans == IT_SWIGLU) {
251
+ const bf16* xr = xin + (long long)srcm * 2 * K;
252
+ for (long long k = threadIdx.x; k < K; k += blockDim.x) {
253
+ const float g = bf2f(xr[k]);
254
  const float sig = 1.f / (1.f + expf(-g));
255
  const float sg = bf2f(f2bf(g * sig));
256
+ xs[(long long)m * K + k] = f2bf(bf2f(f2bf(sg * bf2f(xr[K + k]))));
 
 
 
 
257
  }
258
+ } else if (itrans == IT_ATTNFIN) {
259
+ const int pm = pos + srcm;
260
+ const int nch = (pm + ATTN_CHUNK) / ATTN_CHUNK;
261
+ const float* mpart =
262
+ part + (long long)srcm * ((long long)P[11] * maxch * (D + 2));
263
+ for (long long k = threadIdx.x; k < K; k += blockDim.x)
264
+ xs[(long long)m * K + k] = f2bf(attn_finalize(
265
+ mpart, (int)(k / D), (int)(k % D), D, maxch, nch));
266
+ } else {
267
+ const bf16* xr = xin + (long long)srcm * K;
268
+ for (long long k = threadIdx.x; k < K; k += blockDim.x)
269
+ xs[(long long)m * K + k] = xr[k];
270
  }
271
  }
272
  __syncthreads();
 
275
  const long long warp =
276
  ((long long)blockIdx.x * blockDim.x + threadIdx.x) >> 5;
277
  const long long nwarps = ((long long)gridDim.x * blockDim.x) >> 5;
 
278
  unsigned long long best = amax_key(-INFINITY, 0xFFFFFFFFu);
279
+ for (long long row = warp; row < N; row += nwarps) {
 
 
 
 
 
 
 
 
 
 
280
  const bf16* wr = W + row * K;
281
+ float acc[8];
282
+ #pragma unroll
283
+ for (int m = 0; m < 8; ++m) acc[m] = 0.f;
284
+ // 16-byte weight loads, next tile prefetched; each tile feeds all
285
+ // staged rows.
286
+ long long k = (long long)lane * 8;
287
+ if (k + 7 < K) {
288
  int4 wv4 = *reinterpret_cast<const int4*>(wr + k);
289
+ for (; k + 7 < K; k += 256) {
290
  int4 nxt;
291
+ const bool more = (k + 256 + 7 < K);
292
  if (more) nxt = *reinterpret_cast<const int4*>(wr + k + 256);
293
  const __nv_bfloat162* wp =
294
  reinterpret_cast<const __nv_bfloat162*>(&wv4);
 
 
295
  #pragma unroll
296
+ for (int m = 0; m < 8; ++m) {
297
+ if (m < Ma) {
298
+ const __nv_bfloat162* xp =
299
+ reinterpret_cast<const __nv_bfloat162*>(xs +
300
+ (long long)m * K + k);
301
+ #pragma unroll
302
+ for (int j = 0; j < 4; ++j) {
303
+ acc[m] = fmaf(__low2float(wp[j]), __low2float(xp[j]), acc[m]);
304
+ acc[m] = fmaf(__high2float(wp[j]), __high2float(xp[j]),
305
+ acc[m]);
306
+ }
307
+ }
308
  }
309
  if (more) wv4 = nxt;
310
  }
311
  }
312
+ #pragma unroll
313
+ for (int m = 0; m < 8; ++m) {
314
+ if (m < Ma) {
315
+ const float a = warp_sum(acc[m]);
316
+ if (lane == 0) {
317
+ const int srcm = m0 + m;
318
+ if (epi == EP_RESID)
319
+ reinterpret_cast<bf16*>(P[3])[(long long)srcm * N + row] =
320
+ f2bf(bf2f(resid[(long long)srcm * N + row]) + a);
321
+ else if (epi == EP_F32)
322
+ reinterpret_cast<float*>(P[3])[row] = a;
323
+ else if (epi == EP_F32_AMAX) {
324
+ reinterpret_cast<float*>(P[3])[row] = a;
325
+ const unsigned long long kk = amax_key(bf2f(f2bf(a)),
326
+ (unsigned)row);
327
+ best = (kk > best) ? kk : best;
328
+ } else {
329
+ reinterpret_cast<bf16*>(P[3])[(long long)srcm * N + row] =
330
+ f2bf(a);
331
+ }
332
+ }
333
  }
334
  }
335
  }
 
341
  }
342
  }
343
 
344
+ // Norm/rope K and copy V into the cache for one (kv-head, position);
345
+ // block-cooperative, semantics identical to the eager reference.
346
+ __device__ void kv_norm_rope_append(const bf16* qkv, bf16* kc, bf16* vc,
347
+ const bf16* knw, int qk_flag, float eps,
348
+ float theta, int Hq, int Hkv, int kvh,
349
+ int D, int pm, float* s_red,
350
+ float* s_inv, float* hbuf) {
351
+ const int half = D >> 1;
352
+ const bf16* ksrc = qkv + (long long)(Hq + kvh) * D;
353
+ const bf16* vsrc = qkv + (long long)(Hq + Hkv + kvh) * D;
354
+ if (qk_flag & 1) {
355
+ float ss = 0.f;
356
+ for (int d = threadIdx.x; d < D; d += blockDim.x) {
357
+ const float x = bf2f(ksrc[d]);
358
+ ss += x * x;
359
+ }
360
+ ss = block_sum(ss, s_red);
361
+ if (threadIdx.x == 0) *s_inv = rsqrtf(ss / (float)D + eps);
362
+ __syncthreads();
363
+ for (int d = threadIdx.x; d < D; d += blockDim.x)
364
+ hbuf[d] = bf2f(f2bf(bf2f(knw[d]) *
365
+ bf2f(f2bf(bf2f(ksrc[d]) * (*s_inv)))));
366
+ } else {
367
+ for (int d = threadIdx.x; d < D; d += blockDim.x) hbuf[d] = bf2f(ksrc[d]);
368
+ }
369
+ __syncthreads();
370
+ bf16* krow = kc + (long long)pm * D;
371
+ bf16* vrow = vc + (long long)pm * D;
372
+ for (int d = threadIdx.x; d < half; d += blockDim.x) {
373
+ const float inv_freq = 1.f / powf(theta, (float)(2 * d) / (float)D);
374
+ const float ang = (float)pm * inv_freq;
375
+ const float c = bf2f(f2bf(cosf(ang)));
376
+ const float s = bf2f(f2bf(sinf(ang)));
377
+ const float x1 = hbuf[d], x2 = hbuf[d + half];
378
+ krow[d] = f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s)));
379
+ krow[d + half] = f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s)));
380
+ }
381
+ for (int d = threadIdx.x; d < D; d += blockDim.x) vrow[d] = vsrc[d];
382
+ __threadfence_block();
383
+ __syncthreads();
384
+ }
385
 
386
+ // Attention with rope fused in, cached positions split across chunks, and
387
+ // an M-token prefill loop. Slots: p1 qkv [M rows], p2 kcache, p3 vcache,
388
+ // p4 fp32 partials [M][Hq][maxch][D+2], p5 q-norm w, p6 k-norm w,
389
+ // 7 max_seq, 8 qk flag, 9 eps bits, 10 theta bits, 11 Hq, 12 Hkv, 13 D,
390
+ // 14 scale, 15 maxch. In decode (M == 1) the chunk owning `pos` also
 
391
  // norms/ropes K and appends K/V for its group (duplicate identical writes
392
+ // across GQA members are benign); in prefill chunks the OP_KV_APPEND phase
393
+ // has already appended all M positions, so causal reads within the chunk
394
+ // are safe. The O-projection finalizes the partials.
395
+ __device__ void ph_attn(const long long* P, int pos, int M) {
396
  const long long max_seq = P[7];
397
  const int qk_flag = (int)P[8];
398
  const float eps = bits2f(P[9]);
 
400
  const int Hq = (int)P[11], Hkv = (int)P[12], D = (int)P[13];
401
  const float scale = bits2f(P[14]);
402
  const int maxch = (int)P[15];
 
 
 
403
  const int rep = Hq / Hkv;
404
+ const long long KT = (long long)(Hq + 2 * Hkv) * D;
405
 
406
  const bf16* qnw = reinterpret_cast<const bf16*>(P[5]);
407
  const bf16* knw = reinterpret_cast<const bf16*>(P[6]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
  __shared__ float qs[MAX_HEAD_DIM];
410
  __shared__ float hbuf[MAX_HEAD_DIM];
 
412
  __shared__ float s_red[32];
413
  __shared__ float s_inv, s_m;
414
 
415
+ // (m, head, chunk) flattened into one block-stride space so prefill
416
+ // chunks use M times the parallelism; per-m chunk counts vary, so
417
+ // out-of-range (m, ch) pairs skip uniformly.
418
+ const int nch_max =
419
+ (pos + M - 1 + 1 + ATTN_CHUNK - 1) / ATTN_CHUNK;
420
+ {
421
+ const int total = M * Hq * nch_max;
422
+ for (int v = blockIdx.x; v < total; v += gridDim.x) {
423
+ const int m = v / (Hq * nch_max);
424
+ const int rem = v % (Hq * nch_max);
425
+ const int head = rem / nch_max;
426
+ const int ch = rem % nch_max;
427
+ const int pm = pos + m;
428
+ const int S = pm + 1;
429
+ const int nch = (S + ATTN_CHUNK - 1) / ATTN_CHUNK;
430
+ if (ch >= nch) continue;
431
+ const int own_ch = pm / ATTN_CHUNK;
432
+ const bf16* qkv =
433
+ reinterpret_cast<const bf16*>(P[1]) + (long long)m * KT;
434
+ float* partm = reinterpret_cast<float*>(P[4]) +
435
+ (long long)m * ((long long)Hq * maxch * (D + 2));
436
+ const int kvh = head / rep;
437
+ bf16* kc = reinterpret_cast<bf16*>(P[2]) + (long long)kvh * max_seq * D;
438
+ bf16* vc = reinterpret_cast<bf16*>(P[3]) + (long long)kvh * max_seq * D;
439
+ float* part = partm + ((long long)head * maxch + ch) * (D + 2);
440
+ const int half = D >> 1;
441
+
442
+ // stage rope'd q (bf16-rounded values) into shared
443
+ {
444
+ const bf16* q = qkv + (long long)head * D;
445
+ if (qk_flag & 1) {
446
+ float ss = 0.f;
447
+ for (int d = threadIdx.x; d < D; d += blockDim.x) {
448
+ const float x = bf2f(q[d]);
449
+ ss += x * x;
450
+ }
451
+ ss = block_sum(ss, s_red);
452
+ if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)D + eps);
453
+ __syncthreads();
454
+ for (int d = threadIdx.x; d < D; d += blockDim.x)
455
+ hbuf[d] =
456
+ bf2f(f2bf(bf2f(qnw[d]) * bf2f(f2bf(bf2f(q[d]) * s_inv))));
457
+ } else {
458
+ for (int d = threadIdx.x; d < D; d += blockDim.x)
459
+ hbuf[d] = bf2f(q[d]);
460
+ }
461
+ __syncthreads();
462
+ for (int d = threadIdx.x; d < half; d += blockDim.x) {
463
+ const float inv_freq = 1.f / powf(theta, (float)(2 * d) / (float)D);
464
+ const float ang = (float)pm * inv_freq;
465
+ const float c = bf2f(f2bf(cosf(ang)));
466
+ const float s = bf2f(f2bf(sinf(ang)));
467
+ const float x1 = hbuf[d], x2 = hbuf[d + half];
468
+ qs[d] = bf2f(f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s))));
469
+ qs[d + half] = bf2f(f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s))));
470
  }
 
 
471
  __syncthreads();
 
 
 
 
 
 
472
  }
473
+
474
+ // decode only: the chunk owning `pos` appends K/V for its group
475
+ if (M == 1 && ch == own_ch) {
476
+ kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, theta, Hq, Hkv,
477
+ kvh, D, pm, s_red, &s_inv, hbuf);
 
 
 
 
478
  }
 
 
479
 
480
+ // score this chunk: eager rounds QK^T to bf16, scales as a bf16 op,
481
+ // upcasts to fp32 for the softmax
482
+ const int s0 = ch * ATTN_CHUNK;
483
+ const int s1 = (s0 + ATTN_CHUNK < S) ? s0 + ATTN_CHUNK : S;
484
+ const int cn = s1 - s0;
485
+ const int wid = threadIdx.x >> 5, lane = threadIdx.x & 31;
486
+ const int nw = blockDim.x >> 5;
487
+ for (int si = wid; si < cn; si += nw) {
488
+ const bf16* kr = kc + (long long)(s0 + si) * D;
489
+ float acc = 0.f;
490
+ for (int d = lane; d < D; d += 32) acc = fmaf(qs[d], bf2f(kr[d]), acc);
491
+ acc = warp_sum(acc);
492
+ if (lane == 0) {
493
+ float x = bf2f(f2bf(acc));
494
+ sc[si] = bf2f(f2bf(x * scale));
495
  }
 
 
 
 
 
 
 
 
 
496
  }
497
  __syncthreads();
498
+
499
+ float mx = -INFINITY;
500
+ for (int si = threadIdx.x; si < cn; si += blockDim.x)
501
+ mx = fmaxf(mx, sc[si]);
502
+ mx = block_max(mx, s_red);
503
+ if (threadIdx.x == 0) s_m = mx;
504
+ __syncthreads();
505
+
506
+ float lsum = 0.f;
507
+ for (int si = threadIdx.x; si < cn; si += blockDim.x) {
508
+ const float p = expf(sc[si] - s_m);
509
+ sc[si] = p;
510
+ lsum += p;
511
  }
512
+ lsum = block_sum(lsum, s_red);
 
 
513
  __syncthreads();
 
514
 
515
+ if (threadIdx.x < D) {
516
+ const int d = threadIdx.x;
517
+ float acc = 0.f;
518
+ for (int si = 0; si < cn; ++si)
519
+ acc = fmaf(sc[si], bf2f(vc[(long long)(s0 + si) * D + d]), acc);
520
+ part[d] = acc;
521
+ }
522
+ if (threadIdx.x == 0) {
523
+ part[D] = s_m;
524
+ part[D + 1] = lsum;
 
 
 
 
 
525
  }
526
+ __syncthreads();
527
  }
528
+ }
529
+ }
530
 
531
+ // Prefill chunks: norm/rope K and copy V into the cache for every position
532
+ // of the chunk, one (m, kv-head) pair per block iteration. Runs before the
533
+ // attention phase so causal reads inside the chunk see complete K/V.
534
+ __device__ void ph_kv_append(const long long* P, int pos, int M) {
535
+ const long long max_seq = P[7];
536
+ const int qk_flag = (int)P[8];
537
+ const float eps = bits2f(P[9]);
538
+ const float theta = bits2f(P[10]);
539
+ const int Hq = (int)P[11], Hkv = (int)P[12], D = (int)P[13];
540
+ const long long KT = (long long)(Hq + 2 * Hkv) * D;
541
+ const bf16* knw = reinterpret_cast<const bf16*>(P[6]);
542
 
543
+ __shared__ float s_red[32];
544
+ __shared__ float s_inv;
545
+ __shared__ float hbuf[MAX_HEAD_DIM];
 
 
 
 
 
546
 
547
+ const int total = M * Hkv;
548
+ for (int v = blockIdx.x; v < total; v += gridDim.x) {
549
+ const int m = v / Hkv;
550
+ const int kvh = v % Hkv;
551
+ const int pm = pos + m;
552
+ const bf16* qkv = reinterpret_cast<const bf16*>(P[1]) + (long long)m * KT;
553
+ bf16* kc = reinterpret_cast<bf16*>(P[2]) + (long long)kvh * max_seq * D;
554
+ bf16* vc = reinterpret_cast<bf16*>(P[3]) + (long long)kvh * max_seq * D;
555
+ kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, theta, Hq, Hkv, kvh,
556
+ D, pm, s_red, &s_inv, hbuf);
 
 
557
  }
558
  }
 
559
  __device__ void ph_argmax_part(const long long* P) {
560
  const float* lg = reinterpret_cast<const float*>(P[1]);
561
  unsigned long long* parts = reinterpret_cast<unsigned long long*>(P[3]);
 
594
  }
595
 
596
  __device__ __forceinline__ void dispatch_phase(const long long* P, int pos,
597
+ int M, int step_slot,
598
+ int prompt_len) {
599
  switch ((int)P[0]) {
600
+ case OP_EMBED: ph_embed(P, pos, M, prompt_len); break;
601
+ case OP_GEMV: ph_gemv(P, pos, M); break;
602
+ case OP_ATTN: ph_attn(P, pos, M); break;
603
  case OP_ARGMAX_PART: ph_argmax_part(P); break;
604
  case OP_ARGMAX_FIN: ph_argmax_fin(P, step_slot); break;
605
+ case OP_KV_APPEND: ph_kv_append(P, pos, M); break;
606
  default: break;
607
  }
608
  }
 
633
  __syncthreads();
634
  }
635
 
636
+ // One launch consumes `total` positions: prompt positions in chunks of up
637
+ // to `chunk_m` (weights amortized across the chunk), generated positions
638
+ // one at a time with the argmax of each step feeding the next embedding
639
+ // read. The grid barrier carries across every boundary. OP_KV_APPEND
640
+ // phases run only inside multi-token chunks; in decode both the dispatch
641
+ // and the barrier are skipped uniformly, so they cost nothing.
642
  __global__ void mega_kernel(const long long* __restrict__ prog, int n_phases,
643
+ int pos0, int total, int slot0, int prompt_len,
644
+ int chunk_m, int* bar_cnt, int* bar_sense) {
645
  __shared__ int s_sense;
646
  if (threadIdx.x == 0) s_sense = atomicAdd(bar_sense, 0);
647
  __syncthreads();
648
+ int pos = pos0;
649
+ const int pos_end = pos0 + total;
650
+ while (pos < pos_end) {
651
+ int M = 1;
652
+ if (pos < prompt_len) {
653
+ M = prompt_len - pos;
654
+ if (M > chunk_m) M = chunk_m;
655
+ if (pos + M > pos_end) M = pos_end - pos;
656
+ }
657
+ const int slot = slot0 + (pos + M - 1 - pos0);
658
  for (int p = 0; p < n_phases; ++p) {
659
+ const long long* P = prog + (long long)p * 16;
660
+ if ((int)P[0] == OP_KV_APPEND && M == 1) continue; // uniform skip
661
+ dispatch_phase(P, pos, M, slot, prompt_len);
662
+ const bool last = (pos + M >= pos_end) && (p + 1 == n_phases);
663
  if (!last) grid_barrier(bar_cnt, bar_sense, &s_sense);
664
  }
665
+ pos += M;
666
  }
667
  }
668
 
669
  __global__ void phase_kernel(const long long* __restrict__ prog, int p,
670
  int pos, int step_slot, int prompt_len) {
671
+ const long long* P = prog + (long long)p * 16;
672
+ if ((int)P[0] == OP_KV_APPEND) return;
673
+ dispatch_phase(P, pos, 1, step_slot, prompt_len);
674
  }
675
 
676
  } // namespace mak
 
704
  }
705
 
706
  cudaError_t mak_launch_mega(const long long* prog, int n_phases, int pos0,
707
+ int total, int slot0, int prompt_len,
708
+ int chunk_m, int* bar_cnt, int* bar_sense,
709
+ int grid, int smem_bytes, cudaStream_t stream) {
710
  mak::mega_kernel<<<grid, BLOCK_THREADS, smem_bytes, stream>>>(
711
+ prog, n_phases, pos0, total, slot0, prompt_len, chunk_m, bar_cnt,
712
+ bar_sense);
713
  return cudaGetLastError();
714
  }
715
 
torch-ext/model_as_a_kernel/__init__.py CHANGED
@@ -111,14 +111,18 @@ class MegaModel:
111
 
112
  # Working buffers. Pointers to these are baked into the program, so
113
  # they (like the packed weights) live for the model's lifetime.
114
- self._hidden = torch.empty(self.hidden, dtype=dt, device=dev)
115
- self._qkv = torch.empty(qdim + 2 * kvdim, dtype=dt, device=dev)
116
- self._gu = torch.empty(2 * self.I, dtype=dt, device=dev)
 
 
 
 
117
  self._logits = torch.empty(self.V, dtype=torch.float32, device=dev)
118
  self._chunk = 256 # ATTN_CHUNK in the kernel
119
  self._maxch = (self.max_seq + self._chunk - 1) // self._chunk
120
  self._partials = torch.empty(
121
- self.Hq * self._maxch * (self.D + 2), dtype=torch.float32,
122
  device=dev)
123
  self._kcache = torch.zeros(self.L, self.Hkv, self.max_seq, self.D,
124
  dtype=dt, device=dev)
@@ -131,7 +135,6 @@ class MegaModel:
131
  device=dev)
132
  self._bar = torch.zeros(2, dtype=torch.int32, device=dev)
133
 
134
- self._maxk = max(self.hidden, qdim, self.I)
135
  probe = torch.zeros(1, 16, dtype=torch.int64, device=dev)
136
  self._nblocks = int(ops.mak_num_blocks(probe, self._maxk))
137
  self._parts = torch.zeros(self._nblocks, dtype=torch.int64, device=dev)
@@ -153,6 +156,12 @@ class MegaModel:
153
  n=qdim + 2 * kvdim, k=self.hidden,
154
  it=_IT_RMSNORM, epi=_EP_STORE, f0=self.eps))
155
  names.append(f"L{li}.qkv")
 
 
 
 
 
 
156
  rows.append(_row(_OP_ATTN, p1=self._qkv.data_ptr(), p2=kc,
157
  p3=vc, p4=self._partials.data_ptr(),
158
  p5=lw["qn"].data_ptr() if self.qk_norm else 0,
@@ -165,7 +174,8 @@ class MegaModel:
165
  rows.append(_row(_OP_GEMV, p1=self._partials.data_ptr(),
166
  p2=lw["wo"].data_ptr(), p3=hid, p5=hid,
167
  n=self.hidden, k=qdim, it=_IT_ATTNFIN,
168
- epi=_EP_RESID, d=self.D, i0=self._maxch))
 
169
  names.append(f"L{li}.o")
170
  rows.append(_row(_OP_GEMV, p1=hid, p2=lw["wgu"].data_ptr(),
171
  p3=self._gu.data_ptr(),
@@ -182,7 +192,8 @@ class MegaModel:
182
  p3=self._logits.data_ptr(),
183
  p4=final_norm.data_ptr(),
184
  p6=self._parts.data_ptr(), n=self.V, k=self.hidden,
185
- it=_IT_RMSNORM, epi=_EP_F32_AMAX, f0=self.eps))
 
186
  names.append("lm_head")
187
  rows.append(_row(_OP_ARGMAX_FIN, p1=self._parts.data_ptr(),
188
  p3=self._token.data_ptr(),
@@ -275,9 +286,17 @@ class MegaModel:
275
  return self._logits, ms
276
 
277
  def prefill(self, ids: Sequence[int], phased: bool = False):
278
- """Feed a prompt token by token; returns the last position's logits."""
279
- for i, t in enumerate(ids):
280
- self.decode_step(int(t), i, phased=phased)
 
 
 
 
 
 
 
 
281
  return self._logits
282
 
283
  def generate(self, prompt_ids: Sequence[int], max_new: int,
@@ -298,7 +317,7 @@ class MegaModel:
298
  self._prompt_in[:P].copy_(
299
  torch.tensor(prompt_ids, dtype=torch.int32))
300
  ops.mak_run_seq(self._prog, self._bar, 0, P + max_new - 1,
301
- 1 - P, P, self._maxk)
302
  return self._tokens_out[:max_new].cpu().tolist()
303
  pos = 0
304
  for t in prompt_ids[:-1]:
 
111
 
112
  # Working buffers. Pointers to these are baked into the program, so
113
  # they (like the packed weights) live for the model's lifetime.
114
+ self._maxk = max(self.hidden, qdim, self.I)
115
+ # prefill chunk size, bounded by the staging shared-memory budget
116
+ self._chunk_m = max(1, min(8, 40960 // (2 * self._maxk)))
117
+ cm = 8 # buffers sized for the maximum chunk
118
+ self._hidden = torch.empty(cm * self.hidden, dtype=dt, device=dev)
119
+ self._qkv = torch.empty(cm * (qdim + 2 * kvdim), dtype=dt, device=dev)
120
+ self._gu = torch.empty(cm * 2 * self.I, dtype=dt, device=dev)
121
  self._logits = torch.empty(self.V, dtype=torch.float32, device=dev)
122
  self._chunk = 256 # ATTN_CHUNK in the kernel
123
  self._maxch = (self.max_seq + self._chunk - 1) // self._chunk
124
  self._partials = torch.empty(
125
+ cm * self.Hq * self._maxch * (self.D + 2), dtype=torch.float32,
126
  device=dev)
127
  self._kcache = torch.zeros(self.L, self.Hkv, self.max_seq, self.D,
128
  dtype=dt, device=dev)
 
135
  device=dev)
136
  self._bar = torch.zeros(2, dtype=torch.int32, device=dev)
137
 
 
138
  probe = torch.zeros(1, 16, dtype=torch.int64, device=dev)
139
  self._nblocks = int(ops.mak_num_blocks(probe, self._maxk))
140
  self._parts = torch.zeros(self._nblocks, dtype=torch.int64, device=dev)
 
156
  n=qdim + 2 * kvdim, k=self.hidden,
157
  it=_IT_RMSNORM, epi=_EP_STORE, f0=self.eps))
158
  names.append(f"L{li}.qkv")
159
+ rows.append(_row(6, p1=self._qkv.data_ptr(), p2=kc, p3=vc,
160
+ p6=lw["kn"].data_ptr() if self.qk_norm else 0,
161
+ n=self.max_seq, k=1 if self.qk_norm else 0,
162
+ it=_fbits(self.eps), epi=_fbits(self.theta),
163
+ hq=self.Hq, hkv=self.Hkv, d=self.D))
164
+ names.append(f"L{li}.kvappend")
165
  rows.append(_row(_OP_ATTN, p1=self._qkv.data_ptr(), p2=kc,
166
  p3=vc, p4=self._partials.data_ptr(),
167
  p5=lw["qn"].data_ptr() if self.qk_norm else 0,
 
174
  rows.append(_row(_OP_GEMV, p1=self._partials.data_ptr(),
175
  p2=lw["wo"].data_ptr(), p3=hid, p5=hid,
176
  n=self.hidden, k=qdim, it=_IT_ATTNFIN,
177
+ epi=_EP_RESID, hq=self.Hq, d=self.D,
178
+ i0=self._maxch))
179
  names.append(f"L{li}.o")
180
  rows.append(_row(_OP_GEMV, p1=hid, p2=lw["wgu"].data_ptr(),
181
  p3=self._gu.data_ptr(),
 
192
  p3=self._logits.data_ptr(),
193
  p4=final_norm.data_ptr(),
194
  p6=self._parts.data_ptr(), n=self.V, k=self.hidden,
195
+ it=_IT_RMSNORM, epi=_EP_F32_AMAX, hkv=1,
196
+ f0=self.eps))
197
  names.append("lm_head")
198
  rows.append(_row(_OP_ARGMAX_FIN, p1=self._parts.data_ptr(),
199
  p3=self._token.data_ptr(),
 
286
  return self._logits, ms
287
 
288
  def prefill(self, ids: Sequence[int], phased: bool = False):
289
+ """Consume a prompt; returns the last position's logits. Runs in
290
+ chunks of up to chunk_m tokens with weight reads amortized across
291
+ the chunk (bitwise identical to token-by-token consumption)."""
292
+ P = len(ids)
293
+ if phased:
294
+ for i, t in enumerate(ids):
295
+ self.decode_step(int(t), i, phased=True)
296
+ return self._logits
297
+ self._prompt_in[:P].copy_(torch.tensor(ids, dtype=torch.int32))
298
+ ops.mak_run_seq(self._prog, self._bar, 0, P, 1 - P, P, self._maxk,
299
+ self._chunk_m)
300
  return self._logits
301
 
302
  def generate(self, prompt_ids: Sequence[int], max_new: int,
 
317
  self._prompt_in[:P].copy_(
318
  torch.tensor(prompt_ids, dtype=torch.int32))
319
  ops.mak_run_seq(self._prog, self._bar, 0, P + max_new - 1,
320
+ 1 - P, P, self._maxk, self._chunk_m)
321
  return self._tokens_out[:max_new].cpu().tolist()
322
  pos = 0
323
  for t in prompt_ids[:-1]:
torch-ext/torch_binding.cpp CHANGED
@@ -1,4 +1,4 @@
1
- #include <torch/library.h>
2
 
3
  #include <ATen/cuda/CUDAContext.h>
4
  #include <c10/cuda/CUDAGuard.h>
@@ -24,9 +24,11 @@ void check_barrier(const at::Tensor& barrier) {
24
  "barrier must be a CUDA int32 tensor with >= 2 elements");
25
  }
26
 
27
- int smem_for(int64_t max_k) {
28
- TORCH_CHECK(max_k > 0 && max_k <= 16384, "max_k out of range");
29
- return (int)(max_k * 2);
 
 
30
  }
31
 
32
  } // namespace
@@ -46,7 +48,7 @@ void mak_run(at::Tensor prog, at::Tensor barrier, int64_t pos,
46
  const int smem = smem_for(max_k);
47
  const cudaError_t err = mak_launch_mega(
48
  reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
49
- (int)prog.size(0), (int)pos, 1, (int)step_slot, 0, bar, bar + 1,
50
  mak_grid_blocks_impl(smem), smem, stream.stream());
51
  TORCH_CHECK(err == cudaSuccess, "mak_run launch failed: ",
52
  cudaGetErrorString(err));
@@ -66,8 +68,8 @@ void mak_run_steps(at::Tensor prog, at::Tensor barrier, int64_t pos0,
66
  const int grid = mak_grid_blocks_impl(smem);
67
  for (int64_t i = 0; i < steps; ++i) {
68
  const cudaError_t err =
69
- mak_launch_mega(pp, n, (int)(pos0 + i), 1, (int)(slot0 + i), 0, bar,
70
- bar + 1, grid, smem, stream.stream());
71
  TORCH_CHECK(err == cudaSuccess, "mak_run_steps launch failed: ",
72
  cudaGetErrorString(err));
73
  }
@@ -75,18 +77,20 @@ void mak_run_steps(at::Tensor prog, at::Tensor barrier, int64_t pos0,
75
 
76
  void mak_run_seq(at::Tensor prog, at::Tensor barrier, int64_t pos0,
77
  int64_t steps, int64_t slot0, int64_t prompt_len,
78
- int64_t max_k) {
79
  check_prog(prog);
80
  check_barrier(barrier);
81
  TORCH_CHECK(steps >= 1, "steps must be >= 1");
 
82
  const c10::cuda::CUDAGuard guard(prog.device());
83
  auto stream = at::cuda::getCurrentCUDAStream();
84
  int* bar = barrier.data_ptr<int>();
85
- const int smem = smem_for(max_k);
86
  const cudaError_t err = mak_launch_mega(
87
  reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
88
  (int)prog.size(0), (int)pos0, (int)steps, (int)slot0, (int)prompt_len,
89
- bar, bar + 1, mak_grid_blocks_impl(smem), smem, stream.stream());
 
90
  TORCH_CHECK(err == cudaSuccess, "mak_run_seq launch failed: ",
91
  cudaGetErrorString(err));
92
  }
@@ -136,7 +140,7 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
136
  ops.def("mak_run_steps(Tensor prog, Tensor barrier, int pos0, int steps, "
137
  "int slot0, int max_k) -> ()");
138
  ops.def("mak_run_seq(Tensor prog, Tensor barrier, int pos0, int steps, "
139
- "int slot0, int prompt_len, int max_k) -> ()");
140
  ops.def("mak_run_phased(Tensor prog, int pos, int step_slot, bool timed, "
141
  "int max_k) -> Tensor");
142
  ops.def("mak_num_blocks(Tensor prog, int max_k) -> int");
@@ -151,3 +155,4 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
151
  }
152
 
153
  REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
 
 
1
+ #include <torch/library.h>
2
 
3
  #include <ATen/cuda/CUDAContext.h>
4
  #include <c10/cuda/CUDAGuard.h>
 
24
  "barrier must be a CUDA int32 tensor with >= 2 elements");
25
  }
26
 
27
+ int smem_for(int64_t stage_elems) {
28
+ // bf16 staging must fit the default 48KB dynamic shared memory window
29
+ TORCH_CHECK(stage_elems > 0 && stage_elems <= 24576,
30
+ "staging size out of range");
31
+ return (int)(stage_elems * 2);
32
  }
33
 
34
  } // namespace
 
48
  const int smem = smem_for(max_k);
49
  const cudaError_t err = mak_launch_mega(
50
  reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
51
+ (int)prog.size(0), (int)pos, 1, (int)step_slot, 0, 1, bar, bar + 1,
52
  mak_grid_blocks_impl(smem), smem, stream.stream());
53
  TORCH_CHECK(err == cudaSuccess, "mak_run launch failed: ",
54
  cudaGetErrorString(err));
 
68
  const int grid = mak_grid_blocks_impl(smem);
69
  for (int64_t i = 0; i < steps; ++i) {
70
  const cudaError_t err =
71
+ mak_launch_mega(pp, n, (int)(pos0 + i), 1, (int)(slot0 + i), 0, 1,
72
+ bar, bar + 1, grid, smem, stream.stream());
73
  TORCH_CHECK(err == cudaSuccess, "mak_run_steps launch failed: ",
74
  cudaGetErrorString(err));
75
  }
 
77
 
78
  void mak_run_seq(at::Tensor prog, at::Tensor barrier, int64_t pos0,
79
  int64_t steps, int64_t slot0, int64_t prompt_len,
80
+ int64_t max_k, int64_t chunk_m) {
81
  check_prog(prog);
82
  check_barrier(barrier);
83
  TORCH_CHECK(steps >= 1, "steps must be >= 1");
84
+ TORCH_CHECK(chunk_m >= 1 && chunk_m <= 8, "chunk_m must be in [1, 8]");
85
  const c10::cuda::CUDAGuard guard(prog.device());
86
  auto stream = at::cuda::getCurrentCUDAStream();
87
  int* bar = barrier.data_ptr<int>();
88
+ const int smem = smem_for(max_k * chunk_m);
89
  const cudaError_t err = mak_launch_mega(
90
  reinterpret_cast<const long long*>(prog.data_ptr<int64_t>()),
91
  (int)prog.size(0), (int)pos0, (int)steps, (int)slot0, (int)prompt_len,
92
+ (int)chunk_m, bar, bar + 1, mak_grid_blocks_impl(smem), smem,
93
+ stream.stream());
94
  TORCH_CHECK(err == cudaSuccess, "mak_run_seq launch failed: ",
95
  cudaGetErrorString(err));
96
  }
 
140
  ops.def("mak_run_steps(Tensor prog, Tensor barrier, int pos0, int steps, "
141
  "int slot0, int max_k) -> ()");
142
  ops.def("mak_run_seq(Tensor prog, Tensor barrier, int pos0, int steps, "
143
+ "int slot0, int prompt_len, int max_k, int chunk_m) -> ()");
144
  ops.def("mak_run_phased(Tensor prog, int pos, int step_slot, bool timed, "
145
  "int max_k) -> Tensor");
146
  ops.def("mak_num_blocks(Tensor prog, int max_k) -> int");
 
155
  }
156
 
157
  REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
158
+
torch-ext/torch_binding.h CHANGED
@@ -10,7 +10,7 @@ void mak_run_steps(at::Tensor prog, at::Tensor barrier, int64_t pos0,
10
 
11
  void mak_run_seq(at::Tensor prog, at::Tensor barrier, int64_t pos0,
12
  int64_t steps, int64_t slot0, int64_t prompt_len,
13
- int64_t max_k);
14
 
15
  at::Tensor mak_run_phased(at::Tensor prog, int64_t pos, int64_t step_slot,
16
  bool timed, int64_t max_k);
 
10
 
11
  void mak_run_seq(at::Tensor prog, at::Tensor barrier, int64_t pos0,
12
  int64_t steps, int64_t slot0, int64_t prompt_len,
13
+ int64_t max_k, int64_t chunk_m);
14
 
15
  at::Tensor mak_run_phased(at::Tensor prog, int64_t pos, int64_t step_slot,
16
  bool timed, int64_t max_k);