Skip to content

Commit f9894c0

Browse files
authored
chore: change emlx into a monorepo (#110)
* chore: setup monorepo * fix: ex doc links * fix CI * chore: license each library * chore: maintainers * fix emlx_axon deps * re-lock * fix ci path * fix formatter
1 parent c8c0ebb commit f9894c0

53 files changed

Lines changed: 1071 additions & 927 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
run: sudo apt-get install -y libopenblas0
4848

4949
- name: Compile and check warnings
50+
working-directory: emlx
5051
run: |
5152
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
5253
@@ -62,6 +63,7 @@ jobs:
6263
epmd -daemon
6364
6465
- name: Run tests
66+
working-directory: emlx
6567
run: |
6668
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
6769
if [ "${{ matrix.job.build }}" = "true" ]; then
@@ -102,6 +104,7 @@ jobs:
102104
echo "${ELIXIR_PATH}" >> $GITHUB_PATH
103105
104106
- name: Setup Mix
107+
working-directory: emlx
105108
run: |
106109
mix local.hex --force
107110
mix local.rebar --force
@@ -112,16 +115,18 @@ jobs:
112115
uses: actions/cache@v4
113116
id: mix-cache # id to use in retrieve action
114117
with:
115-
path: ${{ github.workspace }}/deps
116-
key: ${{ runner.os }}-Elixir-v${{ matrix.job.elixir }}-OTP-${{ matrix.job.otp }}-${{ hashFiles(format('{0}/mix.lock', github.workspace)) }}-v1
118+
path: ${{ github.workspace }}/emlx/deps
119+
key: ${{ runner.os }}-Elixir-v${{ matrix.job.elixir }}-OTP-${{ matrix.job.otp }}-${{ hashFiles(format('{0}/emlx/mix.lock', github.workspace)) }}-v1
117120

118121
- name: Install dependencies
119122
if: ${{ steps.mix-cache.outputs.cache-hit != 'true' }}
123+
working-directory: emlx
120124
run: |
121125
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
122126
mix deps.get
123127
124128
- name: Compile and check warnings
129+
working-directory: emlx
125130
run: |
126131
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
127132
if [ "${{ matrix.job.build }}" = "true" ]; then
@@ -131,6 +136,7 @@ jobs:
131136
132137
- name: Check formatting
133138
if: ${{ matrix.job.lint }}
139+
working-directory: emlx
134140
run: |
135141
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
136142
if [ "${{ matrix.job.build }}" = "true" ]; then
@@ -144,6 +150,7 @@ jobs:
144150
epmd -daemon
145151
146152
- name: Run tests
153+
working-directory: emlx
147154
run: |
148155
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
149156
export EMLX_TEST_DEFAULT_GPU="${{ matrix.job.gpu }}"
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
name: Validation
1+
name: EMLXAxon
22

33
on:
44
push:
55
branches:
66
- main
77
paths:
8-
- 'lib/**'
9-
- 'c_src/**'
10-
- 'validation/**'
11-
- 'mix.exs'
12-
- 'mix.lock'
8+
- 'emlx/lib/**'
9+
- 'emlx/c_src/**'
10+
- 'emlx/mix.exs'
11+
- 'emlx/mix.lock'
12+
- 'emlx_axon/lib/**'
13+
- 'emlx_axon/test/**'
14+
- 'emlx_axon/mix.exs'
15+
- 'emlx_axon/mix.lock'
1316
workflow_dispatch:
1417

1518
concurrency:
16-
group: validation-${{ github.ref }}
19+
group: emlx-axon-${{ github.ref }}
1720
cancel-in-progress: true
1821

1922
jobs:
20-
validation:
21-
name: Validation suite (macOS arm64)
23+
emlx_axon:
24+
name: EMLXAxon suite (macOS arm64)
2225
runs-on: macos-26
2326
env:
2427
MIX_ENV: test
@@ -45,38 +48,45 @@ jobs:
4548
mix local.hex --force
4649
mix local.rebar --force
4750
48-
- name: Cache root deps and build
51+
- name: Cache emlx deps and build
4952
uses: actions/cache@v4
5053
with:
5154
path: |
52-
${{ github.workspace }}/deps
53-
${{ github.workspace }}/_build
54-
key: ${{ runner.os }}-validation-root-${{ hashFiles('mix.lock', 'mix.exs', 'c_src/**') }}-v1
55+
${{ github.workspace }}/emlx/deps
56+
${{ github.workspace }}/emlx/_build
57+
key: ${{ runner.os }}-emlx-axon-emlx-${{ hashFiles('emlx/mix.lock', 'emlx/mix.exs', 'emlx/c_src/**') }}-v1
5558

56-
- name: Cache validation deps
59+
- name: Cache emlx_axon deps and build
5760
uses: actions/cache@v4
5861
with:
5962
path: |
60-
${{ github.workspace }}/validation/deps
61-
${{ github.workspace }}/validation/_build
62-
key: ${{ runner.os }}-validation-deps-${{ hashFiles('validation/mix.lock') }}-v1
63+
${{ github.workspace }}/emlx_axon/deps
64+
${{ github.workspace }}/emlx_axon/_build
65+
key: ${{ runner.os }}-emlx-axon-${{ hashFiles('emlx_axon/mix.lock', 'emlx_axon/mix.exs') }}-v1
6366

64-
- name: Build EMLX (root project)
67+
- name: Build EMLX
68+
working-directory: emlx
6569
run: |
6670
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
6771
mix deps.get
6872
mix compile
6973
70-
- name: Install validation dependencies
71-
working-directory: validation
74+
- name: Install emlx_axon dependencies
75+
working-directory: emlx_axon
7276
run: |
7377
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
7478
mix deps.get
7579
76-
- name: Run validation suite
77-
working-directory: validation
80+
- name: Check formatting
81+
working-directory: emlx_axon
82+
run: |
83+
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
84+
mix format --check-formatted
85+
86+
- name: Run emlx_axon tests
87+
working-directory: emlx_axon
7888
env:
7989
EMLX_TEST_DEFAULT_GPU: "true"
8090
run: |
8191
export PATH="${{ steps.setup.outputs.path }}:${PATH}"
82-
mix test --only validation
92+
mix test --exclude quantized_inference --warnings-as-errors

README.md

Lines changed: 5 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,11 @@
11
# EMLX
22

3-
[![Package](https://img.shields.io/badge/-Package-important)](https://hex.pm/packages/emlx) [![Documentation](https://img.shields.io/badge/-Documentation-blueviolet)](https://hexdocs.pm/emlx)
3+
This repository holds the following projects:
44

5-
EMLX is the Nx Backend for the [MLX](https://github.com/ml-explore/mlx) library.
5+
* [`EMLX`](https://github.com/elixir-nx/emlx/tree/main/emlx#readme) - Nx backend for Apple's [MLX](https://github.com/ml-explore/mlx) library, enabling GPU-accelerated tensor operations on Apple Silicon
66

7-
Because of MLX's nature, EMLX with GPU backend is only supported on macOS.
7+
* [`EMLXAxon`](https://github.com/elixir-nx/emlx/tree/main/emlx_axon#readme) - [Axon](https://github.com/elixir-nx/axon) model rewrites that swap supported nodes for `EMLX.Fast` Metal shader implementations, accelerating LLM inference on Apple Silicon
88

9-
MLX with CPU backend is available on most mainstream platforms, however, the CPU backend may not be as optimized as the GPU backend,
10-
especially for non-macOS OSes, as they're not prioritized for development. Right now, EMLX supports x86_64 and arm64 architectures
11-
on both macOS and Linux.
9+
Each has their own README, which you can access above to learn more.
1210

13-
The M-Series Macs have an unified memory architecture, which allows for more passing data between the CPU and GPU to be effectively a no-op.
14-
15-
Besides the backend, this library also provides a `Nx.Defn.Compiler` implementation that JIT-compiles Nx functions with smart use of MLX command queues.
16-
17-
- **Worker-thread dispatch** — MLX ops run on dedicated threads instead of BEAM dirty schedulers, eliminating scheduler starvation under load.
18-
- **Per-process Metal command queues (`EMLX.CommandQueue`)** — each BEAM process can get its own GPU command queue for true process-level GPU isolation.
19-
- **GPU pointer interop**`Nx.Backend.from_pointer/5` and `to_pointer/2` for zero-copy Metal buffer sharing with other languages, such as with Python via Pythonx.
20-
21-
Metal does not support 64-bit floats, so neither MLX nor EMLX do either.
22-
23-
## Usage
24-
25-
To use EMLX, you can add it as a dependency in your `mix.exs`:
26-
27-
```elixir
28-
def deps do
29-
[
30-
{:emlx, github: "elixir-nx/emlx", branch: "main"}
31-
]
32-
end
33-
```
34-
35-
Then, you just need to set `EMLX.Backend` as the default backend for your Nx functions:
36-
37-
```elixir
38-
Nx.default_backend(EMLX.Backend)
39-
40-
# Setting the device to the CPU (default)
41-
Nx.default_backend({EMLX.Backend, device: :cpu})
42-
43-
# Setting the device to the GPU
44-
Nx.default_backend({EMLX.Backend, device: :gpu})
45-
46-
# or use the application config using one of the alternatives above as the value:
47-
48-
config :nx, :default_backend, EMLX.Backend
49-
config :nx, :default_backend, {EMLX.Backend, device: :cpu}
50-
config :nx, :default_backend, {EMLX.Backend, device: :gpu}
51-
```
52-
53-
If you want to use the JIT compiler, you can set the default compiler as shown below.
54-
55-
```elixir
56-
Nx.Defn.default_options(compiler: EMLX)
57-
58-
# Alternatively, we can set this in the application environment
59-
60-
config :nx, :default_defn_options, compiler: EMLX
61-
```
62-
63-
### MLX binaries
64-
65-
EMLX relies on the [MLX](https://github.com/ml-explore/mlx) library to function, and currently EMLX will download precompiled builds from [mlx-build](https://github.com/cocoa-xu/mlx-build).
66-
67-
#### Using precompiled binaries
68-
69-
While the default configuration should be suitable for most cases, there is however a number of environment variables that you may want to use in order to customize the variant of MLX binary.
70-
71-
The binaries are always downloaded to match the current configuration, so you should set the environment variables in .bash_profile or a similar configuration file so you don't need to export it in every shell session.
72-
73-
##### `LIBMLX_VERSION`
74-
75-
The version of the MLX binary to download. By default EMLX will always use the latest version possible.
76-
77-
##### `LIBMLX_ENABLE_JIT`
78-
79-
Defaults to `false`.
80-
81-
Using JIT compilation for Metal kernels when set to `true`.
82-
83-
##### `LIBMLX_ENABLE_DEBUG`
84-
85-
Defaults to `false`.
86-
87-
Enhance metal debug workflow by enabling debug information in the Metal shaders when set to `true`.
88-
89-
##### `LIBMLX_CACHE`
90-
91-
The directory to store the downloaded and built archives in. Defaults to the standard cache location for the given operating system.
92-
93-
#### Compiling from source
94-
95-
If you want to compile MLX from source, you can do so by setting the `LIBMLX_BUILD` environment variable to `true`.
96-
97-
Environment variables listed in the previous section will still apply.
11+
[Check our organization page for a general introduction to Machine Learning in Elixir](https://github.com/elixir-nx/).

0 commit comments

Comments
 (0)