Expand description
Encoder GPU kernels (WGSL) — the stateless full-sequence compute path for embedding models.
Distinct from the decoder kernels in forward.rs by design: encoders are single-shot over a
known ragged batch (no KV pool, no block tables, no ring buffers), weights are f16/f32 (never
Q4 — encoder models are small and precision-sensitive), and attention is a mask MODE
(bidirectional / causal / local-window) over exact seq_starts ranges rather than paged
causal decode. Every kernel here is parity-tested against the encoder_cpu oracle
(tolerance-gated, never bitwise — see tests/encoder_kernels.rs).
Structs§
- EncKernels
- The compiled encoder kernel set. Built once per device; the f16 GEMM twin exists only when
the adapter reports
SHADER_F16. - Encoder
Gpu - The GPU encoder: a prebuilt dispatch plan (bind groups + uniforms baked at load) over
persistent scratch sized
max_tokens, replayed per batch with only the token-count uniform fields and the ragged-batch buffers rewritten. One submit + one readback perencode.
Constants§
- MAX_
VISION_ BATCH - Max images per batched SigLIP vision replay (
run_layers_gpu_batched). The plan scratch is sizedMAX_VISION_BATCH · n_patches; callers chunk larger batches. 32 × 196 patches × 768 hidden ≈ 19 MB per scratch buffer — negligible VRAM, large dispatch-overhead amortization.
Functions§
- enc_
gemm2_ src - GEMM v2 — the same contract as [
enc_gemm_src] (y[m,n] = act(x[m,k] · Wᵀ + bias), same bindings, sameMeta), register-blocked. - enc_
gemm3_ f16a_ src - GEMM v3-f16a — the v3 structure with F16 ARITHMETIC (the P7 contingency): x is converted to
f16 ONCE at staging, the weight is stored f16
[k,n]-transposed, and the inner FMAs and accumulators are f16 — Apple’s shader ALUs run f16 at twice the f32 rate, which is the whole remaining edge MPSGraph holds at the small/mid shapes. The EPILOGUE is f32 (bias, activation, store), so downstream buffers keep their type. - enc_
gemm3_ sk_ f16a_ src - The f16-ARITHMETIC split-K partial: the same patch over
enc_gemm3_f16a_src— its epilogue already storesf32(acc), so the partials stay f32 and the SAME reduce kernel serves both precisions. - enc_
gemm3_ sk_ src - GEMM v3 SPLIT-K — the occupancy answer for the mid-band widths (n = 768/1024-class) at
SMALL m, where no tile has enough output blocks to fill the GPU (m=64 × n=768 at (16,64) is
48 workgroups on a ~40-core machine). The reduction dimension supplies the missing
parallelism:
grid.zchunks each own a contiguous run of k-tiles and write RAW partials (y_part[c][m,n], no bias, no activation); [ENC_GEMM3_SK_REDUCE] then folds the chunks in fixed order and applies the epilogue. Chunk count isnum_workgroups.z(one pipeline serves any split), the tile is the same vec4/[k,n]-transposed body asenc_gemm3_src, and a chunk whose window falls pastkwrites zeros — never stale scratch. - enc_
gemm3_ src - GEMM v3 — vec4 shared memory, on a PRE-TRANSPOSED weight.
- enc_
gemm4_ f16_ src - GEMM v4 — HARDWARE MATRIX UNITS (
EXPERIMENTAL_COOPERATIVE_MATRIX). - enc_
gemm4_ f16w_ src - v4-f16 over an f16-STORED
[k,n]weight (the f16a upload layout) — w binds asarray<f16>, no conversion at stage; x still converts once. - enc_
gemm4_ src - gelu_
tanh_ on_ gpu - Run the GPU’s
apply_act(tanh-GELU) overxs— the isolation harness for the overflow regression test. The activation is normally FUSED into the GEMM epilogue, which is exactly why it went unnoticed that it could return NaN: nothing ever exercised it on its own. - gemm2_
tile - The row-tile height for an
[m, n]output: the LARGEST tile (best register blocking) whose grid still fills the GPU, else the smallest.