Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Distributed: accept a null stream, and bind clear_backends() and init with an all-gather factory

Open
#134 0 comments 0 reactions 0 assignees View on GitHub

@ronaldmannak is already working on this.

Since Oct 14, 2026.

  • #139 by @ronaldmannak — open

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
c, cpp

Research direction

The issue involves extending the mlx-c distributed API. Start by reviewing the existing mlx_distributed_* functions in the codebase, likely in a header like mlx/distributed.h. Understand how StreamOrDevice and mlx_closure_new_func_payload are implemented. The changes require modifying C function signatures and adding new functions, so familiarity with the project's C API conventions and distributed backend logic is essential. Testing will involve building the C/C++ bindings and verifying the new behavior aligns with the Python API's distributed module.

Written by the indexing model from the issue text.

Description

This is a follow up to PR 482 on MLX-Swift to expose missing distributed functions. Happy to open PRs for the parts that can't be auto-generated, but I'm not sure which ones those are.

1. Accept a null stream in the distributed ops

Python's stream=None lets the group pick its communication stream: the GPU for NCCL, the CPU otherwise. mlx-c can't express that, and communication_stream() is internal, so a binding has to guess. Proposal: map a null stream to StreamOrDevice{} in all eight mlx_distributed_* ops.

int mlx_distributed_all_sum(
    mlx_array* res,
    const mlx_array x,
    const mlx_distributed_group group /* may be null */,
    const mlx_stream s /* may be null */);
s.ctx ? StreamOrDevice{mlx_stream_get_(s)} : StreamOrDevice{}

Doing it in the StreamOrDevice rule for every op would work too.

2. Bind clear_backends()

int mlx_distributed_clear_backends(void);

A non-strict init caches its size-one fallback, so a later strict init of that backend returns it instead of retrying. Python also calls this at exit, which matters once 3 holds a binding's callback.

3. Bind init with an all-gather factory

For mx.distributed.init(backend="jaccl", all_gather_factory=...), flattened into one callback with payload and dtor as in mlx_closure_new_func_payload:

int mlx_distributed_init_with_all_gather(
    mlx_distributed_group* res,
    bool strict,
    const char* bk,
    int (*all_gather)(
        int rank, int size, const char* src, char* dst, size_t n_bytes, void* payload),
    void* payload,
    void (*dtor)(void*));

A non-zero return fails the init, and dtor runs exactly once, including on failure.

4. Status-returning group_rank and group_size (minor)

Both return 0 on error, which is also a valid rank. Either in place, as #110 did for init:

int mlx_distributed_group_rank(int* res, mlx_distributed_group group);
int mlx_distributed_group_size(int* res, mlx_distributed_group group);

or as new mlx_distributed_group_get_rank and _get_size, if you'd rather not break them.

Dominant language
C++
Stars
251
Forks
97
Avg merge
1h 23m
Merged PRs (30d)
2

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ml-explore/mlx-c

All issues in ml-explore/mlx-c

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.