phanerozoic commited on
Commit
d1d3209
·
verified ·
1 Parent(s): e2821a5

model-as-a-kernel v1 source

Browse files
mak_cuda/megakernel.cu CHANGED
@@ -25,7 +25,7 @@
25
  #include "mak_kernels.h"
26
 
27
  #define BLOCK_THREADS 256
28
- #define MAX_HEAD_DIM 256
29
  // GEMV weight tiles stream through a per-warp cp.async shared ring. The
30
  // stage count is a power of two chosen per launch from the device's
31
  // shared-memory budget (deep on high-bandwidth parts, where the
@@ -46,13 +46,17 @@ enum PhaseOp : int {
46
  OP_ARGMAX_PART = 4,
47
  OP_ARGMAX_FIN = 5,
48
  OP_KV_APPEND = 6, // prefill chunks only: rope+append K/V for all m
 
 
49
  };
50
 
51
  enum InputTransform : int {
52
  IT_NONE = 0,
53
  IT_RMSNORM = 1,
54
  IT_SWIGLU = 2,
55
- IT_ATTNFIN = 3, // finalize split-S attention partials on the fly
 
 
56
  };
57
  // slot-9 flag bits above the base transform
58
  #define ITF_BASE_MASK 7
@@ -65,6 +69,8 @@ enum Epilogue : int {
65
  EP_F32 = 2,
66
  EP_F32_AMAX = 3, // fp32 store + per-block argmax partial (lm head)
67
  EP_PARTIAL = 4, // fp32 k-slice partials [NSLICE][N], no rounding
 
 
68
  };
69
 
70
  #define ATTN_CHUNK 128
@@ -81,6 +87,13 @@ __device__ __forceinline__ float bits2f(long long v) {
81
  __device__ __forceinline__ float bf2f(bf16 v) { return __bfloat162float(v); }
82
  __device__ __forceinline__ bf16 f2bf(float v) { return __float2bfloat16(v); }
83
 
 
 
 
 
 
 
 
84
  __device__ __forceinline__ unsigned dyn_smem_size() {
85
  unsigned r;
86
  asm("mov.u32 %0, %%dynamic_smem_size;" : "=r"(r));
@@ -190,6 +203,7 @@ __device__ void ph_embed(const long long* P, int pos, int M, int prompt_len,
190
  __syncthreads();
191
  }
192
 
 
193
  for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;
194
  i < (long long)M * K; i += (long long)gridDim.x * blockDim.x) {
195
  const int m = (int)(i / K);
@@ -198,7 +212,23 @@ __device__ void ph_embed(const long long* P, int pos, int M, int prompt_len,
198
  const long long t = (pm < prompt_len && prompt != nullptr)
199
  ? (long long)prompt[pm]
200
  : (long long)s_tok;
201
- out[i] = tab[t * K + k];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  }
203
  }
204
 
@@ -214,13 +244,13 @@ __device__ __forceinline__ unsigned long long amax_key(float v, unsigned idx) {
214
  // bf16 rounding of the attention output before the O projection.
215
  __device__ __forceinline__ float attn_finalize(const float* mpart, int h,
216
  int d, int D, int maxch,
217
- int nch) {
218
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
219
  float M = -INFINITY;
220
- for (int c = 0; c < nch; ++c)
221
  M = fmaxf(M, base[(long long)c * (D + 2) + D]);
222
  float L = 0.f, acc = 0.f;
223
- for (int c = 0; c < nch; ++c) {
224
  const float* pc = base + (long long)c * (D + 2);
225
  const float w = expf(pc[D] - M);
226
  L = fmaf(pc[D + 1], w, L);
@@ -245,7 +275,8 @@ __device__ __forceinline__ float mk_load_x(const bf16* x, const bf16* gamma,
245
  return bf2f(f2bf(sg * bf2f(x[K + k])));
246
  }
247
  if (itrans == IT_ATTNFIN)
248
- return attn_finalize(part, (int)(k / D), (int)(k % D), D, maxch, nch);
 
249
  return bf2f(x[k]);
250
  }
251
 
@@ -258,7 +289,15 @@ __device__ __forceinline__ void gemv_store(const long long* P, int epi,
258
  f2bf(bf2f(resid[(long long)srcm * N + row]) + a);
259
  else if (epi == EP_F32)
260
  reinterpret_cast<float*>(P[3])[row] = a;
261
- else
 
 
 
 
 
 
 
 
262
  reinterpret_cast<bf16*>(P[3])[(long long)srcm * N + row] = f2bf(a);
263
  }
264
 
@@ -282,7 +321,8 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
282
  const float* part = reinterpret_cast<const float*>(P[1]);
283
  const bf16* xin = reinterpret_cast<const bf16*>(P[1]);
284
  const int D = (int)P[13];
285
- const int maxch = (int)P[15];
 
286
 
287
  const int m0 = mmode ? (M - 1) : 0;
288
  const int Ma = M - m0; // staged rows
@@ -383,6 +423,70 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
383
  *reinterpret_cast<int4*>(xsp) = x4;
384
  }
385
  __syncthreads();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  } else if (itrans == IT_SWIGLU) {
387
  const bf16* xr = xin + (long long)srcm * 2 * K;
388
  for (long long g = threadIdx.x; g < G; g += blockDim.x) {
@@ -414,6 +518,9 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
414
  } else if (itrans == IT_ATTNFIN) {
415
  const int pm = pos + srcm;
416
  const int nch = (pm + CH) / CH;
 
 
 
417
  const float* mpart =
418
  part + (long long)srcm * ((long long)P[11] * maxch * (D + 2));
419
  const int Hq_ = (int)P[11];
@@ -427,10 +534,10 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
427
  for (int h = threadIdx.x; h < Hq_; h += blockDim.x) {
428
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
429
  float Mx = -INFINITY;
430
- for (int c = 0; c < nch; ++c)
431
  Mx = fmaxf(Mx, base[(long long)c * (D + 2) + D]);
432
  float L = 0.f;
433
- for (int c = 0; c < nch; ++c) {
434
  const float* pc = base + (long long)c * (D + 2);
435
  const float w = expf(pc[D] - Mx);
436
  ew[h * nch + c] = w;
@@ -448,7 +555,7 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
448
  const int h = (int)(k / D), d0 = (int)(k % D);
449
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
450
  float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
451
- for (int c = 0; c < nch; ++c) {
452
  const float* pc = base + (long long)c * (D + 2) + d0;
453
  const float2 pa = *reinterpret_cast<const float2*>(pc);
454
  const float2 pb = *reinterpret_cast<const float2*>(pc + 2);
@@ -470,7 +577,7 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
470
  const int h = (int)(k / D), d = (int)(k % D);
471
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
472
  float acc = 0.f;
473
- for (int c = 0; c < nch; ++c)
474
  acc = fmaf(base[(long long)c * (D + 2) + d],
475
  ew[h * nch + c], acc);
476
  xs[(long long)m * K + k] = f2bf(bf2f(f2bf(acc / lw[h])));
@@ -480,7 +587,7 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
480
  } else {
481
  for (long long k = threadIdx.x; k < K; k += blockDim.x)
482
  xs[(long long)m * K + k] = f2bf(attn_finalize(
483
- mpart, (int)(k / D), (int)(k % D), D, maxch, nch));
484
  }
485
  } else {
486
  const bf16* xr = xin + (long long)srcm * K;
@@ -545,6 +652,16 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
545
  const unsigned long long kk = amax_key(bf2f(f2bf(a)),
546
  (unsigned)row);
547
  best = (kk > best) ? kk : best;
 
 
 
 
 
 
 
 
 
 
548
  } else {
549
  gemv_store(P, epi, N, resid, srcm, row, a);
550
  }
@@ -553,7 +670,7 @@ __device__ void ph_gemv(const long long* P, int pos, int M, int rs) {
553
  }
554
  }
555
  }
556
- if (epi == EP_F32_AMAX) {
557
  __syncthreads();
558
  best = block_max_u64(best, s64);
559
  if (threadIdx.x == 0)
@@ -567,7 +684,7 @@ __device__ void kv_norm_rope_append(const bf16* qkv, bf16* kc, bf16* vc,
567
  const bf16* knw, int qk_flag, float eps,
568
  const float* invf, int Hq, int Hkv, int kvh,
569
  int D, int pm, float* s_red,
570
- float* s_inv, float* hbuf) {
571
  const int half = D >> 1;
572
  const bf16* ksrc = qkv + (long long)(Hq + kvh) * D;
573
  const bf16* vsrc = qkv + (long long)(Hq + Hkv + kvh) * D;
@@ -581,8 +698,10 @@ __device__ void kv_norm_rope_append(const bf16* qkv, bf16* kc, bf16* vc,
581
  if (threadIdx.x == 0) *s_inv = rsqrtf(ss / (float)D + eps);
582
  __syncthreads();
583
  for (int d = threadIdx.x; d < D; d += blockDim.x)
584
- hbuf[d] = bf2f(f2bf(bf2f(knw[d]) *
585
- bf2f(f2bf(bf2f(ksrc[d]) * (*s_inv)))));
 
 
586
  }
587
  // without k-norm the rope reads the source row directly (identical
588
  // values), skipping the staging pass and its barrier
@@ -591,14 +710,28 @@ __device__ void kv_norm_rope_append(const bf16* qkv, bf16* kc, bf16* vc,
591
  bf16* vrow = vc + (long long)pm * D;
592
  for (int d = threadIdx.x; d < half; d += blockDim.x) {
593
  const float ang = (float)pm * invf[d];
594
- const float c = bf2f(f2bf(cosf(ang)));
595
- const float s = bf2f(f2bf(sinf(ang)));
596
  const float x1 = (qk_flag & 1) ? hbuf[d] : bf2f(ksrc[d]);
597
  const float x2 = (qk_flag & 1) ? hbuf[d + half] : bf2f(ksrc[d + half]);
598
  krow[d] = f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s)));
599
  krow[d + half] = f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s)));
