@mlx-bun/inference 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +1475 -2
- package/THIRD_PARTY_NOTICES.md +3404 -0
- package/dist/native/libmlx_bun_ane.dylib +0 -0
- package/dist/native/libmlx_bun_expert_io.dylib +0 -0
- package/dist/native/mlx-bun-frame-extract +0 -0
- package/examples/fit-model.ts +25 -0
- package/examples/qwen3-forward.ts +42 -0
- package/examples/qwen3-generate.ts +28 -0
- package/examples/trellis-expand.ts +23 -0
- package/package.json +129 -4
- package/src/adapters/available.ts +110 -0
- package/src/adapters/index.ts +3 -0
- package/src/adapters/manager.ts +316 -0
- package/src/artifacts/auxiliary-files.ts +45 -0
- package/src/artifacts/config-dialects.ts +94 -0
- package/src/artifacts/config.ts +341 -0
- package/src/artifacts/draft-vocabulary.ts +30 -0
- package/src/artifacts/expert-offload-build.ts +75 -0
- package/src/artifacts/expert-offload.ts +89 -0
- package/src/artifacts/experts/index.ts +3 -0
- package/src/artifacts/experts/io.ts +386 -0
- package/src/artifacts/experts/residency.ts +1134 -0
- package/src/artifacts/experts/usage.ts +415 -0
- package/src/artifacts/fingerprint.ts +44 -0
- package/src/artifacts/glm52/container.ts +305 -0
- package/src/artifacts/glm52/expert-layout.ts +249 -0
- package/src/artifacts/glm52/index.ts +7 -0
- package/src/artifacts/glm52/kernel-layout.ts +124 -0
- package/src/artifacts/glm52/layout.ts +152 -0
- package/src/artifacts/glm52/memory.ts +500 -0
- package/src/artifacts/glm52/resident-weights.ts +135 -0
- package/src/artifacts/glm52/weights.ts +503 -0
- package/src/artifacts/glm52-config.ts +262 -0
- package/src/artifacts/identity.ts +94 -0
- package/src/artifacts/index.ts +15 -0
- package/src/artifacts/mmap.ts +90 -0
- package/src/artifacts/safetensors-writer.ts +249 -0
- package/src/artifacts/safetensors.ts +164 -0
- package/src/artifacts/turboquant-fold.ts +42 -0
- package/src/artifacts/weight-audit.ts +43 -0
- package/src/artifacts/weights.ts +255 -0
- package/src/contracts/index.ts +2 -0
- package/src/contracts/mlx/attention.ts +8 -0
- package/src/contracts/mlx/cache.ts +510 -0
- package/src/contracts/mlx/checkpoint.ts +10 -0
- package/src/contracts/mlx/draft-checkpoint.ts +14 -0
- package/src/contracts/mlx/draft-projection.ts +27 -0
- package/src/contracts/mlx/draft-target.ts +84 -0
- package/src/contracts/mlx/drafter.ts +176 -0
- package/src/contracts/mlx/forward-work.ts +23 -0
- package/src/contracts/mlx/glm52-weights.ts +25 -0
- package/src/contracts/mlx/graph.ts +309 -0
- package/src/contracts/mlx/head.ts +6 -0
- package/src/contracts/mlx/index.ts +15 -0
- package/src/contracts/mlx/kv-codec.ts +9 -0
- package/src/contracts/mlx/media.ts +113 -0
- package/src/contracts/mlx/positions.ts +16 -0
- package/src/contracts/mlx/row-table.ts +2 -0
- package/src/contracts/mlx/trainable.ts +103 -0
- package/src/contracts/mlx/whisper.ts +96 -0
- package/src/contracts/portable/composition.ts +106 -0
- package/src/contracts/portable/denoising.ts +17 -0
- package/src/contracts/portable/execution.ts +79 -0
- package/src/contracts/portable/generation.ts +103 -0
- package/src/contracts/portable/graph.ts +80 -0
- package/src/contracts/portable/index.ts +15 -0
- package/src/contracts/portable/memory-plan.ts +26 -0
- package/src/contracts/portable/object-cache.ts +8 -0
- package/src/contracts/portable/prefill.ts +63 -0
- package/src/contracts/portable/prefix-cache.ts +33 -0
- package/src/contracts/portable/prompt.ts +2 -0
- package/src/contracts/portable/resources.ts +25 -0
- package/src/contracts/portable/rollback.ts +7 -0
- package/src/contracts/portable/sampling.ts +44 -0
- package/src/contracts/portable/scheduling.ts +54 -0
- package/src/contracts/portable/token-delivery.ts +4 -0
- package/src/contracts/portable/tsconfig.json +14 -0
- package/src/embeddings/index.ts +1 -0
- package/src/embeddings/text.ts +85 -0
- package/src/execution/admission.ts +59 -0
- package/src/execution/batch-group.ts +1240 -0
- package/src/execution/batch-types.ts +225 -0
- package/src/execution/cancellation.ts +38 -0
- package/src/execution/continuation-request.ts +45 -0
- package/src/execution/continuation-types.ts +10 -0
- package/src/execution/coordinator.ts +70 -0
- package/src/execution/denoising-group.ts +180 -0
- package/src/execution/engine.ts +56 -0
- package/src/execution/fill-group.ts +505 -0
- package/src/execution/fit.ts +184 -0
- package/src/execution/gateway-binding.ts +178 -0
- package/src/execution/grammar-group.ts +224 -0
- package/src/execution/index.ts +18 -0
- package/src/execution/methods.ts +75 -0
- package/src/execution/mixed-iteration.ts +96 -0
- package/src/execution/plan.ts +65 -0
- package/src/execution/prefill-cohort.ts +100 -0
- package/src/execution/prefill-rows.ts +263 -0
- package/src/execution/request-policy.ts +19 -0
- package/src/execution/row-sampling.ts +49 -0
- package/src/execution/scheduler.ts +92 -0
- package/src/execution/session.ts +247 -0
- package/src/execution/speculative-group.ts +797 -0
- package/src/execution/tasks.ts +44 -0
- package/src/generation/autoregressive.ts +840 -0
- package/src/generation/bindings/autoregressive.ts +104 -0
- package/src/generation/bindings/denoising.ts +60 -0
- package/src/generation/bindings/prefill-policy.ts +36 -0
- package/src/generation/checkpoint-identity.ts +60 -0
- package/src/generation/denoising.ts +85 -0
- package/src/generation/diffusion.ts +583 -0
- package/src/generation/fill/echo-index.ts +188 -0
- package/src/generation/fill/index.ts +3 -0
- package/src/generation/fill/proposal.ts +69 -0
- package/src/generation/fill/schema-rows.ts +506 -0
- package/src/generation/fill/session.ts +464 -0
- package/src/generation/fill/tool-boundary.ts +103 -0
- package/src/generation/fill-append.ts +58 -0
- package/src/generation/generate.ts +44 -0
- package/src/generation/grammar-step.ts +39 -0
- package/src/generation/index.ts +14 -0
- package/src/generation/phase-dispatch.ts +115 -0
- package/src/generation/prefill.ts +33 -0
- package/src/generation/result.ts +49 -0
- package/src/generation/scopes.ts +157 -0
- package/src/generation/speculative/acceptance.ts +41 -0
- package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
- package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
- package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
- package/src/generation/speculative/bindings/binding.ts +106 -0
- package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
- package/src/generation/speculative/bindings/draft-target.ts +10 -0
- package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
- package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
- package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
- package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
- package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
- package/src/generation/speculative/cache-identity.ts +16 -0
- package/src/generation/speculative/draft-kind.ts +149 -0
- package/src/generation/speculative/draft-policy.ts +41 -0
- package/src/generation/speculative/draft-registry.ts +118 -0
- package/src/generation/speculative/draft-vocab.ts +110 -0
- package/src/generation/speculative/index.ts +17 -0
- package/src/generation/speculative/output.ts +49 -0
- package/src/generation/speculative/round.ts +307 -0
- package/src/generation/speculative/source.ts +144 -0
- package/src/generation/speculative/sources/assistant-source.ts +64 -0
- package/src/generation/speculative/sources/deepspec-source.ts +70 -0
- package/src/generation/speculative/sources/dflash-source.ts +84 -0
- package/src/generation/speculative/sources/dflash2-source.ts +86 -0
- package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
- package/src/generation/speculative/sources/ngram-source.ts +165 -0
- package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
- package/src/generation/speculative/sources/two-model.ts +75 -0
- package/src/generation/types.ts +194 -0
- package/src/index.ts +20 -0
- package/src/input/audio/audiotoolbox.ts +112 -0
- package/src/input/audio/decode.ts +133 -0
- package/src/input/audio/features.ts +202 -0
- package/src/input/audio/index.ts +6 -0
- package/src/input/audio/transcode.ts +67 -0
- package/src/input/audio/whisper-mel.ts +185 -0
- package/src/input/audio/whisper-tokenizer.ts +238 -0
- package/src/input/chat-template.ts +389 -0
- package/src/input/index.ts +6 -0
- package/src/input/media-fetch.ts +439 -0
- package/src/input/packed-mixed.ts +43 -0
- package/src/input/special-tokens.ts +75 -0
- package/src/input/token-groups.ts +49 -0
- package/src/input/tokenizer.ts +336 -0
- package/src/input/tool-call.ts +689 -0
- package/src/input/vision/index.ts +3 -0
- package/src/input/vision/preprocess.ts +247 -0
- package/src/input/vision/prompt.ts +398 -0
- package/src/input/vision/qwen3vl-preprocess.ts +409 -0
- package/src/input/vision/qwen3vl-prompt.ts +237 -0
- package/src/input/vision/video-frames.ts +100 -0
- package/src/kernels/ane/fill.ts +101 -0
- package/src/kernels/ane/linear.ts +416 -0
- package/src/kernels/attention/flash.ts +694 -0
- package/src/kernels/attention/kvq4-decode.ts +155 -0
- package/src/kernels/attention/masks.ts +40 -0
- package/src/kernels/attention/multi-query.ts +193 -0
- package/src/kernels/attention/paged.ts +130 -0
- package/src/kernels/delta/gated.ts +306 -0
- package/src/kernels/delta/index.ts +3 -0
- package/src/kernels/delta/silu-rms-heads.ts +69 -0
- package/src/kernels/delta/state.ts +74 -0
- package/src/kernels/gated-rms-norm.ts +64 -0
- package/src/kernels/glm52/layout.ts +37 -0
- package/src/kernels/glm52/streamed-experts.ts +360 -0
- package/src/kernels/logits.ts +38 -0
- package/src/kernels/quantization/affine-row.ts +154 -0
- package/src/kernels/quantization/affine-rows.ts +36 -0
- package/src/kernels/quantization/affine-split-mma.ts +144 -0
- package/src/kernels/quantization/affine3-mma.ts +117 -0
- package/src/kernels/quantization/affine3-rows.ts +103 -0
- package/src/kernels/quantization/affine4-mma.ts +113 -0
- package/src/kernels/quantization/affine4-rows.ts +102 -0
- package/src/kernels/residual-rms-norm.ts +65 -0
- package/src/kernels/sampling/index.ts +2 -0
- package/src/kernels/sampling/normalized-argmax.ts +116 -0
- package/src/kernels/sampling/token-bitmask.ts +52 -0
- package/src/kernels/softcap.ts +35 -0
- package/src/kernels/trellis/codebook.ts +123 -0
- package/src/kernels/trellis/down-codebook.ts +184 -0
- package/src/kernels/trellis/down-factored.ts +180 -0
- package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
- package/src/kernels/trellis/down-mma-wide.ts +195 -0
- package/src/kernels/trellis/down-mma.ts +145 -0
- package/src/kernels/trellis/expand.ts +42 -0
- package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
- package/src/kernels/trellis/gate-up-codebook.ts +137 -0
- package/src/kernels/trellis/gate-up-factored.ts +197 -0
- package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
- package/src/kernels/trellis/gate-up-mma.ts +148 -0
- package/src/kernels/trellis/gate-up.ts +248 -0
- package/src/kernels/trellis/geometry.ts +22 -0
- package/src/kernels/trellis/index.ts +103 -0
- package/src/kernels/trellis/launch.ts +12 -0
- package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
- package/src/kernels/trellis/mixed-gate-up.ts +146 -0
- package/src/kernels/trellis/qualified.ts +34 -0
- package/src/kernels/trellis/reduce.ts +150 -0
- package/src/kernels/trellis/scatter.ts +407 -0
- package/src/kernels/trellis/splitk-prefill.ts +152 -0
- package/src/kernels/trellis/tiled-prefill.ts +131 -0
- package/src/kernels/trellis/vector-expand.ts +58 -0
- package/src/kernels/trellis/wide-prefill.ts +89 -0
- package/src/kernels/turboquant/decode.ts +82 -0
- package/src/kernels/turboquant/index.ts +5 -0
- package/src/kernels/turboquant/inverse.ts +122 -0
- package/src/kernels/turboquant/ops.ts +860 -0
- package/src/kernels/turboquant/signs.ts +130 -0
- package/src/kernels/turboquant/tables.ts +124 -0
- package/src/layers/affine-verify-linear.ts +75 -0
- package/src/layers/ane-prefill-split.ts +283 -0
- package/src/layers/compiled-normalization.ts +41 -0
- package/src/layers/delta-heads.ts +23 -0
- package/src/layers/dense-embedding.ts +29 -0
- package/src/layers/dense-linear.ts +32 -0
- package/src/layers/dequantized-linear.ts +72 -0
- package/src/layers/folded-attention.ts +42 -0
- package/src/layers/geglu.ts +130 -0
- package/src/layers/helpers.ts +7 -0
- package/src/layers/index.ts +36 -0
- package/src/layers/kv4-decode-attention.ts +15 -0
- package/src/layers/layer-norm.ts +24 -0
- package/src/layers/loading.ts +92 -0
- package/src/layers/lora.ts +150 -0
- package/src/layers/normalization.ts +9 -0
- package/src/layers/prefix-layout.ts +33 -0
- package/src/layers/quantized-append-attention.ts +32 -0
- package/src/layers/quantized-attention.ts +353 -0
- package/src/layers/quantized-embedding.ts +39 -0
- package/src/layers/quantized-linear.ts +137 -0
- package/src/layers/quantized-switch-linear.ts +37 -0
- package/src/layers/qwen-append.ts +12 -0
- package/src/layers/qwen-mrope.ts +238 -0
- package/src/layers/rope-2d.ts +110 -0
- package/src/layers/rope.ts +389 -0
- package/src/layers/swiglu.ts +23 -0
- package/src/layers/trellis-codebook-down.ts +52 -0
- package/src/layers/trellis-codebook-gate-up.ts +42 -0
- package/src/layers/trellis-down.ts +117 -0
- package/src/layers/trellis-gate-up.ts +71 -0
- package/src/layers/trellis-linear.ts +211 -0
- package/src/layers/trellis-prompt.ts +102 -0
- package/src/layers/trellis-reduce-gate-up.ts +73 -0
- package/src/layers/trellis-scatter-down.ts +98 -0
- package/src/models/ane-install.ts +74 -0
- package/src/models/audio/conformer.ts +656 -0
- package/src/models/cache-composition.ts +44 -0
- package/src/models/capabilities.ts +89 -0
- package/src/models/chat-template.ts +10 -0
- package/src/models/composition.ts +107 -0
- package/src/models/diffusion-gemma/experts.ts +59 -0
- package/src/models/diffusion-gemma/family.ts +21 -0
- package/src/models/diffusion-gemma/model.ts +900 -0
- package/src/models/diffusion-gemma/vision.ts +549 -0
- package/src/models/drafter-loaders.ts +49 -0
- package/src/models/drafters.ts +39 -0
- package/src/models/factory.ts +351 -0
- package/src/models/families.ts +70 -0
- package/src/models/family.ts +125 -0
- package/src/models/gemma4/assistant-target.ts +46 -0
- package/src/models/gemma4/assistant.ts +327 -0
- package/src/models/gemma4/blocks.ts +755 -0
- package/src/models/gemma4/compiled-decode.ts +452 -0
- package/src/models/gemma4/family.ts +39 -0
- package/src/models/gemma4/gemma4-12b.ts +677 -0
- package/src/models/gemma4/gemma4-26b.ts +544 -0
- package/src/models/gemma4/gemma4-e4b.ts +599 -0
- package/src/models/gemma4/graphs.ts +147 -0
- package/src/models/gemma4/media.ts +88 -0
- package/src/models/gemma4/model.ts +860 -0
- package/src/models/gemma4/serving-blocks.ts +72 -0
- package/src/models/gemma4/trainable.ts +186 -0
- package/src/models/gemma4/written-blocks.ts +232 -0
- package/src/models/gemma4/written-forward.ts +159 -0
- package/src/models/gemma4/written-input.ts +29 -0
- package/src/models/glm52/atlas.ts +507 -0
- package/src/models/glm52/coupling.ts +388 -0
- package/src/models/glm52/dsa.ts +601 -0
- package/src/models/glm52/family.ts +20 -0
- package/src/models/glm52/mla.ts +812 -0
- package/src/models/glm52/model.ts +1127 -0
- package/src/models/glm52/moe.ts +223 -0
- package/src/models/glm52/mtp.ts +60 -0
- package/src/models/glm52/pilot.ts +537 -0
- package/src/models/glm52/reference.ts +445 -0
- package/src/models/glm52/residency.ts +557 -0
- package/src/models/glm52/streamed-experts.ts +447 -0
- package/src/models/graph.ts +59 -0
- package/src/models/implementation.ts +230 -0
- package/src/models/index.ts +15 -0
- package/src/models/media-input.ts +15 -0
- package/src/models/memory-plan.ts +39 -0
- package/src/models/minicpm5/family.ts +29 -0
- package/src/models/minicpm5/model.ts +394 -0
- package/src/models/minicpm5/trainable.ts +90 -0
- package/src/models/profile.ts +337 -0
- package/src/models/qwen/blocks.ts +658 -0
- package/src/models/qwen/checkpoint.ts +190 -0
- package/src/models/qwen/families.ts +57 -0
- package/src/models/qwen/media-input.ts +161 -0
- package/src/models/qwen/mtp.ts +101 -0
- package/src/models/qwen/qwen3-moe.ts +355 -0
- package/src/models/qwen/qwen3.ts +323 -0
- package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
- package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
- package/src/models/qwen/qwen3_5.ts +433 -0
- package/src/models/qwen/written-layers.ts +285 -0
- package/src/models/row-table.ts +1 -0
- package/src/models/runtime.ts +73 -0
- package/src/models/speculative/deepspec-artifact.ts +10 -0
- package/src/models/speculative/deepspec.ts +744 -0
- package/src/models/speculative/dflash.ts +553 -0
- package/src/models/speculative/dflash2-artifact.ts +9 -0
- package/src/models/speculative/dflash2-operations.ts +1 -0
- package/src/models/speculative/dflash2.ts +576 -0
- package/src/models/speculative/loader.ts +29 -0
- package/src/models/support.ts +79 -0
- package/src/models/universal/archs.ts +365 -0
- package/src/models/universal/blocks.ts +479 -0
- package/src/models/universal/dense.ts +255 -0
- package/src/models/universal/family.ts +17 -0
- package/src/models/vision/qwen3vl.ts +566 -0
- package/src/models/vision/siglip.ts +520 -0
- package/src/models/vision/unified.ts +176 -0
- package/src/models/whisper/family.ts +19 -0
- package/src/models/whisper/fast.ts +734 -0
- package/src/models/whisper/model.ts +430 -0
- package/src/runtime/compiled-trace.ts +16 -0
- package/src/runtime/config.ts +77 -0
- package/src/runtime/expert-trace.ts +71 -0
- package/src/runtime/graph-forward.ts +65 -0
- package/src/runtime/integers.ts +6 -0
- package/src/runtime/native.ts +16 -0
- package/src/runtime/resources.ts +53 -0
- package/src/runtime/row-operations.ts +42 -0
- package/src/runtime/trace.ts +161 -0
- package/src/sampling/curve.ts +122 -0
- package/src/sampling/draft.ts +55 -0
- package/src/sampling/extras.ts +45 -0
- package/src/sampling/filters.ts +134 -0
- package/src/sampling/grammar.ts +630 -0
- package/src/sampling/hlg.ts +290 -0
- package/src/sampling/index.ts +9 -0
- package/src/sampling/processors.ts +142 -0
- package/src/sampling/sampler.ts +120 -0
- package/src/sampling/step.ts +343 -0
- package/src/sampling/types.ts +161 -0
- package/src/sampling/window-filters.ts +46 -0
- package/src/scoring/decisions.ts +96 -0
- package/src/scoring/full-sequence.ts +103 -0
- package/src/scoring/index.ts +9 -0
- package/src/scoring/kl.ts +29 -0
- package/src/scoring/perplexity.ts +165 -0
- package/src/scoring/response-logps.ts +65 -0
- package/src/state/affine-attention.ts +180 -0
- package/src/state/attachment.ts +68 -0
- package/src/state/attention-read.ts +215 -0
- package/src/state/batched-kv.ts +294 -0
- package/src/state/batched-mask.ts +445 -0
- package/src/state/batched-quant.ts +389 -0
- package/src/state/batched-quantized-kv.ts +209 -0
- package/src/state/batched-rotating-quant.ts +457 -0
- package/src/state/batched-rotating-state.ts +149 -0
- package/src/state/batched-rotating.ts +430 -0
- package/src/state/batched-row-storage.ts +179 -0
- package/src/state/batched-ssm.ts +54 -0
- package/src/state/batched-turboquant-kv.ts +144 -0
- package/src/state/bf16-first-kv.ts +663 -0
- package/src/state/cache-service.ts +592 -0
- package/src/state/capabilities.ts +65 -0
- package/src/state/checkpoint.ts +46 -0
- package/src/state/continuation-persistence.ts +126 -0
- package/src/state/decode-step-inputs.ts +31 -0
- package/src/state/decoded-kv-donor.ts +12 -0
- package/src/state/delayed-quantized-kv.ts +141 -0
- package/src/state/delayed-rotating-quantized-kv.ts +178 -0
- package/src/state/delayed-turboquant-kv.ts +123 -0
- package/src/state/durability.ts +214 -0
- package/src/state/encoder-cache.ts +26 -0
- package/src/state/full-kv-row-append.ts +33 -0
- package/src/state/full-kv-row-donor.ts +54 -0
- package/src/state/full-prefill-padding.ts +56 -0
- package/src/state/full-prefill-row.ts +112 -0
- package/src/state/full-transitioning-kv-rows.ts +70 -0
- package/src/state/glm52-cache.ts +783 -0
- package/src/state/grouped-heads-kv4-cache.ts +26 -0
- package/src/state/index.ts +38 -0
- package/src/state/kv-attention-view.ts +77 -0
- package/src/state/kv-budget.ts +14 -0
- package/src/state/kv-scheme.ts +315 -0
- package/src/state/kv-storage.ts +57 -0
- package/src/state/kv-tensor-rows.ts +212 -0
- package/src/state/kv.ts +324 -0
- package/src/state/kv4-head256-cache.ts +46 -0
- package/src/state/latent-legos.ts +895 -0
- package/src/state/layout.ts +277 -0
- package/src/state/leases.ts +26 -0
- package/src/state/paged/cache.ts +689 -0
- package/src/state/paged/codec.ts +36 -0
- package/src/state/paged/rows.ts +223 -0
- package/src/state/persistence-types.ts +173 -0
- package/src/state/persistence-worker.ts +83 -0
- package/src/state/persistence.ts +1036 -0
- package/src/state/persistence.worker.d.ts +3 -0
- package/src/state/persistence.worker.js +264 -0
- package/src/state/prefix-cache.ts +685 -0
- package/src/state/quantized-kv.ts +369 -0
- package/src/state/quantized-tensor.ts +26 -0
- package/src/state/request-policy.ts +48 -0
- package/src/state/retention-policy.ts +37 -0
- package/src/state/rollback.ts +131 -0
- package/src/state/rotating-kv-donor.ts +35 -0
- package/src/state/rotating-kv-layout.ts +341 -0
- package/src/state/rotating-kv.ts +529 -0
- package/src/state/rotating-quantized-kv.ts +616 -0
- package/src/state/rotating-row-transaction.ts +35 -0
- package/src/state/rows.ts +104 -0
- package/src/state/softcap-attention.ts +118 -0
- package/src/state/speculative/glm52-mtp-state.ts +38 -0
- package/src/state/speculative/qwen-mtp-state.ts +34 -0
- package/src/state/speculative-rotating-kv.ts +113 -0
- package/src/state/ssd-cache.ts +434 -0
- package/src/state/ssm-prefill-padding.ts +39 -0
- package/src/state/ssm.ts +422 -0
- package/src/state/target-layout.ts +7 -0
- package/src/state/tiered-prefix-cache.ts +45 -0
- package/src/state/training-cache.ts +300 -0
- package/src/state/transitioning-kv-rows.ts +267 -0
- package/src/state/turboquant-attention.ts +97 -0
- package/src/state/turboquant-codec.ts +90 -0
- package/src/state/turboquant-kv.ts +442 -0
- package/src/state/views.ts +26 -0
- package/src/transcription/index.ts +3 -0
- package/src/transcription/silero-vad.ts +379 -0
- package/src/transcription/whisper/decode.ts +773 -0
- package/src/transcription/whisper/format.ts +74 -0
- package/src/transcription/whisper/index.ts +4 -0
- package/src/transcription/whisper/timing.ts +274 -0
- package/src/transcription/whisper/transcribe.ts +394 -0
- package/src/transcription/whisper/types.ts +22 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
|
|
5
|
+
// Matmul over MLX affine 3- or 4-bit, group-64 weights on the simdgroup matrix
|
|
6
|
+
// unit: y = x · Wᵀ for any number of activation rows, in blocks of 8 (grid z;
|
|
7
|
+
// the row count is read at run time, one pipeline per projection shape).
|
|
8
|
+
// The A/B layout of affine{3,4}-mma: A is 8 output rows x 8 k with the k index
|
|
9
|
+
// permuted so that A column i at step s is position base + 32·i + s (every lane
|
|
10
|
+
// dequantizes one 64-value group of its row over a 256-value K step); B is xᵀ.
|
|
11
|
+
// For the M1 Max, whose matrix products issue on the ordinary ALU pipes: each
|
|
12
|
+
// simdgroup owns TILES 8-row output tiles that share every B fragment, the
|
|
13
|
+
// threadgroup's SPLITS simdgroups take interleaved K steps and add their tiles
|
|
14
|
+
// through threadgroup memory, and a weight dequantizes as fma(1 + q/2^BITS,
|
|
15
|
+
// 2^BITS·scale, bias − 2^BITS·scale), q shifted into the mantissa of 1.0f.
|
|
16
|
+
// B columns past the last activation row repeat it; those columns of C are
|
|
17
|
+
// never written, so a block's cost does not depend on its rows. Summation order
|
|
18
|
+
// differs from MLX's qmv and qmm (not bit-identical; gate by KL). Measured on
|
|
19
|
+
// the M1 Max above eight rows (3-bit 5120 -> 10240): 9 rows 0.50 ms, 16 0.38,
|
|
20
|
+
// 32 0.66, 64 1.25, against MLX's quantized matmul at 0.74, 0.75, 0.67, 1.27.
|
|
21
|
+
|
|
22
|
+
const SOURCE = String.raw`
|
|
23
|
+
const uint lane = thread_index_in_simdgroup;
|
|
24
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
25
|
+
const uint fm = ((lane / 4u) & 4u) + ((lane / 2u) % 4u);
|
|
26
|
+
const uint fn = ((lane / 4u) & 2u) * 2u + (lane % 2u) * 2u;
|
|
27
|
+
const uint M = (uint)x_shape[0];
|
|
28
|
+
const uint n0 = threadgroup_position_in_grid.y * 8u * (uint)TILES;
|
|
29
|
+
const uint m0 = threadgroup_position_in_grid.z * 8u;
|
|
30
|
+
const uint groups = (uint)K / 64u;
|
|
31
|
+
const uint groupWords = 2u * (uint)BITS;
|
|
32
|
+
const device T* x0 = x + (ulong)metal::min(m0 + fn, M - 1u) * (ulong)K + fm * 32u;
|
|
33
|
+
const device T* x1 = x + (ulong)metal::min(m0 + fn + 1u, M - 1u) * (ulong)K + fm * 32u;
|
|
34
|
+
const device uint32_t* wRow[TILES];
|
|
35
|
+
metal::simdgroup_float8x8 acc[TILES];
|
|
36
|
+
#pragma clang loop unroll(full)
|
|
37
|
+
for (uint t = 0; t < (uint)TILES; ++t) {
|
|
38
|
+
wRow[t] = (const device uint32_t*)w + (ulong)(n0 + 8u * t + fm) * groups * groupWords;
|
|
39
|
+
acc[t] = metal::make_filled_simdgroup_matrix<float, 8, 8>(0.0f);
|
|
40
|
+
}
|
|
41
|
+
for (uint step = sg; step < (uint)K / 256u; step += (uint)SPLITS) {
|
|
42
|
+
const uint base = step * 256u;
|
|
43
|
+
const uint g = step * 4u + fn / 2u;
|
|
44
|
+
uint q[TILES][2 * BITS];
|
|
45
|
+
float s2[TILES], b2[TILES];
|
|
46
|
+
#pragma clang loop unroll(full)
|
|
47
|
+
for (uint t = 0; t < (uint)TILES; ++t) {
|
|
48
|
+
const ulong at = (ulong)(n0 + 8u * t + fm) * groups + g;
|
|
49
|
+
s2[t] = float(scales[at]) * (float)(1u << (uint)BITS);
|
|
50
|
+
b2[t] = float(biases[at]) - s2[t];
|
|
51
|
+
#pragma clang loop unroll(full)
|
|
52
|
+
for (uint i = 0; i < groupWords; ++i) q[t][i] = wRow[t][g * groupWords + i];
|
|
53
|
+
}
|
|
54
|
+
const device metal::vec<T, 4>* xa = (const device metal::vec<T, 4>*)(x0 + base);
|
|
55
|
+
const device metal::vec<T, 4>* xb = (const device metal::vec<T, 4>*)(x1 + base);
|
|
56
|
+
#pragma clang loop unroll(full)
|
|
57
|
+
for (uint s4 = 0; s4 < 8u; ++s4) {
|
|
58
|
+
const float4 va = float4(xa[s4]);
|
|
59
|
+
const float4 vb = float4(xb[s4]);
|
|
60
|
+
#pragma clang loop unroll(full)
|
|
61
|
+
for (uint c = 0; c < 4u; ++c) {
|
|
62
|
+
const uint v = s4 * 4u + c;
|
|
63
|
+
metal::simdgroup_float8x8 bx;
|
|
64
|
+
bx.thread_elements()[0] = va[c];
|
|
65
|
+
bx.thread_elements()[1] = vb[c];
|
|
66
|
+
#pragma clang loop unroll(full)
|
|
67
|
+
for (uint t = 0; t < (uint)TILES; ++t) {
|
|
68
|
+
metal::simdgroup_float8x8 a;
|
|
69
|
+
a.thread_elements()[0] = metal::fma(AFFINE_UNIT(q[t], v), s2[t], b2[t]);
|
|
70
|
+
a.thread_elements()[1] = metal::fma(AFFINE_UNIT(q[t], v + 32u), s2[t], b2[t]);
|
|
71
|
+
metal::simdgroup_multiply_accumulate(acc[t], a, bx, acc[t]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Add the K splits: element (fm, fn | fn + 1) of each tile, then thread e of
|
|
77
|
+
// the threadgroup writes C element e of its tile, columns below M only.
|
|
78
|
+
threadgroup float part[SPLITS][TILES][64];
|
|
79
|
+
#pragma clang loop unroll(full)
|
|
80
|
+
for (uint t = 0; t < (uint)TILES; ++t) {
|
|
81
|
+
part[sg][t][fm * 8u + fn] = acc[t].thread_elements()[0];
|
|
82
|
+
part[sg][t][fm * 8u + fn + 1u] = acc[t].thread_elements()[1];
|
|
83
|
+
}
|
|
84
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
85
|
+
for (uint e = sg * 32u + lane; e < 64u * (uint)TILES; e += 32u * (uint)SPLITS) {
|
|
86
|
+
const uint t = e / 64u, r = (e % 64u) / 8u, m = e % 8u;
|
|
87
|
+
if (m0 + m >= M) continue;
|
|
88
|
+
float sum = 0.0f;
|
|
89
|
+
for (uint s = 0; s < (uint)SPLITS; ++s) sum += part[s][t][r * 8u + m];
|
|
90
|
+
y[(ulong)(m0 + m) * (ulong)N + n0 + 8u * t + r] = T(sum);
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
// Value v of a 64-value group (MLX's LSB-first bit stream) as the exact float
|
|
95
|
+
// 1 + q/2^B: q shifted into the top B mantissa bits of 1.0f; a value may
|
|
96
|
+
// straddle into the next word. The words go by value: v is a constant of the
|
|
97
|
+
// unrolled loop, so every shift and mask folds.
|
|
98
|
+
const HEADER = String.raw`
|
|
99
|
+
template <uint B>
|
|
100
|
+
inline float affine_unit(uint lo, uint hi, uint v) {
|
|
101
|
+
const uint bit = v * B, off = bit & 31u;
|
|
102
|
+
const uint top = 23u - B;
|
|
103
|
+
uint u = off <= top ? lo << (top - off) : lo >> (off - top);
|
|
104
|
+
if (off + B > 32u) u |= hi << (top + 32u - off);
|
|
105
|
+
return as_type<float>((u & (((1u << B) - 1u) << top)) | 0x3f800000u);
|
|
106
|
+
}
|
|
107
|
+
#define AFFINE_UNIT(q, v) affine_unit<BITS>((q)[((v) * BITS) >> 5], (q)[metal::min((((v) * BITS) >> 5) + 1u, 2u * BITS - 1u)], (v))
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
let kernel: MetalKernel | undefined;
|
|
111
|
+
|
|
112
|
+
/** The launch for an N-output projection: output tiles per simdgroup and K splits per threadgroup. Wide outputs take
|
|
113
|
+
* two tiles over two splits; narrow ones (the attention k/v and the DeltaNet a/b projections) one tile over eight,
|
|
114
|
+
* for enough simdgroups in flight. */
|
|
115
|
+
export interface AffineSplitMmaPlan { readonly tiles: number; readonly splits: number }
|
|
116
|
+
export function affineSplitMmaPlan(N: number): AffineSplitMmaPlan {
|
|
117
|
+
return N >= 2048 && N % 16 === 0 ? { tiles: 2, splits: 2 } : { tiles: 1, splits: 8 };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Eligible: affine 3- or 4-bit, group 64, K a multiple of 256, N of 8, at least one row. */
|
|
121
|
+
export function affineSplitMmaEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
|
|
122
|
+
if (spec.mode !== "affine" || spec.groupSize !== 64 || (spec.bits !== 3 && spec.bits !== 4)) return false;
|
|
123
|
+
const N = scales.shape[0]!, K = scales.shape[1]! * 64;
|
|
124
|
+
return rows >= 1 && K % 256 === 0 && N % 8 === 0 && w.shape.length === 2;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** x [..., K] (any number of rows) · Wᵀ → [..., N] in x's dtype, for `bits`-bit weights under `plan`. Borrows
|
|
128
|
+
* inputs. */
|
|
129
|
+
export function affineSplitMma(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray, bits: 3 | 4,
|
|
130
|
+
plan: AffineSplitMmaPlan): MlxArray {
|
|
131
|
+
const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
|
|
132
|
+
if (M < 1) throw new Error("affineSplitMma: no rows");
|
|
133
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_affine_split_mma", inputNames: ["x", "w", "scales", "biases"],
|
|
134
|
+
outputNames: ["y"], source: SOURCE, header: HEADER, ensureRowContiguous: true });
|
|
135
|
+
using x2 = ops.reshape(x, [M, K]);
|
|
136
|
+
const [y] = kernel.apply([x2, w, scales, biases], {
|
|
137
|
+
outputs: [{ shape: [M, N], dtype: x.dtype }],
|
|
138
|
+
grid: [32 * plan.splits, N / (8 * plan.tiles), Math.ceil(M / 8)], threadGroup: [32 * plan.splits, 1, 1],
|
|
139
|
+
templateDtypes: { T: x.dtype },
|
|
140
|
+
templateInts: { N, K, BITS: bits, TILES: plan.tiles, SPLITS: plan.splits },
|
|
141
|
+
});
|
|
142
|
+
using y2 = y!;
|
|
143
|
+
return ops.reshape(y2, [...x.shape.slice(0, -1), N]);
|
|
144
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
|
|
5
|
+
// Verify-width matmul over MLX affine 3-bit, group-64 weights on the simdgroup
|
|
6
|
+
// matrix unit: y = x · Wᵀ for 1..8 activation rows (one block of 8). One
|
|
7
|
+
// simdgroup owns 8 output rows. The K index inside each 8x8 step is permuted so
|
|
8
|
+
// that A column i at step s is position base + 32·i + s: every lane
|
|
9
|
+
// dequantizes 64 contiguous values of its row (exactly one quantization group,
|
|
10
|
+
// MLX's LSB-first bit stream) into its A-fragment elements, and reads two
|
|
11
|
+
// activation rows contiguously. Per-weight work is independent of M. Summation
|
|
12
|
+
// order differs from MLX's qmv (not bit-identical; gate by KL).
|
|
13
|
+
const SG_TG = 4, MT = 1;
|
|
14
|
+
|
|
15
|
+
const SOURCE = String.raw`
|
|
16
|
+
const uint lane = thread_index_in_simdgroup;
|
|
17
|
+
const uint n0 = (threadgroup_position_in_grid.y * (uint)SG_TG + simdgroup_index_in_threadgroup) * 8u;
|
|
18
|
+
if (n0 >= (uint)N) return;
|
|
19
|
+
const uint fm = ((lane / 4u) & 4u) + ((lane / 2u) % 4u);
|
|
20
|
+
const uint fn = ((lane / 4u) & 2u) * 2u + (lane % 2u) * 2u;
|
|
21
|
+
const uint n = n0 + fm;
|
|
22
|
+
const uint groups = (uint)K / 64u;
|
|
23
|
+
const device uint32_t* wRow = (const device uint32_t*)((const device uint8_t*)w + (ulong)n * (ulong)K * 3u / 8u);
|
|
24
|
+
// MT activation blocks of 8 rows; grid z walks chunks of 8·MT rows.
|
|
25
|
+
const uint mBase = threadgroup_position_in_grid.z * 8u * (uint)MT;
|
|
26
|
+
bool m0[MT], m1[MT];
|
|
27
|
+
const device T* x0[MT];
|
|
28
|
+
const device T* x1[MT];
|
|
29
|
+
metal::simdgroup_float8x8 acc[MT];
|
|
30
|
+
#pragma clang loop unroll(full)
|
|
31
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
32
|
+
const uint r0 = mBase + 8u * bk + fn;
|
|
33
|
+
m0[bk] = r0 < (uint)M; m1[bk] = r0 + 1u < (uint)M;
|
|
34
|
+
x0[bk] = x + (ulong)(m0[bk] ? r0 : 0u) * (ulong)K + fm * 32u;
|
|
35
|
+
x1[bk] = x + (ulong)(m1[bk] ? r0 + 1u : 0u) * (ulong)K + fm * 32u;
|
|
36
|
+
acc[bk] = metal::make_filled_simdgroup_matrix<float, 8, 8>(0.0f);
|
|
37
|
+
}
|
|
38
|
+
for (uint base = 0; base < (uint)K; base += 256u) {
|
|
39
|
+
const uint g = base / 64u + fn / 2u;
|
|
40
|
+
const float s = float(scales[(ulong)n * groups + g]);
|
|
41
|
+
const float b = float(biases[(ulong)n * groups + g]);
|
|
42
|
+
// 64 values of one group: 6 words (4-byte aligned).
|
|
43
|
+
uint q[6];
|
|
44
|
+
const device uint32_t* qp = wRow + g * 6u;
|
|
45
|
+
#pragma clang loop unroll(full)
|
|
46
|
+
for (uint i = 0; i < 6u; ++i) q[i] = qp[i];
|
|
47
|
+
#pragma clang loop unroll(full)
|
|
48
|
+
for (uint s4 = 0; s4 < 8u; ++s4) {
|
|
49
|
+
float4 va[MT], vb[MT];
|
|
50
|
+
#pragma clang loop unroll(full)
|
|
51
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
52
|
+
va[bk] = m0[bk] ? float4(((const device metal::vec<T, 4>*)(x0[bk] + base))[s4]) : float4(0.0f);
|
|
53
|
+
vb[bk] = m1[bk] ? float4(((const device metal::vec<T, 4>*)(x1[bk] + base))[s4]) : float4(0.0f);
|
|
54
|
+
}
|
|
55
|
+
#pragma clang loop unroll(full)
|
|
56
|
+
for (uint c = 0; c < 4u; ++c) {
|
|
57
|
+
const uint t = s4 * 4u + c;
|
|
58
|
+
metal::simdgroup_float8x8 a;
|
|
59
|
+
a.thread_elements()[0] = metal::fma(s, affine_q3(q, t), b);
|
|
60
|
+
a.thread_elements()[1] = metal::fma(s, affine_q3(q, t + 32u), b);
|
|
61
|
+
#pragma clang loop unroll(full)
|
|
62
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
63
|
+
metal::simdgroup_float8x8 bx;
|
|
64
|
+
bx.thread_elements()[0] = va[bk][c];
|
|
65
|
+
bx.thread_elements()[1] = vb[bk][c];
|
|
66
|
+
metal::simdgroup_multiply_accumulate(acc[bk], a, bx, acc[bk]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
#pragma clang loop unroll(full)
|
|
72
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
73
|
+
const uint r0 = mBase + 8u * bk + fn;
|
|
74
|
+
if (m0[bk]) y[(ulong)r0 * (ulong)N + n] = T(acc[bk].thread_elements()[0]);
|
|
75
|
+
if (m1[bk]) y[(ulong)(r0 + 1u) * (ulong)N + n] = T(acc[bk].thread_elements()[1]);
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
// Value t of a 64-value group as an exact f32 (2^23 mantissa trick); a 3-bit
|
|
80
|
+
// value may straddle two words.
|
|
81
|
+
const HEADER = String.raw`
|
|
82
|
+
inline float affine_q3(thread const uint* q, uint t) {
|
|
83
|
+
const uint bit = t * 3u;
|
|
84
|
+
const uint wi = bit >> 5;
|
|
85
|
+
const uint off = bit & 31u;
|
|
86
|
+
uint v = q[wi] >> off;
|
|
87
|
+
if (off + 3u > 32u) v |= q[wi + 1] << (32u - off);
|
|
88
|
+
return as_type<float>(0x4b000000u | (v & 7u)) - 8388608.0f;
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
let kernel: MetalKernel | undefined;
|
|
93
|
+
|
|
94
|
+
/** Eligible: affine 3-bit, group 64, K a multiple of 256, N of 8, 1..8 rows. */
|
|
95
|
+
export function affine3MmaEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
|
|
96
|
+
if (spec.mode !== "affine" || spec.groupSize !== 64 || spec.bits !== 3) return false;
|
|
97
|
+
const N = scales.shape[0]!, K = scales.shape[1]! * 64;
|
|
98
|
+
return rows >= 1 && rows <= 8 && K % 256 === 0 && N % 8 === 0 && w.shape.length === 2;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** x [..., K] (1..8 rows) · Wᵀ → [..., N] in x's dtype. Borrows inputs. */
|
|
102
|
+
export function affine3Mma(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray): MlxArray {
|
|
103
|
+
const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
|
|
104
|
+
if (M < 1 || M > 8) throw new Error(`affine3Mma: M=${M} outside 1..8`);
|
|
105
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_affine3_mma", inputNames: ["x", "w", "scales", "biases"],
|
|
106
|
+
outputNames: ["y"], source: SOURCE, header: HEADER, ensureRowContiguous: true });
|
|
107
|
+
using x2 = ops.reshape(x, [M, K]);
|
|
108
|
+
const [y] = kernel.apply([x2, w, scales, biases], {
|
|
109
|
+
outputs: [{ shape: [M, N], dtype: x.dtype }],
|
|
110
|
+
grid: [32 * SG_TG, Math.ceil(N / (8 * SG_TG)), 1], threadGroup: [32 * SG_TG, 1, 1],
|
|
111
|
+
templateDtypes: { T: x.dtype },
|
|
112
|
+
templateInts: { M, N, K, SG_TG, MT },
|
|
113
|
+
});
|
|
114
|
+
const out = ops.reshape(y!, [...x.shape.slice(0, -1), N]);
|
|
115
|
+
y!.dispose();
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
|
|
5
|
+
// Verify-width matmul over MLX affine 3-bit, group-64 weights: y = x · Wᵀ for
|
|
6
|
+
// 2..8 activation rows. MLX's qmv runs each row as its own threadgroups, so
|
|
7
|
+
// every weight is fetched and decoded M times. Here each simdgroup owns NR = 4
|
|
8
|
+
// output rows; per 512-value K step, every lane decodes its VPT = 16 values of
|
|
9
|
+
// each output row once (exact floats via the 2^23 mantissa trick; MLX packs
|
|
10
|
+
// eight 3-bit values little-endian into three bytes) and reuses them for all M
|
|
11
|
+
// rows held in registers.
|
|
12
|
+
// SGS = 2 simdgroups per threadgroup. Same algebra as MLX:
|
|
13
|
+
// Σ_g scale·Σ q·x + bias·Σ x. Summation order differs from MLX's qmv (not
|
|
14
|
+
// bit-identical; gate by KL).
|
|
15
|
+
const NR = 4, SGS = 2, VPT = 16;
|
|
16
|
+
|
|
17
|
+
const SOURCE = String.raw`
|
|
18
|
+
const uint lane = thread_index_in_simdgroup;
|
|
19
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
20
|
+
const uint n0 = (threadgroup_position_in_grid.y * (uint)SGS + sg) * (uint)NR;
|
|
21
|
+
if (n0 >= (uint)N) return;
|
|
22
|
+
const ulong rowBytes = (ulong)K * 3u / 8u;
|
|
23
|
+
const uint groups = (uint)K / 64u;
|
|
24
|
+
const device uint8_t* wb = (const device uint8_t*)w;
|
|
25
|
+
float acc[NR][M];
|
|
26
|
+
#pragma clang loop unroll(full)
|
|
27
|
+
for (uint r = 0; r < (uint)NR; ++r)
|
|
28
|
+
#pragma clang loop unroll(full)
|
|
29
|
+
for (uint m = 0; m < (uint)M; ++m) acc[r][m] = 0.0f;
|
|
30
|
+
for (uint k0 = lane * (uint)VPT; k0 < (uint)K; k0 += 32u * (uint)VPT) {
|
|
31
|
+
float xr[M][VPT];
|
|
32
|
+
float xs[M];
|
|
33
|
+
#pragma clang loop unroll(full)
|
|
34
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
35
|
+
const device metal::vec<T, 4>* xp = (const device metal::vec<T, 4>*)(x + (ulong)m * (ulong)K + k0);
|
|
36
|
+
float sum = 0.0f;
|
|
37
|
+
#pragma clang loop unroll(full)
|
|
38
|
+
for (uint v = 0; v < (uint)VPT / 4u; ++v) {
|
|
39
|
+
const float4 f = float4(xp[v]);
|
|
40
|
+
xr[m][4 * v] = f.x; xr[m][4 * v + 1] = f.y; xr[m][4 * v + 2] = f.z; xr[m][4 * v + 3] = f.w;
|
|
41
|
+
sum += (f.x + f.y) + (f.z + f.w);
|
|
42
|
+
}
|
|
43
|
+
xs[m] = sum;
|
|
44
|
+
}
|
|
45
|
+
const uint g = k0 / 64u;
|
|
46
|
+
#pragma clang loop unroll(full)
|
|
47
|
+
for (uint r = 0; r < (uint)NR; ++r) {
|
|
48
|
+
const uint n = n0 + r;
|
|
49
|
+
const device uint8_t* p = wb + (ulong)n * rowBytes + (ulong)k0 * 3u / 8u;
|
|
50
|
+
float q[VPT];
|
|
51
|
+
#pragma clang loop unroll(full)
|
|
52
|
+
for (uint c = 0; c < (uint)VPT / 8u; ++c) {
|
|
53
|
+
const uint packed = uint(p[3 * c]) | (uint(p[3 * c + 1]) << 8) | (uint(p[3 * c + 2]) << 16);
|
|
54
|
+
#pragma clang loop unroll(full)
|
|
55
|
+
for (uint j = 0; j < 8u; ++j)
|
|
56
|
+
q[8 * c + j] = as_type<float>(0x4b000000u | ((packed >> (3u * j)) & 7u)) - 8388608.0f;
|
|
57
|
+
}
|
|
58
|
+
const float s = float(scales[(ulong)n * groups + g]);
|
|
59
|
+
const float b = float(biases[(ulong)n * groups + g]);
|
|
60
|
+
#pragma clang loop unroll(full)
|
|
61
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
62
|
+
float dot = 0.0f;
|
|
63
|
+
#pragma clang loop unroll(full)
|
|
64
|
+
for (uint j = 0; j < (uint)VPT; ++j) dot = metal::fma(q[j], xr[m][j], dot);
|
|
65
|
+
acc[r][m] = metal::fma(s, dot, metal::fma(b, xs[m], acc[r][m]));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
#pragma clang loop unroll(full)
|
|
70
|
+
for (uint r = 0; r < (uint)NR; ++r)
|
|
71
|
+
#pragma clang loop unroll(full)
|
|
72
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
73
|
+
const float v = metal::simd_sum(acc[r][m]);
|
|
74
|
+
if (lane == 0u) y[(ulong)m * (ulong)N + n0 + r] = T(v);
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
let kernel: MetalKernel | undefined;
|
|
79
|
+
|
|
80
|
+
/** Eligible: affine 3-bit, group 64, K a multiple of 512, N of 4, 2..8 rows. */
|
|
81
|
+
export function affine3RowsEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
|
|
82
|
+
if (spec.mode !== "affine" || spec.groupSize !== 64 || spec.bits !== 3) return false;
|
|
83
|
+
const N = scales.shape[0]!, K = scales.shape[1]! * 64;
|
|
84
|
+
return rows >= 2 && rows <= 8 && K % (32 * VPT) === 0 && N % NR === 0 && w.shape.length === 2;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** x [..., K] (2..8 rows) · Wᵀ → [..., N] in x's dtype. Borrows inputs. */
|
|
88
|
+
export function affine3Rows(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray): MlxArray {
|
|
89
|
+
const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
|
|
90
|
+
if (M < 2 || M > 8) throw new Error(`affine3Rows: M=${M} outside 2..8`);
|
|
91
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_affine3_rows", inputNames: ["x", "w", "scales", "biases"],
|
|
92
|
+
outputNames: ["y"], source: SOURCE, ensureRowContiguous: true });
|
|
93
|
+
using x2 = ops.reshape(x, [M, K]);
|
|
94
|
+
const [y] = kernel.apply([x2, w, scales, biases], {
|
|
95
|
+
outputs: [{ shape: [M, N], dtype: x.dtype }],
|
|
96
|
+
grid: [32 * SGS, Math.ceil(N / (NR * SGS)), 1], threadGroup: [32 * SGS, 1, 1],
|
|
97
|
+
templateDtypes: { T: x.dtype },
|
|
98
|
+
templateInts: { M, N, K, NR, SGS, VPT },
|
|
99
|
+
});
|
|
100
|
+
const out = ops.reshape(y!, [...x.shape.slice(0, -1), N]);
|
|
101
|
+
y!.dispose();
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
|
|
5
|
+
// Verify-width matmul over MLX affine 4-bit, group-64 weights on the simdgroup
|
|
6
|
+
// matrix unit: y = x · Wᵀ for 1..8 activation rows (one block of 8). One
|
|
7
|
+
// simdgroup owns 8 output rows. The K index inside each 8x8 step is permuted so
|
|
8
|
+
// that A column i at step s is position base + 32·i + s: every lane
|
|
9
|
+
// dequantizes 64 contiguous values of its row (exactly one quantization group,
|
|
10
|
+
// MLX's LSB-first bit stream) into its A-fragment elements, and reads two
|
|
11
|
+
// activation rows contiguously. Per-weight work is independent of M. Summation
|
|
12
|
+
// order differs from MLX's qmv (not bit-identical; gate by KL).
|
|
13
|
+
const SG_TG = 4, MT = 1;
|
|
14
|
+
|
|
15
|
+
const SOURCE = String.raw`
|
|
16
|
+
const uint lane = thread_index_in_simdgroup;
|
|
17
|
+
const uint n0 = (threadgroup_position_in_grid.y * (uint)SG_TG + simdgroup_index_in_threadgroup) * 8u;
|
|
18
|
+
if (n0 >= (uint)N) return;
|
|
19
|
+
const uint fm = ((lane / 4u) & 4u) + ((lane / 2u) % 4u);
|
|
20
|
+
const uint fn = ((lane / 4u) & 2u) * 2u + (lane % 2u) * 2u;
|
|
21
|
+
const uint n = n0 + fm;
|
|
22
|
+
const uint groups = (uint)K / 64u;
|
|
23
|
+
const device uint32_t* wRow = (const device uint32_t*)((const device uint8_t*)w + (ulong)n * (ulong)K / 2u);
|
|
24
|
+
// MT activation blocks of 8 rows; grid z walks chunks of 8·MT rows.
|
|
25
|
+
const uint mBase = threadgroup_position_in_grid.z * 8u * (uint)MT;
|
|
26
|
+
bool m0[MT], m1[MT];
|
|
27
|
+
const device T* x0[MT];
|
|
28
|
+
const device T* x1[MT];
|
|
29
|
+
metal::simdgroup_float8x8 acc[MT];
|
|
30
|
+
#pragma clang loop unroll(full)
|
|
31
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
32
|
+
const uint r0 = mBase + 8u * bk + fn;
|
|
33
|
+
m0[bk] = r0 < (uint)M; m1[bk] = r0 + 1u < (uint)M;
|
|
34
|
+
x0[bk] = x + (ulong)(m0[bk] ? r0 : 0u) * (ulong)K + fm * 32u;
|
|
35
|
+
x1[bk] = x + (ulong)(m1[bk] ? r0 + 1u : 0u) * (ulong)K + fm * 32u;
|
|
36
|
+
acc[bk] = metal::make_filled_simdgroup_matrix<float, 8, 8>(0.0f);
|
|
37
|
+
}
|
|
38
|
+
for (uint base = 0; base < (uint)K; base += 256u) {
|
|
39
|
+
const uint g = base / 64u + fn / 2u;
|
|
40
|
+
const float s = float(scales[(ulong)n * groups + g]);
|
|
41
|
+
const float b = float(biases[(ulong)n * groups + g]);
|
|
42
|
+
// 64 values of one group: 8 words.
|
|
43
|
+
uint q[8];
|
|
44
|
+
const device uint32_t* qp = wRow + g * 8u;
|
|
45
|
+
#pragma clang loop unroll(full)
|
|
46
|
+
for (uint i = 0; i < 8u; ++i) q[i] = qp[i];
|
|
47
|
+
#pragma clang loop unroll(full)
|
|
48
|
+
for (uint s4 = 0; s4 < 8u; ++s4) {
|
|
49
|
+
float4 va[MT], vb[MT];
|
|
50
|
+
#pragma clang loop unroll(full)
|
|
51
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
52
|
+
va[bk] = m0[bk] ? float4(((const device metal::vec<T, 4>*)(x0[bk] + base))[s4]) : float4(0.0f);
|
|
53
|
+
vb[bk] = m1[bk] ? float4(((const device metal::vec<T, 4>*)(x1[bk] + base))[s4]) : float4(0.0f);
|
|
54
|
+
}
|
|
55
|
+
#pragma clang loop unroll(full)
|
|
56
|
+
for (uint c = 0; c < 4u; ++c) {
|
|
57
|
+
const uint t = s4 * 4u + c;
|
|
58
|
+
metal::simdgroup_float8x8 a;
|
|
59
|
+
a.thread_elements()[0] = metal::fma(s, affine_q4(q, t), b);
|
|
60
|
+
a.thread_elements()[1] = metal::fma(s, affine_q4(q, t + 32u), b);
|
|
61
|
+
#pragma clang loop unroll(full)
|
|
62
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
63
|
+
metal::simdgroup_float8x8 bx;
|
|
64
|
+
bx.thread_elements()[0] = va[bk][c];
|
|
65
|
+
bx.thread_elements()[1] = vb[bk][c];
|
|
66
|
+
metal::simdgroup_multiply_accumulate(acc[bk], a, bx, acc[bk]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
#pragma clang loop unroll(full)
|
|
72
|
+
for (uint bk = 0; bk < (uint)MT; ++bk) {
|
|
73
|
+
const uint r0 = mBase + 8u * bk + fn;
|
|
74
|
+
if (m0[bk]) y[(ulong)r0 * (ulong)N + n] = T(acc[bk].thread_elements()[0]);
|
|
75
|
+
if (m1[bk]) y[(ulong)(r0 + 1u) * (ulong)N + n] = T(acc[bk].thread_elements()[1]);
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
// Value t of a 64-value group as an exact f32 (2^23 mantissa trick). 4-bit
|
|
80
|
+
// values never straddle a word.
|
|
81
|
+
const HEADER = String.raw`
|
|
82
|
+
inline float affine_q4(thread const uint* q, uint t) {
|
|
83
|
+
const uint bit = t * 4u;
|
|
84
|
+
return as_type<float>(0x4b000000u | ((q[bit >> 5] >> (bit & 31u)) & 15u)) - 8388608.0f;
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
let kernel: MetalKernel | undefined;
|
|
89
|
+
|
|
90
|
+
/** Eligible: affine 4-bit, group 64, K a multiple of 256, N of 8, 1..8 rows. */
|
|
91
|
+
export function affine4MmaEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
|
|
92
|
+
if (spec.mode !== "affine" || spec.groupSize !== 64 || spec.bits !== 4) return false;
|
|
93
|
+
const N = scales.shape[0]!, K = scales.shape[1]! * 64;
|
|
94
|
+
return rows >= 1 && rows <= 8 && K % 256 === 0 && N % 8 === 0 && w.shape.length === 2;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** x [..., K] (1..8 rows) · Wᵀ → [..., N] in x's dtype. Borrows inputs. */
|
|
98
|
+
export function affine4Mma(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray): MlxArray {
|
|
99
|
+
const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
|
|
100
|
+
if (M < 1 || M > 8) throw new Error(`affine4Mma: M=${M} outside 1..8`);
|
|
101
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_affine4_mma", inputNames: ["x", "w", "scales", "biases"],
|
|
102
|
+
outputNames: ["y"], source: SOURCE, header: HEADER, ensureRowContiguous: true });
|
|
103
|
+
using x2 = ops.reshape(x, [M, K]);
|
|
104
|
+
const [y] = kernel.apply([x2, w, scales, biases], {
|
|
105
|
+
outputs: [{ shape: [M, N], dtype: x.dtype }],
|
|
106
|
+
grid: [32 * SG_TG, Math.ceil(N / (8 * SG_TG)), 1], threadGroup: [32 * SG_TG, 1, 1],
|
|
107
|
+
templateDtypes: { T: x.dtype },
|
|
108
|
+
templateInts: { M, N, K, SG_TG, MT },
|
|
109
|
+
});
|
|
110
|
+
const out = ops.reshape(y!, [...x.shape.slice(0, -1), N]);
|
|
111
|
+
y!.dispose();
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
|
|
5
|
+
// Verify-width matmul over MLX affine 4-bit, group-64 weights: y = x · Wᵀ for
|
|
6
|
+
// 2..8 activation rows. MLX's qmv runs each row as its own threadgroups, so
|
|
7
|
+
// every weight is fetched and decoded M times. Here each simdgroup owns NR = 4
|
|
8
|
+
// output rows; per 512-value K step, every lane decodes its VPT = 16 values of
|
|
9
|
+
// each output row once (exact floats via the 2^23 mantissa trick, MLX's
|
|
10
|
+
// little-endian packing) and reuses them for all M rows held in registers.
|
|
11
|
+
// SGS = 2 simdgroups per threadgroup. Same algebra as MLX:
|
|
12
|
+
// Σ_g scale·Σ q·x + bias·Σ x. Summation order differs from MLX's qmv (not
|
|
13
|
+
// bit-identical; gate by KL).
|
|
14
|
+
const NR = 4, SGS = 2, VPT = 16;
|
|
15
|
+
|
|
16
|
+
const SOURCE = String.raw`
|
|
17
|
+
const uint lane = thread_index_in_simdgroup;
|
|
18
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
19
|
+
const uint n0 = (threadgroup_position_in_grid.y * (uint)SGS + sg) * (uint)NR;
|
|
20
|
+
if (n0 >= (uint)N) return;
|
|
21
|
+
const ulong rowBytes = (ulong)K / 2u;
|
|
22
|
+
const uint groups = (uint)K / 64u;
|
|
23
|
+
const device uint8_t* wb = (const device uint8_t*)w;
|
|
24
|
+
float acc[NR][M];
|
|
25
|
+
#pragma clang loop unroll(full)
|
|
26
|
+
for (uint r = 0; r < (uint)NR; ++r)
|
|
27
|
+
#pragma clang loop unroll(full)
|
|
28
|
+
for (uint m = 0; m < (uint)M; ++m) acc[r][m] = 0.0f;
|
|
29
|
+
for (uint k0 = lane * (uint)VPT; k0 < (uint)K; k0 += 32u * (uint)VPT) {
|
|
30
|
+
float xr[M][VPT];
|
|
31
|
+
float xs[M];
|
|
32
|
+
#pragma clang loop unroll(full)
|
|
33
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
34
|
+
const device metal::vec<T, 4>* xp = (const device metal::vec<T, 4>*)(x + (ulong)m * (ulong)K + k0);
|
|
35
|
+
float sum = 0.0f;
|
|
36
|
+
#pragma clang loop unroll(full)
|
|
37
|
+
for (uint v = 0; v < (uint)VPT / 4u; ++v) {
|
|
38
|
+
const float4 f = float4(xp[v]);
|
|
39
|
+
xr[m][4 * v] = f.x; xr[m][4 * v + 1] = f.y; xr[m][4 * v + 2] = f.z; xr[m][4 * v + 3] = f.w;
|
|
40
|
+
sum += (f.x + f.y) + (f.z + f.w);
|
|
41
|
+
}
|
|
42
|
+
xs[m] = sum;
|
|
43
|
+
}
|
|
44
|
+
const uint g = k0 / 64u;
|
|
45
|
+
#pragma clang loop unroll(full)
|
|
46
|
+
for (uint r = 0; r < (uint)NR; ++r) {
|
|
47
|
+
const uint n = n0 + r;
|
|
48
|
+
const device uint8_t* p = wb + (ulong)n * rowBytes + (ulong)k0 / 2u;
|
|
49
|
+
float q[VPT];
|
|
50
|
+
#pragma clang loop unroll(full)
|
|
51
|
+
for (uint c = 0; c < (uint)VPT / 8u; ++c) {
|
|
52
|
+
const uint packed = ((const device uint32_t*)p)[c];
|
|
53
|
+
#pragma clang loop unroll(full)
|
|
54
|
+
for (uint j = 0; j < 8u; ++j)
|
|
55
|
+
q[8 * c + j] = as_type<float>(0x4b000000u | ((packed >> (4u * j)) & 15u)) - 8388608.0f;
|
|
56
|
+
}
|
|
57
|
+
const float s = float(scales[(ulong)n * groups + g]);
|
|
58
|
+
const float b = float(biases[(ulong)n * groups + g]);
|
|
59
|
+
#pragma clang loop unroll(full)
|
|
60
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
61
|
+
float dot = 0.0f;
|
|
62
|
+
#pragma clang loop unroll(full)
|
|
63
|
+
for (uint j = 0; j < (uint)VPT; ++j) dot = metal::fma(q[j], xr[m][j], dot);
|
|
64
|
+
acc[r][m] = metal::fma(s, dot, metal::fma(b, xs[m], acc[r][m]));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
#pragma clang loop unroll(full)
|
|
69
|
+
for (uint r = 0; r < (uint)NR; ++r)
|
|
70
|
+
#pragma clang loop unroll(full)
|
|
71
|
+
for (uint m = 0; m < (uint)M; ++m) {
|
|
72
|
+
const float v = metal::simd_sum(acc[r][m]);
|
|
73
|
+
if (lane == 0u) y[(ulong)m * (ulong)N + n0 + r] = T(v);
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
let kernel: MetalKernel | undefined;
|
|
78
|
+
|
|
79
|
+
/** Eligible: affine 4-bit, group 64, K a multiple of 512, N of 4, 2..8 rows. */
|
|
80
|
+
export function affine4RowsEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
|
|
81
|
+
if (spec.mode !== "affine" || spec.groupSize !== 64 || spec.bits !== 4) return false;
|
|
82
|
+
const N = scales.shape[0]!, K = scales.shape[1]! * 64;
|
|
83
|
+
return rows >= 2 && rows <= 8 && K % (32 * VPT) === 0 && N % NR === 0 && w.shape.length === 2;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** x [..., K] (2..8 rows) · Wᵀ → [..., N] in x's dtype. Borrows inputs. */
|
|
87
|
+
export function affine4Rows(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray): MlxArray {
|
|
88
|
+
const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
|
|
89
|
+
if (M < 2 || M > 8) throw new Error(`affine4Rows: M=${M} outside 2..8`);
|
|
90
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_affine4_rows", inputNames: ["x", "w", "scales", "biases"],
|
|
91
|
+
outputNames: ["y"], source: SOURCE, ensureRowContiguous: true });
|
|
92
|
+
using x2 = ops.reshape(x, [M, K]);
|
|
93
|
+
const [y] = kernel.apply([x2, w, scales, biases], {
|
|
94
|
+
outputs: [{ shape: [M, N], dtype: x.dtype }],
|
|
95
|
+
grid: [32 * SGS, Math.ceil(N / (NR * SGS)), 1], threadGroup: [32 * SGS, 1, 1],
|
|
96
|
+
templateDtypes: { T: x.dtype },
|
|
97
|
+
templateInts: { M, N, K, NR, SGS, VPT },
|
|
98
|
+
});
|
|
99
|
+
const out = ops.reshape(y!, [...x.shape.slice(0, -1), N]);
|
|
100
|
+
y!.dispose();
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Copyright © 2024 Apple Inc. RMS reduction adapted under the MIT license;
|
|
2
|
+
// see THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
4
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
5
|
+
|
|
6
|
+
// Reduction and rounding follow MLX 0.32.2's rms_norm.metal (Apple Inc., MIT).
|
|
7
|
+
// Round the residual sum before squaring, and the normalized value before
|
|
8
|
+
// multiplying by the weight, just as add followed by fast::rms_norm does.
|
|
9
|
+
const SOURCE = String.raw`
|
|
10
|
+
const uint row = threadgroup_position_in_grid.x;
|
|
11
|
+
const uint tid = thread_position_in_threadgroup.x;
|
|
12
|
+
const uint lane = thread_index_in_simdgroup;
|
|
13
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
14
|
+
threadgroup float sums[32];
|
|
15
|
+
threadgroup float inv[1];
|
|
16
|
+
float acc = 0.0f;
|
|
17
|
+
for (uint r = 0; r < uint(D); r += uint(THREADS) * 4u) {
|
|
18
|
+
for (uint i = 0; i < 4u; ++i) {
|
|
19
|
+
const uint col = r + tid * 4u + i;
|
|
20
|
+
if (col < uint(D)) {
|
|
21
|
+
const uint index = row * uint(D) + col;
|
|
22
|
+
const T value = T(float(x[index]) + float(update[index]));
|
|
23
|
+
residual[index] = value;
|
|
24
|
+
const float v = float(value);
|
|
25
|
+
acc += v * v;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
acc = metal::simd_sum(acc);
|
|
30
|
+
if (sg == 0) sums[lane] = 0.0f;
|
|
31
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
32
|
+
if (lane == 0) sums[sg] = acc;
|
|
33
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
34
|
+
if (sg == 0) {
|
|
35
|
+
acc = metal::simd_sum(sums[lane]);
|
|
36
|
+
if (lane == 0) inv[0] = metal::precise::rsqrt(acc / float(D) + as_type<float>(uint(EPS)));
|
|
37
|
+
}
|
|
38
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup | metal::mem_flags::mem_device);
|
|
39
|
+
for (uint r = 0; r < uint(D); r += uint(THREADS) * 4u) {
|
|
40
|
+
for (uint i = 0; i < 4u; ++i) {
|
|
41
|
+
const uint col = r + tid * 4u + i;
|
|
42
|
+
if (col < uint(D)) {
|
|
43
|
+
const uint index = row * uint(D) + col;
|
|
44
|
+
normalized[index] = weight[col] * T(float(residual[index]) * inv[0]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
let kernel: MetalKernel | undefined;
|
|
51
|
+
|
|
52
|
+
/** Equal-shaped, row-contiguous inputs and a same-dtype vector weight. Returns
|
|
53
|
+
* two owned arrays: the rounded residual and its weighted RMS normalization. */
|
|
54
|
+
export function residualRmsNorm(x: MlxArray, update: MlxArray, weight: MlxArray, eps: number): [MlxArray, MlxArray] {
|
|
55
|
+
const shape = x.shape, width = shape.at(-1)!;
|
|
56
|
+
const threads = Math.min(1024, Math.ceil(width / 128) * 32);
|
|
57
|
+
const epsBits = new Uint32Array(new Float32Array([eps]).buffer)[0]!;
|
|
58
|
+
kernel ??= new MetalKernel({ name: "mlx_bun_residual_rms_norm", inputNames: ["x", "update", "weight"],
|
|
59
|
+
outputNames: ["residual", "normalized"], source: SOURCE });
|
|
60
|
+
return kernel.apply([x, update, weight], {
|
|
61
|
+
outputs: [{ shape, dtype: x.dtype }, { shape, dtype: x.dtype }],
|
|
62
|
+
grid: [x.size / width * threads, 1, 1], threadGroup: [threads, 1, 1],
|
|
63
|
+
templateDtypes: { T: x.dtype }, templateInts: { D: width, THREADS: threads, EPS: epsBits },
|
|
64
|
+
}) as [MlxArray, MlxArray];
|
|
65
|
+
}
|