Skip to content

Fix all_gather benchmark collapsing its input to a scalar - #4017

Merged
zcbenz merged 1 commit into
ml-explore:mainfrom
ayaangazali:fix-all-gather-benchmark
Aug 6, 2026
Merged

zcbenz merged 1 commit into
ml-explore:mainfrom
ayaangazali:fix-all-gather-benchmark

Conversation

@ayaangazali

Copy link
Copy Markdown
Contributor

Proposed changes

synchronize_bench.py crashes before it can report the all gather timing:

File "benchmarks/python/synchronize_bench.py", line 45, in fn
    x = mx.distributed.all_gather(x)[0]
ValueError: too many indices for array: array is 0-dimensional

The loop indexes with [0] where it means to slice. all_gather grows axis 0, but [0] removes that axis instead of trimming it, so the array loses a dimension on every pass and runs out after two:

start          (5, 5)
iter0: all_gather -> (5, 5)   [0] -> (5,)
iter1: all_gather -> (5,)     [0] -> ()
iter2: all_gather -> ()       [0] -> ValueError

Slicing back to the original first dimension keeps the shape fixed, which is what a benchmark loop wants:

iter0 -> (5, 5)
iter1 -> (5, 5)
iter2 -> (5, 5)

With that change the script completes and prints both numbers:

All Reduce: time per iteration 0.004029 (ms)
All gather: time per iteration 0.000597 (ms)

Note this is not specific to a single process. all_gather only scales axis 0, so [0] drops a dimension no matter how many ranks there are, and the third iteration fails either way. I could only run it with a group of one here, so I have not exercised it under mlx.launch with several ranks.

Checklist

Put an x in the boxes that apply.

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the necessary documentation (if needed)

(benchmark script, so no test; verified by running it before and after)


honest note on process: freshman contributor, Claude Code helps me sweep, but i found this by running the benchmark scripts, then printed the shape at each iteration to work out why it died rather than guessing at the fix. flagging clearly that the multi rank path is untested on my machine, in case that changes how you want it written.

def fn(x):
for _ in range(its_per_eval):
x = mx.distributed.all_gather(x)[0]
x = mx.distributed.all_gather(x)[: a.shape[0]]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it just slice all?

Suggested change
x = mx.distributed.all_gather(x)[: a.shape[0]]
x = mx.distributed.all_gather(x)[:]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for a single process, but i think it grows once there is more than one rank. all_gather concatenates along the first axis, so the output first dimension is group_size times the input, and [:] keeps all of it. With N ranks the input to the next iteration is 5N, then 5N^2, and the loop runs 100 times, so for N=8 it would blow up almost immediately.

Slicing back to the original first dimension keeps every iteration doing the same amount of work, which is what makes the per iteration number meaningful:

iter0 -> (5, 5)
iter1 -> (5, 5)
iter2 -> (5, 5)

That is why i went with [: a.shape[0]] rather than [0] or [:].

I could only test with a group of one here, so if you know all_gather behaves differently under a real multi rank group, say so and i will push [:] instead. Happy either way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, thanks!

@zcbenz
zcbenz merged commit 26db505 into ml-explore:main Aug 6, 2026
@BrewTestBot BrewTestBot mentioned this pull request Aug 18, 2026
1 task done
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.

2 participants