600
  }
601
- for (int d = threadIdx.x; d < D; d += blockDim.x) vrow[d] = vsrc[d];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602
  __threadfence_block();
603
  __syncthreads();
604
  }
@@ -615,7 +748,10 @@ __device__ void kv_norm_rope_append(const bf16* qkv, bf16* kc, bf16* vc,
615
  // are safe. The O-projection finalizes the partials.
616
  __device__ void ph_attn(const long long* P, int pos, int M) {
617
  const long long max_seq = P[7];
618
- const int qk_flag = (int)P[8];
 
 
 
619
  const float eps = bits2f(P[9]);
620
  const float* invf = reinterpret_cast<const float*>(P[10]);
621
  const int Hq = (int)P[11], Hkv = ((int)P[12]) & 0xFFFF;
@@ -625,7 +761,10 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
625
  const int chbits = (int)(P[12] >> 16) & 0xFFFF;
626
  const int CH = (chbits > 0 && chbits <= ATTN_CHUNK) ? chbits : ATTN_CHUNK;
627
  const float scale = bits2f(P[14]);
628
- const int maxch = (int)P[15];
 
 
 
629
  const int rep = Hq / Hkv;
630
  const long long KT = (long long)(Hq + 2 * Hkv) * D;
631
 
@@ -654,6 +793,8 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
654
  const int S = pm + 1;
655
  const int nch = (S + CH - 1) / CH;
656
  if (ch >= nch) continue;
 
 
657
  const int own_ch = pm / CH;
658
  const bf16* qkv =
659
  reinterpret_cast<const bf16*>(P[1]) + (long long)m * KT;
@@ -666,15 +807,18 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
666
  const int half = D >> 1;
667
 
668
  // decode only: the chunk owning `pos` appends K/V for its group,
669
- // before the chunk copy below so the copy sees the appended row
670
- if (M == 1 && ch == own_ch) {
 
 
671
  kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, invf, Hq, Hkv,
672
- kvh, D, pm, s_red, &s_inv, hbuf);
673
  }
674
 
675
  const int s0 = ch * CH;
676
  const int s1 = (s0 + CH < S) ? s0 + CH : S;
677
  const int cn = s1 - s0;
 
678
 
679
  // Copy this chunk's K/V into the dynamic-shared region (idle during
680
  // attention) when it fits: the score loop otherwise walks the chunk
@@ -713,16 +857,18 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
713
  if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)D + eps);
714
  __syncthreads();
715
  for (int d = threadIdx.x; d < D; d += blockDim.x)
716
- hbuf[d] =
717
- bf2f(f2bf(bf2f(qnw[d]) * bf2f(f2bf(bf2f(q[d]) * s_inv))));
 
 
718
  }
719
  // without q-norm the rope reads q directly (identical values),
720
  // skipping the staging pass and its barrier
721
  if (qk_flag & 1) __syncthreads();
