mlx-bun 0.4.0 → 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.
Files changed (601) hide show
  1. package/README.md +1453 -88
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +10 -8
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +12 -10
  78. package/src/{serve → server}/chat-stage.ts +27 -22
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +42 -42
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +42 -83
  85. package/src/server/generated-token-history.ts +57 -0
  86. package/src/{serve → server}/http.ts +25 -13
  87. package/src/{serve → server}/inference-request.ts +5 -5
  88. package/src/server/job-routes.ts +20 -0
  89. package/src/server/media-prompt.ts +63 -0
  90. package/src/server/memory-completion-client.ts +46 -0
  91. package/src/server/model-routes.ts +192 -0
  92. package/src/{serve → server}/openai-wire.ts +3 -2
  93. package/src/{serve → server}/pipeline.ts +1 -1
  94. package/src/server/prompt-builder.ts +13 -0
  95. package/src/server/prompt-contracts.ts +17 -0
  96. package/src/server/proxy-routes.ts +187 -0
  97. package/src/server/publishing-routes.ts +41 -0
  98. package/src/{serve → server}/request-plan.ts +8 -8
  99. package/src/{serve → server}/request-prep.ts +82 -68
  100. package/src/{serve → server}/responses-client.ts +30 -13
  101. package/src/{responses.ts → server/responses.ts} +12 -93
  102. package/src/server/routes.ts +205 -0
  103. package/src/server/start.ts +72 -0
  104. package/src/server/status-routes.ts +108 -0
  105. package/src/{serve → server}/text-completion-stage.ts +25 -4
  106. package/src/server/text-prompt.ts +12 -0
  107. package/src/{serve → server}/token-streams.ts +36 -38
  108. package/src/server/worker-routes.ts +311 -0
  109. package/src/storage/paths.ts +61 -0
  110. package/src/web/assets.ts +37 -0
  111. package/src/web/{src → browser}/api.ts +5 -23
  112. package/src/web/browser/chat-host.ts +17 -0
  113. package/src/web/browser/installed-panels.ts +6 -0
  114. package/src/web/browser/main.ts +55 -0
  115. package/src/web/browser/markdown.ts +279 -0
  116. package/src/web/browser/memory-panel.ts +9 -0
  117. package/src/web/browser/palette.ts +64 -0
  118. package/src/web/browser/protocol.ts +13 -0
  119. package/src/web/browser/shell.ts +467 -0
  120. package/src/web/{src → browser}/status.ts +32 -36
  121. package/src/web/build.ts +51 -0
  122. package/src/web/public/app.html +1054 -0
  123. package/src/web/{sw.js → public/sw.js} +4 -3
  124. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  125. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  126. package/THIRD_PARTY_LICENSES.md +0 -51
  127. package/docs/README.md +0 -94
  128. package/docs/archive/hlg-sampling.md +0 -350
  129. package/docs/archive/investigations/audio-input-plan.md +0 -356
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  174. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  175. package/docs/design/batching.md +0 -1841
  176. package/docs/design/decode-speed-program.md +0 -2349
  177. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  178. package/docs/design/generic-model-support.md +0 -705
  179. package/docs/design/kv-cache.md +0 -824
  180. package/docs/design/orpo-training.md +0 -680
  181. package/docs/design/speculative-decoding.md +0 -1990
  182. package/docs/design/turboquant.md +0 -2212
  183. package/docs/design/unified-engine-frontier-plan.md +0 -1880
  184. package/docs/design/web-chat-redesign.md +0 -695
  185. package/docs/planning/PRODUCT_ROADMAP.md +0 -594
  186. package/docs/planning/ResearchTopics.md +0 -119
  187. package/docs/planning/release-notes-v0.4.0.md +0 -45
  188. package/docs/planning/video-series.md +0 -421
  189. package/docs/reference/benchmarks.md +0 -3613
  190. package/docs/reference/cli.md +0 -585
  191. package/docs/reference/distribution.md +0 -355
  192. package/docs/reference/environment.md +0 -398
  193. package/docs/reference/glossary.md +0 -39
  194. package/docs/reference/library-api.md +0 -674
  195. package/docs/reference/memory.md +0 -260
  196. package/docs/reference/models.md +0 -334
  197. package/docs/reference/server-api.md +0 -1775
  198. package/docs/reference/server-config.md +0 -846
  199. package/docs/reference/training.md +0 -462
  200. package/docs/reference/troubleshooting.md +0 -158
  201. package/src/admission-plan.ts +0 -18
  202. package/src/audio/conformer.ts +0 -647
  203. package/src/audio/decode.ts +0 -133
  204. package/src/audio/features.ts +0 -202
  205. package/src/audio/transcode.ts +0 -67
  206. package/src/backends/mlx/assistant-target.ts +0 -46
  207. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  208. package/src/backends/mlx/autoregressive.ts +0 -108
  209. package/src/backends/mlx/batch-group.ts +0 -1790
  210. package/src/backends/mlx/cache-attachment.ts +0 -67
  211. package/src/backends/mlx/cache-layout.ts +0 -31
  212. package/src/backends/mlx/checkpoint-state.ts +0 -53
  213. package/src/backends/mlx/continuation-persistence.ts +0 -106
  214. package/src/backends/mlx/continuation-request.ts +0 -43
  215. package/src/backends/mlx/continuation.ts +0 -77
  216. package/src/backends/mlx/diffusion.ts +0 -39
  217. package/src/backends/mlx/draft-target.ts +0 -32
  218. package/src/backends/mlx/evaluation.ts +0 -105
  219. package/src/backends/mlx/fill-append.ts +0 -45
  220. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  221. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  222. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  223. package/src/backends/mlx/gateway-binding.ts +0 -130
  224. package/src/backends/mlx/graph.ts +0 -38
  225. package/src/backends/mlx/kv-codec.ts +0 -9
  226. package/src/backends/mlx/kv-maintenance.ts +0 -133
  227. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  228. package/src/backends/mlx/memory-client.ts +0 -189
  229. package/src/backends/mlx/methods.ts +0 -90
  230. package/src/backends/mlx/model-prompt.ts +0 -164
  231. package/src/backends/mlx/model-serving.ts +0 -79
  232. package/src/backends/mlx/prefill-cohort.ts +0 -89
  233. package/src/backends/mlx/prefill-rows.ts +0 -200
  234. package/src/backends/mlx/prefill.ts +0 -33
  235. package/src/backends/mlx/projected-context-rows.ts +0 -72
  236. package/src/backends/mlx/request-policy.ts +0 -20
  237. package/src/backends/mlx/request-state-policy.ts +0 -41
  238. package/src/backends/mlx/rollback.ts +0 -89
  239. package/src/backends/mlx/row-sampling.ts +0 -49
  240. package/src/backends/mlx/serial-executor.ts +0 -242
  241. package/src/backends/mlx/speculative-group.ts +0 -359
  242. package/src/backends/mlx/speculative-round.ts +0 -161
  243. package/src/backends/mlx/speculative.ts +0 -114
  244. package/src/backends/mlx/state-rows.ts +0 -104
  245. package/src/backends/mlx/state-views.ts +0 -46
  246. package/src/backends/mlx/target-layout-capability.ts +0 -35
  247. package/src/backends/mlx/text-engine.ts +0 -73
  248. package/src/backends/mlx/transitioning-kv-rows.ts +0 -165
  249. package/src/chat-template.ts +0 -382
  250. package/src/cli.ts +0 -3168
  251. package/src/config.ts +0 -399
  252. package/src/contracts/completion.ts +0 -16
  253. package/src/contracts/execution.ts +0 -60
  254. package/src/contracts/generation.ts +0 -103
  255. package/src/contracts/host.ts +0 -7
  256. package/src/contracts/pi-provider.ts +0 -30
  257. package/src/contracts/pi.ts +0 -259
  258. package/src/contracts/prefix-cache.ts +0 -23
  259. package/src/contracts/resources.ts +0 -25
  260. package/src/contracts/sampling.ts +0 -23
  261. package/src/contracts/scheduling.ts +0 -45
  262. package/src/contracts/tsconfig.json +0 -14
  263. package/src/dataset/generators.ts +0 -1087
  264. package/src/dataset/index.ts +0 -12
  265. package/src/dataset/job.ts +0 -81
  266. package/src/dataset/llm.ts +0 -71
  267. package/src/dataset/registry.ts +0 -528
  268. package/src/diffusion/diffusion-generate.ts +0 -507
  269. package/src/download.ts +0 -505
  270. package/src/embed.ts +0 -77
  271. package/src/engine/admission.ts +0 -59
  272. package/src/engine/cancellation.ts +0 -38
  273. package/src/engine/engine.ts +0 -56
  274. package/src/engine/execution-coordinator.ts +0 -70
  275. package/src/engine/execution-plan.ts +0 -53
  276. package/src/engine/resources.ts +0 -53
  277. package/src/engine/scheduler.ts +0 -72
  278. package/src/engine/session.ts +0 -247
  279. package/src/eval/README.md +0 -130
  280. package/src/eval/ifeval.ts +0 -81
  281. package/src/eval/kl-prompts.ts +0 -29
  282. package/src/eval/kl.ts +0 -590
  283. package/src/eval/perplexity.ts +0 -164
  284. package/src/eval/quality-db.ts +0 -92
  285. package/src/eval/reference-logits.ts +0 -207
  286. package/src/eval/runner.ts +0 -124
  287. package/src/eval/score.ts +0 -49
  288. package/src/eval/tasks/bfcl.ts +0 -535
  289. package/src/eval/tasks/gsm8k.ts +0 -98
  290. package/src/eval/tasks/hashhop.ts +0 -139
  291. package/src/eval/tasks/humaneval.ts +0 -275
  292. package/src/eval/tasks/ifeval.ts +0 -525
  293. package/src/eval/tasks/mmlu.ts +0 -157
  294. package/src/eval/ultrafeedback.ts +0 -60
  295. package/src/evaldb.ts +0 -199
  296. package/src/expert-io.ts +0 -393
  297. package/src/expert-offload-build.ts +0 -77
  298. package/src/expert-offload.ts +0 -76
  299. package/src/expert-residency.ts +0 -1134
  300. package/src/expert-usage.ts +0 -415
  301. package/src/fill/echo-index.ts +0 -188
  302. package/src/fill/fill-session.ts +0 -452
  303. package/src/fill/proposal.ts +0 -68
  304. package/src/fill/schema-rows.ts +0 -506
  305. package/src/fill/tool-boundary.ts +0 -103
  306. package/src/fit.ts +0 -249
  307. package/src/generate.ts +0 -1346
  308. package/src/grammar.ts +0 -591
  309. package/src/harness-pi.ts +0 -184
  310. package/src/hf-push.ts +0 -628
  311. package/src/hub-rest.ts +0 -242
  312. package/src/index.ts +0 -63
  313. package/src/inference/denoising.ts +0 -17
  314. package/src/inference/draft-acceptance.ts +0 -41
  315. package/src/inference/draft-output.ts +0 -49
  316. package/src/inference/graph.ts +0 -33
  317. package/src/inference/prefill.ts +0 -39
  318. package/src/inference/rollback.ts +0 -7
  319. package/src/jobs/index.ts +0 -31
  320. package/src/jobs/job-entry.ts +0 -107
  321. package/src/jobs/task-client.ts +0 -19
  322. package/src/jobs/types.ts +0 -2
  323. package/src/kv-scheme.ts +0 -257
  324. package/src/kv-store.ts +0 -1099
  325. package/src/lab/README.md +0 -18
  326. package/src/lab/curve/curve-designer.html +0 -303
  327. package/src/lab/curve/curve-sampler.ts +0 -122
  328. package/src/lab/expert-trace/expert-trace.ts +0 -69
  329. package/src/lab/paged-kv/paged-kv-rows.ts +0 -83
  330. package/src/lab/paged-kv/paged-kv.ts +0 -270
  331. package/src/library.ts +0 -40
  332. package/src/lora.ts +0 -408
  333. package/src/md-modules.d.ts +0 -49
  334. package/src/media-fetch.ts +0 -442
  335. package/src/memory/article.ts +0 -558
  336. package/src/memory/chunk-validation.ts +0 -150
  337. package/src/memory/chunk.ts +0 -318
  338. package/src/memory/cluster.ts +0 -351
  339. package/src/memory/coverage.ts +0 -134
  340. package/src/memory/crosslink.ts +0 -0
  341. package/src/memory/db.ts +0 -253
  342. package/src/memory/entity.ts +0 -203
  343. package/src/memory/gate.ts +0 -187
  344. package/src/memory/history.ts +0 -152
  345. package/src/memory/ingest.ts +0 -335
  346. package/src/memory/model.ts +0 -183
  347. package/src/memory/normalize.ts +0 -671
  348. package/src/memory/parse.ts +0 -22
  349. package/src/memory/pipeline.ts +0 -275
  350. package/src/memory/prompts.ts +0 -65
  351. package/src/memory/query.ts +0 -334
  352. package/src/memory/reconcile.ts +0 -734
  353. package/src/memory/redirect.ts +0 -108
  354. package/src/memory/reindex.ts +0 -325
  355. package/src/memory/resolve.ts +0 -307
  356. package/src/memory/rest.ts +0 -245
  357. package/src/memory/route.ts +0 -272
  358. package/src/memory/schedule.ts +0 -165
  359. package/src/memory/stages.ts +0 -777
  360. package/src/memory/synthesize.ts +0 -1638
  361. package/src/memory/tools.ts +0 -749
  362. package/src/memory/vault.ts +0 -1194
  363. package/src/memory/wikify.ts +0 -1064
  364. package/src/mlx/array.ts +0 -388
  365. package/src/mlx/autograd.ts +0 -304
  366. package/src/mlx/checkpoint.ts +0 -149
  367. package/src/mlx/compile.ts +0 -168
  368. package/src/mlx/custom-vjp.ts +0 -188
  369. package/src/mlx/ffi.ts +0 -512
  370. package/src/mlx/materialize.ts +0 -23
  371. package/src/mlx/metal-kernel.ts +0 -159
  372. package/src/mlx/ops.ts +0 -1021
  373. package/src/mlx/quantized-rows.ts +0 -36
  374. package/src/mlx/token-bitmask.ts +0 -52
  375. package/src/mlx/turboquant-kv-decode.ts +0 -82
  376. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  377. package/src/mlx/turboquant-ops.ts +0 -861
  378. package/src/mlx/turboquant-tables.ts +0 -237
  379. package/src/mmap.ts +0 -90
  380. package/src/model/artifact-identity.ts +0 -73
  381. package/src/model/batched-kv.ts +0 -235
  382. package/src/model/batched-mask.ts +0 -279
  383. package/src/model/batched-quant.ts +0 -212
  384. package/src/model/batched-quantized-kv.ts +0 -144
  385. package/src/model/batched-rotating-quant.ts +0 -353
  386. package/src/model/batched-rotating-state.ts +0 -149
  387. package/src/model/batched-rotating.ts +0 -338
  388. package/src/model/batched-row-storage.ts +0 -179
  389. package/src/model/batched-ssm.ts +0 -52
  390. package/src/model/batched-turboquant-kv.ts +0 -93
  391. package/src/model/compiled-decode.ts +0 -712
  392. package/src/model/decoded-kv-donor.ts +0 -12
  393. package/src/model/delayed-quantized-kv.ts +0 -72
  394. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  395. package/src/model/delayed-turboquant-kv.ts +0 -55
  396. package/src/model/diffusion-gemma.ts +0 -1026
  397. package/src/model/factory.ts +0 -189
  398. package/src/model/fingerprint.ts +0 -33
  399. package/src/model/flash-attention.ts +0 -694
  400. package/src/model/full-prefill-padding.ts +0 -56
  401. package/src/model/full-prefill-row.ts +0 -105
  402. package/src/model/gemma4-base.ts +0 -2351
  403. package/src/model/gemma4.ts +0 -1315
  404. package/src/model/generated/gemma4-12b.ts +0 -429
  405. package/src/model/generated/gemma4-26b.ts +0 -448
  406. package/src/model/generated/gemma4-e4b.ts +0 -717
  407. package/src/model/generated/index.ts +0 -18
  408. package/src/model/glm52-atlas.ts +0 -511
  409. package/src/model/glm52-cache.ts +0 -792
  410. package/src/model/glm52-config.ts +0 -262
  411. package/src/model/glm52-container.ts +0 -305
  412. package/src/model/glm52-coupling.ts +0 -388
  413. package/src/model/glm52-dsa.ts +0 -606
  414. package/src/model/glm52-expert-layout.ts +0 -253
  415. package/src/model/glm52-layout.ts +0 -152
  416. package/src/model/glm52-memory.ts +0 -500
  417. package/src/model/glm52-mla.ts +0 -792
  418. package/src/model/glm52-moe.ts +0 -223
  419. package/src/model/glm52-pilot.ts +0 -537
  420. package/src/model/glm52-reference.ts +0 -445
  421. package/src/model/glm52-residency.ts +0 -557
  422. package/src/model/glm52-resident-weights.ts +0 -135
  423. package/src/model/glm52-streamed-experts.ts +0 -453
  424. package/src/model/glm52-streamed-metal.ts +0 -610
  425. package/src/model/glm52-weights.ts +0 -503
  426. package/src/model/glm52.ts +0 -1132
  427. package/src/model/implementation.ts +0 -69
  428. package/src/model/kv-attention-view.ts +0 -67
  429. package/src/model/minicpm5.ts +0 -376
  430. package/src/model/profile.ts +0 -443
  431. package/src/model/qwen-append.ts +0 -12
  432. package/src/model/qwen-conv.ts +0 -52
  433. package/src/model/qwen3-delta-state.ts +0 -74
  434. package/src/model/qwen3-delta.ts +0 -451
  435. package/src/model/qwen3-moe.ts +0 -404
  436. package/src/model/qwen3-mrope.ts +0 -252
  437. package/src/model/qwen3.ts +0 -277
  438. package/src/model/qwen3_5-checkpoint.ts +0 -164
  439. package/src/model/qwen3_5.ts +0 -819
  440. package/src/model/rotating-kv-donor.ts +0 -35
  441. package/src/model/rotating-kv-layout.ts +0 -262
  442. package/src/model/rotating-row-transaction.ts +0 -35
  443. package/src/model/speculative-rotating-kv.ts +0 -101
  444. package/src/model/ssm-prefill-padding.ts +0 -39
  445. package/src/model/support.ts +0 -102
  446. package/src/model/trellis-balanced-scatter.ts +0 -51
  447. package/src/model/trellis-linear.ts +0 -672
  448. package/src/model/trellis-shared-m.ts +0 -151
  449. package/src/model/trellis-shared-scatter.ts +0 -144
  450. package/src/model/trellis-splitk-prefill.ts +0 -158
  451. package/src/model/trellis-tiled-prefill.ts +0 -136
  452. package/src/model/trellis-vector-expand.ts +0 -62
  453. package/src/model/trellis-wide-prefill.ts +0 -94
  454. package/src/model/turboquant-codec.ts +0 -83
  455. package/src/model/universal/archs.ts +0 -332
  456. package/src/model/universal/dense.ts +0 -612
  457. package/src/model/universal/modules.ts +0 -170
  458. package/src/model/universal/rope.ts +0 -381
  459. package/src/native/expert_io.c +0 -523
  460. package/src/native/frame_extract.swift +0 -140
  461. package/src/native-pack.ts +0 -162
  462. package/src/pi-provider.ts +0 -53
  463. package/src/pi-session.ts +0 -81
  464. package/src/pi-terminal.ts +0 -274
  465. package/src/pi-web.ts +0 -1932
  466. package/src/preflight.ts +0 -158
  467. package/src/prompt-cache.ts +0 -460
  468. package/src/quantize/allocator.ts +0 -627
  469. package/src/quantize/atomic-output.ts +0 -38
  470. package/src/quantize/calibration.ts +0 -181
  471. package/src/quantize/config-writer.ts +0 -145
  472. package/src/quantize/index.ts +0 -84
  473. package/src/quantize/job.ts +0 -162
  474. package/src/quantize/quantizer.ts +0 -666
  475. package/src/quantize/rotate.ts +0 -603
  476. package/src/quantize/safetensors-writer.ts +0 -249
  477. package/src/quantize/sensitivity.ts +0 -216
  478. package/src/quantize/trellis.ts +0 -586
  479. package/src/quantize/weight-transform.ts +0 -292
  480. package/src/registry.ts +0 -602
  481. package/src/runtime-config.ts +0 -81
  482. package/src/safetensors.ts +0 -158
  483. package/src/sampler.ts +0 -1011
  484. package/src/serve/admin-routes.ts +0 -184
  485. package/src/serve/assets/curve-terrain.html +0 -105
  486. package/src/serve/assets/training-inference-map.html +0 -575
  487. package/src/serve/aux-routes.ts +0 -132
  488. package/src/serve/batch-scheduler.ts +0 -8
  489. package/src/serve/checkpoint-identity.ts +0 -64
  490. package/src/serve/generation-gateway.ts +0 -496
  491. package/src/serve/isolate.ts +0 -609
  492. package/src/serve/kv-budget.ts +0 -14
  493. package/src/serve/lab-routes.ts +0 -341
  494. package/src/serve/lane-registry.ts +0 -45
  495. package/src/serve/model-admin-routes.ts +0 -155
  496. package/src/serve/model-binding.ts +0 -43
  497. package/src/serve/model-host.ts +0 -511
  498. package/src/serve/parent-application.ts +0 -56
  499. package/src/serve/prompt-response-trace.ts +0 -147
  500. package/src/serve/response-lease.ts +0 -24
  501. package/src/serve/session-search.ts +0 -326
  502. package/src/serve/static-routes.ts +0 -101
  503. package/src/server.ts +0 -1314
  504. package/src/spec/assistant-rows.ts +0 -92
  505. package/src/spec/assistant-source.ts +0 -126
  506. package/src/spec/cache-identity.ts +0 -22
  507. package/src/spec/deepspec-rows.ts +0 -29
  508. package/src/spec/deepspec-source.ts +0 -145
  509. package/src/spec/dflash-source.ts +0 -149
  510. package/src/spec/drafter.ts +0 -304
  511. package/src/spec/dspark/ab-stats.ts +0 -136
  512. package/src/spec/dspark/calibration.ts +0 -112
  513. package/src/spec/dspark/data-dflash.ts +0 -156
  514. package/src/spec/dspark/data.ts +0 -201
  515. package/src/spec/dspark/deepspec-module.ts +0 -776
  516. package/src/spec/dspark/generate-dflash.ts +0 -245
  517. package/src/spec/dspark/generate.ts +0 -290
  518. package/src/spec/dspark/loader.ts +0 -23
  519. package/src/spec/dspark/loss.ts +0 -213
  520. package/src/spec/dspark/module-dflash.ts +0 -560
  521. package/src/spec/dspark/module.ts +0 -540
  522. package/src/spec/dspark/quantize-drafter.ts +0 -91
  523. package/src/spec/dspark/sample.ts +0 -112
  524. package/src/spec/generate.ts +0 -276
  525. package/src/spec/glm52-mtp-graph.ts +0 -29
  526. package/src/spec/glm52-mtp-rows.ts +0 -132
  527. package/src/spec/glm52-mtp-source.ts +0 -317
  528. package/src/spec/glm52-mtp-state.ts +0 -33
  529. package/src/spec/ngram-source.ts +0 -204
  530. package/src/spec/projected-draft-rows.ts +0 -73
  531. package/src/spec/qwen-mtp-module.ts +0 -171
  532. package/src/spec/qwen-mtp-rows.ts +0 -219
  533. package/src/spec/qwen-mtp-source.ts +0 -383
  534. package/src/spec/qwen-mtp-state.ts +0 -30
  535. package/src/spec/serve-loop.ts +0 -585
  536. package/src/spec/source.ts +0 -215
  537. package/src/spec/two-model-rows.ts +0 -61
  538. package/src/spec/two-model.ts +0 -113
  539. package/src/ssd-cache.ts +0 -372
  540. package/src/ssd-durability.ts +0 -211
  541. package/src/tokenizer.ts +0 -335
  542. package/src/tool-approvals.ts +0 -131
  543. package/src/tool-call.ts +0 -695
  544. package/src/train/dataset.ts +0 -484
  545. package/src/train/diffusion-lora.ts +0 -216
  546. package/src/train/export.ts +0 -32
  547. package/src/train/flash-cce.ts +0 -1234
  548. package/src/train/forward.ts +0 -266
  549. package/src/train/fuse.ts +0 -243
  550. package/src/train/index.ts +0 -46
  551. package/src/train/job.ts +0 -205
  552. package/src/train/lora-params.ts +0 -254
  553. package/src/train/loss.ts +0 -1591
  554. package/src/train/merge.ts +0 -195
  555. package/src/train/optimizer.ts +0 -183
  556. package/src/train/prefix-shared.ts +0 -490
  557. package/src/train/rank.ts +0 -137
  558. package/src/train/segmented.ts +0 -1828
  559. package/src/train/steel-qmm-header.ts +0 -1461
  560. package/src/train/trainer.ts +0 -1221
  561. package/src/train/watch.ts +0 -542
  562. package/src/vision/diffusion-vision.ts +0 -620
  563. package/src/vision/embedder.ts +0 -168
  564. package/src/vision/preprocess.ts +0 -247
  565. package/src/vision/prompt.ts +0 -373
  566. package/src/vision/qwen3vl-preprocess.ts +0 -409
  567. package/src/vision/qwen3vl-prompt.ts +0 -196
  568. package/src/vision/qwen3vl-tower.ts +0 -555
  569. package/src/vision/siglip.ts +0 -599
  570. package/src/vision/video-frames.ts +0 -148
  571. package/src/web/app.html +0 -2143
  572. package/src/web/app.js +0 -5703
  573. package/src/web/skills/memory/SKILL.md +0 -120
  574. package/src/web/skills/web-research/SKILL.md +0 -46
  575. package/src/web/skills.ts +0 -97
  576. package/src/web/src/adapters-panel.ts +0 -288
  577. package/src/web/src/assistant.ts +0 -451
  578. package/src/web/src/chat.ts +0 -1057
  579. package/src/web/src/composer.ts +0 -959
  580. package/src/web/src/dataset.ts +0 -132
  581. package/src/web/src/finetune.ts +0 -267
  582. package/src/web/src/hub.ts +0 -428
  583. package/src/web/src/main.ts +0 -98
  584. package/src/web/src/markdown.ts +0 -0
  585. package/src/web/src/memory-panel.ts +0 -572
  586. package/src/web/src/model-picker.ts +0 -159
  587. package/src/web/src/palette.ts +0 -346
  588. package/src/web/src/protocol.ts +0 -26
  589. package/src/web/src/quantize.ts +0 -167
  590. package/src/web/src/rag.ts +0 -274
  591. package/src/web/src/sessions.ts +0 -469
  592. package/src/web/src/shell.ts +0 -804
  593. package/src/web/src/ui-catalog.ts +0 -78
  594. package/src/web/vendor/README.md +0 -61
  595. package/src/web-assets.ts +0 -47
  596. package/src/web-tools.ts +0 -478
  597. package/src/weights.ts +0 -255
  598. /package/src/{serve → server}/completion-sink.ts +0 -0
  599. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  600. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  601. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,1234 +0,0 @@
