feat: sequence-parallel Ring Attention for MLX - #2255
Open
abendrothj wants to merge 1 commit into
Open
Conversation
abendrothj
force-pushed
the
feat/ring-attention
branch
from
September 2, 2026 01:44
c030c5c to
3f46b67
Compare
Implement distributed Ring Attention prefill for verified MLX Llama and Qwen3 architectures: RingAttentionLayer wraps only the attention module, splits the sequence across ranks, and rotates KV blocks around the ring while accumulating attention with a numerically stable online-softmax merge. Receives are posted before the current block's attention is scheduled so transfers overlap compute. Decode is replicated with no communication, so it forwards to the wrapped attention module and inherits every cache type the model supports, including BatchKVCache -- continuous batching needs no special casing. Comm streams are kept free of GPU dependencies: KV payloads are materialised before sends are posted and receive templates are allocated on the CPU receive stream, so transport never waits on unfinished GPU work and always drains. This removes a cross-rank circular wait observed on long-sequence prefill under MLX_METAL_FAST_SYNCH. Ring prefill also runs under a progress watchdog (EXO_PREFILL_STALL_TIMEOUT, default 300s) that dumps thread stacks and exits, turning any residual stall into a clean instance failure instead of an indefinite hang. Placement gates Sharding.Ring on MlxRing transport, >=2 nodes, model-card supports_ring, and per-node admission of the replicated weights plus an estimated long-context prefill working set -- weight-only admission let a 4GB rank into 16K-context placements it could never serve.
abendrothj
force-pushed
the
feat/ring-attention
branch
from
September 2, 2026 02:04
3f46b67 to
ba9ed00
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related to/Closes #39
Sequence-parallel Ring Attention for MLX
Implements distributed Ring Attention prefill for verified MLX Llama and Qwen3 architectures, with replicated decode caches, capability-gated placement, per-node memory admission, failure-safe cleanup, dashboard selection, integration coverage, and reproducible benchmarks.
What's included
RingAttentionLayer(src/exo/worker/engines/mlx/ring_attention.py): wraps only the attention module (never a full decoder block), splits the sequence across ranks, and rotates KV blocks around the ring while accumulating attention with a numerically stable online-softmax merge.mx.async_eval-ed to create the computation window the transfer overlaps with. Transport peer (always the ring neighbor) is correctly separated from KV origin rank.BatchKVCache.--no-batchis not required.MLX_METAL_FAST_SYNCH, where comm ops posted with unfinished GPU inputs block on Metal shared events inside a bounded command-buffer queue.EXO_PREFILL_STALL_TIMEOUT, default 300s, kicked per chunk) that dumps all thread stacks and exits the runner, converting any residual hang into a clean instance failure rather than a stuck request.Sharding.Ringis gated onMlxRingtransport, ≥2 nodes, model-cardsupports_ring, and per-node admission (see below).supports_ringdefaults off for everything else.MLX_METAL_FAST_SYNCH=1),exo-bench --sharding ringsupport.Memory admission
Every ring rank replicates the full weights and must hold the long-context prefill working set, so admitting on weights alone over-admits: a 4 GB rank passed the old check for a model whose 16K prefill peaks well past 4 GB.
estimate_ring_node_memoryadds an estimated KV working set (16K-capped context, 4x multiplier derived from observed Metal/CUDA peaks) to the replicated model size.This is a pure function of the model card and uses whatever memory figures the node already reports. An earlier revision of this branch also added NVML-based VRAM reporting, which overlaps #2216 (filed before this branch, using
nvidia-smi). That half has been dropped — #2216 should own accelerator-memory reporting, and this gate picks up VRAM-aware numbers automatically once it lands, with no change here.Benchmarks
Collected with
exo-benchagainst a live 2-node exo cluster running this branch (--warmup 1 --repeat 3, prefix caching disabled,MLX_METAL_FAST_SYNCH=1 uv run exoon both nodes).Environment: both nodes on a single MacBook Pro (M4 Pro, 14-core, 24 GB, macOS 26.5.2, MLX 0.32.0.dev20260709). Model:
mlx-community/Llama-3.2-1B-Instruct-4bit.Ring (sequence-parallel prefill), 2 nodes
Pipeline baseline (same nodes, transport, model, params)
¹ cold first iteration (kernel compilation) · ² transient slowdown, likely thermal/contention
Reproduce with:
uv run bench/exo_bench.py --model Llama-3.2-1B-Instruct-4bit \ --pp 2048,4096,8192 --tg 64,64,64 --min-nodes 2 --max-nodes 2 \ --instance-meta ring --sharding ring --warmup 1 --repeat 3 # baseline: same command with --sharding pipelineReal two-node validation — heterogeneous Metal + CUDA over a physical network
MLX_METAL_FAST_SYNCH=1)mlx-cuda-120.32.0, driver 610.43.02), ~6 ms RTT over Tailscale LANResult: 20/20 consecutive 4096-token ring prefills, zero hangs, identical deterministic output, ~468 prompt tok/s (428–508), ~42 tok/s decode. 8K/16K OOM the 4 GB rank cleanly (surfaced as HTTP 500, no wedged runners). Concurrent requests batch correctly, and a 3570-token ring prefill produced output identical to the pipeline baseline at temperature 0. The 16K same-host deadlock could not be probed on this hardware due to VRAM constraints.
Note that reproducing the CUDA half of this setup currently requires #2290 and #2291.