722
  for (int d = threadIdx.x; d < half; d += blockDim.x) {
723
  const float ang = (float)pm * invf[d];
724
- const float c = bf2f(f2bf(cosf(ang)));
725
- const float s = bf2f(f2bf(sinf(ang)));
726
  const float x1 = (qk_flag & 1) ? hbuf[d] : bf2f(q[d]);
727
  const float x2 =
728
  (qk_flag & 1) ? hbuf[d + half] : bf2f(q[d + half]);
@@ -738,6 +884,7 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
738
  const int wid = threadIdx.x >> 5, lane = threadIdx.x & 31;
739
  const int nw = blockDim.x >> 5;
740
  for (int si = wid; si < cn; si += nw) {
 
741
  const bf16* kr = kv_staged ? ks_sh + (long long)si * D
742
  : kc + (long long)(s0 + si) * D;
743
  float acc = 0.f;
@@ -752,13 +899,14 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
752
 
753
  float mx = -INFINITY;
754
  for (int si = threadIdx.x; si < cn; si += blockDim.x)
755
- mx = fmaxf(mx, sc[si]);
756
  mx = block_max(mx, s_red);
757
  if (threadIdx.x == 0) s_m = mx;
758
  __syncthreads();
759
 
760
  float lsum = 0.f;
761
  for (int si = threadIdx.x; si < cn; si += blockDim.x) {
 
762
  const float p = expf(sc[si] - s_m);
763
  sc[si] = p;
764
  lsum += p;
@@ -775,8 +923,9 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
775
  const int d = threadIdx.x - g * D;
776
  if (g < NG) {
777
  const int csz = (cn + NG - 1) / NG;
778
- const int a0 = g * csz;
779
- const int a1 = (a0 + csz < cn) ? a0 + csz : cn;
 
780
  float acc = 0.f;
781
  for (int si = a0; si < a1; ++si)
782
  acc = fmaf(sc[si],
@@ -786,20 +935,21 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
786
  pv[g * D + d] = acc;
787
  }
788
  __syncthreads();
789
- if (threadIdx.x < D) {
790
  float acc = 0.f;
791
- for (int g2 = 0; g2 < NG; ++g2) acc += pv[g2 * D + threadIdx.x];
792
- part[threadIdx.x] = acc;
 
 
 
 
 
 
 
 
 
 
793
  }
794
- } else if (threadIdx.x < D) {
795
- const int d = threadIdx.x;
796
- float acc = 0.f;
797
- for (int si = 0; si < cn; ++si)
798
- acc = fmaf(sc[si],
799
- bf2f(kv_staged ? vs_sh[(long long)si * D + d]
800
- : vc[(long long)(s0 + si) * D + d]),
801
- acc);
802
- part[d] = acc;
803
  }
804
  if (threadIdx.x == 0) {
805
  part[D] = s_m;
@@ -815,10 +965,13 @@ __device__ void ph_attn(const long long* P, int pos, int M) {
815
  // attention phase so causal reads inside the chunk see complete K/V.
816
  __device__ void ph_kv_append(const long long* P, int pos, int M) {
817
  const long long max_seq = P[7];
818
- const int qk_flag = (int)P[8];
819
  const float eps = bits2f(P[9]);
820
  const float* invf = reinterpret_cast<const float*>(P[10]);
821
- const int Hq = (int)P[11], Hkv = (int)P[12], D = (int)P[13];
 
 
 
822
  const long long KT = (long long)(Hq + 2 * Hkv) * D;
823
  const bf16* knw = reinterpret_cast<const bf16*>(P[6]);
824
 
@@ -835,9 +988,71 @@ __device__ void ph_kv_append(const long long* P, int pos, int M) {
835
  bf16* kc = reinterpret_cast<bf16*>(P[2]) + (long long)kvh * max_seq * D;
836
  bf16* vc = reinterpret_cast<bf16*>(P[3]) + (long long)kvh * max_seq * D;
837
  kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, invf, Hq, Hkv, kvh,
838
- D, pm, s_red, &s_inv, hbuf);
839
  }
840
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
841
  __device__ void ph_argmax_part(const long long* P) {
842
  const float* lg = reinterpret_cast<const float*>(P[1]);
843
  unsigned long long* parts = reinterpret_cast<unsigned long long*>(P[3]);
@@ -886,6 +1101,8 @@ __device__ __forceinline__ void dispatch_phase(const long long* P, int pos,
886
  case OP_ARGMAX_PART: ph_argmax_part(P); break;
887
  case OP_ARGMAX_FIN: ph_argmax_fin(P, step_slot); break;
888
  case OP_KV_APPEND: ph_kv_append(P, pos, M); break;
 
 
889
  default: break;
890
  }
891
  }
 
25
  #include "mak_kernels.h"
26
 
27
  #define BLOCK_THREADS 256
28
+ #define MAX_HEAD_DIM 512
29
  // GEMV weight tiles stream through a per-warp cp.async shared ring. The
30
  // stage count is a power of two chosen per launch from the device's
31
  // shared-memory budget (deep on high-bandwidth parts, where the
 
46
  OP_ARGMAX_PART = 4,
47
  OP_ARGMAX_FIN = 5,
48
  OP_KV_APPEND = 6, // prefill chunks only: rope+append K/V for all m
49
+ OP_NORMRES = 7, // gemma post-sublayer norm + residual add
50
+ OP_PLEMIX = 8, // gemma per-layer-input mix (norm ctx, add tok, scale)
51
  };
52
 
53
  enum InputTransform : int {
54
  IT_NONE = 0,
55
  IT_RMSNORM = 1,
56
  IT_SWIGLU = 2,
57
+ IT_ATTNFIN = 3, // finalize split-S attention partials on the fly
58
+ IT_RMSNORM_G = 4, // gemma norm: fp32 throughout, raw weight, one rounding
59
+ IT_GELU_GLU = 5, // gelu_tanh(gate) * up
60
  };
61
  // slot-9 flag bits above the base transform
62
  #define ITF_BASE_MASK 7
 
69
  EP_F32 = 2,
70
  EP_F32_AMAX = 3, // fp32 store + per-block argmax partial (lm head)
71
  EP_PARTIAL = 4, // fp32 k-slice partials [NSLICE][N], no rounding
72
+ EP_GELU_PLE = 5, // gemma: gelu_tanh(out) * per-layer-input element
73
+ EP_F32_AMAX_CAP = 6, // lm head with final logit softcapping
74
  };
75
 
76
  #define ATTN_CHUNK 128
 
87
  __device__ __forceinline__ float bf2f(bf16 v) { return __bfloat162float(v); }
88
  __device__ __forceinline__ bf16 f2bf(float v) { return __float2bfloat16(v); }
89
 
90
+ // torch gelu(approximate="tanh") in fp32
91
+ __device__ __forceinline__ float gelu_tanh_f(float x) {
92
+ const float c = 0.7978845608028654f; // sqrt(2/pi)
93
+ const float t = tanhf(c * (x + 0.044715f * x * x * x));
94
+ return 0.5f * x * (1.f + t);
95
+ }
96
+
97
  __device__ __forceinline__ unsigned dyn_smem_size() {
98
  unsigned r;
99
  asm("mov.u32 %0, %%dynamic_smem_size;" : "=r"(r));
 
203
  __syncthreads();
204
  }
205
 
206
+ const float hsc = bits2f(P[14]); // gemma embed scale (0 bits = off)
207
  for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;
208
  i < (long long)M * K; i += (long long)gridDim.x * blockDim.x) {
209
  const int m = (int)(i / K);
 
212
  const long long t = (pm < prompt_len && prompt != nullptr)
213
  ? (long long)prompt[pm]
214
  : (long long)s_tok;
215
+ out[i] = (hsc != 0.f) ? f2bf(bf2f(tab[t * K + k]) * hsc)
216
+ : tab[t * K + k];
217
+ }
218
+
219
+ // gemma: gather this token's per-layer-input row, scaled (decode only;
220
+ // the gemma builder pins chunk_m to 1)
221
+ const bf16* ptab = reinterpret_cast<const bf16*>(P[1]);
222
+ bf16* pout = reinterpret_cast<bf16*>(P[9]);
223
+ if (ptab != nullptr && pout != nullptr && M == 1) {
224
+ const long long PL = P[12];
225
+ const float esc = bits2f(P[15]);
226
+ const long long t = (pos < prompt_len && prompt != nullptr)
227
+ ? (long long)prompt[pos]
228
+ : (long long)s_tok;
229
+ for (long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;
230
+ i < PL; i += (long long)gridDim.x * blockDim.x)
231
+ pout[i] = f2bf(bf2f(ptab[t * PL + i]) * esc);
232
  }
233
  }
234
 
 
244
  // bf16 rounding of the attention output before the O projection.
245
  __device__ __forceinline__ float attn_finalize(const float* mpart, int h,
246
  int d, int D, int maxch,
247
+ int nch, int nch_lo) {
248
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
249
  float M = -INFINITY;
250
+ for (int c = nch_lo; c < nch; ++c)
251
  M = fmaxf(M, base[(long long)c * (D + 2) + D]);
252
  float L = 0.f, acc = 0.f;
253
+ for (int c = nch_lo; c < nch; ++c) {
254
  const float* pc = base + (long long)c * (D + 2);
255
  const float w = expf(pc[D] - M);
256
  L = fmaf(pc[D + 1], w, L);
 
275
  return bf2f(f2bf(sg * bf2f(x[K + k])));
276
  }
277
  if (itrans == IT_ATTNFIN)
278
+ return attn_finalize(part, (int)(k / D), (int)(k % D), D, maxch, nch,
279
+ 0);
280
  return bf2f(x[k]);
281
  }
282
 
 
289
  f2bf(bf2f(resid[(long long)srcm * N + row]) + a);
290
  else if (epi == EP_F32)
291
  reinterpret_cast<float*>(P[3])[row] = a;
292
+ else if (epi == EP_GELU_PLE) {
293
+ // gemma per-layer-input gate: bf16 linear output, gelu in fp32
294
+ // rounded back, multiplied by the layer's per-layer-input element
295
+ const bf16* ple = reinterpret_cast<const bf16*>(P[6]);
296
+ const float g = bf2f(f2bf(a));
297
+ const float act = bf2f(f2bf(gelu_tanh_f(g)));
298
+ reinterpret_cast<bf16*>(P[3])[(long long)srcm * N + row] =
299
+ f2bf(act * bf2f(ple[row]));
300
+ } else
301
  reinterpret_cast<bf16*>(P[3])[(long long)srcm * N + row] = f2bf(a);
302
  }
303
 
 
321
  const float* part = reinterpret_cast<const float*>(P[1]);
322
  const bf16* xin = reinterpret_cast<const bf16*>(P[1]);
323
  const int D = (int)P[13];
324
+ const int maxch = (int)(P[15] & 0xFFFF);
325
+ const int fin_win = (int)((P[15] >> 16) & 0xFFFF); // 0 = unwindowed
326
 
327
  const int m0 = mmode ? (M - 1) : 0;
328
  const int Ma = M - m0; // staged rows
 
423
  *reinterpret_cast<int4*>(xsp) = x4;
424
  }
425
  __syncthreads();
