# EMLX
[](https://hex.pm/packages/emlx) [](https://hexdocs.pm/emlx)
EMLX is the Nx Backend for the [MLX](https://github.com/ml-explore/mlx) library.
Because of MLX's nature, EMLX with GPU backend is only supported on macOS.
MLX with CPU backend is available on most mainstream platforms, however, the CPU backend may not be as optimized as the GPU backend,
especially for non-macOS OSes, as they're not prioritized for development. Right now, EMLX supports x86_64 and arm64 architectures
on both macOS and Linux.
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.
Besides the backend, this library also provides a `Nx.Defn.Compiler` implementation that JIT-compiles Nx functions with smart use of MLX command queues.
- **Worker-thread dispatch** — MLX ops run on dedicated threads instead of BEAM dirty schedulers, eliminating scheduler starvation under load.
- **Per-process Metal command queues (`EMLX.CommandQueue`)** — each BEAM process can get its own GPU command queue for true process-level GPU isolation.
- **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.
Metal does not support 64-bit floats, so neither MLX nor EMLX do either.
## Usage
To use EMLX, you can add it as a dependency in your `mix.exs`:
```elixir
def deps do
[
{:emlx, github: "elixir-nx/emlx", branch: "main"}
]
end
```
Then, you just need to set `EMLX.Backend` as the default backend for your Nx functions:
```elixir
Nx.default_backend(EMLX.Backend)
# Setting the device to the CPU (default)
Nx.default_backend({EMLX.Backend, device: :cpu})
# Setting the device to the GPU
Nx.default_backend({EMLX.Backend, device: :gpu})
# or use the application config using one of the alternatives above as the value:
config :nx, :default_backend, EMLX.Backend
config :nx, :default_backend, {EMLX.Backend, device: :cpu}
config :nx, :default_backend, {EMLX.Backend, device: :gpu}
```
If you want to use the JIT compiler, you can set the default compiler as shown below.
```elixir
Nx.Defn.default_options(compiler: EMLX)
# Alternatively, we can set this in the application environment
config :nx, :default_defn_options, compiler: EMLX
```
### MLX binaries
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).
#### Using precompiled binaries
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.
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.
##### `LIBMLX_VERSION`
The version of the MLX binary to download. By default EMLX will always use the latest version possible.
##### `LIBMLX_ENABLE_JIT`
Defaults to `false`.
Using JIT compilation for Metal kernels when set to `true`.
##### `LIBMLX_ENABLE_DEBUG`
Defaults to `false`.
Enhance metal debug workflow by enabling debug information in the Metal shaders when set to `true`.
##### `LIBMLX_CACHE`
The directory to store the downloaded and built archives in. Defaults to the standard cache location for the given operating system.
#### Compiling from source
If you want to compile MLX from source, you can do so by setting the `LIBMLX_BUILD` environment variable to `true`.
Environment variables listed in the previous section will still apply.