Skip to content

Releases: joshuaswarren/metal2vk

metal2vk v0.3.0

Choose a tag to compare

@joshuaswarren joshuaswarren released this 12 Oct 01:08
v0.3.0
04c1cf4

metal2vk v0.3.0 adds a corpus of 119 MLX custom kernels that translate to Vulkan SPIR-V. It also adds a native Metal timing harness for the same kernels, and a measured comparison with MLX's own kernels on the M2 Max. The default translation path is unchanged. 610 of 610 uzu and TensorFold programs give valid SPIR-V (606 by default, 4 in the opt-in 64-bit mode). This is 30 commits since v0.2.1.

What the results show

  • MLX kernels. sweep/mlx-batch/ holds 119 kernels assembled from the pinned mlx-lm, mlx-examples, mlx-vlm and mlx-audio sources. 118 give valid SPIR-V (spirv-val --target-env vulkan1.3). They cover 105 of the 107 metal_kernel() call sites in those trees. The other 2 sites repeat a body that is already assembled. The earlier corpus has 32 kernels, 31 valid, and 1 needs an M5-only feature. Together that is 149 of 151.
  • One kernel is open. quantized_verify_nvfp4_argmax fails because clspv emits an OpSelect over differently-typed workgroup pointers. A reduced case and a 21-case matrix are in the directory. The fix belongs in a clspv pass. It is not done.
  • Compile only. No kernel in this corpus has run on a GPU, and the shapes are representative values read from each call site. sweep/mlx-batch/SUMMARY.md has the counts and how to re-run them.
  • Shim. include/mlx_prelude.h ports the pieces of MLX's device prelude that its steel kernels need. It is additive: SPIR-V for five unrelated corpus modules is byte-identical before and after.
  • Native Metal harness. bench/native-metal/ compiles the original uzu Metal source at run time and times the same 12 cases as bench/m2v-bench.py, with the same shapes, grids, constants and CPU checks. All 12 checks passed on an M1 Ultra Mac Studio, and on the 16-inch M1 Max in 3 runs. The 16-inch run started 36 seconds after boot, so its load was not settled and it counts as a first pass. No translated-against-native number is published yet.
  • Translated kernels against MLX's own kernels. On the 14-inch M2 Max, in two runs (Mesa git 6543eeb7df7, load average 1.30 at the start), softmax f32 takes 0.75 and 0.76 of the MLX time and softmax f16 0.91 and 0.92. The 4x4 register-tiled matrix multiply at 1024³ takes 1.28 and 1.22 times the MLX time. The two run files are in bench/.

Changes

  • bench/run-all.sh and bench/sweep.py take --device and --icd (also as --name=value). compile.sh deletes stale .ll, .spv and .json before it compiles (issue 18).
  • Docs: the M2 Max table in docs/bench-status.md, a sourced explanation of the "4x" and "27x" figures in docs/how-it-works.md, and a clearer title for docs/gemm-gap.md.

Known limits

  • The CPU governor changes decode speed on the M1-class chips, which can move any bench that includes MLX's host code. The M2 Max rows above ran under the governor the lab had at the time and did not record it.
  • The 4 kimi/experts kernels still have no GPU run.
  • There is no tokens-per-second number for whole models through this route.
  • Stock Mesa Honeykrisp does not offer cooperative matrix. The omarchy-mlx fork does.

metal2vk v0.2.1

Choose a tag to compare

@joshuaswarren joshuaswarren released this 11 Oct 12:35
v0.2.1
dfc093d

metal2vk v0.2.1 translates all 610 uzu and TensorFold kernel programs to valid Vulkan SPIR-V: 606 by default and the last 4 in a new opt-in 64-bit mode. The four are valid SPIR-V only. None has run on a GPU.