426
+ } else if (itrans == IT_RMSNORM_G) {
427
+ // gemma norm: fp32 throughout, raw weight, one rounding at the end
428
+ const bf16* xr = xin + (long long)srcm * K;
429
+ float ss = 0.f;
430
+ for (long long g = threadIdx.x; g < G; g += blockDim.x) {
431
+ const int4 x4 = *reinterpret_cast<const int4*>(xr + g * 8);
432
+ *reinterpret_cast<int4*>(xs + (long long)m * K + g * 8) = x4;
433
+ const __nv_bfloat162* xp =
434
+ reinterpret_cast<const __nv_bfloat162*>(&x4);
435
+ #pragma unroll
436
+ for (int j = 0; j < 4; ++j) {
437
+ const float lo = __low2float(xp[j]);
438
+ const float hi = __high2float(xp[j]);
439
+ ss += lo * lo;
440
+ ss += hi * hi;
441
+ }
442
+ }
443
+ ss = block_sum(ss, s_red);
444
+ if (threadIdx.x == 0) s_scale = rsqrtf(ss / (float)K + eps);
445
+ __syncthreads();
446
+ const float inv = s_scale;
447
+ for (long long g = threadIdx.x; g < G; g += blockDim.x) {
448
+ bf16* xsp = xs + (long long)m * K + g * 8;
449
+ int4 x4 = *reinterpret_cast<const int4*>(xsp);
450
+ const int4 g4 = *reinterpret_cast<const int4*>(gamma + g * 8);
451
+ __nv_bfloat162* xp = reinterpret_cast<__nv_bfloat162*>(&x4);
452
+ const __nv_bfloat162* gp =
453
+ reinterpret_cast<const __nv_bfloat162*>(&g4);
454
+ #pragma unroll
455
+ for (int j = 0; j < 4; ++j) {
456
+ xp[j].x = f2bf((__low2float(xp[j]) * inv) * __low2float(gp[j]));
457
+ xp[j].y =
458
+ f2bf((__high2float(xp[j]) * inv) * __high2float(gp[j]));
459
+ }
460
+ *reinterpret_cast<int4*>(xsp) = x4;
461
+ }
462
+ __syncthreads();
463
+ } else if (itrans == IT_GELU_GLU) {
464
+ // gemma MLP gating: gelu_tanh(gate) rounded to bf16, times up
465
+ const bf16* xr = xin + (long long)srcm * 2 * K;
466
+ for (long long g = threadIdx.x; g < G; g += blockDim.x) {
467
+ const int4 g4 = *reinterpret_cast<const int4*>(xr + g * 8);
468
+ const int4 u4 = *reinterpret_cast<const int4*>(xr + K + g * 8);
469
+ int4 o4;
470
+ const __nv_bfloat162* gp =
471
+ reinterpret_cast<const __nv_bfloat162*>(&g4);
472
+ const __nv_bfloat162* up =
473
+ reinterpret_cast<const __nv_bfloat162*>(&u4);
474
+ __nv_bfloat162* op = reinterpret_cast<__nv_bfloat162*>(&o4);
475
+ #pragma unroll
476
+ for (int j = 0; j < 4; ++j) {
477
+ {
478
+ const float gv = __low2float(gp[j]);
479
+ const float act = bf2f(f2bf(gelu_tanh_f(gv)));
480
+ op[j].x = f2bf(act * __low2float(up[j]));
481
+ }
482
+ {
483
+ const float gv = __high2float(gp[j]);
484
+ const float act = bf2f(f2bf(gelu_tanh_f(gv)));
485
+ op[j].y = f2bf(act * __high2float(up[j]));
486
+ }
487
+ }
488
+ *reinterpret_cast<int4*>(xs + (long long)m * K + g * 8) = o4;
489
+ }
490
  } else if (itrans == IT_SWIGLU) {
491
  const bf16* xr = xin + (long long)srcm * 2 * K;
492
  for (long long g = threadIdx.x; g < G; g += blockDim.x) {
 
518
  } else if (itrans == IT_ATTNFIN) {
519
  const int pm = pos + srcm;
520
  const int nch = (pm + CH) / CH;
521
+ const int s_lo = (fin_win > 0 && pm + 1 > fin_win)
522
+ ? pm + 1 - fin_win : 0;
523
+ const int nch_lo = s_lo / CH;
524
  const float* mpart =
525
  part + (long long)srcm * ((long long)P[11] * maxch * (D + 2));
526
  const int Hq_ = (int)P[11];
 
534
  for (int h = threadIdx.x; h < Hq_; h += blockDim.x) {
535
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
536
  float Mx = -INFINITY;
537
+ for (int c = nch_lo; c < nch; ++c)
538
  Mx = fmaxf(Mx, base[(long long)c * (D + 2) + D]);
539
  float L = 0.f;
540
+ for (int c = nch_lo; c < nch; ++c) {
541
  const float* pc = base + (long long)c * (D + 2);
542
  const float w = expf(pc[D] - Mx);
543
  ew[h * nch + c] = w;
 
555
  const int h = (int)(k / D), d0 = (int)(k % D);
556
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
557
  float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
558
+ for (int c = nch_lo; c < nch; ++c) {
559
  const float* pc = base + (long long)c * (D + 2) + d0;
560
  const float2 pa = *reinterpret_cast<const float2*>(pc);
561
  const float2 pb = *reinterpret_cast<const float2*>(pc + 2);
 
577
  const int h = (int)(k / D), d = (int)(k % D);
578
  const float* base = mpart + ((long long)h * maxch) * (D + 2);
579
  float acc = 0.f;
580
+ for (int c = nch_lo; c < nch; ++c)
581
  acc = fmaf(base[(long long)c * (D + 2) + d],
582
  ew[h * nch + c], acc);
583
  xs[(long long)m * K + k] = f2bf(bf2f(f2bf(acc / lw[h])));
 
587
  } else {
588
  for (long long k = threadIdx.x; k < K; k += blockDim.x)
589
  xs[(long long)m * K + k] = f2bf(attn_finalize(
590
+ mpart, (int)(k / D), (int)(k % D), D, maxch, nch, nch_lo));
591
  }
592
  } else {
593
  const bf16* xr = xin + (long long)srcm * K;
 
652
  const unsigned long long kk = amax_key(bf2f(f2bf(a)),
653
  (unsigned)row);
654
  best = (kk > best) ? kk : best;
655
+ } else if (epi == EP_F32_AMAX_CAP) {
656
+ // final logit softcapping, mirroring the eager bf16 op chain
657
+ const float cap = bits2f(P[15]);
658
+ const bf16 t1 = f2bf(a);
659
+ const bf16 t2 = f2bf(bf2f(t1) / cap);
660
+ const bf16 t3 = f2bf(tanhf(bf2f(t2)));
661
+ const float v = bf2f(f2bf(bf2f(t3) * cap));
662
+ reinterpret_cast<float*>(P[3])[row] = v;
663
+ const unsigned long long kk = amax_key(v, (unsigned)row);
664
+ best = (kk > best) ? kk : best;
665
  } else {
666
  gemv_store(P, epi, N, resid, srcm, row, a);
667
  }
 
670
  }
671
  }
672
  }
673
+ if (epi == EP_F32_AMAX || epi == EP_F32_AMAX_CAP) {
674
  __syncthreads();
675
  best = block_max_u64(best, s64);
676
  if (threadIdx.x == 0)
 
684
  const bf16* knw, int qk_flag, float eps,
685
  const float* invf, int Hq, int Hkv, int kvh,
686
  int D, int pm, float* s_red,
687
+ float* s_inv, float* hbuf, float ascale) {
688
  const int half = D >> 1;
689
  const bf16* ksrc = qkv + (long long)(Hq + kvh) * D;
690
  const bf16* vsrc = qkv + (long long)(Hq + Hkv + kvh) * D;
 
698
  if (threadIdx.x == 0) *s_inv = rsqrtf(ss / (float)D + eps);
699
  __syncthreads();
700
  for (int d = threadIdx.x; d < D; d += blockDim.x)
701
+ hbuf[d] = (qk_flag & 2)
702
+ ? bf2f(f2bf((bf2f(ksrc[d]) * (*s_inv)) * bf2f(knw[d])))
703
+ : bf2f(f2bf(bf2f(knw[d]) *
704
+ bf2f(f2bf(bf2f(ksrc[d]) * (*s_inv)))));
705
  }
706
  // without k-norm the rope reads the source row directly (identical
707
  // values), skipping the staging pass and its barrier
 
710
  bf16* vrow = vc + (long long)pm * D;
711
  for (int d = threadIdx.x; d < half; d += blockDim.x) {
712
  const float ang = (float)pm * invf[d];
713
+ const float c = bf2f(f2bf(cosf(ang) * ascale));
714
+ const float s = bf2f(f2bf(sinf(ang) * ascale));
715
  const float x1 = (qk_flag & 1) ? hbuf[d] : bf2f(ksrc[d]);
716
  const float x2 = (qk_flag & 1) ? hbuf[d + half] : bf2f(ksrc[d + half]);
717
  krow[d] = f2bf(bf2f(f2bf(x1 * c)) + bf2f(f2bf(-x2 * s)));
718
  krow[d + half] = f2bf(bf2f(f2bf(x2 * c)) + bf2f(f2bf(x1 * s)));
719
  }
720
+ if (qk_flag & 4) {
721
+ // gemma v-norm: weightless RMS over v, fp32, one rounding
722
+ float ss = 0.f;
723
+ for (int d = threadIdx.x; d < D; d += blockDim.x) {
724
+ const float x = bf2f(vsrc[d]);
725
+ ss += x * x;
726
+ }
727
+ ss = block_sum(ss, s_red);
728
+ if (threadIdx.x == 0) *s_inv = rsqrtf(ss / (float)D + eps);
729
+ __syncthreads();
730
+ for (int d = threadIdx.x; d < D; d += blockDim.x)
731
+ vrow[d] = f2bf(bf2f(vsrc[d]) * (*s_inv));
732
+ } else {
733
+ for (int d = threadIdx.x; d < D; d += blockDim.x) vrow[d] = vsrc[d];
734
+ }
735
  __threadfence_block();
736
  __syncthreads();
737
  }
 
748
  // are safe. The O-projection finalizes the partials.
749
  __device__ void ph_attn(const long long* P, int pos, int M) {
750
  const long long max_seq = P[7];
751
+ // slot 8: bit0 q/k norm, bit1 gemma norm semantics, bit2 v-norm;
752
+ // bits 16+ sliding window length (0 = unwindowed)
753
+ const int qk_flag = ((int)P[8]) & 0xFFFF;
754
+ const int win = (int)((P[8] >> 16) & 0xFFFFFFFFll);
755
  const float eps = bits2f(P[9]);
756
  const float* invf = reinterpret_cast<const float*>(P[10]);
757
  const int Hq = (int)P[11], Hkv = ((int)P[12]) & 0xFFFF;
 
761
  const int chbits = (int)(P[12] >> 16) & 0xFFFF;
762
  const int CH = (chbits > 0 && chbits <= ATTN_CHUNK) ? chbits : ATTN_CHUNK;
763
  const float scale = bits2f(P[14]);
764
+ const int maxch = (int)(P[15] & 0xFFFF);
765
+ // rope attention scaling in slot 15 bits 32+ (0 bits = 1.0)
766
+ const int asbits = (int)(P[15] >> 32);
767
+ const float ascale = (asbits != 0) ? __int_as_float(asbits) : 1.0f;
768
  const int rep = Hq / Hkv;
769
  const long long KT = (long long)(Hq + 2 * Hkv) * D;
770
 
 
793
  const int S = pm + 1;
794
  const int nch = (S + CH - 1) / CH;
795
  if (ch >= nch) continue;
796
+ const int s_lo = (win > 0 && S > win) ? S - win : 0;
797
+ if (ch < s_lo / CH) continue; // fully outside the sliding window
798
  const int own_ch = pm / CH;
799
  const bf16* qkv =
800
  reinterpret_cast<const bf16*>(P[1]) + (long long)m * KT;
 
807
  const int half = D >> 1;
808
 
809
  // decode only: the chunk owning `pos` appends K/V for its group,
810
+ // before the chunk copy below so the copy sees the appended row.
811
+ // Shared-KV layers (flag bit 3) reuse an earlier layer's cache and
812
+ // append nothing.
813
+ if (M == 1 && ch == own_ch && !(qk_flag & 8)) {
814
  kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, invf, Hq, Hkv,
815
+ kvh, D, pm, s_red, &s_inv, hbuf, ascale);
816
  }
817
 
818
  const int s0 = ch * CH;
819
  const int s1 = (s0 + CH < S) ? s0 + CH : S;
820
  const int cn = s1 - s0;
821
+ const int si0 = (s0 < s_lo) ? (s_lo - s0) : 0; // window mask
822
 
823
  // Copy this chunk's K/V into the dynamic-shared region (idle during
824
  // attention) when it fits: the score loop otherwise walks the chunk
 
857
  if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)D + eps);
858
  __syncthreads();
859
  for (int d = threadIdx.x; d < D; d += blockDim.x)
860
+ hbuf[d] = (qk_flag & 2)
861
+ ? bf2f(f2bf((bf2f(q[d]) * s_inv) * bf2f(qnw[d])))
862
+ : bf2f(f2bf(bf2f(qnw[d]) *
863
+ bf2f(f2bf(bf2f(q[d]) * s_inv))));
864
  }
865
  // without q-norm the rope reads q directly (identical values),
866
  // skipping the staging pass and its barrier
867
  if (qk_flag & 1) __syncthreads();
