Skip to content

feat: add fused kv_cache+sdpa#124

Merged
polvalente merged 4 commits into
mainfrom
pv-feat/perf-improvements
Jul 7, 2026
Merged

feat: add fused kv_cache+sdpa#124
polvalente merged 4 commits into
mainfrom
pv-feat/perf-improvements

Conversation

@polvalente

@polvalente polvalente commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Increases best-case scenario Native Qwen3 0.6B 4bit implementation to 300 tok/s under nice conditions and EMLX Axon rewrites to 100 tok/s

Benchmarks below (M4 Max 64GB)
For each entry, > 1 means row is faster than col

Qwen3-0.6B (dense bf16)

row ∖ col (row / col) bb base bb+rewrite native emily-eager emily-native
bb base 0.51× 0.77× 5.71× 0.82×
bb+rewrite 1.95× 1.51× 11.17× 1.6×
native 1.3× 0.66× 7.42× 1.06×
emily-eager 0.17× 0.09× 0.13× 0.14×
emily-native 1.23× 0.63× 0.94× 7.0×
  • bb base: median=64.0 mean=63.2±1.7 min/max=60.5/65.1 tok/s
  • bb+rewrite: median=125.1 mean=124.5±2.5 min/max=120.0/127.6 tok/s
  • native: median=83.1 mean=83.0±0.2 min/max=82.6/83.2 tok/s
  • emily-eager: median=11.2 mean=11.2±0.1 min/max=11.0/11.4 tok/s
  • emily-native: median=78.4 mean=78.0±2.0 min/max=75.0/80.7 tok/s

Qwen3-0.6B-MLX-4bit

row ∖ col (row / col) bb base bb+rewrite native emily-quantized
bb base 0.53× 0.19×
native 5.27× 2.8× 8.55×
emily-quantized 0.62× 0.33× 0.12×
  • bb base: median=62.1 mean=62.0±0.9 min/max=60.7/63.3 tok/s
  • bb+rewrite: median=116.9 mean=117.5±2.6 min/max=114.4/122.4 tok/s
  • native: median=327.3 mean=320.5±14.0 min/max=294.1/332.8 tok/s
  • emily-quantized: median=38.3 mean=38.3±0.1 min/max=38.2/38.4 tok/s

@polvalente polvalente self-assigned this Jul 6, 2026
@polvalente polvalente merged commit fb386ec into main Jul 7, 2026
6 checks passed
@polvalente polvalente deleted the pv-feat/perf-improvements branch July 7, 2026 02:10
nshkrdotcom added a commit to North-Shore-AI/emlx that referenced this pull request Jul 7, 2026
Merges elixir-nx/emlx main (through the fused kv_cache+sdpa PR) into our
phase-9-qwen3-trace fork. Keeps our own EMLXAxon.Qwen3.{Model,Attention,
Generate} trace-mode implementation as-is (upstream shipped an independent,
non-instrumented Qwen3 path under the same module names with no tracing
support), while picking up the new EMLX.Fast primitives and IR lowering
compiler improvements from upstream, which our own implementation can now
build on.
nshkrdotcom added a commit to North-Shore-AI/emlx that referenced this pull request Jul 7, 2026
The merge auto-combined loader.ex and text_generation.ex with upstream's
parallel edits (no textual conflict, since our commit hadn't touched those
exact lines), leaving both calling upstream's EMLXAxon.Qwen3.Model API
(Model.layer/11, init_native_kv_cache/2) that doesn't exist on our kept
Model. Reverts both files to our pre-merge versions.

Also drops emlx_axon/lib/emlx_axon/qwen3/dense_loader.ex and its tests
(qwen3_dense_loader_test.exs, qwen3_generate_test.exs, qwen3_native_test.exs,
text_generation_test.exs) — upstream's own, independent dense-HF-safetensors
Qwen3 pipeline, added under the same EMLXAxon.Qwen3.* module names as our
trace-mode implementation. We're keeping our own instrumented Model/
Attention/Generate (Crucible's mechinterp bridge depends on
generate_trace/3, which upstream's competing implementation never had), so
upstream's parallel pipeline has no caller here and can't compile against
our Model shape.

Documents in attention.ex why we keep calling the eager
EMLX.kv_cache_sdpa_update/7 rather than the new traced
EMLX.Fast.kv_cache_sdpa_update/8 from elixir-nx#124: that op only pays
off inside a compiled :while decode loop (which this isn't), and upstream's
own PR description calls it a spike not expected to improve throughput.

Verified on Linux (CPU MLX backend, no Apple Silicon available): both emlx
and emlx_axon compile with --warnings-as-errors; our own tests pass
(qwen3_trace_test.exs, qwen3_quantized_test.exs, axon_test.exs — metal/
bumblebee/quantized_inference-tagged tests excluded, as before). emlx's
own new native-compiler test suite (test/emlx/native/expr_test.exs) has
104 failures under this same CPU backend, but a pristine-upstream baseline
check (elixir-nx/emlx main, no fork changes at all) reproduces 89 of them
in the same environment, so this predates the merge and isn't something we
introduced — consistent with the existing "Apple Silicon hardware gate"
note in trinity_framework/guides/runtime_profiles.md.
nshkrdotcom added a commit to North-Shore-AI/crucible_bumblebee that referenced this pull request Jul 7, 2026
Points at the refreshed north-shore/emlx phase-9-qwen3-trace branch,
now merged through elixir-nx/emlx main (fused kv_cache+sdpa, elixir-nx/emlx#124)
while keeping our own instrumented Qwen3 Model/Attention/Generate rather
than upstream's independent, non-instrumented dense-generation path shipped
under the same module names.
nshkrdotcom added a commit to nshkrdotcom/trinity_framework that referenced this pull request Jul 7, 2026
The EMLX Qwen3 bridge's fork pin (crucible_bumblebee) now tracks
elixir-nx/emlx main through the fused kv_cache+sdpa PR (elixir-nx/emlx#124),
keeping our own instrumented Qwen3 implementation rather than upstream's
independent, non-instrumented one shipped under the same module names.
No code changes needed here: crucible_bumblebee is a path dependency and
its EMLXQwen3 bridge is compile-time decoupled from emlx_axon.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant