Buckets:
| // tests/kernel_backend_cuda.c - tests/kernel_backend.h on the CUDA kernels (engine/kernels_cuda.h), as the CUDA engine | |
| // launches them. Each call uploads its inputs, runs on the default stream, copies the outputs back and frees. | |
| static uint32_t *g_stab24, *g_stab32; // the seed tables: lfsr_stream24 (SEED4) and lfsr_stream32 (SEED4P4) | |
| static void* g_live[64]; // this call's device allocations | |
| static int g_nlive; | |
| static void* dev(const void* p, size_t n) { // a device copy of n bytes (zeros when p is NULL) | |
| void* d = NULL; | |
| if (cudaMalloc(&d, n ? n : 16) != cudaSuccess) return NULL; | |
| if (p) cudaMemcpy(d, p, n, cudaMemcpyHostToDevice); | |
| else cudaMemset(d, 0, n ? n : 16); | |
| g_live[g_nlive++] = d; | |
| return d; | |
| } | |
| static int done(const char* what, void* host, const void* d, size_t n) { // sync, copy back, free the call's buffers | |
| cudaError_t e = cudaDeviceSynchronize(); | |
| if (e == cudaSuccess && host) e = cudaMemcpy(host, d, n, cudaMemcpyDeviceToHost); | |
| if (e == cudaSuccess) e = cudaGetLastError(); | |
| for (int i = 0; i < g_nlive; ++i) cudaFree(g_live[i]); | |
| g_nlive = 0; | |
| if (e != cudaSuccess) { printf("FAIL: %s: %s\n", what, cudaGetErrorString(e)); return -1; } | |
| return 0; | |
| } | |
| // an output before the last one: copied back now (0), or the call's buffers freed and -1 | |
| static int fetch(const char* what, void* host, const void* d, size_t n) { | |
| if (cudaDeviceSynchronize() == cudaSuccess && cudaMemcpy(host, d, n, cudaMemcpyDeviceToHost) == cudaSuccess) return 0; | |
| done(what, NULL, NULL, 0); | |
| printf("FAIL: %s: %s\n", what, cudaGetErrorString(cudaGetLastError())); | |
| return -1; | |
| } | |
| int kt_open(char* err, int errlen) { | |
| int n = 0; | |
| if (cudaGetDeviceCount(&n) != cudaSuccess || n < 1) { snprintf(err, (size_t) errlen, "no CUDA device"); return -1; } | |
| uint32_t* g = (uint32_t*) malloc(65536 * 4); | |
| for (uint32_t s = 0; s < 65536; ++s) g[s] = lfsr_stream24((uint16_t) s); | |
| cudaMalloc((void**) &g_stab24, 65536 * 4); | |
| cudaMemcpy(g_stab24, g, 65536 * 4, cudaMemcpyHostToDevice); | |
| for (uint32_t s = 0; s < 65536; ++s) g[s] = lfsr_stream32((uint16_t) s); | |
| cudaMalloc((void**) &g_stab32, 65536 * 4); | |
| cudaMemcpy(g_stab32, g, 65536 * 4, cudaMemcpyHostToDevice); | |
| free(g); | |
| if (cudaGetLastError() != cudaSuccess) { snprintf(err, (size_t) errlen, "CUDA init failed"); return -1; } | |
| return 0; | |
| } | |
| void kt_close(void) { | |
| cudaFree(g_stab24); | |
| cudaFree(g_stab32); | |
| } | |
| const char* kt_name(void) { return "CUDA"; } | |
| int kt_mm_tile(void) { return MMT_BN; } | |
| int kt_attn_key_tile(void) { return FA_BK; } | |
| int kt_seed_gemm_exact(int on) { | |
| kc_seed_gemm_f32(on); | |
| return 1; | |
| } | |
| // The weight's slices (device addresses in freshly uploaded streams) and its seed table. | |
| static void slices(const KtWeight* w, WSlice* out, const uint32_t** G) { | |
| const uint8_t* W = (const uint8_t*) dev(w->w, w->wn); | |
| const uint8_t* S = (const uint8_t*) dev(w->s, w->sn); | |
| const uint8_t* B = (const uint8_t*) dev(w->b, w->bn); | |
| const uint8_t* E = (const uint8_t*) dev(w->e, w->en); | |
| const size_t rk = (size_t) w->R * w->K; | |
| for (int s = 0; s < w->S; ++s) { | |
| WSlice* o = &out[s]; | |
| memset(o, 0, sizeof *o); | |
| switch (w->fmt) { | |
| case MF_BF16: o->p[0] = W + s * rk * 2; break; | |
| case MF_Q8: case MF_Q4: | |
| o->p[0] = W + s * rk / (w->fmt == MF_Q8 ? 1 : 2); | |
| o->p[1] = S + s * rk / (w->group ? w->group : 64) * 2; | |
| o->p[2] = B + s * rk / (w->group ? w->group : 64) * 2; | |
| o->gs = w->group == 32 ? 5 : 6; | |
| break; | |
| default: // seeds | |
| o->p[0] = W + s * rk / 8 * 2; | |
| o->p[1] = S + s * rk / 8 * 2; | |
| if (w->fmt == MF_SEED4P4) o->p[3] = E + s * rk / 16; | |
| o->eb = ((const int32_t*) w->b)[s]; | |
| } | |
| } | |
| *G = w->fmt == MF_SEED4P4 ? g_stab32 : g_stab24; | |
| } | |
| int kt_mv(const KtWeight* w, int T, int add, const float* x, float* y) { | |
| WSlice ws[8]; | |
| const uint32_t* G; | |
| slices(w, ws, &G); | |
| const size_t yn = sizeof(float) * (size_t) T * w->R; | |
| float* dy = (float*) dev(y, yn); | |
| kc_mv(0, w->fmt, ws[0], w->K, w->R, (const float*) dev(x, sizeof(float) * (size_t) T * w->K), w->K, dy, w->R, T, add, G); | |
| return done("kc_mv", y, dy, yn); | |
| } | |
| int kt_mv_sel(const KtWeight* w, int P, int xdiv, const int32_t* sel, const float* x, float* y) { | |
| WSlice ws[8]; | |
| const uint32_t* G; | |
| slices(w, ws, &G); | |
| const size_t yn = sizeof(float) * (size_t) P * w->R; | |
| float* dy = (float*) dev(NULL, yn); | |
| kc_mv_sel(0, w->fmt, (const WSlice*) dev(ws, sizeof(WSlice) * (size_t) w->S), w->K, w->R, | |
| (const float*) dev(x, sizeof(float) * (size_t) ((P + xdiv - 1) / xdiv) * w->K), w->K, dy, w->R, | |
| (const int32_t*) dev(sel, 4 * (size_t) P), P, xdiv, G); | |
| return done("kc_mv_sel", y, dy, yn); | |
| } | |
| int kt_mv_gu(const KtWeight* w, int P, int xdiv, const int32_t* sel, const float* x, float* a) { | |
| WSlice ws[8]; | |
| const uint32_t* G; | |
| slices(w, ws, &G); | |
| const size_t yn = sizeof(float) * (size_t) P * w->R; | |
| float* da = (float*) dev(NULL, yn); | |
| const WSlice* d = (const WSlice*) dev(ws, sizeof(WSlice) * (size_t) w->S); | |
| kc_mv_gu(0, w->fmt, d, d + 1, w->K, w->R, (const float*) dev(x, sizeof(float) * (size_t) ((P + xdiv - 1) / xdiv) * w->K), | |
| w->K, da, w->R, (const int32_t*) dev(sel, 4 * (size_t) P), P, xdiv, G); | |
| return done("kc_mv_gu", a, da, yn); | |
| } | |
| int kt_mm(const KtWeight* w, int T, const float* x, float* y) { | |
| WSlice ws[8]; | |
| const uint32_t* G; | |
| slices(w, ws, &G); | |
| const size_t yn = sizeof(float) * (size_t) T * w->R; | |
| float* dy = (float*) dev(y, yn); | |
| kc_mm(0, w->fmt, ws[0], w->K, w->R, (const float*) dev(x, sizeof(float) * (size_t) T * w->K), w->K, dy, w->R, T, 1, G); | |
| return done("kc_mm", y, dy, yn); | |
| } | |
| int kt_mm_grouped(const KtWeight* w, int P, int xdiv, const int32_t* perm, const MmTile* tiles, int nt, const float* x, | |
| float* y) { | |
| WSlice ws[8]; | |
| const uint32_t* G; | |
| slices(w, ws, &G); | |
| const size_t yn = sizeof(float) * (size_t) P * w->R; | |
| float* dy = (float*) dev(NULL, yn); | |
| kc_mm_grouped(0, w->fmt, (const WSlice*) dev(ws, sizeof(WSlice) * (size_t) w->S), w->K, w->R, | |
| (const float*) dev(x, sizeof(float) * (size_t) ((P + xdiv - 1) / xdiv) * w->K), w->K, dy, w->R, xdiv, | |
| (const int32_t*) dev(perm, 4 * (size_t) P), (const MmTile*) dev(tiles, sizeof(MmTile) * (size_t) nt), nt, G); | |
| return done("kc_mm_grouped", y, dy, yn); | |
| } | |
| int kt_gnorm(int d, float eps, const float* x, const uint16_t* w, float* y, int T) { | |
| const size_t n = sizeof(float) * (size_t) T * d; | |
| float* dy = (float*) dev(NULL, n); | |
| kc_gnorm(0, d, eps, (const float*) dev(x, n), (const uint16_t*) dev(w, 2 * (size_t) d), dy, T); | |
| return done("kc_gnorm", y, dy, n); | |
| } | |
| int kt_router(RouterArgs a, const uint16_t* W, const uint16_t* bias, const float* x, int T, int32_t* ind, float* wt) { | |
| int32_t* di = (int32_t*) dev(NULL, 4 * (size_t) T * a.top_k); | |
| float* dw = (float*) dev(NULL, 4 * (size_t) T * a.top_k); | |
| kc_router(0, a, (const uint16_t*) dev(W, 2 * (size_t) a.n * a.d), (const uint16_t*) dev(bias, 2 * (size_t) a.n), | |
| (const float*) dev(x, 4 * (size_t) T * a.d), (float*) dev(NULL, 4 * (size_t) T * a.n), | |
| (float*) dev(NULL, 4 * (size_t) T * a.n), di, dw, T); | |
| if (fetch("kc_router", ind, di, 4 * (size_t) T * a.top_k)) return -1; | |
| return done("kc_router", wt, dw, 4 * (size_t) T * a.top_k); | |
| } | |
| int kt_router_topk(RouterArgs a, const float* score, const float* sel, int T, int32_t* ind, float* wt) { | |
| int32_t* di = (int32_t*) dev(NULL, 4 * (size_t) T * a.top_k); | |
| float* dw = (float*) dev(NULL, 4 * (size_t) T * a.top_k); | |
| kc_router_topk(0, a, (const float*) dev(score, 4 * (size_t) T * a.n), (const float*) dev(sel, 4 * (size_t) T * a.n), di, | |
| dw, T); | |
| if (fetch("kc_router_topk", ind, di, 4 * (size_t) T * a.top_k)) return -1; | |
| return done("kc_router_topk", wt, dw, 4 * (size_t) T * a.top_k); | |
| } | |
| int kt_swiglu(const float* g, const float* u, float* a, int n) { | |
| float* da = (float*) dev(NULL, 4 * (size_t) n); | |
| kc_swiglu(0, (const float*) dev(g, 4 * (size_t) n), (const float*) dev(u, 4 * (size_t) n), da, n); | |
| return done("kc_swiglu", a, da, 4 * (size_t) n); | |
| } | |
| int kt_combine(const float* D, const float* w, const float* shared, float* x, float* v, int d, int k, int T) { | |
| const float* dD = (const float*) dev(D, 4 * (size_t) T * k * d); | |
| const float* dw = (const float*) dev(w, 4 * (size_t) T * k); | |
| float* dx = (float*) dev(x, 4 * (size_t) T * d); | |
| float* dv = (float*) dev(NULL, 4 * (size_t) T * d); | |
| kc_moe_combine(0, dD, dw, (const float*) dev(shared, 4 * (size_t) T * d), dx, d, k, T); | |
| kc_vcombine(0, dD, dw, dv, d, k, T); | |
| if (fetch("kc_moe_combine", x, dx, 4 * (size_t) T * d)) return -1; | |
| return done("kc_vcombine", v, dv, 4 * (size_t) T * d); | |
| } | |
| static KvView bf16_kv(void* K, void* V) { // every position in segment a | |
| KvView kv; | |
| memset(&kv, 0, sizeof kv); | |
| kv.a.k = K; | |
| kv.a.v = V; | |
| kv.b = kv.a; | |
| kv.nv = 1 << 30; | |
| kv.fmt = KV_BF16; | |
| return kv; | |
| } | |
| static int attn(AttnArgs a, float* dq, KvView kv, const RowInfo* dri, const float* g, float* o, int T, const char* what) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128; | |
| float* dout = (float*) dev(NULL, qn); | |
| float* part = (float*) dev(NULL, 4 * (size_t) T * a.n_head * a.n_splits * 130); | |
| kc_attn(0, a, dq, kv, dri, part, (const float*) dev(g, qn), dout, T); | |
| return done(what, o, dout, qn); | |
| } | |
| int kt_rope_attn(AttnArgs a, float* q, const float* k, const float* v, uint16_t* Kc, uint16_t* Vc, int npos, | |
| const RowInfo* ri, const float* inv, const float* g, float* o, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, kn = 4 * (size_t) T * a.n_kv * 128, cn = 2 * (size_t) npos * a.n_kv * 128; | |
| float* dq = (float*) dev(q, qn); | |
| void *dK = dev(Kc, cn), *dV = dev(Vc, cn); | |
| const KvView kv = bf16_kv(dK, dV); | |
| const RowInfo* dri = (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T); | |
| kc_rope_kv(0, dq, (const float*) dev(k, kn), (const float*) dev(v, kn), kv, dri, (const float*) dev(inv, 4 * 64), | |
| a.n_head, a.n_kv, T); | |
| if (fetch("kc_rope_kv", q, dq, qn) || fetch("kc_rope_kv", Kc, dK, cn) || fetch("kc_rope_kv", Vc, dV, cn)) return -1; | |
| return attn(a, dq, kv, dri, g, o, T, "kc_rope_kv + kc_attn"); | |
| } | |
| int kt_attn(AttnArgs a, const float* q, const uint16_t* Kc, const uint16_t* Vc, int npos, const RowInfo* ri, | |
| const float* g, float* o, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, cn = 2 * (size_t) npos * a.n_kv * 128; | |
| return attn(a, (float*) dev(q, qn), bf16_kv(dev(Kc, cn), dev(Vc, cn)), (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), | |
| g, o, T, "kc_attn"); | |
| } | |
| int kt_attn_prefill(AttnArgs a, const float* q, const uint16_t* Kc, const uint16_t* Vc, int npos, const RowInfo* ri, | |
| const float* g, float* o, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, cn = 2 * (size_t) npos * a.n_kv * 128; | |
| float* dout = (float*) dev(NULL, qn); | |
| kc_attn_prefill(0, a, (const float*) dev(q, qn), bf16_kv(dev(Kc, cn), dev(Vc, cn)), | |
| (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), (const float*) dev(g, qn), dout, T); | |
| return done("kc_attn_prefill", o, dout, qn); | |
| } | |
| static KvView q8_kv(const KtKvQ8* kv, int npos, int nkv, void** dk, void** dv, float** dks, float** dvs) { // segment a | |
| const size_t cn = (size_t) npos * nkv * 128, sn = 4 * (size_t) npos * nkv; | |
| *dk = dev(kv->k, cn); *dv = dev(kv->v, cn); | |
| *dks = (float*) dev(kv->ks, sn); *dvs = (float*) dev(kv->vs, sn); | |
| KvView v; | |
| memset(&v, 0, sizeof v); | |
| v.a.k = *dk; v.a.v = *dv; v.a.ks = *dks; v.a.vs = *dvs; | |
| v.b = v.a; | |
| v.nv = 1 << 30; | |
| v.fmt = KV_Q8; | |
| return v; | |
| } | |
| int kt_rope_kv_q8(AttnArgs a, float* q, const float* k, const float* v, KtKvQ8 kv, int npos, const RowInfo* ri, | |
| const float* inv, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, kn = 4 * (size_t) T * a.n_kv * 128; | |
| const size_t cn = (size_t) npos * a.n_kv * 128, sn = 4 * (size_t) npos * a.n_kv; | |
| void *dk, *dv; | |
| float *dks, *dvs; | |
| const KvView view = q8_kv(&kv, npos, a.n_kv, &dk, &dv, &dks, &dvs); | |
| float* dq = (float*) dev(q, qn); | |
| kc_rope_kv(0, dq, (const float*) dev(k, kn), (const float*) dev(v, kn), view, (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), | |
| (const float*) dev(inv, 4 * 64), a.n_head, a.n_kv, T); | |
| if (fetch("kc_rope_kv (Q8)", q, dq, qn) || fetch("kc_rope_kv (Q8)", kv.k, dk, cn) || fetch("kc_rope_kv (Q8)", kv.v, dv, cn) || | |
| fetch("kc_rope_kv (Q8)", kv.ks, dks, sn)) | |
| return -1; | |
| return done("kc_rope_kv (Q8)", kv.vs, dvs, sn); | |
| } | |
| int kt_attn_q8(AttnArgs a, const float* q, KtKvQ8 kv, int npos, const RowInfo* ri, const float* g, float* o, int T) { | |
| void *dk, *dv; | |
| float *dks, *dvs; | |
| const KvView view = q8_kv(&kv, npos, a.n_kv, &dk, &dv, &dks, &dvs); | |
| return attn(a, (float*) dev(q, 4 * (size_t) T * a.n_head * 128), view, (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), g, | |
| o, T, "kc_attn (Q8)"); | |
| } | |
| int kt_attn_prefill_q8(AttnArgs a, const float* q, KtKvQ8 kv, int npos, const RowInfo* ri, const float* g, float* o, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128; | |
| void *dk, *dv; | |
| float *dks, *dvs; | |
| const KvView view = q8_kv(&kv, npos, a.n_kv, &dk, &dv, &dks, &dvs); | |
| float* dout = (float*) dev(NULL, qn); | |
| kc_attn_prefill(0, a, (const float*) dev(q, qn), view, (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), | |
| (const float*) dev(g, qn), dout, T); | |
| return done("kc_attn_prefill (Q8)", o, dout, qn); | |
| } | |
| // ---- MLA --------------------------------------------------------------------------------------------------------------- | |
| int kt_heads_mv(int H, int O, int I, const uint16_t* W, const float* x, int xs, int hs, const float* g, float* y, int T) { | |
| const size_t xn = 4 * ((size_t) (T - 1) * xs + (size_t) (H - 1) * hs + I), yn = 4 * (size_t) T * H * O; | |
| const HmvArgs a = {H, O, I, xs, hs, g != NULL, {0}}; | |
| float* dy = (float*) dev(NULL, yn); | |
| WSlice w; | |
| memset(&w, 0, sizeof w); | |
| w.p[0] = dev(W, 2 * (size_t) H * O * I); | |
| kc_heads_mv(0, a, MF_BF16, w, NULL, (const float*) dev(x, xn), g ? (const float*) dev(g, yn) : NULL, dy, T); | |
| return done("kc_heads_mv", y, dy, yn); | |
| } | |
| // The MLA cache (RoPE keys [npos][128], latents [npos][r]) split in two segments at npos / 2, as the engine's VRAM and | |
| // host rows | |
| static KvView mla_kv(void* K, void* V, int npos, int r) { | |
| KvView kv; | |
| memset(&kv, 0, sizeof kv); | |
| kv.nv = npos / 2; | |
| kv.a.k = K; | |
| kv.a.v = V; | |
| kv.b.k = (uint16_t*) K + (size_t) kv.nv * 128; | |
| kv.b.v = (uint16_t*) V + (size_t) kv.nv * r; | |
| kv.fmt = KV_BF16; | |
| return kv; | |
| } | |
| int kt_mla_attn_x(int n_head, float scale, const int* kb, int nb, const float* qn, const float* qr, const float* kn, | |
| const float* vn, const uint16_t* Kc, int npos, const RowInfo* ri, const float* g, float* o, int T) { | |
| return 1; // Metal only so far | |
| } | |
| int kt_mla_rope(MlaArgs a, float* qr, const float* kr, const float* c, uint16_t* Kc, uint16_t* Vc, int npos, | |
| const RowInfo* ri, const float* inv, int T) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, kn = 2 * (size_t) npos * 128, vn = 2 * (size_t) npos * a.r; | |
| float* dq = (float*) dev(qr, qn); | |
| void *dK = dev(Kc, kn), *dV = dev(Vc, vn); | |
| kc_mla_rope(0, a, dq, (const float*) dev(kr, 4 * (size_t) T * 128), (const float*) dev(c, 4 * (size_t) T * a.r), | |
| mla_kv(dK, dV, npos, a.r), (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), (const float*) dev(inv, 4 * 64), T); | |
| if (fetch("kc_mla_rope", qr, dq, qn) || fetch("kc_mla_rope", Kc, dK, kn)) return -1; | |
| return done("kc_mla_rope", Vc, dV, vn); | |
| } | |
| int kt_mla_attn(MlaArgs a, const float* ql, const float* qr, const uint16_t* Kc, const uint16_t* Vc, int npos, | |
| const RowInfo* ri, float* olat, int T) { | |
| const size_t on = 4 * (size_t) T * a.n_head * a.r; | |
| float* dout = (float*) dev(NULL, on); | |
| kc_mla_attn(0, a, (const float*) dev(ql, on), (const float*) dev(qr, 4 * (size_t) T * a.n_head * 128), | |
| mla_kv(dev(Kc, 2 * (size_t) npos * 128), dev(Vc, 2 * (size_t) npos * a.r), npos, a.r), | |
| (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), (float*) dev(NULL, 4 * (size_t) T * a.n_head * a.n_splits * (a.r + 2)), | |
| dout, T); | |
| return done("kc_mla_attn", olat, dout, on); | |
| } | |
| int kt_mla_prefill(MlaArgs a, const float* q, const float* qr, const uint16_t* Kc, const uint16_t* Vc, int npos, const RowInfo* ri, | |
| const uint16_t* Wql, const uint16_t* Wvu, const float* g, float* o, int T, int dec_keys, uint16_t* Kn_out, | |
| uint16_t* Vd_out) { | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128; | |
| const int group = a.pad[1] == 4 ? 4 : 1, heads = a.n_head / group; | |
| const int kv0 = ri[0].kv0, end = ri[T - 1].pos + 1; | |
| const KvView kv = mla_kv(dev(Kc, 2 * (size_t) npos * 128), dev(Vc, 2 * (size_t) npos * a.r), npos, a.r); | |
| const float *dq = (const float*) dev(q, qn), *dqr = (const float*) dev(qr, qn), *dg = (const float*) dev(g, qn); | |
| const RowInfo* dri = (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T); | |
| const uint16_t* dql = (const uint16_t*) dev(Wql, 2 * (size_t) a.n_head * a.r * 128); | |
| const uint16_t* dvu = (const uint16_t*) dev(Wvu, 2 * (size_t) a.n_head * a.r * 128); | |
| uint16_t* Kn = (uint16_t*) dev(NULL, 2 * (size_t) dec_keys * heads * 128); | |
| uint16_t* Vd = (uint16_t*) dev(NULL, 2 * (size_t) dec_keys * heads * 128); | |
| float* st = (float*) dev(NULL, 4 * (size_t) T * a.n_head * 130); | |
| float* dout = (float*) dev(NULL, qn); | |
| uint16_t* compact = group == 4 ? malloc(2 * (size_t) dec_keys * heads * 128) : NULL; | |
| if (group == 4 && !compact) return -1; | |
| for (int kb0 = 0; kb0 < end; kb0 += dec_keys) { | |
| const int kb1 = end - kb0 < dec_keys ? end : kb0 + dec_keys; | |
| WSlice wq, wv; | |
| memset(&wq, 0, sizeof wq); | |
| memset(&wv, 0, sizeof wv); | |
| wq.p[0] = dql; | |
| wv.p[0] = dvu; | |
| kc_mla_decomp(0, a, kv, kv0, kb0, kb0, kb1, MF_BF16, wq, MF_BF16, wv, Kn, Vd); | |
| const size_t off = (size_t) kb0 * a.n_head * 128, n = 2 * (size_t) (kb1 - kb0) * heads * 128; | |
| // The test API returns all logical heads; the CUDA buffers may store one map per group of4. | |
| for (int which = 0; which < 2; ++which) { | |
| uint16_t* dst = (which ? Vd_out : Kn_out) + off; | |
| if (fetch("kc_mla_decomp", group == 4 ? compact : dst, which ? Vd : Kn, n)) { free(compact); return -1; } | |
| if (group == 4) for (int p = 0; p < kb1 - kb0; ++p) for (int h = 0; h < a.n_head; ++h) | |
| memcpy(dst + ((size_t) p * a.n_head + h) * 128, | |
| compact + ((size_t) p * heads + h / group) * 128, 2 * 128); | |
| } | |
| kc_mla_prefill(0, a, dq, dqr, kv, dri, Kn, Vd, kb0, kb1, kb0 == 0, kb1 == end, st, dg, dout, T); | |
| } | |
| free(compact); | |
| return done("kc_mla_prefill", o, dout, qn); | |
| } | |
| int kt_has_fmt(int fmt) { return fmt >= MF_BF16 && fmt <= MF_SEED4P4; } // no SEED6P8 kernels yet | |
| int kt_embed_seed(const KtWeight* w, const int32_t* ids, int n, float* x) { (void) w; (void) ids; (void) n; (void) x; return 1; } | |
| int kt_embed(int fmt, const uint16_t* E, const uint32_t* q8, const uint16_t* s8, const uint16_t* b8, int V, int d, | |
| const int32_t* ids, int n, float* x) { | |
| WSlice w; | |
| memset(&w, 0, sizeof w); | |
| if (fmt == MF_Q8) { | |
| w.p[0] = dev(q8, (size_t) V * d); | |
| w.p[1] = dev(s8, 2 * (size_t) V * d / 64); | |
| w.p[2] = dev(b8, 2 * (size_t) V * d / 64); | |
| } else w.p[0] = dev(E, 2 * (size_t) V * d); | |
| float* dx = (float*) dev(NULL, 4 * (size_t) n * d); | |
| kc_embed(0, fmt, w, d, (const int32_t*) dev(ids, 4 * (size_t) n), dx, n); | |
| return done("kc_embed", x, dx, 4 * (size_t) n * d); | |
| } | |
| int kt_bf16_f32(const uint16_t* x, float* y, int n) { return 1; } // Metal only so far | |
| int kt_xtx(const float* x, int T, int xs, int D, int nb, float* H) { | |
| const size_t hn = 4 * (size_t) nb * D * D; | |
| float* dH = (float*) dev(H, hn); | |
| kc_xtx(0, (const float*) dev(x, 4 * ((size_t) (T - 1) * xs + (size_t) nb * D)), D, nb, xs, D, T, dH); | |
| return done("kc_xtx", H, dH, hn); | |
| } | |
| // The streams of an H x rows x cols tensor in fmt (BF16 values; Q8 / Q4 codes, then BF16 scales and biases per 64) on | |
| // the device | |
| static WSlice heads_dev(int fmt, int H, size_t n1, const void* codes, const uint16_t* scales, const uint16_t* biases) { | |
| const size_t n = (size_t) H * n1; | |
| WSlice w; | |
| memset(&w, 0, sizeof w); | |
| w.p[0] = dev(codes, fmt == MF_BF16 ? 2 * n : fmt == MF_Q8 ? n : n / 2); | |
| if (fmt != MF_BF16) { w.p[1] = dev(scales, n / 32); w.p[2] = dev(biases, n / 32); } | |
| return w; | |
| } | |
| // Per-head maps by W_h transposed (W [H][I][O], O = 128, I a multiple of 32): the CUDA engine reads weights so only | |
| // inside k_mla_decomp (keys expanded by q_lat^T), so head h's inputs go in as the latent rows of a BF16 cache, and its | |
| // row of the expanded keys Kn[t][h] is y[t][h]. 1 for other shapes. | |
| static int heads_t(int fmt, int H, int O, int I, const void* codes, const uint16_t* scales, const uint16_t* biases, | |
| const float* x, int xs, int hs, float* y, int T) { | |
| if (O != 128 || I % 32) return 1; | |
| uint16_t* c = (uint16_t*) malloc(2 * (size_t) T * I); | |
| uint16_t* kn = (uint16_t*) malloc(2 * (size_t) T * H * 128); | |
| const MlaArgs a = {H, I, 128, 1, 1.0f, {0}}; | |
| int rc = 0; | |
| for (int h = 0; h < H && !rc; ++h) { | |
| for (int t = 0; t < T; ++t) | |
| for (int i = 0; i < I; ++i) { | |
| uint32_t u; | |
| memcpy(&u, &x[(size_t) t * xs + (size_t) h * hs + i], 4); | |
| c[(size_t) t * I + i] = (uint16_t) (u >> 16); // the inputs are BF16 values | |
| } | |
| const WSlice w = heads_dev(fmt, H, (size_t) I * O, codes, scales, biases); | |
| KvView kv; | |
| memset(&kv, 0, sizeof kv); | |
| kv.nv = T; | |
| kv.fmt = KV_BF16; | |
| kv.a.k = dev(NULL, 2 * (size_t) T * 128); | |
| kv.a.v = dev(c, 2 * (size_t) T * I); | |
| uint16_t* dK = (uint16_t*) dev(NULL, 2 * (size_t) T * H * 128), *dV = (uint16_t*) dev(NULL, 2 * (size_t) T * H * 128); | |
| kc_mla_decomp(0, a, kv, 0, 0, 0, T, fmt, w, fmt, w, dK, dV); // the V half reads w as [H][128][I]: not used | |
| rc = done("kc_mla_decomp (transposed per-head maps)", kn, dK, 2 * (size_t) T * H * 128); | |
| for (int t = 0; t < T && !rc; ++t) | |
| for (int o = 0; o < O; ++o) { | |
| const uint32_t u = (uint32_t) kn[((size_t) t * H + h) * 128 + o] << 16; | |
| memcpy(&y[((size_t) t * H + h) * O + o], &u, 4); | |
| } | |
| } | |
| free(c); | |
| free(kn); | |
| return rc; | |
| } | |
| int kt_mla_decomp_q(MlaArgs a, int qfmt, const void* qc, const uint16_t* qs, const uint16_t* qb, int vfmt, const void* vc, | |
| const uint16_t* vs, const uint16_t* vb, const uint16_t* c, int n, uint16_t* Kn, uint16_t* Vd) { | |
| const size_t on = 2 * (size_t) n * a.n_head * 128, hn = (size_t) a.r * 128; | |
| KvView kv; | |
| memset(&kv, 0, sizeof kv); | |
| kv.nv = n; | |
| kv.fmt = KV_BF16; | |
| kv.a.k = dev(NULL, 2 * (size_t) n * 128); | |
| kv.a.v = dev(c, 2 * (size_t) n * a.r); | |
| const WSlice wq = heads_dev(qfmt, a.n_head, hn, qc, qs, qb), wv = heads_dev(vfmt, a.n_head, hn, vc, vs, vb); | |
| uint16_t* dK = (uint16_t*) dev(NULL, on), *dV = (uint16_t*) dev(NULL, on); | |
| kc_mla_decomp(0, a, kv, 0, 0, 0, n, qfmt, wq, vfmt, wv, dK, dV); | |
| if (fetch("kc_mla_decomp (Q8 / Q4 maps)", Kn, dK, on)) return -1; | |
| return done("kc_mla_decomp (Q8 / Q4 maps)", Vd, dV, on); | |
| } | |
| int kt_heads_mm_t(int H, int O, int I, const uint16_t* W, const float* x, int xs, int hs, float* y, int T) { | |
| return heads_t(MF_BF16, H, O, I, W, NULL, NULL, x, xs, hs, y, T); | |
| } | |
| int kt_heads_q(int fmt, int tr, int H, int O, int I, const void* codes, const uint16_t* scales, const uint16_t* biases, | |
| const float* x, int xs, int hs, const float* g, float* y, int T) { | |
| if (tr) return heads_t(fmt, H, O, I, codes, scales, biases, x, xs, hs, y, T); | |
| const size_t xn = 4 * ((size_t) (T - 1) * xs + (size_t) (H - 1) * hs + I), yn = 4 * (size_t) T * H * O; | |
| const HmvArgs a = {H, O, I, xs, hs, g != NULL, {0}}; | |
| float* dy = (float*) dev(NULL, yn); | |
| kc_heads_mv(0, a, fmt, heads_dev(fmt, H, (size_t) O * I, codes, scales, biases), NULL, (const float*) dev(x, xn), | |
| g ? (const float*) dev(g, yn) : NULL, dy, T); // T > 8: the prompt GEMM | |
| return done("kc_heads_mv (Q8 / Q4)", y, dy, yn); | |
| } | |
| // An FP8 / FP4 MLA cache (nslm/kvq.h: the RoPE key all FP8; the latent FP8, or FP4 past its first KVQ_FP4_LEAD values), | |
| // codes and scales on the device, split in two segments at npos / 2 as mla_kv | |
| static int mla_lead_(int fmt, int r) { return fmt == KV_FP4 && r > KVQ_FP4_LEAD ? KVQ_FP4_LEAD : r; } | |
| static KtKvMla mla_kvq_dev(int fmt, KtKvMla h, int npos, int r) { | |
| const int ld = mla_lead_(fmt, r); | |
| KtKvMla d = {(uint8_t*) dev(h.k, (size_t) npos * 128), (uint8_t*) dev(h.v, (size_t) npos * kvq_row_bytes(ld, r)), | |
| (uint8_t*) dev(h.ks, (size_t) npos * 4), (uint8_t*) dev(h.vs, (size_t) npos * kvq_row_scales(ld, r))}; | |
| return d; | |
| } | |
| static KvView mla_kvq(int fmt, KtKvMla d, int npos, int r) { | |
| const int ld = mla_lead_(fmt, r), nv = npos / 2; | |
| KvView kv; | |
| memset(&kv, 0, sizeof kv); | |
| kv.nv = nv; | |
| kv.fmt = fmt; | |
| kv.a.k = d.k; kv.a.v = d.v; kv.a.ks = (float*) d.ks; kv.a.vs = (float*) d.vs; | |
| kv.b.k = d.k + (size_t) nv * 128; | |
| kv.b.v = d.v + (size_t) nv * kvq_row_bytes(ld, r); | |
| kv.b.ks = (float*) (d.ks + (size_t) nv * 4); | |
| kv.b.vs = (float*) (d.vs + (size_t) nv * kvq_row_scales(ld, r)); | |
| return kv; | |
| } | |
| int kt_mla_rope_q(MlaArgs a, int fmt, float* qr, const float* kr, const float* c, KtKvMla kv, int npos, const RowInfo* ri, | |
| const float* inv, int T) { | |
| const int ld = mla_lead_(fmt, a.r); | |
| const size_t qn = 4 * (size_t) T * a.n_head * 128, vb = (size_t) npos * kvq_row_bytes(ld, a.r), vsb = (size_t) npos * kvq_row_scales(ld, a.r); | |
| float* dq = (float*) dev(qr, qn); | |
| const KtKvMla d = mla_kvq_dev(fmt, kv, npos, a.r); | |
| kc_mla_rope(0, a, dq, (const float*) dev(kr, 4 * (size_t) T * 128), (const float*) dev(c, 4 * (size_t) T * a.r), | |
| mla_kvq(fmt, d, npos, a.r), (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), (const float*) dev(inv, 4 * 64), T); | |
| if (fetch("kc_mla_rope (quantized)", qr, dq, qn) || fetch("kc_mla_rope (quantized)", kv.k, d.k, (size_t) npos * 128) || | |
| fetch("kc_mla_rope (quantized)", kv.ks, d.ks, (size_t) npos * 4) || fetch("kc_mla_rope (quantized)", kv.v, d.v, vb)) | |
| return -1; | |
| return done("kc_mla_rope (quantized)", kv.vs, d.vs, vsb); | |
| } | |
| int kt_mla_attn_q(MlaArgs a, int fmt, const float* ql, const float* qr, KtKvMla kv, int npos, const RowInfo* ri, float* olat, int T) { | |
| const size_t on = 4 * (size_t) T * a.n_head * a.r; | |
| float* dout = (float*) dev(NULL, on); | |
| kc_mla_attn(0, a, (const float*) dev(ql, on), (const float*) dev(qr, 4 * (size_t) T * a.n_head * 128), | |
| mla_kvq(fmt, mla_kvq_dev(fmt, kv, npos, a.r), npos, a.r), (const RowInfo*) dev(ri, sizeof(RowInfo) * (size_t) T), | |
| (float*) dev(NULL, 4 * (size_t) T * a.n_head * a.n_splits * (a.r + 2)), dout, T); | |
| return done("kc_mla_attn (quantized)", olat, dout, on); | |
| } | |
| int kt_mla_attn_xq(int fmt, int n_head, float scale, const int* kb, int nb, const float* qn, const float* qr, const float* kn, | |
| const float* vn, const uint8_t* Kq, const uint8_t* Ks, int npos, const RowInfo* ri, const float* g, float* o, int T) { return 1; } | |
| int kt_kv_f32(int fmt, const uint8_t* codes, const uint8_t* scales, int len, int rows, float* y) { | |
| const int ld = mla_lead_(fmt, len); | |
| float* dy = (float*) dev(NULL, 4 * (size_t) len * rows); | |
| kc_kv_f32(0, fmt, (const uint8_t*) dev(codes, (size_t) rows * kvq_row_bytes(ld, len)), | |
| (const uint8_t*) dev(scales, (size_t) rows * kvq_row_scales(ld, len)), len, rows, dy); | |
| return done("kc_kv_f32", y, dy, 4 * (size_t) len * rows); | |
| } | |
| int kt_argmax(const float* logits, int V, int n, int32_t* out) { | |
| int32_t* d = (int32_t*) dev(NULL, 4 * (size_t) n); | |
| kc_argmax(0, (const float*) dev(logits, 4 * (size_t) V * n), d, V, n); | |
| return done("kc_argmax", out, d, 4 * (size_t) n); | |
| } | |
| // SEED4P4 per-head maps as the engine runs them: decode rows (T <= 8, tr 0) inline in k_heads_mv; prompt rows from the | |
| // map decoded to BF16 once (kc_heads_deq), then the BF16 GEMM (tr 0) or k_mla_decomp's transposed reads (tr 1) | |
| int kt_heads_seed8(int tr, int H, int O, int I, const uint16_t* seeds, const uint32_t* coefs, const int32_t* ebias, | |
| const uint8_t* ecodes, const float* x, int xs, int hs, const float* g, float* y, int T) { | |
| (void) tr; (void) H; (void) O; (void) I; (void) seeds; (void) coefs; (void) ebias; (void) ecodes; (void) x; (void) xs; (void) hs; | |
| (void) g; (void) y; (void) T; | |
| return 1; // no SEED6P8 kernels yet | |
| } | |
| int kt_heads_seed(int tr, int H, int O, int I, const uint16_t* seeds, const uint16_t* coefs, const int32_t* ebias, | |
| const uint8_t* ecodes, const float* x, int xs, int hs, const float* g, float* y, int T) { | |
| const int rows = tr ? I : O, cols = tr ? O : I; | |
| const size_t n = (size_t) H * O * I, nb = n / 8; | |
| const size_t xn = 4 * ((size_t) (T - 1) * xs + (size_t) (H - 1) * hs + I), yn = 4 * (size_t) T * H * O; | |
| const HmvArgs a = {H, O, I, xs, hs, g != NULL, {0}}; | |
| WSlice w; | |
| memset(&w, 0, sizeof w); | |
| w.p[0] = dev(seeds, 2 * nb); | |
| w.p[1] = dev(coefs, 2 * nb); | |
| w.p[2] = dev(ebias, 4 * (size_t) H); | |
| w.p[3] = dev(ecodes, nb / 2); | |
| memcpy(&w.eb, ebias, 4); | |
| if (!tr && T <= 8) { | |
| float* dy = (float*) dev(NULL, yn); | |
| kc_heads_mv(0, a, MF_SEED4P4, w, g_stab32, (const float*) dev(x, xn), g ? (const float*) dev(g, yn) : NULL, dy, T); | |
| return done("kc_heads_mv (SEED4P4)", y, dy, yn); | |
| } | |
| uint16_t* d = (uint16_t*) dev(NULL, 2 * n); | |
| kc_heads_deq(0, MF_SEED4P4, w, g_stab32, H, rows, cols, d); | |
| if (!tr) { | |
| WSlice b; | |
| memset(&b, 0, sizeof b); | |
| b.p[0] = d; | |
| float* dy = (float*) dev(NULL, yn); | |
| kc_heads_mv(0, a, MF_BF16, b, NULL, (const float*) dev(x, xn), g ? (const float*) dev(g, yn) : NULL, dy, T); | |
| return done("kc_heads_deq + kc_heads_mm", y, dy, yn); | |
| } | |
| uint16_t* hw = (uint16_t*) malloc(2 * n); | |
| int rc = done("kc_heads_deq", hw, d, 2 * n); | |
| if (!rc) rc = heads_t(MF_BF16, H, O, I, hw, NULL, NULL, x, xs, hs, y, T); | |
| free(hw); | |
| return rc; | |
| } | |
| // ---- qwen4 ----------------------------------------------------------------------------------------------------------- | |
| int kt_q4_gdn(const Qwen4Cfg* c, const Qwen4GdnW* w, const float* qkv, const float* z, const float* a, const float* b, | |
| float* conv, float* S, float* y, int T) { | |
| const size_t cd = (size_t) 2 * c->lin_hk * c->lin_dk + (size_t) c->lin_hv * c->lin_dv, vd = (size_t) c->lin_hv * c->lin_dv; | |
| const size_t nc = qwen4_gdn_conv_len(c), ns = qwen4_gdn_state_len(c), hv = (size_t) c->lin_hv; | |
| const Q4GdnDev dw = {(const float*) dev(w->conv_w, 4 * cd * c->lin_conv), (const float*) dev(w->A_log, 4 * hv), | |
| (const float*) dev(w->dt_bias, 4 * hv), (const float*) dev(w->norm_w, 4 * (size_t) c->lin_dv)}; | |
| float* dconv = (float*) dev(conv, 4 * nc); | |
| float* dS = (float*) dev(S, 4 * ns); | |
| float* dy = (float*) dev(NULL, 4 * vd * T); | |
| if (kq4_gdn(0, c, dw, (const float*) dev(qkv, 4 * cd * T), (const float*) dev(z, 4 * vd * T), (const float*) dev(a, 4 * hv * T), | |
| (const float*) dev(b, 4 * hv * T), dconv, dS, (float*) dev(NULL, 4 * cd * T), dy, T)) { | |
| done("kq4_gdn", NULL, NULL, 0); | |
| printf("FAIL: kq4_gdn: head widths not supported\n"); | |
| return -1; | |
| } | |
| if (fetch("kq4_gdn conv", conv, dconv, 4 * nc) || fetch("kq4_gdn state", S, dS, 4 * ns)) return -1; | |
| return done("kq4_gdn", y, dy, 4 * vd * T); | |
| } | |
| int kt_q4_norm1p(const float* x, int len, int g, const float* w, float eps, float* y, int T) { | |
| float* dy = (float*) dev(NULL, 4 * (size_t) len * T); | |
| kq4_norm1p(0, (const float*) dev(x, 4 * (size_t) len * T), len, g, (const float*) dev(w, 4 * (size_t) len), eps, dy, T); | |
| return done("kq4_norm1p", y, dy, 4 * (size_t) len * T); | |
| } | |
| int kt_q4_hc(const Qwen4Cfg* c, float* lo, const float* normed, const float* up, float* mixed, float* inj, float* H, | |
| const float* branch, int T) { | |
| const size_t HD = (size_t) c->hc * c->d, nl = (size_t) c->hc_rank * T, ni = (size_t) c->hc * T; | |
| float* dlo = (float*) dev(lo, 4 * nl); | |
| float* dmix = (float*) dev(NULL, 4 * (size_t) c->d * T); | |
| float* dinj = (float*) dev(inj, 4 * ni); | |
| float* dH = (float*) dev(H, 4 * HD * T); | |
| kq4_hc_act(0, c->hc, dlo, (int) nl); | |
| kq4_hc_mix(0, c->hc, c->d, (const float*) dev(normed, 4 * HD * T), (const float*) dev(up, 4 * HD * T), dmix, T); | |
| kq4_hc_gate(0, c->hc, dinj, (int) ni); | |
| kq4_hc_add(0, c->hc, c->d, dH, (const float*) dev(branch, 4 * (size_t) c->d * T), dinj, T); | |
| if (fetch("kq4_hc_act", lo, dlo, 4 * nl) || fetch("kq4_hc_mix", mixed, dmix, 4 * (size_t) c->d * T) || | |
| fetch("kq4_hc_gate", inj, dinj, 4 * ni)) | |
| return -1; | |
| return done("kq4_hc_add", H, dH, 4 * HD * T); | |
| } | |
| int kt_q4_head_norm_rope(const Qwen4Cfg* c, float* x, int rs, int nh, int hs, int hd, const float* w, int pos0, int T) { | |
| float* dx = (float*) dev(x, 4 * (size_t) rs * T); | |
| kq4_head_norm_rope(0, c, dx, rs, nh, hs, hd, (const float*) dev(w, 4 * (size_t) hd), pos0, NULL, T); | |
| return done("kq4_head_norm_rope", x, dx, 4 * (size_t) rs * T); | |
| } | |
| int kt_q4_route(const Qwen4Cfg* c, const float* logits, int32_t* ind, float* wt, int T) { | |
| int32_t* di = (int32_t*) dev(NULL, 4 * (size_t) T * c->top_k); | |
| float* dw = (float*) dev(NULL, 4 * (size_t) T * c->top_k); | |
| kq4_route(0, c, (const float*) dev(logits, 4 * (size_t) T * c->n_exp), di, dw, T); | |
| if (fetch("kq4_route", ind, di, 4 * (size_t) T * c->top_k)) return -1; | |
| return done("kq4_route", wt, dw, 4 * (size_t) T * c->top_k); | |
| } | |
| int kt_q4_ple(const Qwen4Cfg* c, const float* keys, const float* qs, const float* vals, float* gated, const float* w, const float* cin, | |
| float* hist, float* H, int T) { | |
| const size_t HD = (size_t) c->hc * c->d, nh = qwen4_ple_hist_len(c); | |
| float* dg = (float*) dev(NULL, 4 * HD * T); | |
| float* dh = (float*) dev(hist, 4 * nh); | |
| float* dH = (float*) dev(H, 4 * HD * T); | |
| kq4_ple_gate(0, c, (const float*) dev(keys, 4 * HD * T), (const float*) dev(qs, 4 * HD * T), (const float*) dev(vals, 4 * (size_t) c->d * T), dg, T); | |
| kq4_ple_conv(0, c, (const float*) dev(w, 4 * HD * c->ple_conv), (const float*) dev(cin, 4 * HD * T), dg, dh, dH, T); | |
| if (fetch("kq4_ple_gate", gated, dg, 4 * HD * T) || fetch("kq4_ple_conv hist", hist, dh, 4 * nh)) return -1; | |
| return done("kq4_ple_conv", H, dH, 4 * HD * T); | |
| } | |
| int kt_q4_qsa(const Qwen4Cfg* c, const float* raw, const float* w, int nb, float* blk, const float* iq, int rs, const int32_t* done_, | |
| int32_t* sel, int T) { | |
| const int D = c->ix_dim, k = c->ix_budget / c->ix_ratio; | |
| int maxdone = 0; | |
| for (int t = 0; t < T; ++t) if (done_[t] > maxdone) maxdone = done_[t]; | |
| float* db = (float*) dev(NULL, 4 * (size_t) nb * D); | |
| int32_t* ds = (int32_t*) dev(sel, 4 * (size_t) T * k); | |
| kq4_qsa_blocks(0, c, (const float*) dev(raw, 4 * (size_t) nb * c->ix_ratio * D), (const float*) dev(w, 4 * (size_t) D), 0, NULL, | |
| nb * c->ix_ratio, db); | |
| kq4_qsa_select(0, c, (const float*) dev(iq, 4 * (size_t) rs * T), rs, db, (const int32_t*) dev(done_, 4 * (size_t) T), maxdone, | |
| (float*) dev(NULL, 4 * (size_t) T * (maxdone ? maxdone : 1)), ds, T); | |
| if (fetch("kq4_qsa_blocks", blk, db, 4 * (size_t) nb * D)) return -1; | |
| return done("kq4_qsa_select", sel, ds, 4 * (size_t) T * k); | |
| } | |
| int kt_q4_attend(const Qwen4Cfg* c, const float* q, const float* K, const float* V, int npos, int pos0, const int32_t* sel, | |
| const int32_t* nsel, float* o, int T) { | |
| const size_t kv = (size_t) npos * c->n_kv * c->head_dim, no = (size_t) T * c->n_head * c->head_dim; | |
| float* dout = (float*) dev(NULL, 4 * no); | |
| if (kq4_attend(0, c, (const float*) dev(q, 4 * no * 2), (const float*) dev(K, 4 * kv), (const float*) dev(V, 4 * kv), pos0, NULL, | |
| (const int32_t*) dev(sel, 4 * (size_t) T * (c->ix_budget / c->ix_ratio)), (const int32_t*) dev(nsel, 4 * (size_t) T), dout, T)) { | |
| done("kq4_attend", NULL, NULL, 0); | |
| printf("FAIL: kq4_attend: head_dim not supported\n"); | |
| return -1; | |
| } | |
| return done("kq4_attend", o, dout, 4 * no); | |
| } | |
Xet Storage Details
- Size:
- 37.2 kB
- Xet hash:
- 3a5a7a254a6bdc5949960dc2b9ceb162bea45204193fb3f38c9a46e1b2381994
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.