868
  for (int d = threadIdx.x; d < half; d += blockDim.x) {
869
  const float ang = (float)pm * invf[d];
870
+ const float c = bf2f(f2bf(cosf(ang) * ascale));
871
+ const float s = bf2f(f2bf(sinf(ang) * ascale));
872
  const float x1 = (qk_flag & 1) ? hbuf[d] : bf2f(q[d]);
873
  const float x2 =
874
  (qk_flag & 1) ? hbuf[d + half] : bf2f(q[d + half]);
 
884
  const int wid = threadIdx.x >> 5, lane = threadIdx.x & 31;
885
  const int nw = blockDim.x >> 5;
886
  for (int si = wid; si < cn; si += nw) {
887
+ if (si < si0) continue;
888
  const bf16* kr = kv_staged ? ks_sh + (long long)si * D
889
  : kc + (long long)(s0 + si) * D;
890
  float acc = 0.f;
 
899
 
900
  float mx = -INFINITY;
901
  for (int si = threadIdx.x; si < cn; si += blockDim.x)
902
+ if (si >= si0) mx = fmaxf(mx, sc[si]);
903
  mx = block_max(mx, s_red);
904
  if (threadIdx.x == 0) s_m = mx;
905
  __syncthreads();
906
 
907
  float lsum = 0.f;
908
  for (int si = threadIdx.x; si < cn; si += blockDim.x) {
909
+ if (si < si0) continue;
910
  const float p = expf(sc[si] - s_m);
911
  sc[si] = p;
912
  lsum += p;
 
923
  const int d = threadIdx.x - g * D;
924
  if (g < NG) {
925
  const int csz = (cn + NG - 1) / NG;
926
+ int a0 = g * csz;
927
+ if (a0 < si0) a0 = si0;
928
+ const int a1 = ((g * csz) + csz < cn) ? (g * csz) + csz : cn;
929
  float acc = 0.f;
930
  for (int si = a0; si < a1; ++si)
931
  acc = fmaf(sc[si],
 
935
  pv[g * D + d] = acc;
936
  }
937
  __syncthreads();
938
+ for (int d = threadIdx.x; d < D; d += blockDim.x) {
939
  float acc = 0.f;
940
+ for (int g2 = 0; g2 < NG; ++g2) acc += pv[g2 * D + d];
941
+ part[d] = acc;
942
+ }
943
+ } else {
944
+ for (int d = threadIdx.x; d < D; d += blockDim.x) {
945
+ float acc = 0.f;
946
+ for (int si = si0; si < cn; ++si)
947
+ acc = fmaf(sc[si],
948
+ bf2f(kv_staged ? vs_sh[(long long)si * D + d]
949
+ : vc[(long long)(s0 + si) * D + d]),
950
+ acc);
951
+ part[d] = acc;
952
  }
 
 
 
 
 
 
 
 
 
953
  }
954
  if (threadIdx.x == 0) {
955
  part[D] = s_m;
 
965
  // attention phase so causal reads inside the chunk see complete K/V.
966
  __device__ void ph_kv_append(const long long* P, int pos, int M) {
967
  const long long max_seq = P[7];
968
+ const int qk_flag = ((int)P[8]) & 0xFFFF;
969
  const float eps = bits2f(P[9]);
970
  const float* invf = reinterpret_cast<const float*>(P[10]);
971
+ const int Hq = (int)P[11], Hkv = ((int)P[12]) & 0xFFFF;
972
+ const int D = (int)P[13];
973
+ const int asbits = (int)(P[15] >> 32);
974
+ const float ascale = (asbits != 0) ? __int_as_float(asbits) : 1.0f;
975
  const long long KT = (long long)(Hq + 2 * Hkv) * D;
976
  const bf16* knw = reinterpret_cast<const bf16*>(P[6]);
977
 
 
988
  bf16* kc = reinterpret_cast<bf16*>(P[2]) + (long long)kvh * max_seq * D;
989
  bf16* vc = reinterpret_cast<bf16*>(P[3]) + (long long)kvh * max_seq * D;
990
  kv_norm_rope_append(qkv, kc, vc, knw, qk_flag, eps, invf, Hq, Hkv, kvh,
991
+ D, pm, s_red, &s_inv, hbuf, ascale);
992
  }
993
  }
994
+ // gemma post-sublayer norm + residual: hid = hid + norm(t) (+ optional
995
+ // layer scale). One vector; block 0 computes while the rest fall through.
996
+ __device__ void ph_normres(const long long* P) {
997
+ if (blockIdx.x != 0) return;
998
+ const bf16* t = reinterpret_cast<const bf16*>(P[1]);
999
+ const bf16* w = reinterpret_cast<const bf16*>(P[2]);
1000
+ bf16* hid = reinterpret_cast<bf16*>(P[3]);
1001
+ const long long n = P[7];
1002
+ const float eps = bits2f(P[14]);
1003
+ const float lscale = bits2f(P[15]);
1004
+ __shared__ float s_red[32];
1005
+ __shared__ float s_inv;
1006
+ float ss = 0.f;
1007
+ for (long long k = threadIdx.x; k < n; k += blockDim.x) {
1008
+ const float v = bf2f(t[k]);
1009
+ ss += v * v;
1010
+ }
1011
+ ss = block_sum(ss, s_red);
1012
+ if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)n + eps);
1013
+ __syncthreads();
1014
+ for (long long k = threadIdx.x; k < n; k += blockDim.x) {
1015
+ const float nv = bf2f(f2bf((bf2f(t[k]) * s_inv) * bf2f(w[k])));
1016
+ float h = bf2f(f2bf(bf2f(hid[k]) + nv));
1017
+ if (lscale != 1.f) h = bf2f(f2bf(h * lscale));
1018
+ hid[k] = f2bf(h);
1019
+ }
1020
+ }
1021
+
1022
+ // gemma per-layer-input mix: for each layer row, scale the context
1023
+ // projection (bf16 round), gemma-norm it over the 256-wide block, add the
1024
+ // token-identity row, and scale by 1/sqrt(2), mirroring the eager op
1025
+ // order exactly.
1026
+ __device__ void ph_plemix(const long long* P) {
1027
+ const bf16* ctx = reinterpret_cast<const bf16*>(P[1]);
1028
+ const bf16* w = reinterpret_cast<const bf16*>(P[2]);
1029
+ bf16* out = reinterpret_cast<bf16*>(P[3]);
1030
+ const bf16* tok = reinterpret_cast<const bf16*>(P[4]);
1031
+ const long long L = P[7], C = P[8];
1032
+ const float eps = bits2f(P[14]);
1033
+ const float pscale = bits2f(P[15]);
1034
+ __shared__ float s_red[32];
1035
+ __shared__ float s_inv;
1036
+ __shared__ float xbuf[512];
1037
+ for (long long l = blockIdx.x; l < L; l += gridDim.x) {
1038
+ float ss = 0.f;
1039
+ for (int j = threadIdx.x; j < (int)C; j += blockDim.x) {
1040
+ const float v = bf2f(f2bf(bf2f(ctx[l * C + j]) * pscale));
1041
+ xbuf[j] = v;
1042
+ ss += v * v;
1043
+ }
1044
+ ss = block_sum(ss, s_red);
1045
+ if (threadIdx.x == 0) s_inv = rsqrtf(ss / (float)C + eps);
1046
+ __syncthreads();
1047
+ for (int j = threadIdx.x; j < (int)C; j += blockDim.x) {
1048
+ const float nv = bf2f(f2bf((xbuf[j] * s_inv) * bf2f(w[j])));
1049
+ const float mixed = bf2f(f2bf(nv + bf2f(tok[l * C + j])));
1050
+ out[l * C + j] = f2bf(mixed * 0.70710678118654752440f);
1051
+ }
1052
+ __syncthreads();
1053
+ }
1054
+ }
1055
+
1056
  __device__ void ph_argmax_part(const long long* P) {
1057
  const float* lg = reinterpret_cast<const float*>(P[1]);
1058
  unsigned long long* parts = reinterpret_cast<unsigned long long*>(P[3]);
 
1101
  case OP_ARGMAX_PART: ph_argmax_part(P); break;
1102
  case OP_ARGMAX_FIN: ph_argmax_fin(P, step_slot); break;
1103
  case OP_KV_APPEND: ph_kv_append(P, pos, M); break;
1104
+ case OP_NORMRES: ph_normres(P); break;
1105
+ case OP_PLEMIX: ph_plemix(P); break;
1106
  default: break;
1107
  }
1108
  }
tests/test_mak.py CHANGED
@@ -276,3 +276,45 @@ def test_generate_closed_loop_and_speed(models):
276
  speedup = dt_hf / dt_ours
277
  print(f"speedup vs transformers eager generate: {speedup:.2f}x")
278
  assert speedup > 2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  speedup = dt_hf / dt_ours
277
  print(f"speedup vs transformers eager generate: {speedup:.2f}x")
278
  assert speedup > 2.0
279
+
280
+
281
+ @requires_cuda
282
+ def test_gemma4_e2b_coverage():
283
+ """gemma-4-E2B-it: gemma norms, PLE pathway, shared KV, sliding
284
+ windows, dual head dims, softcapped head. Needs both model copies
285
+ resident, so it runs where memory allows."""
286
+ if torch.cuda.get_device_properties(0).total_memory < 30e9:
287
+ pytest.skip("needs ~24GB free for eager + packed copies")
288
+ transformers = pytest.importorskip("transformers")
289
+ from transformers import DynamicCache
290
+ ref = transformers.AutoModelForCausalLM.from_pretrained(
291
+ "google/gemma-4-E2B-it", dtype=torch.bfloat16,
292
+ attn_implementation="eager").cuda().eval()
293
+ mm = mak.MegaModel.from_pretrained(ref, max_seq=512, max_gen=512)
294
+ torch.manual_seed(5)
295
+ seq = torch.randint(0, mm.V, (32,)).tolist()
296
+ cap = ref.config.text_config.final_logit_softcapping
297
+ cache = DynamicCache(config=ref.config.text_config)
298
+ out = torch.empty(len(seq), mm.V, device="cuda")
299
+ with torch.no_grad():
300
+ for i, t_ in enumerate(seq):
301
+ r = ref.model.language_model(
302
+ input_ids=torch.tensor([[t_]], device="cuda"),
303
+ past_key_values=cache, use_cache=True)
304
+ cache = r.past_key_values
305
+ lg = ref.lm_head(r.last_hidden_state)[0, -1].float()
306
+ lg = torch.tanh(lg.bfloat16() / cap).bfloat16() * cap
307
+ out[i] = lg.float()
308
+ mine = torch.empty(len(seq), mm.V, device="cuda")
309
+ for i, t_ in enumerate(seq):
310
+ mine[i] = mm.decode_step(t_, i).clone()
311
+ agree = (mine.argmax(-1) == out.argmax(-1)).sum().item()
312
+ d = (mine.bfloat16().float() - out.bfloat16().float()).abs()
313
+ print(f"gemma4-E2B band mean={d.mean().item():.4f} "
314
+ f"agree={agree}/{len(seq)}")
315
+ assert agree >= len(seq) * 0.85
316
+ a = mm.decode_step(seq[0], 0).clone()
317
+ b = mm.decode_step(seq[0], 0, phased=True).clone()
318
+ assert torch.equal(a, b)
319
+ del ref, mm
320
+ torch.cuda.empty_cache()
torch-ext/model_as_a_kernel/__init__.py CHANGED
@@ -27,6 +27,8 @@ __all__ = ["MegaModel"]
27
  _OP_EMBED, _OP_GEMV, _OP_QKV_POST, _OP_ATTN = 0, 1, 2, 3
28
  _OP_ARGMAX_PART, _OP_ARGMAX_FIN = 4, 5
29
  _IT_NONE, _IT_RMSNORM, _IT_SWIGLU, _IT_ATTNFIN = 0, 1, 2, 3
 
 
30
  _ITF_REDUCE, _ITF_WRITEBACK = 8, 16
31
  _EP_STORE, _EP_RESID, _EP_F32, _EP_F32_AMAX, _EP_PARTIAL = 0, 1, 2, 3, 4
32
  _NSLICE = 4
@@ -265,6 +267,8 @@ class MegaModel:
265
  from transformers import AutoModelForCausalLM
