mlx-bun 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -32
- package/THIRD_PARTY_LICENSES.md +10 -0
- package/docs/archive/mlx-bun-lab-report.html +275 -0
- package/docs/archive/wwdc-mlx-bun.html +848 -0
- package/docs/design/audio-input-plan.md +356 -0
- package/docs/design/batching-perf-path.md +58 -35
- package/docs/design/batching-v2-plan.md +23 -9
- package/docs/design/bucketing-stage.md +2 -1
- package/docs/design/colibri-glm52-port.md +1479 -0
- package/docs/design/compat-cli-surface-design.md +4 -3
- package/docs/design/decode-speed-program.md +172 -0
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dspark-serving-program.md +371 -0
- package/docs/design/dspark-speculative-decoding.md +167 -5
- package/docs/design/faithful-l1-consolidation.md +310 -0
- package/docs/design/generic-model-support.md +8 -5
- package/docs/design/grammar-spec-batching-integration.md +301 -0
- package/docs/design/minicpm5-decode-megakernel.md +16 -11
- package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
- package/docs/design/omlx-adoption-map.md +14 -6
- package/docs/design/orpo-training.md +34 -30
- package/docs/design/paged-kv-cache.md +214 -0
- package/docs/design/parallel-slots.md +82 -49
- package/docs/design/parity-tier-dag.md +43 -33
- package/docs/design/pre-colibri-stabilization.md +329 -0
- package/docs/design/repo-cleanup-plan.md +189 -0
- package/docs/design/runtime-isolation.md +19 -3
- package/docs/design/segmented-backward-training.md +3 -2
- package/docs/design/ssd-kv-cold-tier.md +130 -0
- package/docs/design/structured-output.md +484 -0
- package/docs/design/the-dreaming-master-plan.md +2 -3
- package/docs/design/turboquant-kv.md +235 -0
- package/docs/design/unified-engine-frontier-plan.md +774 -0
- package/docs/design/web-chat-beat-matrix.md +315 -0
- package/docs/design/web-chat-redesign.md +1112 -0
- package/docs/investigations/colibri-oracle-pin.md +1284 -0
- package/docs/investigations/dspark-handoff.md +182 -16
- package/docs/investigations/indexshare-performance-spike.md +357 -0
- package/docs/investigations/lab-build-journal.md +2 -2
- package/docs/investigations/openwiki-evaluation.md +249 -0
- package/docs/planning/IDEAS.md +14 -0
- package/docs/planning/PRODUCT_ROADMAP.md +2 -3
- package/docs/planning/chat-ui-vision-memory-first.md +199 -0
- package/docs/planning/chat-ui-vision.md +50 -0
- package/docs/planning/memory-docs-and-dag-plan.md +15 -6
- package/docs/planning/release-notes-v0.0.11.md +239 -0
- package/docs/planning/release-notes-v0.0.12.md +80 -0
- package/docs/reference/cli.md +63 -18
- package/docs/reference/distribution.md +37 -6
- package/docs/reference/embedding.md +40 -16
- package/docs/reference/features-matrix.md +99 -0
- package/docs/reference/library-api.md +69 -20
- package/docs/reference/memory.md +26 -6
- package/docs/reference/models.md +60 -3
- package/docs/reference/orpo-quickstart.md +3 -4
- package/docs/reference/server-api.md +717 -24
- package/docs/reference/server-config.md +265 -129
- package/docs/reference/training.md +24 -16
- package/package.json +5 -3
- package/src/anthropic.ts +67 -44
- package/src/audio/conformer.ts +647 -0
- package/src/audio/decode.ts +133 -0
- package/src/audio/features.ts +202 -0
- package/src/audio/transcode.ts +67 -0
- package/src/chat-template.ts +155 -10
- package/src/cli.ts +483 -113
- package/src/config.ts +116 -24
- package/src/diffusion/diffusion-generate.ts +14 -8
- package/src/download.ts +143 -12
- package/src/eval/README.md +7 -6
- package/src/eval/ifeval.ts +63 -120
- package/src/eval/kl.ts +59 -3
- package/src/eval/quality-db.ts +2 -2
- package/src/eval/runner.ts +17 -8
- package/src/eval/tasks/ifeval.ts +167 -31
- package/src/expert-io.ts +391 -0
- package/src/expert-offload.ts +15 -2
- package/src/expert-residency.ts +1134 -0
- package/src/expert-usage.ts +415 -0
- package/src/flags.ts +13 -0
- package/src/generate.ts +438 -35
- package/src/grammar.ts +585 -0
- package/src/hf-push.ts +1 -1
- package/src/hub-rest.ts +242 -0
- package/src/index.ts +14 -2
- package/src/jobs/runner.ts +37 -18
- package/src/kv-store.ts +586 -93
- package/src/lora.ts +85 -14
- package/src/md-modules.d.ts +42 -0
- package/src/media-fetch.ts +426 -0
- package/src/memory/rest.ts +245 -0
- package/src/memory/vault.ts +74 -0
- package/src/mlx/array.ts +149 -19
- package/src/mlx/autograd.ts +53 -16
- package/src/mlx/checkpoint.ts +46 -13
- package/src/mlx/custom-vjp.ts +83 -35
- package/src/mlx/ffi.ts +27 -3
- package/src/mlx/metal-kernel.ts +7 -4
- package/src/mlx/ops.ts +153 -23
- package/src/mlx/turboquant-ops.ts +831 -0
- package/src/mlx/turboquant-tables.ts +237 -0
- package/src/model/batched-mask.ts +62 -1
- package/src/model/batched-quant.ts +206 -0
- package/src/model/batched-rotating-quant.ts +346 -0
- package/src/model/batched-rotating.ts +46 -1
- package/src/model/compiled-decode.ts +4 -15
- package/src/model/factory.ts +103 -6
- package/src/model/gemma4-base.ts +395 -39
- package/src/model/gemma4.ts +122 -33
- package/src/model/generated/gemma4-12b.ts +9 -13
- package/src/model/generated/gemma4-26b.ts +10 -5
- package/src/model/generated/gemma4-e4b.ts +14 -9
- package/src/model/glm52-atlas.ts +511 -0
- package/src/model/glm52-cache.ts +706 -0
- package/src/model/glm52-config.ts +262 -0
- package/src/model/glm52-container.ts +305 -0
- package/src/model/glm52-coupling.ts +388 -0
- package/src/model/glm52-dsa.ts +602 -0
- package/src/model/glm52-expert-layout.ts +253 -0
- package/src/model/glm52-layout.ts +152 -0
- package/src/model/glm52-memory.ts +473 -0
- package/src/model/glm52-mla.ts +786 -0
- package/src/model/glm52-moe.ts +223 -0
- package/src/model/glm52-pilot.ts +537 -0
- package/src/model/glm52-reference.ts +445 -0
- package/src/model/glm52-residency.ts +557 -0
- package/src/model/glm52-resident-weights.ts +135 -0
- package/src/model/glm52-streamed-experts.ts +453 -0
- package/src/model/glm52-streamed-metal.ts +610 -0
- package/src/model/glm52-weights.ts +503 -0
- package/src/model/glm52.ts +1153 -0
- package/src/model/minicpm5.ts +58 -102
- package/src/model/paged-kv.ts +253 -0
- package/src/model/qwen3-delta.ts +76 -15
- package/src/model/qwen3-moe.ts +404 -0
- package/src/model/qwen3.ts +9 -8
- package/src/model/qwen3_5.ts +88 -23
- package/src/model/support.ts +19 -1
- package/src/model/universal/dense.ts +40 -5
- package/src/model/universal/rope.ts +36 -21
- package/src/native/expert_io.c +523 -0
- package/src/native-pack.ts +6 -4
- package/src/pi-web.ts +1299 -82
- package/src/preflight.ts +27 -8
- package/src/prompt-cache.ts +293 -33
- package/src/quantize/quantizer.ts +16 -1
- package/src/registry.ts +57 -2
- package/src/responses.ts +62 -49
- package/src/serve/batch-scheduler.ts +1050 -78
- package/src/serve/generation-gateway.ts +252 -24
- package/src/serve/isolate.ts +417 -0
- package/src/serve/lane-registry.ts +44 -0
- package/src/serve/session-search.ts +326 -0
- package/src/server.ts +1701 -175
- package/src/spec/assistant-source.ts +150 -0
- package/src/spec/deepspec-source.ts +143 -0
- package/src/spec/dflash-source.ts +124 -0
- package/src/spec/dspark/ab-stats.ts +136 -0
- package/src/spec/dspark/calibration.ts +112 -0
- package/src/spec/dspark/deepspec-module.ts +801 -0
- package/src/spec/dspark/generate-dflash.ts +72 -20
- package/src/spec/dspark/loader.ts +23 -0
- package/src/spec/dspark/module-dflash.ts +278 -36
- package/src/spec/dspark/quantize-drafter.ts +91 -0
- package/src/spec/generate.ts +59 -25
- package/src/spec/glm52-mtp-source.ts +392 -0
- package/src/spec/ngram-source.ts +132 -0
- package/src/spec/serve-loop.ts +617 -0
- package/src/spec/source.ts +118 -0
- package/src/spec/two-model.ts +171 -0
- package/src/ssd-cache.ts +111 -35
- package/src/tokenizer.ts +288 -2
- package/src/tool-approvals.ts +131 -0
- package/src/tool-call.ts +438 -22
- package/src/train/flash-cce.ts +2 -2
- package/src/train/job.ts +0 -12
- package/src/train/lora-params.ts +17 -6
- package/src/train/trainer.ts +60 -30
- package/src/vision/prompt.ts +217 -48
- package/src/web/app.html +1197 -1791
- package/src/web/app.js +5694 -0
- package/src/web/icon.svg +13 -0
- package/src/web/manifest.webmanifest +24 -0
- package/src/web/src/adapters-panel.ts +288 -0
- package/src/web/src/api.ts +57 -0
- package/src/web/src/assistant.ts +485 -0
- package/src/web/src/chat.ts +1050 -0
- package/src/web/src/composer.ts +959 -0
- package/src/web/src/dataset.ts +132 -0
- package/src/web/src/finetune.ts +267 -0
- package/src/web/src/hub.ts +428 -0
- package/src/web/src/main.ts +98 -0
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +572 -0
- package/src/web/src/model-picker.ts +159 -0
- package/src/web/src/palette.ts +346 -0
- package/src/web/src/protocol.ts +62 -0
- package/src/web/src/quantize.ts +167 -0
- package/src/web/src/rag.ts +274 -0
- package/src/web/src/sessions.ts +469 -0
- package/src/web/src/shell.ts +804 -0
- package/src/web/src/status.ts +300 -0
- package/src/web/src/ui-catalog.ts +92 -0
- package/src/web/sw.js +71 -0
- package/src/web/vendor/README.md +61 -0
- package/src/web/vendor/hljs-LICENSE +29 -0
- package/src/web/vendor/hljs-theme.css +59 -0
- package/src/web/vendor/hljs.js +26 -0
- package/src/web-tools.ts +54 -2
- package/src/weights.ts +82 -19
- package/src/model/fused-decode-kernel.ts +0 -312
- package/src/model/fused-geglu-kernel.ts +0 -198
- package/src/model/fused-mlp-kernel.ts +0 -169
- package/src/model/fused-swiglu-kernel.ts +0 -240
- package/src/model/steel-linear-kernel.ts +0 -96
- package/src/status-page.html +0 -497
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// Fused SwiGLU MLP expansion kernel (cpm5-generate-and-fuse-inference): compute
|
|
2
|
-
// hidden = silu(gate_proj(x)) · up_proj(x)
|
|
3
|
-
// in ONE Metal dispatch, reusing MLX's verbatim steel quantized BlockMMA GEMM
|
|
4
|
-
// (src/train/steel-qmm-header.ts — the same building block the ORPO flash-CCE
|
|
5
|
-
// head uses). Replaces the spelled-out path's two quantized_matmul dispatches +
|
|
6
|
-
// the gate/up/silu/product global-memory materialization (minicpm5.ts LlamaMLP):
|
|
7
|
-
// gate = qmm(x, Wg); up = qmm(x, Wu); silu = gate·σ(gate); hidden = silu·up
|
|
8
|
-
// down_proj is LEFT as a separate qmm so the `hidden` boundary stays bit-exact —
|
|
9
|
-
// the existing minicpm5 L1/L2 parity goldens (maxDiff===0) gate this path directly
|
|
10
|
-
// (their 6-token prompt prefill is an M>1 MLP call).
|
|
11
|
-
//
|
|
12
|
-
// WHERE THE WIN IS: prefill / M>1, where the BlockMMA GEMM is the right tool and
|
|
13
|
-
// the [M, intermediate] materialization is real. At decode (M=1) the matmuls are
|
|
14
|
-
// memory-bound GEMVs (reading Wg/Wu dominates), so the fused kernel only saves the
|
|
15
|
-
// per-token dispatch + a tiny [1,I] round-trip — the caller falls back to unfused
|
|
16
|
-
// below M_MIN. Benchmarks (prefill AND decode) decide the default.
|
|
17
|
-
//
|
|
18
|
-
// PRECISION (two variants, env-flagged — "flag and try both"):
|
|
19
|
-
// - MATCH (default): T = the activation dtype (bf16/f16). The gate/up tiles are
|
|
20
|
-
// stored bf16-rounded out of the float BlockMMA accumulator, exactly as MLX's
|
|
21
|
-
// quantized_matmul casts its float accum to the input dtype; σ/·/· then run
|
|
22
|
-
// op-for-op with ops.sigmoid+ops.mul+ops.mul on bf16. AIM: maxDiff===0 vs the
|
|
23
|
-
// unfused path (like the fused-gelu kernel, kl=0). Only residual: the metal
|
|
24
|
-
// exp() vs MLX's math lib — quantified by the parity test.
|
|
25
|
-
// - F32 (MLX_BUN_FUSED_SWIGLU_F32=1): keep gate/up/silu in f32 through the fusion,
|
|
26
|
-
// round to T only at the final store. More accurate, NOT bit-exact → L3, gated
|
|
27
|
-
// by teacher-forced KL like the fused-decode kernel.
|
|
28
|
-
//
|
|
29
|
-
// Never inside a compiled-decode trace: a CustomKernel has no output_shapes (same
|
|
30
|
-
// rule as fusedDecodeSdpa / fusedGeglu) — the caller guards on !isCompiledTrace().
|
|
31
|
-
|
|
32
|
-
import { MlxArray } from "../mlx/array";
|
|
33
|
-
import { Dtype } from "../mlx/ffi";
|
|
34
|
-
import { MetalKernel } from "../mlx/metal-kernel";
|
|
35
|
-
import { STEEL_QMM_HEADER } from "../train/steel-qmm-header";
|
|
36
|
-
import * as ops from "../mlx/ops";
|
|
37
|
-
import type { QuantSpec } from "../mlx/ops";
|
|
38
|
-
|
|
39
|
-
/** OFF by default until the prefill/decode benchmark + parity delta are in (then
|
|
40
|
-
* flip to on like FUSED_GELU if it lands bit-exact). MLX_BUN_FUSED_SWIGLU=1 opts in. */
|
|
41
|
-
export function fusedSwigluEnabled(): boolean {
|
|
42
|
-
return process.env.MLX_BUN_FUSED_SWIGLU === "1";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** f32-internal variant (NOT bit-exact). Default off → the bit-exact MATCH path. */
|
|
46
|
-
function f32Internal(): boolean {
|
|
47
|
-
return process.env.MLX_BUN_FUSED_SWIGLU_F32 === "1";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Dispatch counter — a gate test asserts the kernel actually ran. */
|
|
51
|
-
export let fusedSwigluCalls = 0;
|
|
52
|
-
|
|
53
|
-
/** Debug bisect: cap how many fused calls actually run (rest fall back). */
|
|
54
|
-
function maxCalls(): number {
|
|
55
|
-
const v = Number(process.env.MLX_BUN_SWIGLU_MAXCALLS);
|
|
56
|
-
return Number.isFinite(v) && v >= 0 ? v : Infinity;
|
|
57
|
-
}
|
|
58
|
-
export function swigluUnderCap(): boolean {
|
|
59
|
-
return fusedSwigluCalls < maxCalls();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Below this many rows (M) the matmuls are memory-bound GEMVs and the fusion win
|
|
63
|
-
// is just dispatch overhead; the caller keeps the unfused path. Tunable via env
|
|
64
|
-
// for the decode-vs-prefill sweep.
|
|
65
|
-
export function swigluMMin(): number {
|
|
66
|
-
const v = Number(process.env.MLX_BUN_FUSED_SWIGLU_MMIN);
|
|
67
|
-
return Number.isFinite(v) && v > 0 ? v : 2;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const BM = 32, BN = 32, BK = 32, WM = 2, WN = 2;
|
|
71
|
-
const TGN = WM * WN * 32; // 128 threads / threadgroup
|
|
72
|
-
|
|
73
|
-
// One BM×BN output tile per threadgroup (grid.y = I-tiles, grid.z = M-tiles), the
|
|
74
|
-
// K=H reduction looped inside. Xs is loaded once and fed to BOTH the gate and up
|
|
75
|
-
// BlockMMA (the whole point of fusing — x is read from global once for two GEMMs).
|
|
76
|
-
const SOURCE = String.raw`
|
|
77
|
-
// M from the x_shape helper (varies per call); I, H BAKED as template literals
|
|
78
|
-
// (model-constant) — no runtime `+"`shape`"+` input array to thread through the
|
|
79
|
-
// lazy graph. The per-loader quant constants (GS_G/BITS_G gate, GS_U/BITS_U up)
|
|
80
|
-
// are baked too — the de-branch from the known per-tensor mixed-precision config,
|
|
81
|
-
// so the gate=4/up=8 layers fuse instead of falling back.
|
|
82
|
-
const uint M=x_shape[0]; constexpr int I=I_T, H=H_T;
|
|
83
|
-
constexpr int gsG=GS_G, btsG=BITS_G, gsU=GS_U, btsU=BITS_U;
|
|
84
|
-
constexpr int pfG=get_pack_factor<btsG,8>(), bppG=get_bytes_per_pack<btsG>();
|
|
85
|
-
constexpr int pfU=get_pack_factor<btsU,8>(), bppU=get_bytes_per_pack<btsU>();
|
|
86
|
-
constexpr int BKp=BK_T+16/sizeof(T);
|
|
87
|
-
threadgroup T Xs[BM_T*BKp];
|
|
88
|
-
threadgroup T Wgs[BN_T*BKp];
|
|
89
|
-
threadgroup T Wus[BN_T*BKp];
|
|
90
|
-
threadgroup T Lg[BM_T*BN_T];
|
|
91
|
-
threadgroup T Lu[BM_T*BN_T];
|
|
92
|
-
using mma_t=mlx::steel::BlockMMA<T,T,BM_T,BN_T,BK_T,WM_T,WN_T,false,true,BKp,BKp>;
|
|
93
|
-
using lx_t=mlx::steel::BlockLoader<T,BM_T,BK_T,BKp,1,WM_T*WN_T*SIMD_SIZE>;
|
|
94
|
-
using lwg_t=QuantizedBlockLoader<T,BN_T,BK_T,BKp,1,WM_T*WN_T*SIMD_SIZE,gsG,btsG>;
|
|
95
|
-
using lwu_t=QuantizedBlockLoader<T,BN_T,BK_T,BKp,1,WM_T*WN_T*SIMD_SIZE,gsU,btsU>;
|
|
96
|
-
const uint sg=simdgroup_index_in_threadgroup, sl=thread_index_in_simdgroup;
|
|
97
|
-
const uint lid=thread_position_in_threadgroup.x;
|
|
98
|
-
const int Hh=(int)H;
|
|
99
|
-
const int K_wG=Hh*bppG/pfG, K_gG=Hh/gsG; // gate: packed-row bytes, group count
|
|
100
|
-
const int K_wU=Hh*bppU/pfU, K_gU=Hh/gsU; // up
|
|
101
|
-
const int m0=(int)threadgroup_position_in_grid.z*BM_T;
|
|
102
|
-
const int n0=(int)threadgroup_position_in_grid.y*BN_T;
|
|
103
|
-
const short num_els=(short)min(BM_T,(int)M-m0);
|
|
104
|
-
const short num_outs=(short)min(BN_T,(int)I-n0);
|
|
105
|
-
|
|
106
|
-
#if SWIGLU_TGZERO
|
|
107
|
-
// Diagnostic: zero threadgroup memory so a back-to-back dispatch can't read the
|
|
108
|
-
// previous kernel's leftover staging (the cross-dispatch hazard hypothesis).
|
|
109
|
-
for (uint z=lid; z<(uint)(BM_T*BKp); z+=TGN_T) Xs[z]=T(0);
|
|
110
|
-
for (uint z=lid; z<(uint)(BN_T*BKp); z+=TGN_T){ Wgs[z]=T(0); Wus[z]=T(0); }
|
|
111
|
-
for (uint z=lid; z<(uint)(BM_T*BN_T); z+=TGN_T){ Lg[z]=T(0); Lu[z]=T(0); }
|
|
112
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
113
|
-
#endif
|
|
114
|
-
mma_t mg(sg,sl), mu(sg,sl);
|
|
115
|
-
lx_t lx (x + (int64_t)m0*Hh, Hh, Xs, sg, sl);
|
|
116
|
-
lwg_t lwg((const device uint8_t*)wg + (int64_t)n0*K_wG, sg_w + (int64_t)n0*K_gG, bg_w + (int64_t)n0*K_gG, Hh, Wgs, sg, sl);
|
|
117
|
-
lwu_t lwu((const device uint8_t*)wu + (int64_t)n0*K_wU, su_w + (int64_t)n0*K_gU, bu_w + (int64_t)n0*K_gU, Hh, Wus, sg, sl);
|
|
118
|
-
|
|
119
|
-
for (int k=0; k<Hh; k+=BK_T){
|
|
120
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
121
|
-
if (num_els<BM_T) lx.load_safe(short2(BK_T,num_els)); else lx.load_unsafe();
|
|
122
|
-
if (num_outs<BN_T){ lwg.load_safe(short2(BK_T,num_outs)); lwu.load_safe(short2(BK_T,num_outs)); }
|
|
123
|
-
else { lwg.load_unsafe(); lwu.load_unsafe(); }
|
|
124
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
125
|
-
mg.mma(Xs,Wgs);
|
|
126
|
-
mu.mma(Xs,Wus);
|
|
127
|
-
lx.next(); lwg.next(); lwu.next();
|
|
128
|
-
}
|
|
129
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
130
|
-
// Stage both accumulators to threadgroup. store<T,...> casts the float accum to
|
|
131
|
-
// T (bf16) — the SAME rounding MLX's quantized_matmul applies to its output, so
|
|
132
|
-
// Lg/Lu hold the bf16 gate/up the unfused path would have written to global.
|
|
133
|
-
// Xs/Wgs/Wus alias Lg/Lu only if a kernel reused the same tgmem — they don't here,
|
|
134
|
-
// but the stores write threadgroup that the epilogue reads, so bracket with barriers.
|
|
135
|
-
mg.Ctile.template store<T,WM_T,WN_T,BN_T,1>(Lg + mg.sm*BN_T + mg.sn);
|
|
136
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
137
|
-
mu.Ctile.template store<T,WM_T,WN_T,BN_T,1>(Lu + mu.sm*BN_T + mu.sn);
|
|
138
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
139
|
-
|
|
140
|
-
for (uint idx=lid; idx<(uint)(BM_T*BN_T); idx+=TGN_T){
|
|
141
|
-
int r=(int)idx/BN_T, c=(int)idx%BN_T;
|
|
142
|
-
if (m0+r>=(int)M || n0+c>=(int)I) continue;
|
|
143
|
-
#if SWIGLU_DEBUG_GATE
|
|
144
|
-
hidden[(int64_t)(m0+r)*(int64_t)I + n0+c] = Lg[idx]; // raw gate proj (debug)
|
|
145
|
-
#elif SWIGLU_F32
|
|
146
|
-
// f32-internal: gate/up stayed bf16 in Lg/Lu (the matmul output is the same),
|
|
147
|
-
// but σ·/· run in f32 with no intermediate rounding → NOT bit-exact.
|
|
148
|
-
float g=(float)Lg[idx], u=(float)Lu[idx];
|
|
149
|
-
float sig=1.0f/(1.0f+metal::precise::exp(-g));
|
|
150
|
-
hidden[(int64_t)(m0+r)*(int64_t)I + n0+c] = (T)(g*sig*u);
|
|
151
|
-
#else
|
|
152
|
-
// MATCH: round σ, silu, product to T op-for-op with ops.sigmoid+mul+mul on bf16.
|
|
153
|
-
T gT=Lg[idx], uT=Lu[idx];
|
|
154
|
-
T sigT=(T)(1.0f/(1.0f+metal::precise::exp(-(float)gT)));
|
|
155
|
-
T siluT=(T)((float)gT*(float)sigT);
|
|
156
|
-
hidden[(int64_t)(m0+r)*(int64_t)I + n0+c] = (T)((float)siluT*(float)uT);
|
|
157
|
-
#endif
|
|
158
|
-
}
|
|
159
|
-
`;
|
|
160
|
-
|
|
161
|
-
const _kernels = new Map<string, MetalKernel>();
|
|
162
|
-
function getKernel(gsG: number, btsG: number, gsU: number, btsU: number, f32: boolean): MetalKernel {
|
|
163
|
-
const key = `${gsG}:${btsG}:${gsU}:${btsU}:${f32 ? 1 : 0}`;
|
|
164
|
-
let k = _kernels.get(key);
|
|
165
|
-
if (!k) {
|
|
166
|
-
k = new MetalKernel({
|
|
167
|
-
name: "mlx_bun_fused_swiglu",
|
|
168
|
-
inputNames: ["x", "wg", "sg_w", "bg_w", "wu", "su_w", "bu_w"],
|
|
169
|
-
outputNames: ["hidden"],
|
|
170
|
-
source: SOURCE,
|
|
171
|
-
header:
|
|
172
|
-
`${STEEL_QMM_HEADER}\n` +
|
|
173
|
-
`#define GS_G ${gsG}\n#define BITS_G ${btsG}\n#define GS_U ${gsU}\n#define BITS_U ${btsU}\n` +
|
|
174
|
-
`#define BM_T ${BM}\n#define BN_T ${BN}\n#define BK_T ${BK}\n` +
|
|
175
|
-
`#define WM_T ${WM}\n#define WN_T ${WN}\n#define TGN_T ${TGN}\n` +
|
|
176
|
-
`#define SWIGLU_F32 ${f32 ? 1 : 0}\n` +
|
|
177
|
-
`#define SWIGLU_TGZERO ${process.env.MLX_BUN_SWIGLU_TGZERO === "1" ? 1 : 0}\n` +
|
|
178
|
-
`#define SWIGLU_DEBUG_GATE ${process.env.MLX_BUN_SWIGLU_DEBUG_GATE === "1" ? 1 : 0}\n`,
|
|
179
|
-
ensureRowContiguous: true,
|
|
180
|
-
});
|
|
181
|
-
_kernels.set(key, k);
|
|
182
|
-
}
|
|
183
|
-
return k;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/** Each loader's (bits, groupSize) is baked independently, so gate and up may
|
|
187
|
-
* differ (the per-tensor mixed-precision layers). Requires bf16/f16 activations,
|
|
188
|
-
* affine biases, group size a multiple of BK, and H % BK == 0. */
|
|
189
|
-
export function fusedSwigluSupported(
|
|
190
|
-
x: MlxArray, gateSpec: QuantSpec, upSpec: QuantSpec,
|
|
191
|
-
gateBias: MlxArray | null, upBias: MlxArray | null,
|
|
192
|
-
): boolean {
|
|
193
|
-
const H = x.shape[x.shape.length - 1]!;
|
|
194
|
-
const specOk = (s: QuantSpec) =>
|
|
195
|
-
(s.bits === 2 || s.bits === 4 || s.bits === 8) && s.groupSize % BK === 0 && BK <= s.groupSize;
|
|
196
|
-
return (
|
|
197
|
-
(x.dtype === Dtype.bfloat16 || x.dtype === Dtype.float16) &&
|
|
198
|
-
gateBias != null && upBias != null &&
|
|
199
|
-
specOk(gateSpec) && specOk(upSpec) &&
|
|
200
|
-
H % BK === 0
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** silu(gate_proj(x)) · up_proj(x), fused. `x` is [..., H] (collapsed to [M, H]);
|
|
205
|
-
* wg/wu are the packed quantized weights [I, H*bits/32], with per-group
|
|
206
|
-
* scales/biases [I, H/groupSize]. gate and up may use different quant specs.
|
|
207
|
-
* Returns hidden [M, I] in x.dtype. Caller owns it. */
|
|
208
|
-
export function fusedSwiglu(
|
|
209
|
-
x: MlxArray,
|
|
210
|
-
wg: MlxArray, sg: MlxArray, bg: MlxArray,
|
|
211
|
-
wu: MlxArray, su: MlxArray, bu: MlxArray,
|
|
212
|
-
gateSpec: QuantSpec, upSpec: QuantSpec = gateSpec,
|
|
213
|
-
): MlxArray {
|
|
214
|
-
fusedSwigluCalls++;
|
|
215
|
-
const H = x.shape[x.shape.length - 1]!;
|
|
216
|
-
const M = x.shape.reduce((a, b) => a * b, 1) / H;
|
|
217
|
-
const I = wg.shape[0]!;
|
|
218
|
-
const x2 = x.shape.length === 2 ? x : ops.reshape(x, [M, H]);
|
|
219
|
-
try {
|
|
220
|
-
const [hidden] = getKernel(gateSpec.groupSize, gateSpec.bits, upSpec.groupSize, upSpec.bits, f32Internal()).apply(
|
|
221
|
-
[x2, wg, sg, bg, wu, su, bu],
|
|
222
|
-
{
|
|
223
|
-
outputShapeFn: (ins) => [{ shape: [ins[0]!.shape[0]!, I], dtype: x.dtype }],
|
|
224
|
-
grid: (ins) => [TGN, Math.ceil(I / BN), Math.ceil(ins[0]!.shape[0]! / BM)],
|
|
225
|
-
threadGroup: [TGN, 1, 1],
|
|
226
|
-
templateInts: { I_T: I, H_T: H },
|
|
227
|
-
templateDtypes: { T: x.dtype },
|
|
228
|
-
},
|
|
229
|
-
);
|
|
230
|
-
// Restore the leading dims the caller passed (e.g. [B, T, I]).
|
|
231
|
-
if (x.shape.length !== 2) {
|
|
232
|
-
const out = ops.reshape(hidden!, [...x.shape.slice(0, -1), I]);
|
|
233
|
-
hidden!.dispose();
|
|
234
|
-
return out;
|
|
235
|
-
}
|
|
236
|
-
return hidden!;
|
|
237
|
-
} finally {
|
|
238
|
-
if (x2 !== x && process.env.MLX_BUN_SWIGLU_NODISPOSE !== "1") x2.dispose();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// Plain quantized linear y = x @ Wᵀ as OUR dispatch, composed from the steel
|
|
2
|
-
// building blocks already in src/train/steel-qmm-header.ts (BlockMMA + Block/
|
|
3
|
-
// QuantizedBlockLoader — MLX's verbatim GEMM, the same code qmm_t_impl wraps).
|
|
4
|
-
// This is the projection primitive for the controlled forward (q/k/v/o/down):
|
|
5
|
-
// we own the dispatch and the eval boundary instead of emitting an ops.* node
|
|
6
|
-
// into MLX's lazy graph. N (out) and H (in/K) are baked as template literals;
|
|
7
|
-
// M comes from the x_shape helper. Output bit-matches the BlockMMA store epilogue
|
|
8
|
-
// (TransformNone → cast to T), the same store qmm_t_impl uses.
|
|
9
|
-
|
|
10
|
-
import { MlxArray } from "../mlx/array";
|
|
11
|
-
import { MetalKernel } from "../mlx/metal-kernel";
|
|
12
|
-
import { STEEL_QMM_HEADER } from "../train/steel-qmm-header";
|
|
13
|
-
import * as ops from "../mlx/ops";
|
|
14
|
-
import type { QuantSpec } from "../mlx/ops";
|
|
15
|
-
|
|
16
|
-
const BM = 32, BN = 32, BK = 32, WM = 2, WN = 2;
|
|
17
|
-
const TGN = WM * WN * 32;
|
|
18
|
-
|
|
19
|
-
// One BM×BN output tile per threadgroup (grid.y = N-tiles, grid.z = M-tiles), the
|
|
20
|
-
// K=H reduction looped inside — the qmm_t_impl body, dims via templateInts.
|
|
21
|
-
const SOURCE = String.raw`
|
|
22
|
-
const uint M=x_shape[0]; constexpr int N=N_T, H=H_T;
|
|
23
|
-
constexpr int gs=GS_T, bts=BITS_T;
|
|
24
|
-
constexpr int pf=get_pack_factor<bts,8>(), bpp=get_bytes_per_pack<bts>();
|
|
25
|
-
constexpr int BKp=BK_T+16/sizeof(T);
|
|
26
|
-
threadgroup T Xs[BM_T*BKp];
|
|
27
|
-
threadgroup T Ws[BN_T*BKp];
|
|
28
|
-
using mma_t=mlx::steel::BlockMMA<T,T,BM_T,BN_T,BK_T,WM_T,WN_T,false,true,BKp,BKp>;
|
|
29
|
-
using lx_t=mlx::steel::BlockLoader<T,BM_T,BK_T,BKp,1,WM_T*WN_T*SIMD_SIZE>;
|
|
30
|
-
using lw_t=QuantizedBlockLoader<T,BN_T,BK_T,BKp,1,WM_T*WN_T*SIMD_SIZE,gs,bts>;
|
|
31
|
-
const uint sg=simdgroup_index_in_threadgroup, sl=thread_index_in_simdgroup;
|
|
32
|
-
const int Hh=(int)H, K_w=Hh*bpp/pf, K_g=Hh/gs;
|
|
33
|
-
const int m0=(int)threadgroup_position_in_grid.z*BM_T;
|
|
34
|
-
const int n0=(int)threadgroup_position_in_grid.y*BN_T;
|
|
35
|
-
const short num_els=(short)min(BM_T,(int)M-m0);
|
|
36
|
-
const short num_outs=(short)min(BN_T,(int)N-n0);
|
|
37
|
-
|
|
38
|
-
mma_t mma_op(sg,sl);
|
|
39
|
-
lx_t lx(x + (int64_t)m0*Hh, Hh, Xs, sg, sl);
|
|
40
|
-
lw_t lw((const device uint8_t*)w + (int64_t)n0*K_w, scales + (int64_t)n0*K_g, biases + (int64_t)n0*K_g, Hh, Ws, sg, sl);
|
|
41
|
-
|
|
42
|
-
for (int k=0; k<Hh; k+=BK_T){
|
|
43
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
44
|
-
if (num_els<BM_T) lx.load_safe(short2(BK_T,num_els)); else lx.load_unsafe();
|
|
45
|
-
if (num_outs<BN_T) lw.load_safe(short2(BK_T,num_outs)); else lw.load_unsafe();
|
|
46
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
47
|
-
mma_op.mma(Xs,Ws);
|
|
48
|
-
lx.next(); lw.next();
|
|
49
|
-
}
|
|
50
|
-
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
51
|
-
// Store straight to device y[m0.., n0..] via the BlockMMA epilogue (float accum
|
|
52
|
-
// → T, the SAME rounding MLX's quantized_matmul applies).
|
|
53
|
-
device T* yp = y + (int64_t)m0*(int64_t)N + n0;
|
|
54
|
-
if (num_els<BM_T || num_outs<BN_T) mma_op.store_result_safe(yp, N, short2(num_outs,num_els));
|
|
55
|
-
else mma_op.store_result(yp, N);
|
|
56
|
-
`;
|
|
57
|
-
|
|
58
|
-
const _kernels = new Map<string, MetalKernel>();
|
|
59
|
-
function getKernel(gs: number, bits: number): MetalKernel {
|
|
60
|
-
const key = `${gs}:${bits}`;
|
|
61
|
-
let k = _kernels.get(key);
|
|
62
|
-
if (!k) {
|
|
63
|
-
k = new MetalKernel({
|
|
64
|
-
name: "mlx_bun_steel_linear",
|
|
65
|
-
inputNames: ["x", "w", "scales", "biases"],
|
|
66
|
-
outputNames: ["y"],
|
|
67
|
-
source: SOURCE,
|
|
68
|
-
header:
|
|
69
|
-
`${STEEL_QMM_HEADER}\n` +
|
|
70
|
-
`#define GS_T ${gs}\n#define BITS_T ${bits}\n` +
|
|
71
|
-
`#define BM_T ${BM}\n#define BN_T ${BN}\n#define BK_T ${BK}\n` +
|
|
72
|
-
`#define WM_T ${WM}\n#define WN_T ${WN}\n#define TGN_T ${TGN}\n`,
|
|
73
|
-
ensureRowContiguous: true,
|
|
74
|
-
});
|
|
75
|
-
_kernels.set(key, k);
|
|
76
|
-
}
|
|
77
|
-
return k;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** y = x @ Wᵀ, quantized, as our own dispatch. `x` is [M, H] (bf16/f16); `w` is
|
|
81
|
-
* the packed weight [N, H*bits/32] with per-group scales/biases [N, H/groupSize].
|
|
82
|
-
* Returns [M, N] in x.dtype. H % BK == 0 and groupSize % BK == 0 required. */
|
|
83
|
-
export function steelLinear(
|
|
84
|
-
x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray, spec: QuantSpec,
|
|
85
|
-
): MlxArray {
|
|
86
|
-
const M = x.shape[0]!, H = x.shape[1]!, N = w.shape[0]!;
|
|
87
|
-
const [y] = getKernel(spec.groupSize, spec.bits).apply([x, w, scales, biases], {
|
|
88
|
-
outputShapeFn: (ins) => [{ shape: [ins[0]!.shape[0]!, N], dtype: x.dtype }],
|
|
89
|
-
grid: (ins) => [TGN, Math.ceil(N / BN), Math.ceil(ins[0]!.shape[0]! / BM)],
|
|
90
|
-
threadGroup: [TGN, 1, 1],
|
|
91
|
-
templateInts: { N_T: N, H_T: H },
|
|
92
|
-
templateDtypes: { T: x.dtype },
|
|
93
|
-
});
|
|
94
|
-
void M;
|
|
95
|
-
return y!;
|
|
96
|
-
}
|