1
- // Flash Cut-Cross-Entropy Metal kernel for the ORPO log-prob head — the vocab
2
- // loop lives INSIDE the kernel, so neither the [M,V] logits nor a dequantized
3
- // [V,hidden] head ever touch HBM (the residency bound pure MLX can't reach; see
4
- // docs/design/orpo-training.md → "Vocab-blocked online-softmax"). Computes the
5
- // per-token response log-prob `logp = target_logit − logsumexp_v(h·W_v)`.
6
- //
7
- // TRANSPILE SOURCE — this is, in effect, Apple's Cut Cross Entropy + Liger's
8
- // FusedLinearCrossEntropy Triton kernels transpiled Triton → Metal:
9
- // - Apple CCE (github.com/apple/ml-cross-entropy):
10
- // cce_lse_forward.py — forward: tile (BLOCK_B tokens × BLOCK_V vocab),
11
- // accum = tl.dot(E_tile, Cᵀ_tile) over a D-loop, per-block
12
- // this_lse = mx + log(Σ exp(logit−mx)), merged across vocab blocks into a
13
- // per-token LSE via locked atomic logaddexp; mask offs_v<V → −inf.
14
- // cce_backward.py — backward: same (token×vocab) tiling, recompute logits,
15
- // d_accum = exp(accum − lse); d_accum += where(is_target, −1, 0); then
16
- // dE += d_accum @ C and dC += d_accumᵀ @ E via locked atomic adds;
17
- // optional skip when |d_accum| < filter_eps.
18
- // - Liger FusedLinearCrossEntropy — token-chunking + the in-place softmax−onehot
19
- // gradient written back over the logit buffer.
20
- //
21
- // DIVERGENCES (why this isn't a line-for-line port):
22
- // 1. Our classifier C is the QUANTIZED head (4/8-bit affine). Apple loads C
23
- // dense (bf16); we keep the in-Metal dequant for `tl.dot` (the qdot pattern
24
- // from mlx's quantized.h qdot pattern). This is the one substantive change.
25
- // 2. The head is FROZEN (not a LoRA target) → we need only dE (=dh), NOT dC.
26
- // 3. The LSE cross-vocab-block merge is done in cheap MLX ops on per-block
27
- // partials, not Metal atomic-logaddexp spinlocks (simpler, same result).
28
- //
29
- // STATUS: v1 (one threadgroup per token, whole vocab serially) is correct +
30
- // memory-bounded (validated MiniCPM5→M=8192, e4b→M=512) but its single-token
31
- // threadgroup runs the full 262k vocab serially → trips the GPU watchdog on e4b
32
- // at higher M. The vocab-PARALLEL form below (grid over token × vocab blocks, à la
33
- // Apple CCE) bounds per-threadgroup work and is the production path.
34
-
35
- import { MetalKernel } from "../mlx/metal-kernel";
36
- import { MlxArray } from "../mlx/array";
37
- import { Dtype } from "../mlx/ffi";
38
- import * as ops from "../mlx/ops";
39
- import { STEEL_QMM_HEADER } from "./steel-qmm-header";
40
- import { runtimeValue } from "../runtime-config";
41
-
42
- /** The quantized head pieces the kernel needs (packed weight + per-group affine
43
- * scale/bias + bits/group_size). Mirrors HeadQuant in loss.ts. */
44
- export interface FlashCceHead {
45
- w: MlxArray; // packed quantized weight, uint32 [V, hidden*bits/32]
46
- scales: MlxArray; // [V, hidden/groupSize]
47
- biases: MlxArray; // [V, hidden/groupSize] (affine — required)
48
- bits: number; // 4 or 8
49
- groupSize: number; // 64
50
- softcap: number | null;
51
- }
52
-
53
- const H_MAX = 4096; // covers MiniCPM5 (1536) and e4b (2560) hidden sizes
54
- const TG = 128; // threads per token-threadgroup
55
- // Vocab rows per threadgroup (Apple CCE's BLOCK_V analog). Bounds per-threadgroup
56
- // runtime so a single dispatch never loops the whole vocab — the e4b watchdog fix.
57
- const VOCAB_BLOCK = 8192;
58
- // Tokens processed together per threadgroup (Apple CCE's BLOCK_B). Each thread
59
- // dequantizes a vocab row's weights ONCE and reuses them across BLOCK_B tokens,
60
- // amortizing the ALU-bound dequant BLOCK_B× (the dominant cost in the v1
61
- // one-token-per-threadgroup kernel). Forward and backward use different tile
62
- // widths: the backward holds a per-token output accumulator `dacc[BLOCK_B·DPER]`
63
- // in registers, so it is register-bound to a smaller tile (BLOCK_B=8 spills).
64
- const BLOCK_B = 8; // forward (online stats are only ~7·BLOCK_B floats/thread)
65
- const BLOCK_B_BWD = 4; // backward (dacc[BLOCK_B·DPER] — 8 overflows the register file)
66
- // simdgroup_matrix forward GEMM tiles (Apple CCE's BLOCK_B×BLOCK_V×BLOCK_D, mapped
67
- // to Metal 8×8 simdgroup_matrix). BB tokens × BV vocab logit tile, accumulated over
68
- // BD hidden chunks. BV/8 = 4 column-blocks → one per simdgroup (TG/32 = 4).
69
- const SG_BB = 8; // token tile (one 8-row simdgroup block)
70
- const SG_BV = 32; // vocab tile (4 column-blocks)
71
- const SG_BD = 32; // hidden chunk (4 k-blocks). BD=64 (GS-aligned, ½ the barriers)
72
- // measured SLOWER (fwd 844→1010, bwd 3311→3799): the 8KB Csh tile
73
- // cuts occupancy more than fewer barriers help. 32 is the sweet spot.
74
-
75
- // VOCAB-PARALLEL forward (Apple CCE cce_lse_forward, transpiled): one threadgroup
76
- // per (token=grid.z, vocab-block=grid.y) handles a bounded V/NBLK vocab slice → no
77
- // single threadgroup runs the whole vocab (the watchdog fix). Each writes per-block
78
- // partials (blockMax, blockSumexp-relative-to-blockMax, target logit) to [M,NBLK];
79
- // the cross-vocab-block LSE merge is done in cheap MLX ops (vs Apple's locked
80
- // atomic logaddexp). 128 threads stride the slice, per-thread online softmax,
81
- // merged across the threadgroup.
82
- const FWD_SOURCE = String.raw`
83
- const uint tokBlk = threadgroup_position_in_grid.z; // token-block index
84
- const uint vb = threadgroup_position_in_grid.y; // vocab-block index
85
- const uint tid = thread_position_in_threadgroup.x; // 0..TG-1
86
- const uint M = shape[0];
87
- const uint V = shape[1];
88
- const uint H = shape[2];
89
- const uint GS = shape[3];
90
- const uint BITS = shape[4];
91
- const uint WPR = shape[5]; // uint32 words per packed weight row
92
- const uint GR = shape[6]; // groups per row (H/GS)
93
- const uint NBLK = shape[7]; // number of vocab blocks
94
- const float cap = capv[0];
95
- const uint perWord = 32u / BITS;
96
- const uint qmask = (BITS == 32u) ? 0xffffffffu : ((1u << BITS) - 1u);
97
- const uint wordsPerGroup = GS / perWord;
98
- const uint blockV = (V + NBLK - 1u) / NBLK;
99
- const uint nStart = vb * blockV;
100
- const uint nEnd = min(nStart + blockV, V);
101
- const uint tokBase = tokBlk * BLOCK_B;
102
-
103
- // Per-token online state (registers). Each thread keeps BLOCK_B tokens' running
104
- // softmax stats so one vocab-row dequant feeds all BLOCK_B tokens.
105
- float mx[BLOCK_B], sumexp[BLOCK_B], tgtLogit[BLOCK_B];
106
- uint tgtId[BLOCK_B];
107
- for (uint b = 0u; b < BLOCK_B; ++b) {
108
- mx[b] = -INFINITY; sumexp[b] = 0.0f; tgtLogit[b] = 0.0f;
109
- tgtId[b] = (tokBase + b < M) ? targets[tokBase + b] : 0xffffffffu;
110
- }
111
-
112
- for (uint n = nStart + tid; n < nEnd; n += TG) {
113
- const device uint* row = wp + (ulong)n * WPR;
114
- const uint sbBase = n * GR;
115
- float partial[BLOCK_B];
116
- for (uint b = 0u; b < BLOCK_B; ++b) partial[b] = 0.0f;
117
- // qdot factoring per group; the nibble dequant (q) is computed ONCE per
118
- // element and reused across all BLOCK_B tokens (the amortization).
119
- for (uint grp = 0u; grp < GR; ++grp) {
120
- const float sc = scales[sbBase + grp];
121
- const float bi = biases[sbBase + grp];
122
- const uint d0 = grp * GS;
123
- float gacc[BLOCK_B], ghsum[BLOCK_B];
124
- for (uint b = 0u; b < BLOCK_B; ++b) { gacc[b] = 0.0f; ghsum[b] = 0.0f; }
125
- for (uint wi = 0u; wi < wordsPerGroup; ++wi) {
126
- const uint word = row[d0 / perWord + wi];
127
- const uint dbase = d0 + wi * perWord;
128
- for (uint k = 0u; k < perWord; ++k) {
129
- const float q = (float)((word >> (BITS * k)) & qmask);
130
- const uint d = dbase + k;
131
- for (uint b = 0u; b < BLOCK_B; ++b) {
132
- const uint tb = min(tokBase + b, M - 1u); // clamp padding lanes (last block) — result discarded in merge
133
- // h is passed TRANSPOSED [H, M], so the BLOCK_B inner reads hit one
134
- // cache line (h[d, tb..tb+BLOCK_B] contiguous) instead of BLOCK_B
135
- // separate lines — the memory-access fix for the strided pattern.
136
- const float hd = (float)h[(ulong)d * M + tb];
137
- gacc[b] += hd * q; ghsum[b] += hd;
138
- }
139
- }
140
- }
141
- for (uint b = 0u; b < BLOCK_B; ++b) partial[b] += sc * gacc[b] + bi * ghsum[b];
142
- }
143
- for (uint b = 0u; b < BLOCK_B; ++b) {
144
- float logit = partial[b];
145
- if (cap > 0.0f) logit = cap * tanh(logit / cap);
146
- const float nm = max(mx[b], logit);
147
- sumexp[b] = sumexp[b] * exp(mx[b] - nm) + exp(logit - nm);
148
- mx[b] = nm;
149
- if (n == tgtId[b]) tgtLogit[b] = logit;
150
- }
151
- }
152
-
153
- // Merge the TG threads' partials per token → per-(token, vocab-block) output.
154
- threadgroup float mM[TG], mS[TG], mT[TG];
155
- for (uint b = 0u; b < BLOCK_B; ++b) {
156
- if (tokBase + b >= M) break;
157
- mM[tid] = mx[b]; mS[tid] = sumexp[b]; mT[tid] = tgtLogit[b];
158
- threadgroup_barrier(mem_flags::mem_threadgroup);
159
- if (tid == 0u) {
160
- float gm = -INFINITY, gs = 0.0f, gt = 0.0f;
161
- for (uint i = 0u; i < TG; ++i) {
162
- const float im = mM[i];
163
- const float nm = max(gm, im);
164
- if (nm > -INFINITY) { gs = gs * exp(gm - nm) + mS[i] * exp(im - nm); gm = nm; }
165
- gt += mT[i]; // target logit (one thread nonzero), else 0
166
- }
167
- blockMax_out[(tokBase + b) * NBLK + vb] = gm;
168
- blockSum_out[(tokBase + b) * NBLK + vb] = gs;
169
- blockTgt_out[(tokBase + b) * NBLK + vb] = gt;
170
- }
171
- threadgroup_barrier(mem_flags::mem_threadgroup);
172
- }
173
- `;
174
-
175
- // SIMDGROUP_MATRIX forward GEMM (the speed tier): per (token-block, vocab-block)
176
- // threadgroup, loop the vocab slice in BV-chunks; for each chunk compute the
177
- // [BB, BV] logit tile with 8×8 simdgroup_matrix mma over BD hidden chunks, then a
178
- // scalar online-softmax update. The classifier tile C [BV, BD] is dequantized into
179
- // threadgroup once per (vocab-chunk, D-chunk) and reused across all BB tokens by
180
- // the matmul — so the dequant is amortized AND the matmul runs at simdgroup-matrix
181
- // throughput (the scalar madd was the bottleneck after BLOCK_B tiling). BV/8 = 4
182
- // column-blocks → one per simdgroup (TG/32 = 4). logit[b,v] = Σ_d E[b,d]·C[v,d] =
183
- // E @ Cᵀ (the C block is loaded transposed).
184
- const FWD_SG_SOURCE = String.raw`
185
- const uint tokBlk = threadgroup_position_in_grid.z;
186
- const uint vb = threadgroup_position_in_grid.y;
187
- const uint tid = thread_position_in_threadgroup.x;
188
- const uint sg = tid / 32u; // simdgroup 0..3
189
- const uint M=shape[0], V=shape[1], H=shape[2], GS=shape[3], BITS=shape[4], WPR=shape[5], GR=shape[6], NBLK=shape[7];
190
- const float cap = capv[0];
191
- const uint perWord = 32u / BITS;
192
- const uint qmask = (BITS == 32u) ? 0xffffffffu : ((1u << BITS) - 1u);
193
- const uint blockV = (V + NBLK - 1u) / NBLK;
194
- const uint nStart = vb * blockV;
195
- const uint nEnd = min(nStart + blockV, V);
196
- const uint tokBase = tokBlk * BB;
197
-
198
- // +4-float padded leading dims (MLX steel's BK_padded trick): a stride of 32 ==
199
- // 32 banks → 32-way bank conflicts on every simdgroup_load; 36 breaks alignment.
200
- threadgroup float Esh[BB * BDX];
201
- threadgroup float Csh[BV * BDX];
202
- threadgroup float Lsh[BB * BVX];
203
- threadgroup float tgMax[BB], tgSum[BB], tgTgt[BB];
204
- threadgroup uint tgTid[BB];
205
- if (tid < BB) {
206
- tgMax[tid] = -INFINITY; tgSum[tid] = 0.0f; tgTgt[tid] = 0.0f;
207
- tgTid[tid] = (tokBase + tid < M) ? targets[tokBase + tid] : 0xffffffffu;
208
- }
209
- threadgroup_barrier(mem_flags::mem_threadgroup);
210
-
211
- for (uint v0 = nStart; v0 < nEnd; v0 += BV) {
212
- simdgroup_float8x8 acc = simdgroup_float8x8(0);
213
- for (uint d0 = 0u; d0 < H; d0 += BD) {
214
- // E tile [BB, BD] (token hiddens)
215
- for (uint i = tid; i < BB * BD; i += TG) {
216
- const uint b = i / BD, dd = i % BD;
217
- const uint tb = min(tokBase + b, M - 1u);
218
- Esh[b * BDX + dd] = (float)h[(ulong)tb * H + d0 + dd];
219
- }
220
- // C tile [BV, BD] (dequantized head, reused across all BB tokens).
221
- #if CCE_SG_SKIPDQ
222
- // PROBE: no global loads — isolates whether the staging loads are the limiter.
223
- for (uint i = tid; i < BV * BD; i += TG) {
224
- const uint vrow = i / BD, dd = i % BD;
225
- Csh[vrow * BDX + dd] = (v0 + vrow < nEnd) ? ((float)((i & 7u)) * 0.01f + 0.001f) : 0.0f;
226
- }
227
- #else
228
- // ONE THREAD PER PACKED WORD: each word loaded once (was perWord-redundant),
229
- // and scale/bias once per word's group (was per-element — the whole BD chunk
230
- // is one GS group, so this collapses ~perWord× of scale/bias traffic too).
231
- const uint wordsPerRow = BD / perWord;
232
- for (uint wi = tid; wi < BV * wordsPerRow; wi += TG) {
233
- const uint vrow = wi / wordsPerRow;
234
- const uint dloc = (wi % wordsPerRow) * perWord;
235
- const uint n = v0 + vrow;
236
- const uint d = d0 + dloc;
237
- uint word = 0u; float sc = 0.0f, bi = 0.0f;
238
- if (n < nEnd) {
239
- word = wp[(ulong)n * WPR + d / perWord];
240
- const uint grp = d / GS;
241
- sc = scales[n * GR + grp]; bi = biases[n * GR + grp];
242
- }
243
- for (uint k = 0u; k < perWord; ++k) {
244
- const float q = (float)((word >> (BITS * k)) & qmask);
245
- Csh[vrow * BDX + dloc + k] = sc * q + bi;
246
- }
247
- }
248
- #endif
249
- threadgroup_barrier(mem_flags::mem_threadgroup);
250
- // acc(this sg's 8 cols) += E @ Cᵀ over BD/8 k-blocks (padded strides BDX)
251
- for (uint kk = 0u; kk < BD / 8u; ++kk) {
252
- simdgroup_float8x8 me, mc;
253
- simdgroup_load(me, Esh + kk * 8u, BDX);
254
- simdgroup_load(mc, Csh + (ulong)sg * 8u * BDX + kk * 8u, BDX, ulong2(0, 0), true);
255
- simdgroup_multiply_accumulate(acc, me, mc, acc);
256
- }
257
- threadgroup_barrier(mem_flags::mem_threadgroup);
258
- }
259
- simdgroup_store(acc, Lsh + sg * 8u, BVX); // [0:BB, sg*8 : sg*8+8], padded stride
260
- threadgroup_barrier(mem_flags::mem_threadgroup);
261
-
262
- // Online-softmax update for BB tokens over this BV chunk (thread b<BB → token b).
263
- if (tid < BB) {
264
- const uint b = tid;
265
- for (uint v = 0u; v < BV; ++v) {
266
- const uint n = v0 + v;
267
- if (n >= nEnd) break;
268
- float logit = Lsh[b * BVX + v];
269
- if (cap > 0.0f) logit = cap * tanh(logit / cap);
270
- const float nm = max(tgMax[b], logit);
271
- tgSum[b] = tgSum[b] * exp(tgMax[b] - nm) + exp(logit - nm);
272
- tgMax[b] = nm;
273
- if (n == tgTid[b]) tgTgt[b] = logit;
274
- }
275
- }
276
- threadgroup_barrier(mem_flags::mem_threadgroup);
277
- }
278
-
279
- if (tid < BB && tokBase + tid < M) {
280
- blockMax_out[(tokBase + tid) * NBLK + vb] = tgMax[tid];
281
- blockSum_out[(tokBase + tid) * NBLK + vb] = tgSum[tid];
282
- blockTgt_out[(tokBase + tid) * NBLK + vb] = tgTgt[tid];
283
- }
284
- `;
285
-
286
- // LANE-SLICED forward (the real memory fix): instead of one thread per vocab row
287
- // (adjacent threads read rows WPR words apart → uncoalesced weight reads, the
288
- // dominant ~43 GB stream at ~7% bandwidth), a SIMDGROUP of 32 lanes cooperates on
289
- // ONE row — lane l reads words {l, l+32, ...} so the 32 lanes hit CONSECUTIVE
290
- // memory each step (coalesced). The lane-partial dots reduce with simd_sum. The
291
- // threadgroup's 4 simdgroups process 4 rows in parallel; BLOCK_B tokens amortize
292
- // the dequant (each row dequantized once, reused across tokens). Same coalesced
293
- // pattern as mlx's quantized.h qdot. h is passed TRANSPOSED [H, M].
294
- const SGN = TG / 32; // simdgroups per threadgroup (4)
295
- const FWD_LANE_SOURCE = String.raw`
296
- const uint tokBlk = threadgroup_position_in_grid.z;
297
- const uint vb = threadgroup_position_in_grid.y;
298
- const uint lane = thread_index_in_simdgroup; // 0..31 within the simdgroup
299
- const uint sg = thread_position_in_threadgroup.x / 32u; // 0..SGN-1
300
- const uint M=shape[0], V=shape[1], H=shape[2], GS=shape[3], BITS=shape[4], WPR=shape[5], GR=shape[6], NBLK=shape[7];
301
- const float cap = capv[0];
302
- const uint perWord = 32u / BITS;
303
- const uint qmask = (BITS == 32u) ? 0xffffffffu : ((1u << BITS) - 1u);
304
- const uint wordsPerLane = WPR / 32u; // WPR is a multiple of 32
305
- const uint blockV = (V + NBLK - 1u) / NBLK;
306
- const uint nStart = vb * blockV;
307
- const uint nEnd = min(nStart + blockV, V);
308
- const uint tokBase = tokBlk * BLOCK_B;
309
-
310
- float mx[BLOCK_B], sumexp[BLOCK_B], tgtLogit[BLOCK_B];
311
- uint tgtId[BLOCK_B];
312
- for (uint b = 0u; b < BLOCK_B; ++b) {
313
- mx[b] = -INFINITY; sumexp[b] = 0.0f; tgtLogit[b] = 0.0f;
314
- tgtId[b] = (tokBase + b < M) ? targets[tokBase + b] : 0xffffffffu;
315
- }
316
-
317
- // each simdgroup owns rows {nStart+sg, nStart+sg+SGN, ...}; its 32 lanes share the row
318
- for (uint n = nStart + sg; n < nEnd; n += SGN) {
319
- const device uint* row = wp + (ulong)n * WPR;
320
- const uint sbBase = n * GR;
321
- float partial[BLOCK_B];
322
- for (uint b = 0u; b < BLOCK_B; ++b) partial[b] = 0.0f;
323
- for (uint j = 0u; j < wordsPerLane; ++j) {
324
- const uint wi = lane + 32u * j; // coalesced across the 32 lanes
325
- const uint word = row[wi];
326
- const uint dbase = wi * perWord;
327
- for (uint k = 0u; k < perWord; ++k) {
328
- const uint d = dbase + k;
329
- const uint grp = d / GS;
330
- const float w = scales[sbBase + grp] * (float)((word >> (BITS * k)) & qmask) + biases[sbBase + grp];
331
- for (uint b = 0u; b < BLOCK_B; ++b) {
332
- const uint tb = min(tokBase + b, M - 1u);
333
- partial[b] += (float)h[(ulong)d * M + tb] * w; // h transposed [H, M]
334
- }
335
- }
336
- }
337
- // reduce each token's lane-partials across the simdgroup → full logit
338
- for (uint b = 0u; b < BLOCK_B; ++b) {
339
- float logit = simd_sum(partial[b]);
340
- if (cap > 0.0f) logit = cap * tanh(logit / cap);
341
- const float nm = max(mx[b], logit);
342
- sumexp[b] = sumexp[b] * exp(mx[b] - nm) + exp(logit - nm);
343
- mx[b] = nm;
344
- if (n == tgtId[b]) tgtLogit[b] = logit;
345
- }
346
- }
347
-
348
- // simd_sum broadcasts, so each simdgroup's online state is replicated across its
349
- // lanes; lane 0 of each sg publishes, then SGN states merge per token.
350
- threadgroup float mM[SGN * BLOCK_B], mS[SGN * BLOCK_B], mT[SGN * BLOCK_B];
351
- if (lane == 0u) {
352
- for (uint b = 0u; b < BLOCK_B; ++b) {
353
- mM[sg * BLOCK_B + b] = mx[b]; mS[sg * BLOCK_B + b] = sumexp[b]; mT[sg * BLOCK_B + b] = tgtLogit[b];
354
- }
355
- }
356
- threadgroup_barrier(mem_flags::mem_threadgroup);
357
- const uint tid = thread_position_in_threadgroup.x;
358
- if (tid < BLOCK_B && tokBase + tid < M) {
359
- const uint b = tid;
360
- float gm = -INFINITY, gs = 0.0f, gt = 0.0f;
361
- for (uint s = 0u; s < SGN; ++s) {
362
- const float im = mM[s * BLOCK_B + b];
363
- const float nm = max(gm, im);
364
- if (nm > -INFINITY) { gs = gs * exp(gm - nm) + mS[s * BLOCK_B + b] * exp(im - nm); gm = nm; }
365
- gt += mT[s * BLOCK_B + b];
366
- }
367
- blockMax_out[(tokBase + b) * NBLK + vb] = gm;
368
- blockSum_out[(tokBase + b) * NBLK + vb] = gs;
369
- blockTgt_out[(tokBase + b) * NBLK + vb] = gt;
370
- }
371
- `;
372
-
373
- let _fwdLaneKernel: MetalKernel | null = null;
374
- function fwdLaneKernel(): MetalKernel {
375
- if (!_fwdLaneKernel) {
376
- _fwdLaneKernel = new MetalKernel({
377
- name: "flash_cce_fwd_lane",
378
- inputNames: ["h", "wp", "scales", "biases", "targets", "shape", "capv"],
379
- outputNames: ["blockMax_out", "blockSum_out", "blockTgt_out"],
380
- source: FWD_LANE_SOURCE,
381
- header: `#define TG ${TG}\n#define BLOCK_B ${BLOCK_B}\n#define SGN ${SGN}\n`,
382
- ensureRowContiguous: true,
383
- });
384
- }
385
- return _fwdLaneKernel;
386
- }
387
-
388
- let _fwdSgKernel: MetalKernel | null = null;
389
- function fwdSgKernel(): MetalKernel {
390
- if (!_fwdSgKernel) {
391
- _fwdSgKernel = new MetalKernel({
392
- name: "flash_cce_fwd_sg",
393
- inputNames: ["h", "wp", "scales", "biases", "targets", "shape", "capv"],
394
- outputNames: ["blockMax_out", "blockSum_out", "blockTgt_out"],
395
- source: FWD_SG_SOURCE,
396
- header: `#include <metal_simdgroup_matrix>\n#define TG ${TG}\n#define BB ${SG_BB}\n#define BV ${SG_BV}\n#define BD ${SG_BD}\n#define BDX ${SG_BD + 4}\n#define BVX ${SG_BV + 4}\n#define CCE_SG_SKIPDQ ${runtimeValue("MLX_BUN_CCE_SG_SKIPDQ") === "1" ? 1 : 0}\n`,
397
- ensureRowContiguous: true,
398
- });
399
- }
400
- return _fwdSgKernel;
401
- }
402
- // simdgroup_matrix forward is now the DEFAULT (fastest): the earlier "not faster"
403
- // verdict was confounded — the SG path was bound by the Csh dequant STAGING loads
404
- // (packed weight + per-group scale/bias), not the matmul, so it never reached the
405
- // compute-bound regime. Removing the staging redundancy (one thread per packed
406
- // word; scale/bias once per group) cut it 1629→848 ms, beating the scalar
407
- // kernel's 1097 ms. Fall back to scalar (MLX_BUN_CCE_SCALAR=1) when H % SG_BD != 0.
408
- const USE_SIMDGROUP_FWD = runtimeValue("MLX_BUN_CCE_SCALAR") !== "1" && runtimeValue("MLX_BUN_CCE_LANE") !== "1";
409
-
410
- // Backward: dh[t,d] = Σ_v coeff_v · dequant(W_v)[d], coeff_v = g_t·(onehot_v −
411
- // softmax_v)·sech²_v, softmax_v = exp(softcap(logit_v) − lse_t). Two-phase per
412
- // 128-row vocab block, NO atomics: phase 1 each thread computes coeff for one
413
- // block row (full dot → into threadgroup coeff[]); phase 2 each thread owns
414
- // H/TG OUTPUT dims and accumulates them over the block's rows into per-thread
415
- // registers (dacc), so each output dim is written by exactly one thread. lse is
416
- // reused from the forward (no recompute of the global softmax normalizer).
417
- const DPER_MAX = H_MAX / TG; // 32
418
- const BWD_SOURCE = String.raw`
419
- const uint tokBlk = threadgroup_position_in_grid.z; // token-block
420
- const uint vb = threadgroup_position_in_grid.y; // vocab-block
421
- const uint tid = thread_position_in_threadgroup.x;
422
- const uint M = shape[0];
423
- const uint V = shape[1];
424
- const uint H = shape[2];
425
- const uint GS = shape[3];
426
- const uint BITS = shape[4];
427
- const uint WPR = shape[5];
428
- const uint GR = shape[6];
429
- const uint NBLK = shape[7];
430
- const float cap = capv[0];
431
- const uint perWord = 32u / BITS;
432
- const uint qmask = (BITS == 32u) ? 0xffffffffu : ((1u << BITS) - 1u);
433
- const uint wordsPerGroup = GS / perWord;
434
- const uint DPER = H / TG; // output dims this thread owns
435
- const uint dStart = tid * DPER;
436
- const uint blockV = (V + NBLK - 1u) / NBLK;
437
- const uint nStart = vb * blockV;
438
- const uint nEnd = min(nStart + blockV, V);
439
- const uint tokBase = tokBlk * BLOCK_B;
440
-
441
- float lseB[BLOCK_B], gB[BLOCK_B]; uint tgtB[BLOCK_B];
442
- for (uint b = 0u; b < BLOCK_B; ++b) {
443
- const uint tk = min(tokBase + b, M - 1u);
444
- lseB[b] = lse[tk]; gB[b] = gv[tk];
445
- tgtB[b] = (tokBase + b < M) ? targets[tokBase + b] : 0xffffffffu;
446
- }
447
-
448
- threadgroup float coeffB[TG * BLOCK_B]; // [row][token]
449
- // BLOCK_B == 4 → one float4 per output dim holds all 4 tokens, so the phase-2
450
- // accumulation (the backward's ~98% hotspot — a scalar coeffᵀ@W GEMM) runs as
451
- // one vector FMA per (row, dim) instead of 4 scalar ones.
452
- float4 dacc[DACC_N]; // dacc[j] = (tok0..tok3) for dim dStart+j
453
- for (uint i = 0u; i < DPER; ++i) dacc[i] = float4(0.0f);
454
- threadgroup_barrier(mem_flags::mem_threadgroup);
455
-
456
- for (uint blk = nStart; blk < nEnd; blk += TG) {
457
- // --- phase 1: row n = blk + tid → coeff for each of BLOCK_B tokens (the
458
- // dequant-dot is computed once per token via shared nibble q) ---
459
- const uint n = blk + tid;
460
- float cf[BLOCK_B];
461
- for (uint b = 0u; b < BLOCK_B; ++b) cf[b] = 0.0f;
462
- if (n < nEnd) {
463
- const device uint* row = wp + (ulong)n * WPR;
464
- const uint sbBase = n * GR;
465
- float raw[BLOCK_B];
466
- for (uint b = 0u; b < BLOCK_B; ++b) raw[b] = 0.0f;
467
- for (uint grp = 0u; grp < GR; ++grp) {
468
- const float sc = scales[sbBase + grp];
469
- const float bi = biases[sbBase + grp];
470
- const uint d0 = grp * GS;
471
- float gacc[BLOCK_B], ghsum[BLOCK_B];
472
- for (uint b = 0u; b < BLOCK_B; ++b) { gacc[b] = 0.0f; ghsum[b] = 0.0f; }
473
- for (uint wi = 0u; wi < wordsPerGroup; ++wi) {
474
- const uint word = row[d0 / perWord + wi];
475
- const uint dbase = d0 + wi * perWord;
476
- for (uint k = 0u; k < perWord; ++k) {
477
- const float q = (float)((word >> (BITS * k)) & qmask);
478
- const uint d = dbase + k;
479
- for (uint b = 0u; b < BLOCK_B; ++b) {
480
- const uint tk = min(tokBase + b, M - 1u);
481
- const float hd = (float)h[(ulong)tk * H + d]; // phase-1 h reads aren't the bwd bottleneck (phase 2 is)
482
- gacc[b] += hd * q; ghsum[b] += hd;
483
- }
484
- }
485
- }
486
- for (uint b = 0u; b < BLOCK_B; ++b) raw[b] += sc * gacc[b] + bi * ghsum[b];
487
- }
488
- for (uint b = 0u; b < BLOCK_B; ++b) {
489
- float logit = raw[b], sech2 = 1.0f;
490
- if (cap > 0.0f) { const float th = tanh(raw[b] / cap); logit = cap * th; sech2 = 1.0f - th * th; }
491
- const float sm = exp(logit - lseB[b]);
492
- float c = gB[b] * ((n == tgtB[b] ? 1.0f : 0.0f) - sm);
493
- if (cap > 0.0f) c *= sech2;
494
- cf[b] = c;
495
- }
496
- }
497
- for (uint b = 0u; b < BLOCK_B; ++b) coeffB[tid * BLOCK_B + b] = cf[b];
498
- threadgroup_barrier(mem_flags::mem_threadgroup);
499
-
500
- // --- phase 2: the coeffᵀ@W accumulation — the backward's ~98% hotspot. Each
501
- // weight dequant (wd) feeds all BLOCK_B tokens via one float4 FMA. (Loads are
502
- // negligible — proven ~137 ms — so the per-element dequant stays; the win is
503
- // vectorizing the arithmetic.) ---
504
- #if !CCE_BWD_SKIP_P2
505
- for (uint r = 0u; r < TG; ++r) {
506
- const uint n2 = blk + r;
507
- if (n2 >= nEnd) break;
508
- const device uint* row = wp + (ulong)n2 * WPR;
509
- const uint sbBase = n2 * GR;
510
- const float4 cf = *(const threadgroup float4*)(coeffB + r * BLOCK_B); // tok0..3
511
- #if CCE_BWD_SKIP_P2DQ
512
- for (uint j = 0u; j < DPER; ++j) dacc[j] += cf * ((float)((dStart + j) & 7u) * 0.01f);
513
- #else
514
- for (uint j = 0u; j < DPER; ++j) {
515
- const uint d = dStart + j;
516
- const uint grp = d / GS;
517
- const uint word = row[d / perWord];
518
- const float q = (float)((word >> (BITS * (d % perWord))) & qmask);
519
- const float wd = scales[sbBase + grp] * q + biases[sbBase + grp];
520
- dacc[j] += cf * wd;
521
- }
522
- #endif
523
- }
524
- #else
525
- // PROBE: skip phase-2 accumulation but consume coeffB so phase 1 isn't DCE'd —
526
- // isolates phase-1 (the coeff dot) cost from phase-2 accumulation.
527
- dacc[0] += float4(coeffB[tid * BLOCK_B]);
528
- #endif
529
- threadgroup_barrier(mem_flags::mem_threadgroup);
530
- }
531
-
532
- // Atomic-accumulate each token's partial dh across the NBLK vocab-block programs.
533
- // dacc[j] = (tok0..tok3) for dim dStart+j.
534
- for (uint b = 0u; b < BLOCK_B; ++b) {
535
- if (tokBase + b >= M) break;
536
- for (uint j = 0u; j < DPER; ++j)
537
- atomic_fetch_add_explicit(&dh[(ulong)(tokBase + b) * H + dStart + j], dacc[j][b], memory_order_relaxed);
538
- }
539
- `;
540
-
541
- // dacc is sized to the ACTUAL per-thread output dims (ceil(H/TG)) rather than the
542
- // worst-case DPER_MAX — right-sizing the register footprint raises occupancy (the
543
- // backward's limiter). Keyed by daccN so a model with a different H rebuilds.
544
- const _bwdKernels = new Map<number, MetalKernel>();
545
- function bwdKernel(daccN: number): MetalKernel {
546
- let k = _bwdKernels.get(daccN);
547
- if (!k) {
548
- k = new MetalKernel({
549
- name: "flash_cce_bwd",
550
- inputNames: ["h", "wp", "scales", "biases", "targets", "lse", "gv", "shape", "capv"],
551
- outputNames: ["dh"],
552
- source: BWD_SOURCE,
553
- header: `#define TG ${TG}\n#define BLOCK_B ${BLOCK_B_BWD}\n#define DACC_N ${daccN}\n#define CCE_BWD_SKIP_P2DQ ${runtimeValue("MLX_BUN_CCE_BWD_SKIP_P2DQ") === "1" ? 1 : 0}\n#define CCE_BWD_SKIP_P2 ${runtimeValue("MLX_BUN_CCE_BWD_SKIP_P2") === "1" ? 1 : 0}\n`,
554
- ensureRowContiguous: true,
555
- atomicOutputs: true, // dh is summed across the NBLK vocab-block programs
556
- });
557
- _bwdKernels.set(daccN, k);
558
- }
559
- return k;
560
- }
561
-
562
- // SIMDGROUP_MATRIX backward (phase-2 GEMM): the phase-2 accumulation dacc = coeffᵀ@W
563
- // is ~69% of the backward (1733 ms phase-1 + 3779 ms phase-2 at e4b M=512) and is
564
- // compute-bound, NOT register-bound (right-sizing dacc gave 0%) — so we can afford
565
- // simdgroup_matrix's larger accumulator. Phase 1 unchanged (one thread per row →
566
- // coeff into threadgroup, BB=8 tokens). Phase 2: each simdgroup owns H/SGN dims as
567
- // DIMTILES persistent 8×8 acc fragments; per 8-row chunk, load coeffᵀ (transpose)
568
- // and the dequantized W tile, mma. dh atomic-added at the end. Needs H % (SGN*8)==0.
569
- const BB_BWD_SG = 8;
570
- const BWD_SG_SOURCE = String.raw`
571
- const uint tokBlk = threadgroup_position_in_grid.z;
572
- const uint vb = threadgroup_position_in_grid.y;
573
- const uint tid = thread_position_in_threadgroup.x;
574
- const uint lane = thread_index_in_simdgroup;
575
- const uint sg = tid / 32u;
576
- const uint M=shape[0],V=shape[1],H=shape[2],GS=shape[3],BITS=shape[4],WPR=shape[5],GR=shape[6],NBLK=shape[7];
577
- const float cap = capv[0];
578
- const uint perWord = 32u / BITS;
579
- const uint qmask = (BITS == 32u) ? 0xffffffffu : ((1u << BITS) - 1u);
580
- const uint HSG = H / SGN; // dims owned by this simdgroup
581
- const uint d0sg = sg * HSG;
582
- const uint blockV = (V + NBLK - 1u) / NBLK;
583
- const uint nStart = vb * blockV;
584
- const uint nEnd = min(nStart + blockV, V);
585
- const uint tokBase = tokBlk * BB;
586
-
587
- float lseB[BB], gB[BB]; uint tgtB[BB];
588
- for (uint b = 0u; b < BB; ++b) {
589
- const uint tk = min(tokBase + b, M - 1u);
590
- lseB[b] = lse[tk]; gB[b] = gv[tk];
591
- tgtB[b] = (tokBase + b < M) ? targets[tokBase + b] : 0xffffffffu;
592
- }
593
-
594
- threadgroup float coeffB[TG * 8]; // [row][token], 8-wide for the simdgroup tile
595
- threadgroup float Wsh[SGN * 64]; // per-simdgroup 8×8 dequantized W tile
596
- threadgroup float Csh[SGN * 64]; // per-simdgroup 8×8 store-out buffer
597
- threadgroup float Esh[BB * BDP]; // phase-1: token hiddens tile (logit GEMM)
598
- threadgroup float Cstg[BVP * BDP]; // phase-1: dequantized W tile (logit GEMM)
599
- threadgroup float Lsh[BB * BVP]; // phase-1: logit[token, row] tile
600
-
601
- #if CCE_BWD_BLOCKSKIP
602
- // blockMax vocab-block early exit: this (token-block, vocab-block) program does
603
- // ~0 work if, for EVERY token, the block's max softmax prob exp(blockMax−lse) is
604
- // below eps AND the target isn't in this block (so no onehot term lives here).
605
- // On peaked logits ~all but one vocab block per token is cold → skips phase 1+2.
606
- // The early return is AFTER the threadgroup decls (uniform across the TG; declaring
607
- // threadgroup arrays after an early return fails to compile).
608
- bool warm = false;
609
- for (uint b = 0u; b < BB; ++b) {
610
- if (tokBase + b < M) {
611
- if (exp(blockMax[(tokBase + b) * NBLK + vb] - lseB[b]) >= CCE_BWD_BLOCK_EPS) warm = true;
612
- if (tgtB[b] >= nStart && tgtB[b] < nEnd) warm = true;
613
- }
614
- }
615
- if (!warm) return;
616
- #endif
617
-
618
- simdgroup_float8x8 acc[DIMTILES];
619
- for (uint t = 0u; t < DIMTILES; ++t) acc[t] = simdgroup_float8x8(0);
620
-
621
- for (uint blk = nStart; blk < nEnd; blk += TG) {
622
- // --- phase 1 (SG-matrix): logit[tok, row] = E @ Wᵀ over H (the SAME tile GEMM
623
- // as the forward, with the sub-block's TG rows as the "vocab"), then coeff. ---
624
- for (uint vc = 0u; vc < TG; vc += BVP) { // TG rows in BVP-row chunks
625
- simdgroup_float8x8 lacc = simdgroup_float8x8(0);
626
- for (uint d0 = 0u; d0 < H; d0 += BDP) {
627
- for (uint i = tid; i < BB * BDP; i += TG) { // E tile [BB tokens, BDP]
628
- const uint b = i / BDP, dd = i % BDP;
629
- const uint tk = min(tokBase + b, M - 1u);
630
- Esh[i] = (float)h[(ulong)tk * H + d0 + dd];
631
- }
632
- const uint wordsPerRow = BDP / perWord; // C tile [BVP rows, BDP] (one thread/word)
633
- for (uint wi = tid; wi < BVP * wordsPerRow; wi += TG) {
634
- const uint vrow = wi / wordsPerRow;
635
- const uint dloc = (wi % wordsPerRow) * perWord;
636
- const uint n2 = blk + vc + vrow;
637
- const uint d = d0 + dloc;
638
- uint word = 0u; float scl = 0.0f, bia = 0.0f;
639
- if (n2 < nEnd) {
640
- word = wp[(ulong)n2 * WPR + d / perWord];
641
- const uint grp = d / GS; scl = scales[n2 * GR + grp]; bia = biases[n2 * GR + grp];
642
- }
643
- for (uint k = 0u; k < perWord; ++k) Cstg[vrow * BDP + dloc + k] = scl * (float)((word >> (BITS * k)) & qmask) + bia;
644
- }
645
- threadgroup_barrier(mem_flags::mem_threadgroup);
646
- for (uint kk = 0u; kk < BDP / 8u; ++kk) { // lacc(sg's 8 rows) += E @ Cᵀ
647
- simdgroup_float8x8 me, mc;
648
- simdgroup_load(me, Esh + kk * 8u, BDP);
649
- simdgroup_load(mc, Cstg + (ulong)sg * 8u * BDP + kk * 8u, BDP, ulong2(0, 0), true);
650
- simdgroup_multiply_accumulate(lacc, me, mc, lacc);
651
- }
652
- threadgroup_barrier(mem_flags::mem_threadgroup);
653
- }
654
- simdgroup_store(lacc, Lsh + sg * 8u, BVP); // Lsh[tok*BVP + row] = logit[tok][row]
655
- threadgroup_barrier(mem_flags::mem_threadgroup);
656
- for (uint i = tid; i < BB * BVP; i += TG) { // derive coeff[row][tok] → coeffB
657
- const uint tok = i / BVP, row = i % BVP;
658
- const uint n2 = blk + vc + row;
659
- float c = 0.0f;
660
- if (n2 < nEnd && tokBase + tok < M) {
661
- float logit = Lsh[tok * BVP + row], sech2 = 1.0f;
662
- if (cap > 0.0f) { const float th = tanh(logit / cap); logit = cap * th; sech2 = 1.0f - th * th; }
663
- const float sm = exp(logit - lseB[tok]);
664
- c = gB[tok] * ((n2 == tgtB[tok] ? 1.0f : 0.0f) - sm);
665
- if (cap > 0.0f) c *= sech2;
666
- }
667
- coeffB[(vc + row) * 8u + tok] = c;
668
- }
669
- threadgroup_barrier(mem_flags::mem_threadgroup);
670
- }
671
-
672
- // --- phase 2: acc[t][tok][dim] += Σ_row coeff[row][tok] · W[row][dim] ---
673
- for (uint rc = 0u; rc < TG; rc += 8u) {
674
- // Apple-CCE near-zero coeff filter: softmax mass concentrates on a few
675
- // tokens, so coeff ≈ 0 for almost every vocab row → most 8-row chunks
676
- // contribute nothing. Skip the chunk's dim-tile dequant+mma when its whole
677
- // [8 rows × 8 tokens] coeff block is below eps. Safe because the SKIPPED
678
- // softmax mass (not each element) is what's tiny. Uniform across the
679
- // simdgroup (all lanes/sgs see the same coeffB[rc]).
680
- #if CCE_BWD_FILTER
681
- float cmax = 0.0f;
682
- for (uint i = lane; i < 64u; i += 32u) cmax = max(cmax, fabs(coeffB[rc * 8u + i]));
683
- if (simd_max(cmax) < CCE_BWD_FILTER_EPS) continue;
684
- #endif
685
- simdgroup_float8x8 mA;
686
- simdgroup_load(mA, coeffB + rc * 8u, 8, ulong2(0, 0), true); // mA[tok][row]
687
- threadgroup float* ws = Wsh + sg * 64u;
688
- for (uint t = 0u; t < DIMTILES; ++t) {
689
- const uint d0 = d0sg + t * 8u;
690
- for (uint i = lane; i < 64u; i += 32u) { // dequant W[8 rows, 8 dims]
691
- const uint rr = i / 8u, dd = i % 8u;
692
- const uint n2 = blk + rc + rr;
693
- float w = 0.0f;
694
- if (n2 < nEnd) {
695
- const uint d = d0 + dd;
696
- const uint word = wp[(ulong)n2 * WPR + d / perWord];
697
- const float q = (float)((word >> (BITS * (d % perWord))) & qmask);
698
- w = scales[n2 * GR + d / GS] * q + biases[n2 * GR + d / GS];
699
- }
700
- ws[i] = w;
701
- }
702
- simdgroup_barrier(mem_flags::mem_threadgroup);
703
- simdgroup_float8x8 mB;
704
- simdgroup_load(mB, ws, 8); // mB[row][dim]
705
- simdgroup_multiply_accumulate(acc[t], mA, mB, acc[t]);
706
- simdgroup_barrier(mem_flags::mem_threadgroup);
707
- }
708
- }
709
- threadgroup_barrier(mem_flags::mem_threadgroup);
710
- }
711
-
712
- // write each dim-tile's acc → dh (atomic across the NBLK vocab-block programs)
713
- threadgroup float* cs = Csh + sg * 64u;
714
- for (uint t = 0u; t < DIMTILES; ++t) {
715
- simdgroup_store(acc[t], cs, 8);
716
- simdgroup_barrier(mem_flags::mem_threadgroup);
717
- const uint d0 = d0sg + t * 8u;
718
- for (uint i = lane; i < 64u; i += 32u) {
719
- const uint tok = i / 8u, dd = i % 8u;
720
- if (tokBase + tok < M)
721
- atomic_fetch_add_explicit(&dh[(ulong)(tokBase + tok) * H + d0 + dd], cs[i], memory_order_relaxed);
722
- }
723
- simdgroup_barrier(mem_flags::mem_threadgroup);
724
- }
725
- `;
726
-
727
- // Apple-CCE coeff filter epsilon (phase-2 per-tile skip). DEFAULT ON at 1e-5
728
- // (flipped 2026-07-02 — the kernel-perf-review backlog #1 flip). The old
729
- // 0.66%→2.7% dh-accuracy scare was on RANDOM targets (flat softmax, the filter's
730
- // worst case — see PLAN.md "filter-on-real-data"); the real-data gate
731
- // (flash-cce-filter-realdata.ts (deleted 2026-08-23; git history)) measured, on REAL chunk-ORPO
732
- // data (M1 Max, 2026-07-02): CPM5 eps 1e-5 = 0.343% dh for 1.41×; e4b = 0.158% dh
733
- // for 1.70× — and the teacher-forced full-logits fidelity shows the filter's added
734
- // error is far below the flash-vs-full fp-reassociation scatter that already
735
- // exists (CPM5 0.850→0.913% relnorm, e4b 1.207→1.220%, cosine ≥0.99993 both).
736
- // (The ~3.5× figure floating around older notes was the retired SG kernel, not
737
- // steel.) MLX_BUN_CCE_BWD_FILTER_EPS=0 restores exact gradients — eps "0"
738
- // compiles the filter OUT (#if CCE_BWD_FILTER 0), the identical pre-flip kernel.
739
- const BWD_FILTER_EPS = runtimeValue("MLX_BUN_CCE_BWD_FILTER_EPS") ?? "1e-5";
740
- // blockMax vocab-block early-exit eps (skip whole cold (token-block,vocab-block) programs
741
- // — attacks BOTH phases). DEFAULT ON at 1e-5 (flipped 2026-07-02). It is ~LOSSLESS when
742
- // it fires (only skips blocks whose max prob < eps AND which lack the target → ~0
743
- // contribution). The M=512 synthetic probe found nothing cold (pure overhead), but the
744
- // real-data gate proved real text DOES go cold: alone it's 1.23× (CPM5) / 2.02× (e4b)
745
- // at ≤0.004% dh; COMBINED with the coeff filter at 1e-5/1e-5 the backward is 1.71×
746
- // (CPM5) / 3.16× (e4b) vs exact. Fires only when the caller supplies blockMax (the
747
- // production head does); MLX_BUN_CCE_BWD_BLOCK_EPS=0 disables (compiled out).
748
- const BWD_BLOCK_EPS = runtimeValue("MLX_BUN_CCE_BWD_BLOCK_EPS") ?? "1e-5";
749
- const _bwdSgKernels = new Map<string, MetalKernel>();
750
- function bwdSgKernel(dimTiles: number, filterEps: string, blockEps: string): MetalKernel {
751
- const filterOn = Number.parseFloat(filterEps) > 0 ? 1 : 0;
752
- const blockOn = Number.parseFloat(blockEps) > 0 ? 1 : 0;
753
- const key = `${dimTiles}:${filterEps}:${blockEps}`;
754
- let k = _bwdSgKernels.get(key);
755
- if (!k) {
756
- const inputNames = ["h", "wp", "scales", "biases", "targets", "lse", "gv", "shape", "capv"];
757
- if (blockOn) inputNames.push("blockMax"); // [M, NBLK] forward partial, for the skip
758
- k = new MetalKernel({
759
- name: "flash_cce_bwd_sg",
760
- inputNames,
761
- outputNames: ["dh"],
762
- source: BWD_SG_SOURCE,
763
- header: `#include <metal_simdgroup_matrix>\n#define TG ${TG}\n#define BB ${BB_BWD_SG}\n#define SGN ${SGN}\n#define DIMTILES ${dimTiles}\n#define BVP ${SG_BV}\n#define BDP ${SG_BD}\n#define CCE_BWD_FILTER ${filterOn}\n#define CCE_BWD_FILTER_EPS ${filterEps}f\n#define CCE_BWD_BLOCKSKIP ${blockOn}\n#define CCE_BWD_BLOCK_EPS ${blockEps}f\n`,
764
- ensureRowContiguous: true,
765
- atomicOutputs: true,
766
- });
767
- _bwdSgKernels.set(key, k);
768
- }
769
- return k;
770
- }
771
-
772
- // STEEL backward (the production path): MLX's verbatim BlockMMA GEMM (steel-qmm-
773
- // header.ts) for BOTH phases of dh = Σ_v coeff_v·dequant(W_v), at MLX speed AND
774
- // [M,V]-free. Per (token-block z = BM=8 tokens, vocab-block y) threadgroup:
775
- // phase 1 — logit[8,32] = X[8,H] @ Wᵀ[H,32] via the SAME steel logit GEMM as the
776
- // forward (mma_t + QuantizedBlockLoader, K-pipelined over H) → coeff epilogue.
777
- // phase 2 — dh[8,H] += coeff[8,32] @ W[32,H], accumulated over the vocab tiles in a
778
- // persistent register accumulator D = MMATile<1, H/32>. To keep threadgroup
779
- // bounded (the full Wd[32,H] is 320 KB at e4b), H is tiled into HTw=128 columns:
780
- // per H-tile a TEMP BlockMMA over a bounded Wd[32,HTw] dequant, whose Ctile is
781
- // accumulated into D's frags LANE-LOCALLY (frag += frag — no get_coord math, the
782
- // per-lane element mapping is identical between temp and D). dh atomic-added
783
- // across the NBLK vocab-block programs. Validated dh ~1e-4 rel vs autograd.
784
- // (Design + parity: steel-bwd-htile-test.ts (deleted 2026-08-23; git history).)
785
- // PHASE-2 DEQUANT: the Wd[32,HTw] staging uses MLX's vectorized+fused
786
- // QuantizedBlockLoader (the SAME loader the forward uses), NOT a manual scalar
787
- // dequant. The wrinkle: phase-2's GEMM contracts over vocab (K=32) and outputs H
788
- // (the dim the quant groups run along), so the loader must run with reduction_dim=0
789
- // and one group per H-tile → HTw is pinned to group_size (so each H-tile is exactly
790
- // one quant group, satisfying BCOLS<=group_size && group_size%BCOLS==0). The loader
791
- // is built fresh per H-tile (correct src/scales/biases group offsets) + a single
792
- // load_unsafe (no next()). TN = HTw/32 = group_size/32 frags per H-tile. The earlier
793
- // manual scalar dequant (recomputing grp/scale/bias per element) was the bottleneck;
794
- // this matches the forward's qmm staging. (Validated: steel-bwd-htile-test.ts.)
795
- // HTw (the phase-2 H-tile width) is pinned to the quant group_size in-kernel (HTw=GS_T),
796
- // so it is not a standalone constant here — the JS side derives NHT_T/NHT4_T from gs.
797
- const BWD_STEEL_SOURCE = String.raw`
798
- const uint M=shape[0], V=shape[1], H=shape[2], NBLK=shape[7], WPR=shape[5];
799
- const uint tokBlk=threadgroup_position_in_grid.z, vb=threadgroup_position_in_grid.y;
800
- // HTw is pinned to the quant group_size so each phase-2 H-tile is exactly one group
801
- // (lets QuantizedBlockLoader stage Wd with reduction_dim=0). TNv = HTw/32 frags/tile;
802
- // NHTc = H/HTw tiles; total accumulator frags = NHTc*TNv = H/32 (= NHT4_T from JS).
803
- constexpr int BM=8,BNv=32,BK=32,WM=1,WN=4,gs=GS_T,bts=BITS_T,HTw=GS_T,NHTc=NHT_T,TNv=GS_T/32;
804
- constexpr int pf=get_pack_factor<bts,8>(), bpp=get_bytes_per_pack<bts>(), BKp=BK+16/sizeof(float);
805
- threadgroup float Xs[BM*BKp]; threadgroup float Ws[BNv*BKp]; threadgroup float Ls[BM*BNv];
806
- threadgroup float coeffS[BM*BNv]; threadgroup float Wd[BNv*HTw];
807
- threadgroup float redMax[4]; threadgroup float tileMaxSh; // coeff-filter reduction scratch
808
- using lmma_t=mlx::steel::BlockMMA<float,float,BM,BNv,BK,WM,WN,false,true,BKp,BKp>;
809
- using dmma_t=mlx::steel::BlockMMA<float,float,BM,HTw,BNv,WM,WN,false,false,BNv,HTw>;
810
- using lx_t=mlx::steel::BlockLoader<float,BM,BK,BKp,1,WM*WN*SIMD_SIZE>;
811
- using lw_t=QuantizedBlockLoader<float,BNv,BK,BKp,1,WM*WN*SIMD_SIZE,gs,bts>;
812
- // phase-2 W loader: BROWS=32 vocab (GEMM K), BCOLS=HTw=group_size (one group/tile),
813
- // reduction_dim=0, dst_ld=HTw — built fresh per H-tile, single load_unsafe.
814
- using lw2_t=QuantizedBlockLoader<float,BNv,HTw,HTw,0,WM*WN*SIMD_SIZE,gs,bts>;
815
- const uint sg=simdgroup_index_in_threadgroup, sl=thread_index_in_simdgroup, lid=thread_position_in_threadgroup.x;
816
- const float cap=capv[0];
817
- const int Hh=(int)H, K_w=Hh*bpp/pf, K_g=Hh/gs, tokBase=(int)tokBlk*BM;
818
- const int blockV=((int)V+(int)NBLK-1)/(int)NBLK, nStart=(int)vb*blockV, nEnd=min(nStart+blockV,(int)V);
819
- const short num_els=(short)min(BM,(int)M-tokBase);
820
- short2 sc2=mlx::steel::BaseMMAFrag<float,8,8>::get_coord(sl); const short fm=sc2.y, fn=sc2.x;
821
- const uint perWord=32u/bts, qmask=(bts==32u)?0xffffffffu:((1u<<bts)-1u);
822
-
823
- #if CCE_BWD_BLOCKSKIP
824
- // Apple-CCE vocab-block early exit: this (token-block, vocab-block) program does ~0
825
- // work if, for EVERY token, the block's max softmax prob exp(blockMax−lse) is below
826
- // eps AND the target isn't in this block (no onehot term lives here). On peaked
827
- // logits ~all but a couple of vocab blocks per token are cold → skips BOTH phases.
828
- // Uniform across the threadgroup (all threads read the same blockMax/lse/targets), so
829
- // the early return can't desync barriers; it is AFTER the threadgroup decls (required).
830
- bool warm = false;
831
- for (int b=0; b<BM; ++b){ int tk=tokBase+b; if (tk<(int)M){
832
- if (exp(blockMax[tk*(int)NBLK + (int)vb] - lse[tk]) >= CCE_BWD_BLOCK_EPS) warm=true;
833
- int tg2=(int)targets[tk]; if (tg2>=nStart && tg2<nEnd) warm=true; } }
834
- if (!warm) return;
835
- #endif
836
-
837
- mlx::steel::MMATile<float,1,NHT4_T> D; // persistent dh accumulator (H/32 frags/sg)
838
-
839
- for (int v0=nStart; v0<nEnd; v0+=BNv){
840
- // phase 1: logit tile → coeff
841
- lmma_t lmma(sg,sl);
842
- lx_t loader_x(h+(int64_t)tokBase*Hh, Hh, Xs, sg, sl);
843
- lw_t loader_w((const device uint8_t*)wp+v0*K_w, scales+v0*K_g, biases+v0*K_g, Hh, Ws, sg, sl);
844
- for(int k=0;k<Hh;k+=BK){ threadgroup_barrier(mem_flags::mem_threadgroup);
845
- if(num_els<BM) loader_x.load_safe(short2(BK,num_els)); else loader_x.load_unsafe();
846
- loader_w.load_unsafe();
847
- threadgroup_barrier(mem_flags::mem_threadgroup); lmma.mma(Xs,Ws); loader_x.next(); loader_w.next(); }
848
- threadgroup_barrier(mem_flags::mem_threadgroup);
849
- lmma.Ctile.store<float,WM,WN,BNv,1>(Ls + lmma.sm*BNv + lmma.sn);
850
- threadgroup_barrier(mem_flags::mem_threadgroup);
851
- if (lid<(uint)BM){ uint b=lid;
852
- if(tokBase+(int)b<(int)M){ const float lseb=lse[tokBase+b], gb=gv[tokBase+b]; const uint tgt=targets[tokBase+b];
853
- for(int v=0;v<BNv;++v){ int n=v0+v; float logit=Ls[b*BNv+v],sech2=1.0f; if(cap>0.0f){float th=tanh(logit/cap);logit=cap*th;sech2=1.0f-th*th;} float sm=exp(logit-lseb); float c=(n<nEnd)?gb*(((uint)n==tgt?1.0f:0.0f)-sm):0.0f; if(cap>0.0f)c*=sech2; coeffS[b*BNv+v]=c; } }
854
- else { for(int v=0;v<BNv;++v) coeffS[b*BNv+v]=0.0f; } }
855
- threadgroup_barrier(mem_flags::mem_threadgroup);
856
-
857
- #if CCE_BWD_FILTER
858
- // Apple-CCE coeff filter: softmax mass concentrates on a few tokens, so coeff ≈ 0 for
859
- // almost every vocab tile even within a warm block → skip the whole phase-2 (Wd staging
860
- // + temp BlockMMA × NHT) when the entire coeff[8,32] tile is below eps. Parallel max
861
- // over the 256 coeff elems (each thread 2, simd_max per simdgroup, combine 4). Uniform
862
- // continue (all threads read the same tileMaxSh) so there is no barrier desync.
863
- { float lm=max(fabs(coeffS[lid]), fabs(coeffS[lid+128u])); lm=simd_max(lm);
864
- if (sl==0u) redMax[sg]=lm; }
865
- threadgroup_barrier(mem_flags::mem_threadgroup);
866
- if (lid==0u) tileMaxSh=max(max(redMax[0],redMax[1]),max(redMax[2],redMax[3]));
867
- threadgroup_barrier(mem_flags::mem_threadgroup);
868
- if (tileMaxSh < CCE_BWD_FILTER_EPS) continue;
869
- #endif
870
-
871
- // phase 2: per H-tile stage Wd[32,HTw] via QuantizedBlockLoader, temp BlockMMA, accum into D
872
- for (int ht=0; ht<NHTc; ++ht){
873
- const int hbase=ht*HTw;
874
- // src at byte addr of W[v0, hbase]; scales/biases at group ht of vocab row v0.
875
- lw2_t loader_w2((const device uint8_t*)wp + (int64_t)v0*K_w + (int64_t)hbase*bpp/pf,
876
- scales + (int64_t)v0*K_g + ht, biases + (int64_t)v0*K_g + ht, Hh, Wd, sg, sl);
877
- loader_w2.load_unsafe();
878
- threadgroup_barrier(mem_flags::mem_threadgroup);
879
- dmma_t tmp(sg,sl); tmp.mma(coeffS, Wd);
880
- for(int j=0;j<TNv;++j) D.frag_at(0, ht*TNv+j) += tmp.Ctile.frag_at(0,j);
881
- threadgroup_barrier(mem_flags::mem_threadgroup);
882
- }
883
- }
884
-
885
- // store D → dh (atomic-add across the NBLK vocab-block programs)
886
- for(int gj=0; gj<NHT4_T; ++gj){
887
- int ht=gj/TNv, j=gj%TNv; int row=tokBase+(int)fm; int col=ht*HTw + 8*(int)sg + (int)fn + j*32;
888
- if(row<(int)M && col+1<=Hh){ thread auto& fr=D.frag_at(0,gj);
889
- atomic_fetch_add_explicit(&dh[(int64_t)row*Hh + col], fr[0], memory_order_relaxed);
890
- atomic_fetch_add_explicit(&dh[(int64_t)row*Hh + col+1], fr[1], memory_order_relaxed); }
891
- }
892
- `;
893
- const BWD_STEEL_BM = 8;
894
- const _bwdSteelKernels = new Map<string, MetalKernel>();
895
- function bwdSteelKernel(gs: number, bits: number, H: number, filterEps: string, blockEps: string): MetalKernel {
896
- // HTw == group_size (one quant group per phase-2 H-tile → QuantizedBlockLoader).
897
- const nht = H / gs; // # H-tiles
898
- const nFrags = nht * (gs / 32); // total accumulator frags/sg = H/32 (NHT4_T name kept)
899
- // Apple-CCE skips: coeff filter (phase-2 per-tile) + blockMax vocab-block early exit
900
- // (whole-program). Both compile out when eps=0 → zero overhead, exact dh (the parity
901
- // gate). blockMax skip needs the forward's [M,NBLK] blockMax as an extra input.
902
- const filterOn = Number.parseFloat(filterEps) > 0 ? 1 : 0;
903
- const blockOn = Number.parseFloat(blockEps) > 0 ? 1 : 0;
904
- const key = `${gs}:${bits}:${H}:${filterEps}:${blockEps}`;
905
- let k = _bwdSteelKernels.get(key);
906
- if (!k) {
907
- const inputNames = ["h", "wp", "scales", "biases", "targets", "lse", "gv", "shape", "capv"];
908
- if (blockOn) inputNames.push("blockMax"); // [M, NBLK] forward partial, for the vocab-block skip
909
- k = new MetalKernel({
910
- name: "flash_cce_bwd_steel",
911
- inputNames,
912
- outputNames: ["dh"],
913
- source: BWD_STEEL_SOURCE,
914
- header: `${STEEL_QMM_HEADER}\n#define GS_T ${gs}\n#define BITS_T ${bits}\n#define NHT_T ${nht}\n#define NHT4_T ${nFrags}\n#define CCE_BWD_FILTER ${filterOn}\n#define CCE_BWD_FILTER_EPS ${filterEps}f\n#define CCE_BWD_BLOCKSKIP ${blockOn}\n#define CCE_BWD_BLOCK_EPS ${blockEps}f\n`,
915
- ensureRowContiguous: true,
916
- atomicOutputs: true,
917
- });
918
- _bwdSteelKernels.set(key, k);
919
- }
920
- return k;
921
- }
922
-
923
- let _fwdKernel: MetalKernel | null = null;
924
- function fwdKernel(): MetalKernel {
925
- if (!_fwdKernel) {
926
- _fwdKernel = new MetalKernel({
927
- name: "flash_cce_fwd",
928
- inputNames: ["h", "wp", "scales", "biases", "targets", "shape", "capv"],
929
- outputNames: ["blockMax_out", "blockSum_out", "blockTgt_out"],
930
- source: FWD_SOURCE,
931
- header: `#define TG ${TG}\n#define BLOCK_B ${BLOCK_B}\n`,
932
- ensureRowContiguous: true,
933
- });
934
- }
935
- return _fwdKernel;
936
- }
937
-
938
- function u32(values: number[]): MlxArray {
939
- // COPY into an mlx-owned array (fromBytesCopy = mlx_array_new_data), NOT the
940
- // pinning fromView. These tiny kernel-arg arrays (shape, targets) feed a LAZY
941
- // kernel and are disposed before it evaluates; pinning leaks one host buffer
942
- // per call, never released (unbounded `pinned` map + a deferred native
943
- // use-after-free under memory pressure — the seg/accum crash). A few-int copy
944
- // is free; correctness is not.
945
- return MlxArray.fromBytesCopy(new Uint8Array(new Uint32Array(values).buffer), [values.length], Dtype.uint32);
946
- }
947
-
948
- /** Flash-CCE forward: per-token response log-prob [M] (f32) + lse [M] (f32),
949
- * computing logits in-kernel from the quantized head — no [M,V] materialized.
950
- * `hResp` is [M, hidden] (any float dtype; cast to f32 for the kernel). `targets`
951
- * are the response target token ids [M]. Caller owns the returned arrays. */
952
- // STEEL forward: MLX's verbatim quantized BlockMMA GEMM (steel-qmm-header.ts) for the
953
- // logit tile (BM=BN=BK=32, WM=WN=2 → 4 simdgroups × 16×16 register tiles, BK_padded),
954
- // with ONLY the ORPO epilogue diverging — softcap + online-softmax over each BN vocab
955
- // tile (no [M,V]). Per (token-block z, vocab-block y) threadgroup; partials [M,NBLK]
956
- // merged in MLX. Same logit math as quantizedMatmul (validated 1e-5) at MLX speed
957
- // (e4b logits 101 ms vs MLX 106 ms; 8.4× our old hand-rolled forward).
958
- const FWD_STEEL_SOURCE = String.raw`
959
- const uint M=shape[0], V=shape[1], H=shape[2], NBLK=shape[7];
960
- const uint tokBlk=threadgroup_position_in_grid.z, vb=threadgroup_position_in_grid.y;
961
- constexpr int BM=32,BK=32,BN=32,WM=2,WN=2,gs=GS_T,bts=BITS_T;
962
- constexpr int pf=get_pack_factor<bts,8>(), bpp=get_bytes_per_pack<bts>(), BKp=BK+16/sizeof(float);
963
- threadgroup float Xs[BM*BKp]; threadgroup float Ws[BN*BKp]; threadgroup float Ls[BM*BN];
964
- threadgroup float tgMax[BM], tgSum[BM], tgTgt[BM]; threadgroup uint tgTid[BM];
965
- using mma_t=mlx::steel::BlockMMA<float,float,BM,BN,BK,WM,WN,false,true,BKp,BKp>;
966
- using lx_t=mlx::steel::BlockLoader<float,BM,BK,BKp,1,WM*WN*SIMD_SIZE>;
967
- using lw_t=QuantizedBlockLoader<float,BN,BK,BKp,1,WM*WN*SIMD_SIZE,gs,bts>;
968
- const uint sg=simdgroup_index_in_threadgroup, sl=thread_index_in_simdgroup, lid=thread_position_in_threadgroup.x;
969
- const float cap=capv[0];
970
- const int Hh=(int)H, K_w=Hh*bpp/pf, K_g=Hh/gs;
971
- const int blockV=((int)V+(int)NBLK-1)/(int)NBLK;
972
- const int nStart=(int)vb*blockV, nEnd=min(nStart+blockV,(int)V), tokBase=(int)tokBlk*BM;
973
- const short num_els=(short)min(BM,(int)M-tokBase);
974
- if (lid<(uint)BM){ tgMax[lid]=-INFINITY; tgSum[lid]=0.0f; tgTgt[lid]=0.0f; tgTid[lid]=(tokBase+(int)lid<(int)M)?targets[tokBase+lid]:0xffffffffu; }
975
- threadgroup_barrier(mem_flags::mem_threadgroup);
976
- for (int v0=nStart; v0<nEnd; v0+=BN){
977
- mma_t mma_op(sg,sl);
978
- lx_t loader_x(h+(int64_t)tokBase*Hh, Hh, Xs, sg, sl);
979
- lw_t loader_w((const device uint8_t*)wp+v0*K_w, scales+v0*K_g, biases+v0*K_g, Hh, Ws, sg, sl);
980
- for(int k=0;k<Hh;k+=BK){ threadgroup_barrier(mem_flags::mem_threadgroup);
981
- if(num_els<BM) loader_x.load_safe(short2(BK,num_els)); else loader_x.load_unsafe();
982
- loader_w.load_unsafe();
983
- threadgroup_barrier(mem_flags::mem_threadgroup); mma_op.mma(Xs,Ws); loader_x.next(); loader_w.next(); }
984
- threadgroup_barrier(mem_flags::mem_threadgroup);
985
- mma_op.Ctile.store<float,WM,WN,BN,1>(Ls + mma_op.sm*BN + mma_op.sn);
986
- threadgroup_barrier(mem_flags::mem_threadgroup);
987
- if (lid<(uint)BM && tokBase+(int)lid<(int)M){ uint b=lid;
988
- for (int v=0; v<BN; ++v){ int n=v0+v; if(n>=nEnd) break;
989
- float logit=Ls[b*BN+v]; if(cap>0.0f) logit=cap*tanh(logit/cap);
990
- float nm=max(tgMax[b],logit); tgSum[b]=tgSum[b]*exp(tgMax[b]-nm)+exp(logit-nm); tgMax[b]=nm;
991
- if((uint)n==tgTid[b]) tgTgt[b]=logit; }
992
- }
993
- threadgroup_barrier(mem_flags::mem_threadgroup);
994
- }
995
- if (lid<(uint)BM && tokBase+(int)lid<(int)M){ uint t=tokBase+lid;
996
- blockMax_out[t*NBLK+vb]=tgMax[lid]; blockSum_out[t*NBLK+vb]=tgSum[lid]; blockTgt_out[t*NBLK+vb]=tgTgt[lid]; }
997
- `;
998
- const STEEL_BM = 32;
999
- const _fwdSteelKernels = new Map<string, MetalKernel>();
1000
- function fwdSteelKernel(gs: number, bits: number): MetalKernel {
1001
- const key = `${gs}:${bits}`;
1002
- let k = _fwdSteelKernels.get(key);
1003
- if (!k) {
1004
- k = new MetalKernel({
1005
- name: "flash_cce_fwd_steel",
1006
- inputNames: ["h", "wp", "scales", "biases", "targets", "shape", "capv"],
1007
- outputNames: ["blockMax_out", "blockSum_out", "blockTgt_out"],
1008
- source: FWD_STEEL_SOURCE,
1009
- header: `${STEEL_QMM_HEADER}\n#define GS_T ${gs}\n#define BITS_T ${bits}\n`,
1010
- ensureRowContiguous: true,
1011
- });
1012
- _fwdSteelKernels.set(key, k);
1013
- }
1014
- return k;
1015
- }
1016
-
1017
- // One-time dispatch-route warnings. A failed tiling guard silently reroutes the
1018
- // whole flash head onto the legacy kernels (~5× slower class: e4b M=512 backward
1019
- // 754 → 3687+ ms) with no observable signal — the exact "forward passes are
1020
- // flag-gated, verify in code" hazard. Name the failed guard once per route so a
1021
- // new model port that misses steel is visible in the training log.
1022
- const _steelFallbackWarned = new Set<string>();
1023
- function warnSteelFallback(which: "forward" | "backward", detail: string, fallback: string): void {
1024
- if (_steelFallbackWarned.has(which)) return;
1025
- _steelFallbackWarned.add(which);
1026
- console.warn(
1027
- `[flash-cce] ${which}: steel kernel guard failed (${detail}) — dispatching the ` +
1028
- `${fallback} instead (~5x-slower class). If this model should be steel-capable, ` +
1029
- `check the tiling guards in flash-cce.ts.`,
1030
- );
1031
- }
1032
-
1033
- export function flashCceForward(
1034
- hResp: MlxArray, head: FlashCceHead, targets: number[],
1035
- ): { logp: MlxArray; lse: MlxArray; blockMax: MlxArray } {
1036
- const M = hResp.shape[0]!;
1037
- const H = hResp.shape[1]!;
1038
- const V = head.scales.shape[0]!;
1039
- const GR = head.scales.shape[1]!;
1040
- const GS = head.groupSize;
1041
- const WPR = head.w.shape[1]!;
1042
- if (H > H_MAX) throw new Error(`flashCceForward: hidden ${H} > H_MAX ${H_MAX}`);
1043
- if (GR * GS !== H) throw new Error(`flashCceForward: GR*GS ${GR * GS} != H ${H}`);
1044
-
1045
- // Vocab blocks sized so each threadgroup handles a bounded slice (≈ VOCAB_BLOCK
1046
- // rows) — independent of V, so a single threadgroup never runs the whole vocab
1047
- // (the e4b watchdog fix). NBLK = ceil(V / VOCAB_BLOCK).
1048
- const NBLK = Math.max(1, Math.ceil(V / VOCAB_BLOCK));
1049
- const hF = hResp.dtype === Dtype.float32 ? hResp : hResp.astype(Dtype.float32);
1050
- const scF = head.scales.dtype === Dtype.float32 ? head.scales : head.scales.astype(Dtype.float32);
1051
- const biF = head.biases.dtype === Dtype.float32 ? head.biases : head.biases.astype(Dtype.float32);
1052
- const tgtArr = u32(targets);
1053
- const shape = u32([M, V, H, GS, head.bits, WPR, GR, NBLK]);
1054
- const capv = MlxArray.fromFloat32(new Float32Array([head.softcap ?? 0]), [1]);
1055
-
1056
- // DEFAULT = simdgroup_matrix kernel with efficient dequant staging (848 ms, the
1057
- // fastest measured — matmul throughput once the staging loads stopped bounding
1058
- // it). MLX_BUN_CCE_SCALAR=1 → one-thread-per-row scalar+coalesced-h (1097 ms;
1059
- // also the H % SG_BD != 0 fallback). MLX_BUN_CCE_LANE=1 → lane-sliced (2.6×
1060
- // slower; scatters the h broadcast — recorded dead end).
1061
- // DEFAULT = STEEL (verbatim MLX BlockMMA GEMM, 8.4× the old SG forward) when the
1062
- // tiling is clean (H,V,blockV all % 32). MLX_BUN_CCE_NOSTEEL=1 falls back to the
1063
- // old kernels (simdgroup / scalar / lane).
1064
- const blockV0 = Math.ceil(V / NBLK);
1065
- const useSteel = runtimeValue("MLX_BUN_CCE_NOSTEEL") !== "1" && H % 32 === 0 && V % 32 === 0 && blockV0 % 32 === 0;
1066
- const useSimd = !useSteel && USE_SIMDGROUP_FWD && H % SG_BD === 0;
1067
- if (!useSteel && runtimeValue("MLX_BUN_CCE_NOSTEEL") !== "1") {
1068
- // NOTE blockV0 = ceil(V/NBLK) can violate %32 even when V%32==0 (e.g. V=50304)
1069
- const fails = [
1070
- H % 32 !== 0 ? `H%32!=0 (H=${H})` : null,
1071
- V % 32 !== 0 ? `V%32!=0 (V=${V})` : null,
1072
- blockV0 % 32 !== 0 ? `blockV0%32!=0 (blockV0=${blockV0}, V=${V}, NBLK=${NBLK})` : null,
1073
- ].filter(Boolean).join(", ");
1074
- warnSteelFallback("forward", fails, useSimd ? "simdgroup kernel" : "scalar/lane kernel");
1075
- }
1076
- const useLane = !useSteel && !useSimd && runtimeValue("MLX_BUN_CCE_LANE") === "1" && WPR % 32 === 0;
1077
- const kernel = useSteel ? fwdSteelKernel(GS, head.bits) : useSimd ? fwdSgKernel() : useLane ? fwdLaneKernel() : fwdKernel();
1078
- const tokTile = useSteel ? STEEL_BM : useSimd ? SG_BB : BLOCK_B;
1079
- // steel + simdgroup read h as [M, H] (M on axis 0); lane + scalar read h TRANSPOSED
1080
- // [H, M] (M on axis 1) — they stage E into threadgroup themselves.
1081
- const transposedH = !useSteel && !useSimd;
1082
- const hIn = transposedH ? ops.transposeAxes(hF, [1, 0]) : hF;
1083
- let bMax: MlxArray | undefined, bSum: MlxArray | undefined, bTgt: MlxArray | undefined;
1084
- try {
1085
- const outs = kernel.apply([hIn, head.w, scF, biF, tgtArr, shape, capv], {
1086
- // Derive outputs + grid from the input shapes (hResp = inputs[0]) so the
1087
- // kernel composes inside an mx.compile'd closure: [M,NBLK] f32 partials, one
1088
- // threadgroup per (token-block, vocab-block). NBLK is fixed (from V).
1089
- outputShapeFn: (ins) => {
1090
- const m = transposedH ? ins[0]!.shape[1]! : ins[0]!.shape[0]!;
1091
- return [
1092
- { shape: [m, NBLK], dtype: Dtype.float32 },
1093
- { shape: [m, NBLK], dtype: Dtype.float32 },
1094
- { shape: [m, NBLK], dtype: Dtype.float32 },
1095
- ];
1096
- },
1097
- grid: (ins) => [TG, NBLK, Math.ceil((transposedH ? ins[0]!.shape[1]! : ins[0]!.shape[0]!) / tokTile)],
1098
- threadGroup: [TG, 1, 1],
1099
- });
1100
- [bMax, bSum, bTgt] = outs as [MlxArray, MlxArray, MlxArray];
1101
- // Cross-vocab-block merge in MLX: gMax = max_b; gSum = Σ_b sum_b·exp(max_b−gMax);
1102
- // lse = gMax + log(gSum); logp = target − lse. (Apple CCE does this with a
1103
- // locked atomic logaddexp in-kernel; the partials are tiny [M,NBLK] so an MLX
1104
- // reduction is simpler and just as exact.)
1105
- const gMax = ops.maxAxis(bMax, 1, false); // [M]
1106
- const gMaxCol = ops.reshape(gMax, [M, 1]);
1107
- // Bind every nested intermediate so its MlxArray wrapper gets disposed — a
1108
- // bare `ops.exp(ops.sub(...))` leaks the inner `sub` (stays live in `active`
1109
- // until the GC finalizer backstop), and this runs TWICE per step (chosen +
1110
- // rejected). gMax was also missing from the dispose list below.
1111
- const sub = ops.sub(bMax, gMaxCol);
1112
- const shifted = ops.exp(sub); // [M, NBLK]
1113
- const prod = ops.mul(bSum, shifted);
1114
- const gSum = ops.sumAxis(prod, 1, false); // [M]
1115
- const logG = ops.log(gSum);
1116
- const lse = ops.add(gMax, logG); // [M]
1117
- const tgt = ops.sumAxis(bTgt, 1, false); // [M] (owning block only is nonzero)
1118
- const logp = ops.sub(tgt, lse);
1119
- for (const a of [gMax, gMaxCol, sub, shifted, prod, gSum, logG, tgt]) a.dispose();
1120
- const blockMax = bMax!; bMax = undefined; // hand off to caller (backward block-skip)
1121
- return { logp, lse, blockMax };
1122
- } finally {
1123
- bMax?.dispose(); bSum?.dispose(); bTgt?.dispose();
1124
- if (hIn !== hF) hIn.dispose();
1125
- if (hF !== hResp) hF.dispose();
1126
- if (scF !== head.scales) scF.dispose();
1127
- if (biF !== head.biases) biF.dispose();
1128
- tgtArr.dispose();
1129
- shape.dispose();
1130
- capv.dispose();
1131
- }
1132
- }
1133
-
1134
- /** Flash-CCE backward: dh [M, hidden] (f32) = ∂(Σ_t cotangent_t · logp_t)/∂h.
1135
- * `lse` is the forward's saved log-sum-exp [M]; `cot` is the per-token cotangent
1136
- * ∂loss/∂logp_t [M]. Recomputes logits in-kernel (no [M,V]); writes dh directly
1137
- * (no atomics). Caller owns the result. */
1138
- export function flashCceBackward(
1139
- hResp: MlxArray, head: FlashCceHead, targets: number[], lse: MlxArray, cot: number[],
1140
- // Apple-CCE coeff filter eps. Defaults to BWD_FILTER_EPS — ON at 1e-5 since
1141
- // 2026-07-02 (real-data gates in BWD_FILTER_EPS's note). Pass "0" explicitly
1142
- // for guaranteed-exact gradients (the parity gate does).
1143
- filterEps: string = BWD_FILTER_EPS,
1144
- // The forward's `blockMax [M, NBLK]` enables the vocab-block early exit (skips
1145
- // whole cold programs — attacks BOTH phases); ON at 1e-5 by default when
1146
- // blockMax is supplied. Omit blockMax (or set blockEps "0") for exact.
1147
- blockMax?: MlxArray, blockEps: string = BWD_BLOCK_EPS,
1148
- ): MlxArray {
1149
- const M = hResp.shape[0]!;
1150
- const H = hResp.shape[1]!;
1151
- const V = head.scales.shape[0]!;
1152
- const GR = head.scales.shape[1]!;
1153
- const GS = head.groupSize;
1154
- const WPR = head.w.shape[1]!;
1155
- if (H > H_MAX) throw new Error(`flashCceBackward: hidden ${H} > H_MAX ${H_MAX}`);
1156
- // NOTE: H % TG is a LEGACY-SCALAR-kernel requirement only (it owns DPER = H/TG
1157
- // dims per thread — floor division, so a remainder would leave dims silently
1158
- // zero). The steel/simdgroup kernels have their own (weaker) guards below; the
1159
- // throw is applied only when dispatch actually lands on the scalar kernel.
1160
-
1161
- const hF = hResp.dtype === Dtype.float32 ? hResp : hResp.astype(Dtype.float32);
1162
- const scF = head.scales.dtype === Dtype.float32 ? head.scales : head.scales.astype(Dtype.float32);
1163
- const biF = head.biases.dtype === Dtype.float32 ? head.biases : head.biases.astype(Dtype.float32);
1164
- const lseF = lse.dtype === Dtype.float32 ? lse : lse.astype(Dtype.float32);
1165
- const NBLK = Math.max(1, Math.ceil(V / VOCAB_BLOCK));
1166
- const tgtArr = u32(targets);
1167
- const gv = MlxArray.fromFloat32(new Float32Array(cot), [M]);
1168
- const shape = u32([M, V, H, GS, head.bits, WPR, GR, NBLK]);
1169
- const capv = MlxArray.fromFloat32(new Float32Array([head.softcap ?? 0]), [1]);
1170
-
1171
- // STEEL is the DEFAULT (verbatim MLX BlockMMA GEMM for both phases, [M,V]-free) when
1172
- // the tiling is clean (H,V,blockV all %32 AND H % HTw for the H-tiled accumulator).
1173
- // MLX_BUN_CCE_BWD_NOSTEEL=1 falls back to the old kernels. The steel backward carries
1174
- // the SAME Apple-CCE skips as the SG kernel — coeff filter (phase-2 per-tile) +
1175
- // blockMax vocab-block early exit (whole-program) — both compiled out at eps=0.
1176
- // BOTH skips default ON at 1e-5 (2026-07-02 flip; measured gates in the
1177
- // BWD_FILTER_EPS / BWD_BLOCK_EPS notes). MLX_BUN_CCE_BWD_{FILTER,BLOCK}_EPS=0
1178
- // restores exact gradients (identical pre-flip kernels).
1179
- const blockV0 = Math.ceil(V / NBLK);
1180
- // HTw == group_size for the phase-2 QuantizedBlockLoader (one group per H-tile); needs
1181
- // H % GS == 0 (always, GR*GS=H) and GS % 32 == 0 (TNv = GS/32 frags per H-tile integral).
1182
- const useSteel = runtimeValue("MLX_BUN_CCE_BWD_NOSTEEL") !== "1" && H % 32 === 0 && V % 32 === 0 && blockV0 % 32 === 0 && H % GS === 0 && GS % 32 === 0;
1183
- // simdgroup_matrix phase-2 GEMM is the next tier (5512→3913 ms, 1.41×) when the H
1184
- // tiling is clean; MLX_BUN_CCE_BWD_SCALAR=1 forces the float4 scalar kernel.
1185
- const useSg = !useSteel && runtimeValue("MLX_BUN_CCE_BWD_SCALAR") !== "1" && H % (SGN * 8) === 0 && H % SG_BD === 0;
1186
- if (!useSteel && runtimeValue("MLX_BUN_CCE_BWD_NOSTEEL") !== "1") {
1187
- // NOTE blockV0 = ceil(V/NBLK) can violate %32 even when V%32==0 (e.g. V=50304)
1188
- const fails = [
1189
- H % 32 !== 0 ? `H%32!=0 (H=${H})` : null,
1190
- V % 32 !== 0 ? `V%32!=0 (V=${V})` : null,
1191
- blockV0 % 32 !== 0 ? `blockV0%32!=0 (blockV0=${blockV0}, V=${V}, NBLK=${NBLK})` : null,
1192
- H % GS !== 0 ? `H%GS!=0 (H=${H}, GS=${GS})` : null,
1193
- GS % 32 !== 0 ? `GS%32!=0 (GS=${GS})` : null,
1194
- ].filter(Boolean).join(", ");
1195
- warnSteelFallback("backward", fails, useSg ? "simdgroup kernel" : "scalar kernel");
1196
- }
1197
- // Legacy scalar kernel: each thread owns DPER = H/TG dims (floor) — a remainder
1198
- // would leave the last H%TG dims silently zero, so it hard-requires H % TG == 0.
1199
- // Only enforced when dispatch actually lands there (steel/SG have weaker guards).
1200
- if (!useSteel && !useSg && H % TG !== 0)
1201
- throw new Error(
1202
- `flashCceBackward: hidden ${H} not divisible by TG ${TG} — required by the legacy ` +
1203
- `scalar kernel, which is the selected route because the steel/simdgroup tiling ` +
1204
- `guards also failed for this shape (see the [flash-cce] warning above).`,
1205
- );
1206
- // Block-skip is on (either kernel) only when blockMax is supplied AND blockEps>0 (the
1207
- // kernel reads it as an extra input). The coeff filter needs no extra input (just eps).
1208
- const useBlockSkip = (useSteel || useSg) && blockMax != null && Number.parseFloat(blockEps) > 0;
1209
- const bmF = useBlockSkip ? (blockMax!.dtype === Dtype.float32 ? blockMax! : blockMax!.astype(Dtype.float32)) : null;
1210
- const kernel = useSteel
1211
- ? bwdSteelKernel(GS, head.bits, H, filterEps, useBlockSkip ? blockEps : "0")
1212
- : useSg ? bwdSgKernel(H / SGN / 8, filterEps, useBlockSkip ? blockEps : "0") : bwdKernel(Math.ceil(H / TG));
1213
- const tokTile = useSteel ? BWD_STEEL_BM : useSg ? BB_BWD_SG : BLOCK_B_BWD;
1214
- const baseInputs = [hF, head.w, scF, biF, tgtArr, lseF, gv, shape, capv];
1215
- try {
1216
- const outs = kernel.apply(useBlockSkip ? [...baseInputs, bmF!] : baseInputs, {
1217
- outputShapeFn: (ins) => [{ shape: [ins[0]!.shape[0]!, H], dtype: Dtype.float32 }], // dh [M, hidden]
1218
- grid: (ins) => [TG, NBLK, Math.ceil(ins[0]!.shape[0]! / tokTile)],
1219
- threadGroup: [TG, 1, 1],
1220
- initValue: 0, // dh starts at 0; vocab-block programs atomic-add into it
1221
- });
1222
- return outs[0]!;
1223
- } finally {
1224
- if (hF !== hResp) hF.dispose();
1225
- if (bmF && bmF !== blockMax) bmF.dispose();
1226
- if (scF !== head.scales) scF.dispose();
1227
- if (biF !== head.biases) biF.dispose();
1228
- if (lseF !== lse) lseF.dispose();
1229
- tgtArr.dispose();
1230
- gv.dispose();
1231
- shape.dispose();
1232
- capv.dispose();
1233
- }
1234
- }