266
  model = AutoModelForCausalLM.from_pretrained(
267
  model, torch_dtype=torch.bfloat16)
 
 
268
  hf = model.config
269
  dec = model.model
270
  attn0 = dec.layers[0].self_attn
@@ -324,6 +328,252 @@ class MegaModel:
324
  return cls(weights, config, device=device, max_seq=max_seq,
325
  max_gen=max_gen)
326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  # ------------------------------------------------------------------
328
  def decode_step(self, token: int, pos: int, phased: bool = False):
329
  """One decode step; returns the live fp32 logits buffer [V]."""
 
27
  _OP_EMBED, _OP_GEMV, _OP_QKV_POST, _OP_ATTN = 0, 1, 2, 3
28
  _OP_ARGMAX_PART, _OP_ARGMAX_FIN = 4, 5
29
  _IT_NONE, _IT_RMSNORM, _IT_SWIGLU, _IT_ATTNFIN = 0, 1, 2, 3
30
+ _IT_RMSNORM_G, _IT_GELU_GLU = 4, 5
31
+ _EP_GELU_PLE, _EP_F32_AMAX_CAP = 5, 6
32
  _ITF_REDUCE, _ITF_WRITEBACK = 8, 16
33
  _EP_STORE, _EP_RESID, _EP_F32, _EP_F32_AMAX, _EP_PARTIAL = 0, 1, 2, 3, 4
34
  _NSLICE = 4
 
267
  from transformers import AutoModelForCausalLM
268
  model = AutoModelForCausalLM.from_pretrained(
269
  model, torch_dtype=torch.bfloat16)
270
+ if "gemma4" in getattr(model.config, "model_type", ""):
271
+ return cls._from_gemma4(model, device, max_seq, max_gen)
272
  hf = model.config
273
  dec = model.model
274
  attn0 = dec.layers[0].self_attn
 
328
  return cls(weights, config, device=device, max_seq=max_seq,
329
  max_gen=max_gen)
330
 