What the receipts show

  • Coverage. 610 of 610 programs pass spirv-val --target-env vulkan1.3. The CI sweep floor is 610, and the sweep passed on f49c9c8. The table by cause is in docs/coverage.md.
  • The four kernels. k3_xp_up_r1, k3_xp_up_r4, k3_xp_down_r1 and k3_xp_down_r4 (kimi experts) read a table of raw GPU addresses. They need physical storage buffer pointers and so a 64-bit module. Set M2V_PSB_CASES to name them, and compile.sh or the sweep driver builds them with clang --target=spir64 and clspv --arch=spir64 --physical-storage-buffers. By the SPIR-V reflection, each pointer argument is an 8-byte address in the push-constant block. A host has to pass device addresses from vkGetBufferDeviceAddress.
  • Default path. The mode is off unless you set it. The author of the change reports that a rebuild of the corpus with the new clspv and the mode off matched the sha256 of all 604 modules in the previous sweep output. CI does not upload modules, so no file holds that comparison.
  • Run stage. The v0.2.0 run on the 606 default modules is unchanged: 578 dispatch with 0 failures on G13C, G13G and G14C, 28 are skipped by the spin-wait screen, and 4 of 4 CPU references match. v0.2.1 adds no run result.

Changes

  • A clspv patch, apply-ptr-convert-route.py: clspv converts between a vector and a pointer through the pointer-width integer. Before, a copy of a table row of pointers aborted the compile with an invalid bitcast (#66).
  • The opt-in 64-bit mode and three IR steps for it: memcpy_mixed_as.py --ptr-copies, psb_promote_ptr_arrays.py and psb_ptr_to_int.py (#66).
  • README and docs state 610 of 610 with both limits, and the coverage table header is corrected to 610 entries (#67).

Known limits

  • The four kernels have no GPU run.
  • There is no tokens-per-second number for whole models through this route.
  • Stock Mesa Honeykrisp does not offer cooperative matrix. The omarchy-mlx fork does.

metal2vk v0.2.0

Choose a tag to compare

@joshuaswarren joshuaswarren released this 10 Oct 23:18
v0.2.0
8e1b353

metal2vk v0.2.0 translates 606 of the 610 uzu and TensorFold kernel programs to valid Vulkan SPIR-V. It also adds a tested quick start, a one-file compile command and a CI gate that keeps the count from falling. This is 44 commits since v0.1.0.

What the receipts show

  • Coverage. 606 of 610 programs pass spirv-val. The table by cause is in docs/coverage.md. The sweep runs on every pull request and fails if a program that used to translate stops translating (#24).
  • Run stage. On 598 modules from the same sweep, 570 dispatch with 0 failures on a 13-inch M1 MacBook Pro (G13G) and on a 16-inch M1 Max MacBook Pro (G13C). The other 28 are skipped by a screen for kernels that spin-wait on another thread. 4 of 4 CPU references match.
  • Quick start. The steps in the README were run in a new directory. The clone, the patch script and compile.sh produced both modules, and spirv-val returned 0. The LLVM build for clspv was not run in that test: a built clspv binary was used. Step 3 ran on the G13C machine, and all four m2v-test results were ok.
  • No give-ups. A clspv pass that cannot finish now keeps no SPIR-V and prints M2V-GIVEUP. The sweep refuses any module whose log has that line. None of the 606 valid programs hit it.
  • omarchy-mlx. The route for oMLX custom kernels is merged in omarchy-mlx (pull request 70). It is opt-in through an environment variable. The default stays the old translator.

Changes

  • clspv patches that raised coverage from 441 to 606 programs (#28, #31, #34, #35, #38, #39, #51, #55, #57, #59). The apply script is tools/apply-clspv-patches.sh (#62).
  • m2v-compile, a cached front door for one file (#32, #33).
  • A build of the uzu kernel pipeline on Linux (#26, #27).
  • Parity references for the kernels the old translator refused (#37, #40 to #45, #49, #53, #56).
  • README and docs rewritten. The README leads with the problem and the measured result (#61 to #65).

Known limits

  • 4 kimi and experts kernels do not translate. They read a runtime array of GPU addresses and need physical storage buffer pointers, which needs a 64-bit module.
  • There is no tokens-per-second number for whole models through this route.
  • Stock Mesa Honeykrisp does not offer cooperative matrix. The omarchy-mlx fork does.