model-as-a-kernel v1 source
Browse files- mak_cuda/megakernel.cu +265 -48
- tests/test_mak.py +42 -0
- torch-ext/model_as_a_kernel/__init__.py +250 -0
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
|
| 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,
|
|
|
|
|
|
|
| 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 =
|
| 221 |
M = fmaxf(M, base[(long long)c * (D + 2) + D]);
|
| 222 |
float L = 0.f, acc = 0.f;
|
| 223 |
-
for (int 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 =
|
| 431 |
Mx = fmaxf(Mx, base[(long long)c * (D + 2) + D]);
|
| 432 |
float L = 0.f;
|
| 433 |
-
for (int 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 =
|
| 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 =
|
| 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] =
|
| 585 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 779 |
-
|
|
|
|
| 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 |
-
|
| 790 |
float acc = 0.f;
|
| 791 |
-
for (int g2 = 0; g2 < NG; ++g2) acc += pv[g2 * D +
|
| 792 |
-
part[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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]
|
|
|
|
|
|
|
|
|
|
| 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]."""
|