331
+ # ------------------------------------------------------------------
332
+ @classmethod
333
+ def _from_gemma4(cls, model, device, max_seq, max_gen):
334
+ """gemma-4-E2B-it program: gemma norms (fp32, raw weight),
335
+ gelu-tanh gating, post-sublayer norm phases, per-layer-input
336
+ pathway, sliding windows, shared KV, dual head dims, softcapped
337
+ head. Scope is the E2B text model only."""
338
+ self = cls.__new__(cls)
339
+ cfg = model.config.text_config if hasattr(model.config, "text_config") \
340
+ else model.config
341
+ dec = model.model.language_model if hasattr(model.model, "language_model") \
342
+ else model.model
343
+ assert cfg.model_type == "gemma4_text", cfg.model_type
344
+ assert not cfg.enable_moe_block
345
+
346
+ self.L = int(cfg.num_hidden_layers)
347
+ self.hidden = int(cfg.hidden_size)
348
+ self.Hq = int(cfg.num_attention_heads)
349
+ self.V = int(cfg.vocab_size)
350
+ self.eps = float(cfg.rms_norm_eps)
351
+ self.qk_norm = True
352
+ self.max_seq = int(max_seq)
353
+ self.max_gen = int(max_gen)
354
+ self.device = torch.device(device)
355
+ self.tiled = False
356
+ win = int(cfg.sliding_window)
357
+ cap = float(cfg.final_logit_softcapping)
358
+ PL = int(cfg.hidden_size_per_layer_input)
359
+ dt = torch.bfloat16
360
+ dev = self.device
361
+ self._keep = []
362
+
363
+ def pack(t):
364
+ t = t.detach().to(device=dev, dtype=dt).contiguous()
365
+ self._keep.append(t)
366
+ return t
367
+
368
+ def packf(t):
369
+ t = t.detach().to(device=dev, dtype=torch.float32).contiguous()
370
+ self._keep.append(t)
371
+ return t
372
+
373
+ embed = pack(dec.embed_tokens.weight)
374
+ ple_tab = pack(dec.embed_tokens_per_layer.weight)
375
+ plm_w = pack(dec.per_layer_model_projection.weight)
376
+ pln_w = pack(dec.per_layer_projection_norm.weight)
377
+ fnorm = pack(dec.norm.weight)
378
+ rot = dec.rotary_emb
379
+ invf = {lt: packf(getattr(rot, f"{lt}_inv_freq").float())
380
+ for lt in set(cfg.layer_types)}
381
+ ascale = {lt: float(getattr(rot, f"{lt}_attention_scaling"))
382
+ for lt in set(cfg.layer_types)}
383
+
384
+ layers = []
385
+ for li, lyr in enumerate(dec.layers):
386
+ a = lyr.self_attn
387
+ D_l = int(a.head_dim)
388
+ shared = bool(a.is_kv_shared_layer)
389
+ ent = {
390
+ "D": D_l, "shared": shared,
391
+ "lscale": float(lyr.layer_scalar.float().item()),
392
+ "type": cfg.layer_types[li],
393
+ "I": int(lyr.mlp.intermediate_size),
394
+ "qn": pack(a.q_norm.weight),
395
+ "o": pack(a.o_proj.weight),
396
+ "ln_in": pack(lyr.input_layernorm.weight),
397
+ "ln_pa": pack(lyr.post_attention_layernorm.weight),
398
+ "ln_pf": pack(lyr.pre_feedforward_layernorm.weight),
399
+ "ln_ff": pack(lyr.post_feedforward_layernorm.weight),
400
+ "ln_pl": pack(lyr.post_per_layer_input_norm.weight),
401
+ "plig": pack(lyr.per_layer_input_gate.weight),
402
+ "plpr": pack(lyr.per_layer_projection.weight),
403
+ "wgu": pack(torch.cat([lyr.mlp.gate_proj.weight,
404
+ lyr.mlp.up_proj.weight], 0)),
405
+ "wdn": pack(lyr.mlp.down_proj.weight),
406
+ }
407
+ if shared:
408
+ ent["wqkv"] = pack(a.q_proj.weight)
409
+ ent["kn"] = None
410
+ else:
411
+ ent["wqkv"] = pack(torch.cat(
412
+ [a.q_proj.weight, a.k_proj.weight, a.v_proj.weight], 0))
413
+ ent["kn"] = pack(a.k_norm.weight)
414
+ ent["kc"] = torch.zeros(self.max_seq, D_l, dtype=dt,
415
+ device=dev)
416
+ ent["vc"] = torch.zeros(self.max_seq, D_l, dtype=dt,
417
+ device=dev)
418
+ self._keep += [ent["kc"], ent["vc"]]
419
+ layers.append(ent)
420
+ # shared layers read the last non-shared layer of their type
421
+ src = {}
422
+ for li, ent in enumerate(layers):
423
+ if not ent["shared"]:
424
+ src[ent["type"]] = ent
425
+ for ent in layers:
426
+ if ent["shared"]:
427
+ ent["kc"] = src[ent["type"]]["kc"]
428
+ ent["vc"] = src[ent["type"]]["vc"]
429
+
430
+ maxD = max(e["D"] for e in layers)
431
+ maxI = max(e["I"] for e in layers)
432
+ self._maxk = max(self.hidden, self.Hq * maxD, maxI)
433
+ self._chunk_m = 1
434
+ self._chunk = 128
435
+ self._maxch = (self.max_seq + self._chunk - 1) // self._chunk
436
+ self._hidden = torch.empty(self.hidden, dtype=dt, device=dev)
437
+ self._tmp = torch.empty(self.hidden, dtype=dt, device=dev)
438
+ self._qkv = torch.empty((self.Hq + 2) * maxD, dtype=dt, device=dev)
439
+ self._gu = torch.empty(2 * maxI, dtype=dt, device=dev)
440
+ self._t256 = torch.empty(PL, dtype=dt, device=dev)
441
+ self._ctxraw = torch.empty(self.L * PL, dtype=dt, device=dev)
442
+ self._pletok = torch.empty(self.L * PL, dtype=dt, device=dev)
443
+ self._ple = torch.empty(self.L * PL, dtype=dt, device=dev)
444
+ self._logits = torch.empty(self.V, dtype=torch.float32, device=dev)
445
+ self._partials = torch.empty(
446
+ self.Hq * self._maxch * (maxD + 2), dtype=torch.float32,
447
+ device=dev)
448
+ self._token = torch.zeros(1, dtype=torch.int32, device=dev)
449
+ self._prompt_in = torch.zeros(self.max_seq, dtype=torch.int32,
450
+ device=dev)
451
+ self._tokens_out = torch.zeros(self.max_gen, dtype=torch.int32,
452
+ device=dev)
453
+ self._bar = torch.zeros(34, dtype=torch.int32, device=dev)
454
+ probe = torch.zeros(1, 16, dtype=torch.int64, device=dev)
455
+ self._nblocks = int(ops.mak_num_blocks(probe, self._maxk))
456
+ self._parts = torch.zeros(self._nblocks, dtype=torch.int64,
457
+ device=dev)
458
+ try:
459
+ self._bw_per_sm = float(ops.mak_bw_per_sm(probe))
460
+ except (AttributeError, RuntimeError):
461
+ self._bw_per_sm = 0.0
462
+ self._invf = invf[layers[0]["type"]]
463
+ self.Hkv, self.D, self.I = 1, maxD, maxI
464
+ self.theta = 0.0
465
+
466
+ emb_scale = float(torch.tensor(self.hidden ** 0.5,
467
+ dtype=torch.float32).to(dt))
468
+ ple_scale = float(torch.tensor(PL ** 0.5,
469
+ dtype=torch.float32).to(dt))
470
+ proj_scale = float(torch.tensor(self.hidden ** -0.5,
471
+ dtype=torch.float32))
472
+ hid = self._hidden.data_ptr()
473
+ tmp = self._tmp.data_ptr()
474
+ rows, names = [], []
475
+ rows.append(_row(_OP_EMBED, p1=ple_tab.data_ptr(),
476
+ p2=embed.data_ptr(), p3=hid,
477
+ p4=self._parts.data_ptr(),
478
+ p5=self._prompt_in.data_ptr(),
479
+ p6=self._token.data_ptr(),
480
+ n=self._tokens_out.data_ptr(), k=self.hidden,
481
+ it=self._pletok.data_ptr(),
482
+ hkv=self.L * PL, f0=emb_scale,
483
+ i0=_fbits(ple_scale)))
484
+ names.append("embed")
485
+ rows.append(_row(_OP_GEMV, p1=hid, p2=plm_w.data_ptr(),
486
+ p3=self._ctxraw.data_ptr(), n=self.L * PL,
487
+ k=self.hidden, it=_IT_NONE, epi=_EP_STORE))
488
+ names.append("plm")
489
+ rows.append(_row(8, p1=self._ctxraw.data_ptr(),
490
+ p2=pln_w.data_ptr(), p3=self._ple.data_ptr(),
491
+ p4=self._pletok.data_ptr(), n=self.L, k=PL,
492
+ f0=self.eps, i0=_fbits(proj_scale)))
493
+ names.append("plemix")
494
+
495
+ for li, e in enumerate(layers):
496
+ D_l, qdim = e["D"], self.Hq * e["D"]
497
+ lt = e["type"]
498
+ w_l = win if lt == "sliding_attention" else 0
499
+ asb = _fbits(ascale[lt]) if ascale[lt] != 1.0 else 0
500
+ flags = 1 | 2 | (0 if e["shared"] else 4) | \
501
+ (8 if e["shared"] else 0)
502
+ n_qkv = qdim if e["shared"] else qdim + 2 * D_l
503
+ rows.append(_row(_OP_GEMV, p1=hid, p2=e["wqkv"].data_ptr(),
504
+ p3=self._qkv.data_ptr(),
505
+ p4=e["ln_in"].data_ptr(), n=n_qkv,
506
+ k=self.hidden, it=_IT_RMSNORM_G,
507
+ epi=_EP_STORE, f0=self.eps))
508
+ names.append(f"L{li}.qkv")
509
+ rows.append(_row(_OP_ATTN, p1=self._qkv.data_ptr(),
510
+ p2=e["kc"].data_ptr(), p3=e["vc"].data_ptr(),
511
+ p4=self._partials.data_ptr(),
512
+ p5=e["qn"].data_ptr(),
513
+ p6=e["kn"].data_ptr() if e["kn"] is not None
514
+ else 0,
515
+ n=self.max_seq,
516
+ k=flags | (w_l << 16), it=_fbits(self.eps),
517
+ epi=invf[lt].data_ptr(), hq=self.Hq,
518
+ hkv=1 | (self._chunk << 16), d=D_l, f0=1.0,
519
+ i0=self._maxch | (asb << 32)))
520
+ names.append(f"L{li}.attn")
521
+ rows.append(_row(_OP_GEMV, p1=self._partials.data_ptr(),
522
+ p2=e["o"].data_ptr(), p3=tmp,
523
+ n=self.hidden, k=qdim, it=_IT_ATTNFIN,
524
+ epi=_EP_STORE, hq=self.Hq,
525
+ hkv=self._chunk << 16, d=D_l,
526
+ i0=self._maxch | (w_l << 16)))
527
+ names.append(f"L{li}.o")
528
+ rows.append(_row(7, p1=tmp, p2=e["ln_pa"].data_ptr(), p3=hid,
529
+ n=self.hidden, f0=self.eps, i0=_fbits(1.0)))
530
+ names.append(f"L{li}.nr_attn")
531
+ rows.append(_row(_OP_GEMV, p1=hid, p2=e["wgu"].data_ptr(),
532
+ p3=self._gu.data_ptr(),
533
+ p4=e["ln_pf"].data_ptr(), n=2 * e["I"],
534
+ k=self.hidden, it=_IT_RMSNORM_G,
535
+ epi=_EP_STORE, f0=self.eps))
536
+ names.append(f"L{li}.gateup")
537
+ rows.append(_row(_OP_GEMV, p1=self._gu.data_ptr(),
538
+ p2=e["wdn"].data_ptr(), p3=tmp,
539
+ n=self.hidden, k=e["I"], it=_IT_GELU_GLU,
540
+ epi=_EP_STORE))
541
+ names.append(f"L{li}.down")
542
+ rows.append(_row(7, p1=tmp, p2=e["ln_ff"].data_ptr(), p3=hid,
543
+ n=self.hidden, f0=self.eps, i0=_fbits(1.0)))
544
+ names.append(f"L{li}.nr_ffw")
545
+ rows.append(_row(_OP_GEMV, p1=hid, p2=e["plig"].data_ptr(),
546
+ p3=self._t256.data_ptr(),
547
+ p6=self._ple.data_ptr() + li * PL * 2,
548
+ n=PL, k=self.hidden, it=_IT_NONE,
549
+ epi=_EP_GELU_PLE))
550
+ names.append(f"L{li}.plig")
551
+ rows.append(_row(_OP_GEMV, p1=self._t256.data_ptr(),
552
+ p2=e["plpr"].data_ptr(), p3=tmp,
553
+ n=self.hidden, k=PL, it=_IT_NONE,
554
+ epi=_EP_STORE))
555
+ names.append(f"L{li}.plproj")
556
+ rows.append(_row(7, p1=tmp, p2=e["ln_pl"].data_ptr(), p3=hid,
557
+ n=self.hidden, f0=self.eps,
558
+ i0=_fbits(e["lscale"])))
559
+ names.append(f"L{li}.nr_ple")
560
+
561
+ rows.append(_row(_OP_GEMV, p1=hid, p2=embed.data_ptr(),
562
+ p3=self._logits.data_ptr(),
563
+ p4=fnorm.data_ptr(),
564
+ p6=self._parts.data_ptr(), n=self.V,
565
+ k=self.hidden, it=_IT_RMSNORM_G,
566
+ epi=_EP_F32_AMAX_CAP, hkv=1, f0=self.eps,
567
+ i0=_fbits(cap)))
568
+ names.append("lm_head")
569
+ rows.append(_row(_OP_ARGMAX_FIN, p1=self._parts.data_ptr(),
570
+ p3=self._token.data_ptr(),
571
+ p4=self._tokens_out.data_ptr()))
572
+ names.append("argmax.fin")
573
+ self._prog = torch.tensor(rows, dtype=torch.int64, device=dev)
574
+ self.phase_names = names
575
+ return self
576
+
577
  # ------------------------------------------------------------------
578
  def decode_step(self, token: int, pos: int, phased: bool = False):
579
  """One decode step; returns the live fp32 logits buffer [V]."""