mlx-bun 0.2.0 → 0.3.0
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 +71 -588
- package/docs/README.md +91 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/design/batching.md +569 -0
- package/docs/design/decode-speed-program.md +3178 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +579 -334
- package/docs/design/kv-cache.md +680 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1627 -0
- package/docs/design/turboquant.md +2102 -0
- package/docs/design/unified-engine-frontier-plan.md +1831 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +34 -1
- package/docs/planning/release-notes-v0.3.0.md +35 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +394 -159
- package/docs/reference/distribution.md +321 -182
- package/docs/reference/environment.md +348 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +104 -6
- package/docs/reference/server-api.md +886 -425
- package/docs/reference/server-config.md +638 -330
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1646 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +47 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/gateway-binding.ts +71 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/memory-guard.ts +40 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/rollback.ts +51 -0
- package/src/backends/mlx/serial-executor.ts +273 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-views.ts +26 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/cli.ts +58 -25
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +49 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/resources.ts +18 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +51 -0
- package/src/engine/resources.ts +42 -0
- package/src/engine/scheduler.ts +67 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +1 -1
- package/src/generate.ts +656 -268
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-store.ts +109 -49
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +9 -10
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/batched-mask.ts +8 -2
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +2 -2
- package/src/model/batched-rotating.ts +1 -1
- package/src/model/compiled-decode.ts +1 -1
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/gemma4-base.ts +26 -9
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +2 -2
- package/src/model/generated/gemma4-26b.ts +2 -2
- package/src/model/generated/gemma4-e4b.ts +2 -2
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +6 -8
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +140 -56
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/universal/dense.ts +1 -1
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/prompt-cache.ts +96 -53
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +4 -4
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +154 -193
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +512 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +19 -15
- package/src/serve/request-prep.ts +346 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +317 -2649
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +8 -8
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +2 -2
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +4 -3
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +1 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +166 -69
- package/src/spec/serve-loop.ts +154 -156
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +80 -12
- package/src/ssd-durability.ts +25 -13
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +5 -3
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Experimental small-M trellis kernels (variant 7).
|
|
2
|
+
// Decode each weight once per SIMD group and reuse it across M input vectors.
|
|
3
|
+
// Callers restrict M to 2..4 and pass variant 6's decoder/rounding constants.
|
|
4
|
+
// Each vector keeps the original two-accumulator FMA order and simd_sum;
|
|
5
|
+
// fused gate/up also keeps the original bf16 activation rounding boundaries.
|
|
6
|
+
// Baseline implementation and the shared 1MAD header: trellis-linear.ts.
|
|
7
|
+
// Full-model paired performance and quality gates are required before a default.
|
|
8
|
+
|
|
9
|
+
export const REDUCE_SHARED_M_SOURCE = String.raw`
|
|
10
|
+
threadgroup float lutTG[4096];
|
|
11
|
+
if ((VARIANT) == 2) {
|
|
12
|
+
for (uint i = thread_position_in_threadgroup.x; i < 4096u; i += 128u) lutTG[i] = lut[i];
|
|
13
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
14
|
+
}
|
|
15
|
+
const uint lane = thread_index_in_simdgroup;
|
|
16
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
17
|
+
const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
|
|
18
|
+
const uint sample = 0u;
|
|
19
|
+
if (row >= (uint)R || sample >= (uint)M) return;
|
|
20
|
+
const uint wpb = (uint)(BT * K / 32);
|
|
21
|
+
const uint runsPerBlock = (uint)BT / 32u;
|
|
22
|
+
const uint nRuns = (uint)C / 32u;
|
|
23
|
+
const device uint32_t* codeRow = codes + (ulong)row * (ulong)((C / BT) * wpb);
|
|
24
|
+
const float scale = float(scales[row]);
|
|
25
|
+
float acc[M], acc2[M];
|
|
26
|
+
#pragma clang loop unroll(full)
|
|
27
|
+
for (uint m = 0; m < (uint)M; ++m) { acc[m] = 0.0f; acc2[m] = 0.0f; }
|
|
28
|
+
for (uint run = lane; run < nRuns; run += 32u) {
|
|
29
|
+
const uint blk = run / runsPerBlock;
|
|
30
|
+
const uint t0 = (run - blk * runsPerBlock) * 32u;
|
|
31
|
+
const device uint32_t* bw = codeRow + blk * wpb;
|
|
32
|
+
const uint w0 = ((uint)BT - 32u - t0) * (uint)K / 32u;
|
|
33
|
+
uint w[K + 1];
|
|
34
|
+
#pragma clang loop unroll(full)
|
|
35
|
+
for (uint i = 0; i <= (uint)K; ++i) {
|
|
36
|
+
const uint wi = w0 + i;
|
|
37
|
+
w[i] = bw[wi >= wpb ? wi - wpb : wi];
|
|
38
|
+
}
|
|
39
|
+
const uint c0 = blk * (uint)BT + t0;
|
|
40
|
+
#pragma clang loop unroll(full)
|
|
41
|
+
for (uint j4 = 0; j4 < 8u; ++j4) {
|
|
42
|
+
float4 xv[M];
|
|
43
|
+
#pragma clang loop unroll(full)
|
|
44
|
+
for (uint m = 0; m < (uint)M; ++m)
|
|
45
|
+
xv[m] = float4(((const device metal::vec<T, 4>*)(x + (ulong)m * (ulong)C + c0))[j4]);
|
|
46
|
+
#pragma clang loop unroll(full)
|
|
47
|
+
for (uint q = 0; q < 4u; ++q) {
|
|
48
|
+
const uint j = j4 * 4u + q;
|
|
49
|
+
const uint b = (31u - j) * (uint)K;
|
|
50
|
+
const uint wi = b >> 5;
|
|
51
|
+
const uint off = b & 31u;
|
|
52
|
+
uint win = w[wi] >> off;
|
|
53
|
+
if (off + (uint)L > 32u) win |= w[wi + 1] << (32u - off);
|
|
54
|
+
win &= (1u << (uint)L) - 1u;
|
|
55
|
+
const float wv = TRELLIS_ROUND(TRELLIS_DECODE(win, lutTG, lut) * scale);
|
|
56
|
+
#pragma clang loop unroll(full)
|
|
57
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
58
|
+
if (q & 1u) acc2[m] = metal::fma(wv, xv[m][q], acc2[m]);
|
|
59
|
+
else acc[m] = metal::fma(wv, xv[m][q], acc[m]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
#pragma clang loop unroll(full)
|
|
65
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
66
|
+
const float v = metal::simd_sum(acc[m] + acc2[m]);
|
|
67
|
+
if (lane == 0u) out[(ulong)m * (ulong)R + row] = T(v);
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
export const GATEUP_SHARED_M_SOURCE = String.raw`
|
|
72
|
+
threadgroup float lutTG[4096];
|
|
73
|
+
if ((VARIANT) == 2) {
|
|
74
|
+
for (uint i = thread_position_in_threadgroup.x; i < 4096u; i += 128u) lutTG[i] = lut[i];
|
|
75
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
76
|
+
}
|
|
77
|
+
const uint lane = thread_index_in_simdgroup;
|
|
78
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
79
|
+
const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
|
|
80
|
+
const uint sample = 0u;
|
|
81
|
+
if (row >= (uint)R || sample >= (uint)M) return;
|
|
82
|
+
const uint wpb = (uint)(BT * K / 32);
|
|
83
|
+
const uint runsPerBlock = (uint)BT / 32u;
|
|
84
|
+
const uint nRuns = (uint)C / 32u;
|
|
85
|
+
const ulong rowWords = (ulong)((C / BT) * wpb);
|
|
86
|
+
const device uint32_t* gRow = gcodes + (ulong)row * rowWords;
|
|
87
|
+
const device uint32_t* uRow = ucodes + (ulong)row * rowWords;
|
|
88
|
+
const float gScale = float(gscales[row]);
|
|
89
|
+
const float uScale = float(uscales[row]);
|
|
90
|
+
float gAcc[M], gAcc2[M], uAcc[M], uAcc2[M];
|
|
91
|
+
#pragma clang loop unroll(full)
|
|
92
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
93
|
+
gAcc[m] = 0.0f; gAcc2[m] = 0.0f; uAcc[m] = 0.0f; uAcc2[m] = 0.0f;
|
|
94
|
+
}
|
|
95
|
+
for (uint run = lane; run < nRuns; run += 32u) {
|
|
96
|
+
const uint blk = run / runsPerBlock;
|
|
97
|
+
const uint t0 = (run - blk * runsPerBlock) * 32u;
|
|
98
|
+
const device uint32_t* gw = gRow + blk * wpb;
|
|
99
|
+
const device uint32_t* uw = uRow + blk * wpb;
|
|
100
|
+
const uint w0 = ((uint)BT - 32u - t0) * (uint)K / 32u;
|
|
101
|
+
uint g[K + 1];
|
|
102
|
+
uint u[K + 1];
|
|
103
|
+
#pragma clang loop unroll(full)
|
|
104
|
+
for (uint i = 0; i <= (uint)K; ++i) {
|
|
105
|
+
const uint wi = w0 + i;
|
|
106
|
+
const uint idx = wi >= wpb ? wi - wpb : wi;
|
|
107
|
+
g[i] = gw[idx];
|
|
108
|
+
u[i] = uw[idx];
|
|
109
|
+
}
|
|
110
|
+
const uint c0 = blk * (uint)BT + t0;
|
|
111
|
+
#pragma clang loop unroll(full)
|
|
112
|
+
for (uint j4 = 0; j4 < 8u; ++j4) {
|
|
113
|
+
float4 xv4[M];
|
|
114
|
+
#pragma clang loop unroll(full)
|
|
115
|
+
for (uint m = 0; m < (uint)M; ++m)
|
|
116
|
+
xv4[m] = float4(((const device metal::vec<T, 4>*)(x + (ulong)m * (ulong)C + c0))[j4]);
|
|
117
|
+
#pragma clang loop unroll(full)
|
|
118
|
+
for (uint q = 0; q < 4u; ++q) {
|
|
119
|
+
const uint j = j4 * 4u + q;
|
|
120
|
+
const uint b = (31u - j) * (uint)K;
|
|
121
|
+
const uint wi = b >> 5;
|
|
122
|
+
const uint off = b & 31u;
|
|
123
|
+
uint gwin = g[wi] >> off;
|
|
124
|
+
uint uwin = u[wi] >> off;
|
|
125
|
+
if (off + (uint)L > 32u) { gwin |= g[wi + 1] << (32u - off); uwin |= u[wi + 1] << (32u - off); }
|
|
126
|
+
gwin &= (1u << (uint)L) - 1u;
|
|
127
|
+
uwin &= (1u << (uint)L) - 1u;
|
|
128
|
+
const float gv = TRELLIS_ROUND(TRELLIS_DECODE(gwin, lutTG, lut) * gScale);
|
|
129
|
+
const float uv = TRELLIS_ROUND(TRELLIS_DECODE(uwin, lutTG, lut) * uScale);
|
|
130
|
+
#pragma clang loop unroll(full)
|
|
131
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
132
|
+
const float xv = xv4[m][q];
|
|
133
|
+
if (q & 1u) { gAcc2[m] = metal::fma(gv, xv, gAcc2[m]); uAcc2[m] = metal::fma(uv, xv, uAcc2[m]); }
|
|
134
|
+
else { gAcc[m] = metal::fma(gv, xv, gAcc[m]); uAcc[m] = metal::fma(uv, xv, uAcc[m]); }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
#pragma clang loop unroll(full)
|
|
140
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
141
|
+
const float gate = metal::simd_sum(gAcc[m] + gAcc2[m]);
|
|
142
|
+
const float up = metal::simd_sum(uAcc[m] + uAcc2[m]);
|
|
143
|
+
if (lane == 0u) {
|
|
144
|
+
const T gateT = T(gate);
|
|
145
|
+
const T upT = T(up);
|
|
146
|
+
const T sigmoidT = T(1.0f / (1.0f + metal::precise::exp(-float(gateT))));
|
|
147
|
+
const T siluT = T(float(gateT) * float(sigmoidT));
|
|
148
|
+
mid[(ulong)m * (ulong)R + row] = T(float(siluT) * float(upT));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Experimental variant 10: reuse each decoded scatter weight across M=2..4.
|
|
2
|
+
// Both kernels retain each output's ordered row accumulation, split boundaries
|
|
3
|
+
// and the caller's partial reduction/cast. M=1 keeps the incumbent kernel.
|
|
4
|
+
// The balanced path requires k=3 / T=256 / L<=12; the generic path preserves
|
|
5
|
+
// the existing packed-word lane mapping for other supported geometries.
|
|
6
|
+
|
|
7
|
+
export const SCATTER_SHARED_M_SOURCE = String.raw`
|
|
8
|
+
threadgroup float lutTG[4096];
|
|
9
|
+
if ((VARIANT) == 2) {
|
|
10
|
+
for (uint i = thread_position_in_threadgroup.x; i < 4096u; i += 128u) lutTG[i] = lut[i];
|
|
11
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
12
|
+
}
|
|
13
|
+
const uint lane = thread_index_in_simdgroup;
|
|
14
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
15
|
+
const uint wpb = (uint)(BT * K / 32);
|
|
16
|
+
const uint nb = 32u / wpb; // blocks per SIMD group
|
|
17
|
+
const uint nBlocks = (uint)C / (uint)BT;
|
|
18
|
+
const uint groups = (nBlocks + nb - 1u) / nb;
|
|
19
|
+
const uint group = thread_position_in_grid.y * (uint)SG_TG + sg;
|
|
20
|
+
const uint zi = thread_position_in_grid.z;
|
|
21
|
+
const uint sample = 0u;
|
|
22
|
+
const uint split = zi;
|
|
23
|
+
const uint bi = lane / wpb;
|
|
24
|
+
const uint wi = lane - bi * wpb;
|
|
25
|
+
const uint blk = group * nb + bi;
|
|
26
|
+
const bool active = (group < groups) && (bi < nb) && (blk < nBlocks) && (sample < (uint)M);
|
|
27
|
+
const uint nbrLane = bi * wpb + ((wi + 1u == wpb) ? 0u : wi + 1u);
|
|
28
|
+
// This lane's positions: symbol offsets p = multiples of K inside [32wi, 32wi+32).
|
|
29
|
+
const uint pBase = 32u * wi;
|
|
30
|
+
const uint p0 = ((pBase + (uint)K - 1u) / (uint)K) * (uint)K;
|
|
31
|
+
uint offs[NP];
|
|
32
|
+
bool valid[NP];
|
|
33
|
+
bool spill[NP];
|
|
34
|
+
uint cols[NP];
|
|
35
|
+
#pragma clang loop unroll(full)
|
|
36
|
+
for (uint i = 0; i < (uint)NP; ++i) {
|
|
37
|
+
const uint p = p0 + i * (uint)K;
|
|
38
|
+
valid[i] = active && (p < pBase + 32u);
|
|
39
|
+
offs[i] = p - pBase;
|
|
40
|
+
spill[i] = offs[i] + (uint)L > 32u;
|
|
41
|
+
const uint t = (uint)BT - 1u - p / (uint)K;
|
|
42
|
+
cols[i] = blk * (uint)BT + t;
|
|
43
|
+
}
|
|
44
|
+
const ulong rowWords = (ulong)(INTERLEAVE ? 2 : nBlocks) * wpb;
|
|
45
|
+
const uint rowsPer = ((uint)R + (uint)SPLITS - 1u) / (uint)SPLITS;
|
|
46
|
+
const uint r0 = split * rowsPer;
|
|
47
|
+
const uint r1 = metal::min((uint)R, r0 + rowsPer);
|
|
48
|
+
|
|
49
|
+
const device uint32_t* col0 = codes + (INTERLEAVE
|
|
50
|
+
? (ulong)(blk / 2) * R * 2 * wpb + (blk % 2) * wpb : (ulong)blk * wpb) + wi;
|
|
51
|
+
const uint mask = (1u << (uint)L) - 1u;
|
|
52
|
+
float acc[M][NP];
|
|
53
|
+
#pragma clang loop unroll(full)
|
|
54
|
+
for (uint m = 0; m < (uint)M; ++m)
|
|
55
|
+
#pragma clang loop unroll(full)
|
|
56
|
+
for (uint i = 0; i < (uint)NP; ++i) acc[m][i] = 0.0f;
|
|
57
|
+
for (uint r = r0; r < r1; ++r) {
|
|
58
|
+
const uint word = active ? col0[(ulong)r * rowWords] : 0u;
|
|
59
|
+
const uint nxt = metal::simd_shuffle(word, (ushort)nbrLane);
|
|
60
|
+
float xr[M];
|
|
61
|
+
#pragma clang loop unroll(full)
|
|
62
|
+
for (uint m = 0; m < (uint)M; ++m) xr[m] = float(x[(ulong)m * (ulong)R + r]);
|
|
63
|
+
const float sr = float(scales[r]);
|
|
64
|
+
#pragma clang loop unroll(full)
|
|
65
|
+
for (uint i = 0; i < (uint)NP; ++i) {
|
|
66
|
+
uint win = word >> offs[i];
|
|
67
|
+
if (spill[i]) win |= nxt << (32u - offs[i]);
|
|
68
|
+
win &= mask;
|
|
69
|
+
const float weight = TRELLIS_ROUND(TRELLIS_DECODE(win, lutTG, lut) * sr);
|
|
70
|
+
#pragma clang loop unroll(full)
|
|
71
|
+
for (uint m = 0; m < (uint)M; ++m) acc[m][i] = metal::fma(weight, xr[m], acc[m][i]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
#pragma clang loop unroll(full)
|
|
75
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
76
|
+
const ulong outBase = ((ulong)m * (ulong)SPLITS + split) * (ulong)C;
|
|
77
|
+
#pragma clang loop unroll(full)
|
|
78
|
+
for (uint i = 0; i < (uint)NP; ++i)
|
|
79
|
+
if (valid[i]) partial[outBase + cols[i]] = acc[m][i];
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
export const BALANCED_SCATTER_SHARED_M_SOURCE = String.raw`
|
|
84
|
+
// Model-owned tuning selects this table for measured small-M shapes.
|
|
85
|
+
// Store integers so the reciprocal multiply retains its original rounding.
|
|
86
|
+
threadgroup short codebookY[CODEBOOK ? 4096 : 1];
|
|
87
|
+
if (CODEBOOK) {
|
|
88
|
+
for (uint i = thread_position_in_threadgroup.x; i < 4096u; i += threads_per_threadgroup.x)
|
|
89
|
+
codebookY[i] = (short)trellis_y(i);
|
|
90
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
91
|
+
}
|
|
92
|
+
const uint lane = thread_index_in_simdgroup;
|
|
93
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
94
|
+
const uint block = thread_position_in_grid.y * (uint)SG_TG + sg;
|
|
95
|
+
const uint nBlocks = (uint)C / (uint)BT;
|
|
96
|
+
const uint zi = thread_position_in_grid.z;
|
|
97
|
+
const uint sample = 0u;
|
|
98
|
+
const uint split = zi;
|
|
99
|
+
if (block >= nBlocks || sample >= (uint)M) return;
|
|
100
|
+
const uint wpb = (uint)(BT * K / 32);
|
|
101
|
+
const ulong rowWords = (ulong)(INTERLEAVE ? 2 : nBlocks) * wpb;
|
|
102
|
+
const uint pBase = lane * 8u * (uint)K;
|
|
103
|
+
const uint wi = pBase >> 5u;
|
|
104
|
+
const uint off = pBase & 31u;
|
|
105
|
+
const uint next = (wi + 1u == wpb) ? 0u : wi + 1u;
|
|
106
|
+
const device uint32_t* words = codes + (INTERLEAVE
|
|
107
|
+
? (ulong)(block / 2) * R * 2 * wpb + (block % 2) * wpb : (ulong)block * wpb);
|
|
108
|
+
const uint rowsPer = ((uint)R + (uint)SPLITS - 1u) / (uint)SPLITS;
|
|
109
|
+
const uint r0 = split * rowsPer;
|
|
110
|
+
const uint r1 = metal::min((uint)R, r0 + rowsPer);
|
|
111
|
+
float acc[M][8];
|
|
112
|
+
#pragma clang loop unroll(full)
|
|
113
|
+
for (uint m = 0; m < (uint)M; ++m)
|
|
114
|
+
#pragma clang loop unroll(full)
|
|
115
|
+
for (uint i = 0; i < 8u; ++i) acc[m][i] = 0.0f;
|
|
116
|
+
for (uint r = r0; r < r1; ++r) {
|
|
117
|
+
const uint word = words[(ulong)r * rowWords + wi];
|
|
118
|
+
const uint nxt = words[(ulong)r * rowWords + next];
|
|
119
|
+
const uint lo = off == 0u ? word : (word >> off) | (nxt << (32u - off));
|
|
120
|
+
const uint hi = off == 0u ? nxt : nxt >> off;
|
|
121
|
+
float xr[M];
|
|
122
|
+
#pragma clang loop unroll(full)
|
|
123
|
+
for (uint m = 0; m < (uint)M; ++m) xr[m] = float(x[(ulong)m * (ulong)R + r]);
|
|
124
|
+
const float sr = float(scales[r]);
|
|
125
|
+
#pragma clang loop unroll(full)
|
|
126
|
+
for (uint i = 0; i < 8u; ++i) {
|
|
127
|
+
const uint shift = i * (uint)K;
|
|
128
|
+
uint win = lo >> shift;
|
|
129
|
+
if (shift + (uint)L > 32u) win |= hi << (32u - shift);
|
|
130
|
+
win &= (1u << (uint)L) - 1u;
|
|
131
|
+
const float value = (CODEBOOK ? (float)codebookY[win] : (float)trellis_y(win)) * (1.0f / 147.800537109375f);
|
|
132
|
+
const float weight = value * sr;
|
|
133
|
+
#pragma clang loop unroll(full)
|
|
134
|
+
for (uint m = 0; m < (uint)M; ++m) acc[m][i] = metal::fma(weight, xr[m], acc[m][i]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
#pragma clang loop unroll(full)
|
|
138
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
139
|
+
const ulong outBase = ((ulong)m * (ulong)SPLITS + split) * (ulong)C;
|
|
140
|
+
#pragma clang loop unroll(full)
|
|
141
|
+
for (uint i = 0; i < 8u; ++i)
|
|
142
|
+
partial[outBase + block * (uint)BT + (uint)BT - 1u - lane * 8u - i] = acc[m][i];
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Packed axis-0 prefill with in-tile weight reconstruction and float partials.
|
|
2
|
+
// Partitioning, SIMD fragment layout and ordered accumulation follow MLX 0.31.2
|
|
3
|
+
// (MIT, Apple Inc.): mlx/backend/metal/matmul.cpp and steel/gemm/kernels/
|
|
4
|
+
// steel_gemm_splitk.h. The shared operation preserves that reduction contract.
|
|
5
|
+
import { MlxArray } from "../mlx/array";
|
|
6
|
+
import { Dtype } from "../mlx/ffi";
|
|
7
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
8
|
+
import type { TrellisGeometry } from "./trellis-linear";
|
|
9
|
+
|
|
10
|
+
export function splitKTrellisPrefillEligible(g: TrellisGeometry, m: number, dtype: Dtype): boolean {
|
|
11
|
+
return dtype === Dtype.bfloat16 && m >= 5 && m <= 8 &&
|
|
12
|
+
g.axis === 0 && g.T === 256 && g.L === 12 && [2, 3, 4].includes(g.k) &&
|
|
13
|
+
g.inFeatures === 17408 && g.outFeatures === 5120;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let partialKernel: MetalKernel | undefined;
|
|
17
|
+
let reductionKernel: MetalKernel | undefined;
|
|
18
|
+
function kernels(): [MetalKernel, MetalKernel] {
|
|
19
|
+
partialKernel ??= new MetalKernel({ name: "mlx_bun_trellis_splitk_prefill",
|
|
20
|
+
inputNames: ["x", "codes", "scales"], outputNames: ["out"],
|
|
21
|
+
header: "#include <metal_simdgroup_matrix>\n", source: String.raw`
|
|
22
|
+
const uint tid=thread_position_in_threadgroup.x;
|
|
23
|
+
const uint lane=thread_index_in_simdgroup;
|
|
24
|
+
const uint sg=simdgroup_index_in_threadgroup;
|
|
25
|
+
const uint mbase=threadgroup_position_in_grid.y*BM;
|
|
26
|
+
const uint nbase=threadgroup_position_in_grid.x*BN;
|
|
27
|
+
const uint fm=((lane/4)&4)+((lane/2)%4);
|
|
28
|
+
const uint fn=((lane/4)&2)*2+(lane%2)*2;
|
|
29
|
+
// The eligible M=5..8 path needs one row of 8x8 SIMD-group tiles.
|
|
30
|
+
constexpr int WM=BM==8?1:2, WN=4/WM, SM=WM*8, SN=WN*8;
|
|
31
|
+
const uint sm=(sg/WN)*8+fm, sn=(sg%WN)*8+fn;
|
|
32
|
+
constexpr int TM=BM/SM, TN=BN/SN;
|
|
33
|
+
threadgroup T As[BM*BK];
|
|
34
|
+
threadgroup T Bs[BK*BN];
|
|
35
|
+
metal::simdgroup_float8x8 acc[TM][TN];
|
|
36
|
+
#pragma clang loop unroll(full)
|
|
37
|
+
for(uint i=0;i<TM;i++) {
|
|
38
|
+
#pragma clang loop unroll(full)
|
|
39
|
+
for(uint j=0;j<TN;j++) {
|
|
40
|
+
acc[i][j].thread_elements()[0]=0.0f;
|
|
41
|
+
acc[i][j].thread_elements()[1]=0.0f;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
constexpr uint wpb=BT*BITS/32;
|
|
45
|
+
const uint split=threadgroup_position_in_grid.z;
|
|
46
|
+
const uint k0=split*PARTITION;
|
|
47
|
+
const uint k1=split+1==SPLITS?KC:k0+PARTITION;
|
|
48
|
+
for(uint kb=k0;kb<k1;kb+=BK) {
|
|
49
|
+
for(uint a=tid;a<BM*BK;a+=128) {
|
|
50
|
+
const uint m=mbase+a/BK, k=kb+a%BK;
|
|
51
|
+
As[(a/BK)*BK+a%BK]=(m<M && k<KC)?x[(ulong)m*KC+k]:T(0);
|
|
52
|
+
}
|
|
53
|
+
for(uint b=tid;b<BK*BN;b+=128) {
|
|
54
|
+
const uint k=kb+b/BN, n=nbase+b%BN;
|
|
55
|
+
T w=T(0);
|
|
56
|
+
if(k<KC && n<N) {
|
|
57
|
+
const uint row=k;
|
|
58
|
+
const uint col=n;
|
|
59
|
+
const uint block=col/BT, t=col%BT;
|
|
60
|
+
const device uint* bw=codes+(INTERLEAVE
|
|
61
|
+
? (ulong)(block/2)*KC*2*wpb+(ulong)row*2*wpb+(block%2)*wpb
|
|
62
|
+
: (ulong)row*(C/BT)*wpb+block*wpb);
|
|
63
|
+
const uint pos=(BT-1-t)*BITS, wi=pos>>5, off=pos&31u;
|
|
64
|
+
uint win=bw[wi]>>off;
|
|
65
|
+
if(off+L>32)win|=bw[wi+1==wpb?0:wi+1]<<(32-off);
|
|
66
|
+
win&=(1u<<L)-1;
|
|
67
|
+
const uint z=win*34038481u+76625530u;
|
|
68
|
+
const uint p=(z&0x00FF00FFu)+((z>>8)&0x00FF00FFu);
|
|
69
|
+
const int code=int((p&0xFFFFu)+(p>>16))-510;
|
|
70
|
+
// Expansion consumes the precise host LUT even for variant 6.
|
|
71
|
+
// Refine the reciprocal product before scaling and bf16 rounding.
|
|
72
|
+
const float reciprocal=1.0f/147.800537109375f;
|
|
73
|
+
const float approx=float(code)*reciprocal;
|
|
74
|
+
const float residual=metal::fma(-approx,147.800537109375f,float(code));
|
|
75
|
+
const float value=metal::fma(residual,reciprocal,approx);
|
|
76
|
+
w=T(value*float(scales[row]));
|
|
77
|
+
}
|
|
78
|
+
Bs[(b/BN)*BN+b%BN]=w;
|
|
79
|
+
}
|
|
80
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
81
|
+
#pragma clang loop unroll(full)
|
|
82
|
+
for(uint kk=0;kk<BK;kk+=8) {
|
|
83
|
+
metal::simdgroup_float8x8 a[TM],b[TN];
|
|
84
|
+
#pragma clang loop unroll(full)
|
|
85
|
+
for(uint i=0;i<TM;i++) {
|
|
86
|
+
a[i].thread_elements()[0]=float(As[(sm+i*SM)*BK+kk+fn]);
|
|
87
|
+
a[i].thread_elements()[1]=float(As[(sm+i*SM)*BK+kk+fn+1]);
|
|
88
|
+
}
|
|
89
|
+
#pragma clang loop unroll(full)
|
|
90
|
+
for(uint j=0;j<TN;j++) {
|
|
91
|
+
b[j].thread_elements()[0]=float(Bs[(kk+fm)*BN+sn+j*SN]);
|
|
92
|
+
b[j].thread_elements()[1]=float(Bs[(kk+fm)*BN+sn+j*SN+1]);
|
|
93
|
+
}
|
|
94
|
+
#pragma clang loop unroll(full)
|
|
95
|
+
for(uint i=0;i<TM;i++) {
|
|
96
|
+
#pragma clang loop unroll(full)
|
|
97
|
+
for(uint j=0;j<TN;j++) {
|
|
98
|
+
const uint nj=(i%2)?TN-1-j:j;
|
|
99
|
+
metal::simdgroup_multiply_accumulate(acc[i][nj],a[i],b[nj],acc[i][nj]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
104
|
+
}
|
|
105
|
+
#pragma clang loop unroll(full)
|
|
106
|
+
for(uint i=0;i<TM;i++) {
|
|
107
|
+
#pragma clang loop unroll(full)
|
|
108
|
+
for(uint j=0;j<TN;j++) {
|
|
109
|
+
const uint m=mbase+sm+i*SM,n=nbase+sn+j*SN;
|
|
110
|
+
if(m<M && n<N)out[(ulong)split*M*N+(ulong)m*N+n]=acc[i][j].thread_elements()[0];
|
|
111
|
+
if(m<M && n+1<N)out[(ulong)split*M*N+(ulong)m*N+n+1]=acc[i][j].thread_elements()[1];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
` });
|
|
115
|
+
reductionKernel ??= new MetalKernel({ name: "mlx_bun_trellis_splitk_accumulate",
|
|
116
|
+
inputNames: ["partials"], outputNames: ["out"], source: String.raw`
|
|
117
|
+
const uint i = thread_position_in_grid.x;
|
|
118
|
+
if (i >= SIZE) return;
|
|
119
|
+
float total = 0.0f;
|
|
120
|
+
for (uint s = 0; s < SPLITS; s++) total += partials[(ulong)s * SIZE + i];
|
|
121
|
+
out[i] = T(total);
|
|
122
|
+
` });
|
|
123
|
+
return [partialKernel, reductionKernel];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Borrow bf16 [M,K] and axis-0 1MAD weights; return owned lazy [M,N].
|
|
127
|
+
* M=5..8, T=256/L=12, k=2/3/4. Match MLX's non-NAX split-K matrix regime;
|
|
128
|
+
* other shapes and hardware require their own dispatch and full-model gates.
|
|
129
|
+
* Peak workspace is SPLITS*M*N float elements, with no dense weight buffer. */
|
|
130
|
+
export function splitKTrellisPrefill(
|
|
131
|
+
x: MlxArray, codes: MlxArray, scales: MlxArray, g: TrellisGeometry,
|
|
132
|
+
): MlxArray {
|
|
133
|
+
const [m, k] = x.shape, n = g.outFeatures;
|
|
134
|
+
if (x.ndim !== 2 || x.dtype !== Dtype.bfloat16 || !m || m < 5 || m > 8 ||
|
|
135
|
+
k !== g.inFeatures || g.axis !== 0 || g.T !== 256 || g.L !== 12 ||
|
|
136
|
+
![2, 3, 4].includes(g.k) || g.cols % 256 !== 0 || n < 1 ||
|
|
137
|
+
k < 128 || k < n || Math.ceil(m / 16) * Math.ceil(n / 16) > 1024)
|
|
138
|
+
throw new Error("unsupported split-K trellis prefill geometry or dtype");
|
|
139
|
+
const tilesM = Math.ceil(m / 32), tilesN = Math.ceil(n / 32), tilesK = Math.floor(k / 16);
|
|
140
|
+
const ratio = Math.max(1, Math.floor(tilesK / (tilesM * tilesN)));
|
|
141
|
+
const splits = Math.min(32, Math.max(2, 2 ** Math.ceil(Math.log2(ratio))));
|
|
142
|
+
const partition = Math.floor(tilesK / splits) * 16;
|
|
143
|
+
const [partial, reduce] = kernels();
|
|
144
|
+
const [workspace] = partial.apply([x, codes, scales], {
|
|
145
|
+
outputs: [{ shape: [splits, m, n], dtype: Dtype.float32 }],
|
|
146
|
+
grid: [Math.ceil(n / 32) * 128, 1, splits], threadGroup: [128, 1, 1],
|
|
147
|
+
templateDtypes: { T: x.dtype }, templateInts: { M: m, N: n, KC: k, C: g.cols,
|
|
148
|
+
BITS: g.k, L: g.L, BT: g.T, BM: 8, BN: 32, BK: 16, SPLITS: splits,
|
|
149
|
+
PARTITION: partition, INTERLEAVE: g.blockInterleave ?? 0 },
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
return reduce.apply([workspace!], {
|
|
153
|
+
outputs: [{ shape: [m, n], dtype: x.dtype }],
|
|
154
|
+
grid: [m * n, 1, 1], threadGroup: [256, 1, 1], templateDtypes: { T: x.dtype },
|
|
155
|
+
templateInts: { SIZE: m * n, SPLITS: splits },
|
|
156
|
+
})[0]!;
|
|
157
|
+
} finally { workspace!.dispose(); }
|
|
158
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Packed axis-1 trellis prefill without a full expanded weight allocation.
|
|
2
|
+
// SIMD-group fragment layout follows MLX steel/gemm/mma.h (MIT, Apple Inc.):
|
|
3
|
+
// https://github.com/ml-explore/mlx/blob/v0.31.2/mlx/backend/metal/kernels/steel/gemm/mma.h
|
|
4
|
+
import { MlxArray } from "../mlx/array";
|
|
5
|
+
import { Dtype } from "../mlx/ffi";
|
|
6
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
7
|
+
import type { TrellisGeometry } from "./trellis-linear";
|
|
8
|
+
import { nativeTrellisWidePrefill } from "./trellis-wide-prefill";
|
|
9
|
+
|
|
10
|
+
/** Dispatch profile measured on Qwen's MLP geometry. No model-name check. */
|
|
11
|
+
export function tiledTrellisPrefillEligible(g: TrellisGeometry, m: number, dtype: Dtype): boolean {
|
|
12
|
+
return dtype === Dtype.bfloat16 && m >= 5 && m <= 32 && !nativeTrellisWidePrefill(m) &&
|
|
13
|
+
g.axis === 1 && g.T === 256 && g.L === 12 && [2, 3, 4].includes(g.k) &&
|
|
14
|
+
g.inFeatures === 5120 && g.outFeatures === 17408;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let kernel: MetalKernel | undefined;
|
|
18
|
+
function tiledKernel(): MetalKernel {
|
|
19
|
+
return kernel ??= new MetalKernel({ name: "mlx_bun_trellis_prefill_tile",
|
|
20
|
+
inputNames: ["x", "codes", "scales"], outputNames: ["out"],
|
|
21
|
+
header: "#include <metal_simdgroup_matrix>\n",
|
|
22
|
+
source: String.raw`
|
|
23
|
+
const uint tid=thread_position_in_threadgroup.x;
|
|
24
|
+
const uint lane=thread_index_in_simdgroup;
|
|
25
|
+
const uint sg=simdgroup_index_in_threadgroup;
|
|
26
|
+
const uint mbase=threadgroup_position_in_grid.y*BM;
|
|
27
|
+
const uint nbase=threadgroup_position_in_grid.x*BN;
|
|
28
|
+
const uint fm=((lane/4)&4)+((lane/2)%4);
|
|
29
|
+
const uint fn=((lane/4)&2)*2+(lane%2)*2;
|
|
30
|
+
// An eight-row tile assigns all four SIMD groups to output columns.
|
|
31
|
+
// Each output keeps the same 8x8 products and K accumulation order.
|
|
32
|
+
constexpr int WM=BM==8?1:2, WN=4/WM, SM=WM*8, SN=WN*8;
|
|
33
|
+
const uint sm=(sg/WN)*8+fm, sn=(sg%WN)*8+fn;
|
|
34
|
+
constexpr int TM=BM/SM, TN=BN/SN;
|
|
35
|
+
threadgroup T As[BM*BK];
|
|
36
|
+
threadgroup T Bs[BK*BN];
|
|
37
|
+
metal::simdgroup_float8x8 acc[TM][TN];
|
|
38
|
+
#pragma clang loop unroll(full)
|
|
39
|
+
for(uint i=0;i<TM;i++) {
|
|
40
|
+
#pragma clang loop unroll(full)
|
|
41
|
+
for(uint j=0;j<TN;j++) {
|
|
42
|
+
acc[i][j].thread_elements()[0]=0.0f;
|
|
43
|
+
acc[i][j].thread_elements()[1]=0.0f;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
constexpr uint wpb=BT*BITS/32;
|
|
47
|
+
for(uint kb=0;kb<KC;kb+=BK) {
|
|
48
|
+
for(uint a=tid;a<BM*BK;a+=128) {
|
|
49
|
+
const uint m=mbase+a/BK, k=kb+a%BK;
|
|
50
|
+
As[a]=(m<M && k<KC)?x[(ulong)m*KC+k]:T(0);
|
|
51
|
+
}
|
|
52
|
+
for(uint b=tid;b<BK*BN;b+=128) {
|
|
53
|
+
const uint k=kb+b/BN, n=nbase+b%BN;
|
|
54
|
+
T w=T(0);
|
|
55
|
+
if(k<KC && n<N) {
|
|
56
|
+
const uint row=AXIS==1?n:k;
|
|
57
|
+
const uint col=AXIS==1?k:n;
|
|
58
|
+
const uint block=col/BT, t=col%BT;
|
|
59
|
+
const device uint* bw=codes+(ulong)row*(C/BT)*wpb+block*wpb;
|
|
60
|
+
const uint pos=(BT-1-t)*BITS, wi=pos>>5, off=pos&31u;
|
|
61
|
+
uint win=bw[wi]>>off;
|
|
62
|
+
if(off+L>32)win|=bw[wi+1==wpb?0:wi+1]<<(32-off);
|
|
63
|
+
win&=(1u<<L)-1;
|
|
64
|
+
const uint z=win*34038481u+76625530u;
|
|
65
|
+
const uint p=(z&0x00FF00FFu)+((z>>8)&0x00FF00FFu);
|
|
66
|
+
const int code=int((p&0xFFFFu)+(p>>16))-510;
|
|
67
|
+
// Expansion consumes the precise host LUT even for variant 6.
|
|
68
|
+
// Refine the reciprocal product before scaling and bf16 rounding.
|
|
69
|
+
const float reciprocal=1.0f/147.800537109375f;
|
|
70
|
+
const float approx=float(code)*reciprocal;
|
|
71
|
+
const float residual=metal::fma(-approx,147.800537109375f,float(code));
|
|
72
|
+
const float value=metal::fma(residual,reciprocal,approx);
|
|
73
|
+
w=T(value*float(scales[row]));
|
|
74
|
+
}
|
|
75
|
+
Bs[b]=w;
|
|
76
|
+
}
|
|
77
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
78
|
+
#pragma clang loop unroll(full)
|
|
79
|
+
for(uint kk=0;kk<BK;kk+=8) {
|
|
80
|
+
metal::simdgroup_float8x8 a[TM],b[TN];
|
|
81
|
+
#pragma clang loop unroll(full)
|
|
82
|
+
for(uint i=0;i<TM;i++) {
|
|
83
|
+
a[i].thread_elements()[0]=float(As[(sm+i*SM)*BK+kk+fn]);
|
|
84
|
+
a[i].thread_elements()[1]=float(As[(sm+i*SM)*BK+kk+fn+1]);
|
|
85
|
+
}
|
|
86
|
+
#pragma clang loop unroll(full)
|
|
87
|
+
for(uint j=0;j<TN;j++) {
|
|
88
|
+
b[j].thread_elements()[0]=float(Bs[(kk+fm)*BN+sn+j*SN]);
|
|
89
|
+
b[j].thread_elements()[1]=float(Bs[(kk+fm)*BN+sn+j*SN+1]);
|
|
90
|
+
}
|
|
91
|
+
#pragma clang loop unroll(full)
|
|
92
|
+
for(uint i=0;i<TM;i++) {
|
|
93
|
+
#pragma clang loop unroll(full)
|
|
94
|
+
for(uint j=0;j<TN;j++) {
|
|
95
|
+
const uint nj=(i%2)?TN-1-j:j;
|
|
96
|
+
metal::simdgroup_multiply_accumulate(acc[i][nj],a[i],b[nj],acc[i][nj]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
101
|
+
}
|
|
102
|
+
#pragma clang loop unroll(full)
|
|
103
|
+
for(uint i=0;i<TM;i++) {
|
|
104
|
+
#pragma clang loop unroll(full)
|
|
105
|
+
for(uint j=0;j<TN;j++) {
|
|
106
|
+
const uint m=mbase+sm+i*SM,n=nbase+sn+j*SN;
|
|
107
|
+
if(m<M && n<N)out[(ulong)m*N+n]=T(acc[i][j].thread_elements()[0]);
|
|
108
|
+
if(m<M && n+1<N)out[(ulong)m*N+n+1]=T(acc[i][j].thread_elements()[1]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
` });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Shared operation for bf16 [M, K], axis-1 1MAD, T=256/L=12, k=2/3/4.
|
|
115
|
+
* M=5..32. Borrow inputs and return owned [M, N]; caller controls evaluation.
|
|
116
|
+
* Reconstruct the precise LUT value before bf16 weight rounding. The default
|
|
117
|
+
* packed matvec's unrefined f32 code value is a different numerical contract.
|
|
118
|
+
* Other geometries need a measured dispatch profile and model-level gates. */
|
|
119
|
+
export function tiledTrellisPrefill(
|
|
120
|
+
x: MlxArray, codes: MlxArray, scales: MlxArray, g: TrellisGeometry,
|
|
121
|
+
): MlxArray {
|
|
122
|
+
const [m, k] = x.shape;
|
|
123
|
+
if (x.ndim !== 2 || x.dtype !== Dtype.bfloat16 || !m || m < 5 || m > 32 ||
|
|
124
|
+
k !== g.inFeatures || g.axis !== 1 || g.T !== 256 || g.L !== 12 ||
|
|
125
|
+
![2, 3, 4].includes(g.k) || g.cols % 256 !== 0 || g.rows < 1)
|
|
126
|
+
throw new Error("unsupported tiled trellis prefill geometry or dtype");
|
|
127
|
+
const bm = m <= 8 ? 8 : m <= 16 ? 16 : 32, bn = 64, bk = 16;
|
|
128
|
+
const [out] = tiledKernel().apply([x, codes, scales], {
|
|
129
|
+
outputs: [{ shape: [m, g.outFeatures], dtype: x.dtype }],
|
|
130
|
+
grid: [Math.ceil(g.outFeatures / bn) * 128, Math.ceil(m / bm), 1],
|
|
131
|
+
threadGroup: [128, 1, 1], templateDtypes: { T: x.dtype },
|
|
132
|
+
templateInts: { M: m, N: g.outFeatures, KC: g.inFeatures, C: g.cols,
|
|
133
|
+
BITS: g.k, L: g.L, BT: g.T, AXIS: g.axis, BM: bm, BN: bn, BK: bk },
|
|
134
|
+
});
|
|
135
|
+
return out!;
|
|
136
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Four adjacent weights share one circular packed-word window. Keep the
|
|
2
|
+
// precise expansion values and MLX's matmul/evaluation boundary unchanged.
|
|
3
|
+
import { MlxArray } from "../mlx/array";
|
|
4
|
+
import { Dtype } from "../mlx/ffi";
|
|
5
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
6
|
+
import type { TrellisGeometry } from "./trellis-linear";
|
|
7
|
+
|
|
8
|
+
const kernel = new MetalKernel({
|
|
9
|
+
name: "mlx_bun_trellis_vector_expand",
|
|
10
|
+
inputNames: ["codes", "scales"], outputNames: ["out"],
|
|
11
|
+
source: String.raw`
|
|
12
|
+
uint c = thread_position_in_grid.x * 4;
|
|
13
|
+
uint r = thread_position_in_grid.y;
|
|
14
|
+
if constexpr (ROW_TILE > 0) {
|
|
15
|
+
constexpr uint groups = C / (BT * 2);
|
|
16
|
+
const uint tg = threadgroup_position_in_grid.x;
|
|
17
|
+
const uint tile = tg / (groups * ROW_TILE), within = tg % (groups * ROW_TILE);
|
|
18
|
+
r = tile * ROW_TILE + within % ROW_TILE;
|
|
19
|
+
c = (within / ROW_TILE) * BT * 2 + thread_position_in_threadgroup.x * 4;
|
|
20
|
+
}
|
|
21
|
+
if (c >= C || r >= R) return;
|
|
22
|
+
constexpr uint wpb = BT * BITS / 32;
|
|
23
|
+
const uint block = c / BT, t = c % BT;
|
|
24
|
+
const device uint* bw = codes + (INTERLEAVE
|
|
25
|
+
? (ulong)(block / 2) * R * 2 * wpb + (ulong)r * 2 * wpb + (block % 2) * wpb
|
|
26
|
+
: (ulong)r * (C / BT) * wpb + block * wpb);
|
|
27
|
+
const float scale = float(scales[r]);
|
|
28
|
+
// The last of four weights starts the window. Earlier weights occupy
|
|
29
|
+
// its higher bits; a second word also handles the circular block edge.
|
|
30
|
+
const uint pos = (BT - 1 - (t + 3)) * BITS, wi = pos >> 5, off = pos & 31u;
|
|
31
|
+
const ulong window = (ulong(bw[wi]) | (ulong(bw[wi + 1 == wpb ? 0 : wi + 1]) << 32)) >> off;
|
|
32
|
+
#pragma clang loop unroll(full)
|
|
33
|
+
for (uint i = 0; i < 4; i++) {
|
|
34
|
+
const uint s = uint(window >> ((3 - i) * BITS)) & ((1u << L) - 1);
|
|
35
|
+
const uint z = s * 34038481u + 76625530u;
|
|
36
|
+
const uint p = (z & 0x00FF00FFu) + ((z >> 8) & 0x00FF00FFu);
|
|
37
|
+
const float y = float(int((p & 0xFFFFu) + (p >> 16)) - 510);
|
|
38
|
+
// Residual refinement reproduces the host f32 LUT before scaling.
|
|
39
|
+
const float reciprocal = 1.0f / 147.800537109375f;
|
|
40
|
+
const float q = y * reciprocal;
|
|
41
|
+
const float value = metal::fma(metal::fma(-q, 147.800537109375f, y), reciprocal, q);
|
|
42
|
+
out[(ulong)r * C + c + i] = T(value * scale);
|
|
43
|
+
}
|
|
44
|
+
`,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export function vectorTrellisExpandEligible(g: TrellisGeometry, dtype: Dtype): boolean {
|
|
48
|
+
return dtype === Dtype.bfloat16 && g.L === 12 && g.T === 256 &&
|
|
49
|
+
(g.k === 2 || g.k === 3 || g.k === 4) && g.rows > 0 && g.cols > 0 && g.cols % 256 === 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Decode the stored matrix; the caller owns the projection's evaluation boundary. */
|
|
53
|
+
export function vectorTrellisExpand(codes: MlxArray, scales: MlxArray, g: TrellisGeometry): MlxArray {
|
|
54
|
+
const rowTile = g.blockInterleave && g.rows % 128 === 0 ? 128 : 0;
|
|
55
|
+
return kernel.apply([codes, scales], {
|
|
56
|
+
outputs: [{ shape: [g.rows, g.cols], dtype: Dtype.bfloat16 }],
|
|
57
|
+
grid: rowTile ? [g.cols / 4 * g.rows, 1, 1] : [g.cols / 4, g.rows, 1], threadGroup: [128, 1, 1],
|
|
58
|
+
templateDtypes: { T: Dtype.bfloat16 },
|
|
59
|
+
templateInts: { R: g.rows, C: g.cols, BT: g.T, BITS: g.k, L: g.L,
|
|
60
|
+
INTERLEAVE: g.blockInterleave ?? 0, ROW_TILE: rowTile },
|
|
61
|
+
})[0]!;
|
|
62
|
+
}
|