Skip to content

mlx-swift fork is 38 commits behind ml-explore/mlx-swift (mlx core 0.30.6 vs 0.32.2), and 4 upstream fixes were lost in a merge #204

Open

Description

@CodeAndCanvas728

(Filing here because issues are disabled on SharpAI/mlx-swift.)

Summary

SwiftLM's mlx-swift submodule (b480, 1a2b3af) is current with the SharpAI fork, but the fork itself has fallen behind ml-explore/mlx-swift:

SharpAI/mlx-swift b480 ml-explore/mlx-swift 0.32.3 (2026-09-30)
Bundled mlx core 0.30.6 0.32.2
Divergence merge base 49d73abc (#387). The fork is 62 commits ahead and 38 behind

Catching up is a merge into the fork rather than a submodule bump in SwiftLM, since a bump can only move to a commit the fork already has.

What the gap costs on Apple Silicon

All of these are merged upstream:

mlx PR In What it does
ml-explore/mlx#4241 0.32.1 Dequantize in float32 (bf16 maths is emulated before M5). Upstream reports +20–24% prefill for 4-bit affine on M1 Max
ml-explore/mlx#4505 0.32.3 Lower SDPA memory for head dim 256. Qwen3.5/3.6 full-attention layers are hd256
ml-explore/mlx#4572, #4567 0.32.3 gather_qmm / gather_mm improvements for MoE prefill
ml-explore/mlx#4020 0.32.3 Native gated-delta-net Metal kernels, which could replace the hand-written kernel in mlx-swift-lm's GatedDelta.swift

The mlx-swift fixes the fork is missing that matter most for a server are listed below. Each is in upstream history and none is in the fork:

  • #461: deadlock between CompiledFunction.lock and evalLock, and a data race on the compile cache
  • #472: pool Streams (Streams leak Metal resources), and fix Device inheritance
  • #463: StreamOrDevice.stream(_:) ignored its argument
  • #448: FinalizerCaptureState leak in MLXArray(rawPointer:…)
  • #398: unchecked mlx_detail_compile / mlx_closure_apply return values
  • #471: wired-memory ticket lifecycle during cancellation

Four upstream fixes were lost in a merge

These commits are in the fork's history, but their code is missing from the current source. Each one's patch still applies cleanly to b480, and none of them reverse-applies. They appear to have been undone while resolving conflicts in a8f5092 ("chore: sync upstream and resolve SharpAI conflicts"):

  • #358: wired-memory race condition (WiredMemory.swift)
  • #363: dictionary key lookup in Module.update(parameters:)
  • #367: x[.ellipsis] returns self instead of a copy (#364)
  • #369: init-order dependency in Module

To check one: git show 70346ae2 -- Source | git apply --check succeeds on b480.

Expected rebase friction

These are notes for whoever picks this up:

  • Merge conflicts. Expect them where the fork's patches meet upstream changes: the TurboQuant additions at the end of fast.h/.cpp (#4020 also appends there) and sdpa_vector.h, which several upstream SDPA PRs touch.
  • Generated code. mlx-generated and the moe_stream codegen have to be regenerated.
  • Threading. Since mlx 0.31.2 every thread gets its own default stream, and Streams are now pooled (#472). Both could interact with the SSD-streaming thread pool, so that path needs a careful retest.
  • Possible cleanup. MLXFast.fromFp8 looks redundant now that upstream wraps mlx's from_fp8 (#435).

Ask

Do you want an outside PR against SharpAI/mlx-swift for this, or would you rather do the sync yourselves, given that it touches the fork's own patches? I'm happy to do it as two PRs:

  1. Restore the four lost fixes. This is small and low risk.
  2. Merge 0.32.3, with run_benchmark.sh numbers before and after.

The SwiftLM bump would then go through update_dependencies.yml as usual.


🤖 Generated with Claude Code

Activity

  1. solderzzc commented on Oct 5, 2026

    @solderzzc
    Member

    Thanks for the detailed report. Most of this is already done; here is where each item stands against SharpAI/mlx-swift main (34bc52f).

    If you want to take the core v0.32.3 bump (with before/after run_benchmark.sh numbers), a PR against SharpAI/mlx-swift is welcome. AI-assisted analysis (Claude Code).

  2. CodeAndCanvas728 commented on Oct 5, 2026

    @CodeAndCanvas728
    ContributorAuthor

    Thanks, that's a thorough check and it matches what I see here. I'll take the core v0.32.3 bump. The plan:

    • Re-vendor mlx v0.32.3. mlx-c stays at ebc88f1 unless the build needs v0.7.0, which is only a version bump.
    • Re-apply the carried patches per UPSTREAM_MERGE_PLAN.md and regenerate mlx-generated.
    • Post run_benchmark.sh before/after numbers from a 16 GB M2 with Qwen3.5-35B-A3B 4-bit, with and without --stream-experts.

    I expect conflicts in sdpa_vector.h, fast.h/.cpp, io/load.* and metal/device.cpp, and the PR will list how each was resolved. With 0.32.3 in, #4020 (mx.fast gated delta) can get its mlx-c and Swift bindings in a follow-up.

    On fromFp8: if it helps, I can move the 6 mlx-swift-lm call sites to fromFP8 so the fork patch can go. Just say so.

  3. solderzzc commented on Oct 5, 2026

    @solderzzc
    Member

    Sure, thanks, looking forward to it.

  4. CodeAndCanvas728 commented on Oct 6, 2026

    @CodeAndCanvas728
    ContributorAuthor

    Update on the core bump: SharpAI/mlx-swift#22 vendors mlx v0.32.3 (re-vendored with git, patches re-applied, mlx-generated regenerated, mlx-c left at ebc88f1).

    • Result on a 16 GB M2 (Qwen3.5-35B-A3B 4-bit, --stream-experts): parity with 0.32.2 on TTFT and decode for the Baseline and TurboQuant KV configs. No speedup there; the 0.32.3 wins that target newer chips (#4505, #4567) don't show on this chip.
    • A crash worth knowing about: plain 0.32.3 killed the server with a Metal GPU timeout on the first decode step of a code prompt (the usual benchmark prompt doesn't trigger it). Bisecting gave mlx #4515 (integer floor_divide), which now builds seven ops where the MoE expert sort in mlx-swift-lm needs one for uint32 indices. The PR carries a small fix: skip the sign correction for unsigned integers.
    • Other changes in the PR: a backport of mlx #4594 (the gated-delta NAX kernel doesn't compile with the macOS 27 SDK), three new CUDA excludes in Package.swift, and the mlx-c gather_qmm call passes nullopt for the new global_scale argument.
    • Tests: mlx-swift 394 tests pass; SwiftLM's 276 tests pass against the branch.

    The issue stays open for #4020 (mx.fast gated delta), which needs its own mlx-c and Swift bindings. I'll file the #4515 finding upstream separately.

    AI-assisted analysis (Claude Code).

  5. solderzzc commented on Oct 6, 2026

    @solderzzc
    Member

    Thank you for taking this on and for the thorough write-up. Bisecting the Metal timeout down to mlx #4515, keeping the fix small (unsigned only, with a test for both signs), and reporting honestly that this chip sees parity rather than a speedup is exactly the care a core bump needs. I am reading SharpAI/mlx-swift#22 now.

    AI-assisted analysis (Claude Code).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions