blob: a815ed118c203375319b51c7413fe04939a805e1 [file] [view] [edit]
# mimalloc for Rust
Rust bindings for [mimalloc](https://github.com/microsoft/mimalloc), built directly
from the mimalloc sources in this repository. As usual for Rust FFI bindings, these are split into two crates:
- `mimalloc-sys` (in `sys/`): builds and links the C library and has the raw C API.
- `mimalloc`: the global allocator, heaps, and safe helpers (with the raw API re-exported as `mimalloc::sys`).
```rust
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
```
- `MiMalloc`: the global allocator (with fast paths for small and sized allocations).
- `Heap`: first-class heaps that can be shared between threads; dropping a heap
keeps its live blocks valid (`Heap::destroy` frees them all at once).
- `Arena`: reserve memory for heaps that only allocate in it (see [Arenas](#arenas)).
- `sys`: the raw C API (`mimalloc.h`).
- Both `MiMalloc` and `&Heap` implement `core::alloc::Allocator` on Rust 1.100+
(or on an older nightly with the `nightly` feature).
- Helpers such as `collect`, `option_set`, `stats_print` and `stats_json`.
## Arenas
An arena is a large area of memory that heaps allocate from. A heap created with
`Heap::new_in_arena` only allocates in its arena, and returns null when the arena is full.
```rust
use mimalloc::{Arena, Heap, ReserveOptions};
let options = ReserveOptions { exclusive: true, ..ReserveOptions::default() };
let arena = Arena::reserve_from_os(64 << 20, options).unwrap();
let heap = Heap::new_in_arena(arena);
```
- `Arena::reserve_from_os` reserves fresh OS memory; `Arena::from_static_memory` uses memory
that lives forever (such as a leaked `Box`); `Arena::from_external_memory` (unsafe) uses any
other memory.
They return `None` if the arena cannot be created.
- An `exclusive` arena keeps all other heaps out; otherwise any heap can use it as well.
- Arenas cannot be removed, so they live until the process exits.
- Arenas need at least `Arena::min_size()` bytes (32 MiB on 64-bit), and their start is
aligned to `Arena::min_alignment()` (256 MiB on 64-bit, or 64 KiB with the `secure` feature),
so memory at an unaligned address needs `max(min_size, min_alignment) + min_alignment` bytes.
## Features
| Feature | Description |
|---------------------|-------------|
| `secure` | Security mitigations (guard pages, randomization, encoded free lists) |
| `override` | Also override `malloc`, `free`, etc. in the final binary |
| `debug` | Build mimalloc with assertions and internal checks |
| `debug_in_debug` | As `debug`, but only when `debug_assertions` are on |
| `local_dynamic_tls` | Use the local-dynamic TLS model (for dynamically loaded libraries) |
| `win_direct_tls` | Use direct TLS slots on Windows |
| `no_thp` | Disable transparent huge pages by default on Linux |
| `opt_arch` | Architecture specific optimizations (as `-DMI_OPT_ARCH=ON`) |
| `mte` | Memory tagging; requires MTE support at runtime (aarch64 Linux, Android, and Apple only (where macOS also needs the MTE entitlement); as `-DMI_MTE=ON`) |
| `pac` | Sign internal pointers with pointer authentication; requires PAC support at runtime (aarch64 Linux, Android, and Apple only; as `-DMI_PAC=ON`) |
| `nightly` | `Allocator` support on nightly compilers before Rust 1.100 (`mimalloc` only) |
All features except `nightly` are build options of the C library in `mimalloc-sys`
(and are forwarded by `mimalloc`). Feature `mte` does not a separate `secure` (but encompasses it).
The combination `mte` and `pac` offers strong hardware supported security mitigation.
Architecture optimizations are also enabled automatically if the Rust target features
already imply them (e.g. `-C target-cpu=native`).
Features can be enabled per target, e.g. to only override `malloc` on Linux:
```toml
[target.'cfg(target_os = "linux")'.dependencies]
mimalloc = { path = "path/to/mimalloc/contrib/rust", features = ["override"] }
```
Other build options can be passed as C defines in the `MIMALLOC_DEFINES` environment
variable (separated by spaces or commas); these take precedence over the defines set by features.
For example: `MIMALLOC_DEFINES="MI_DEBUG=3 MI_SKIP_COLLECT_ON_EXIT=1" cargo build`.
## Using the crate
The C sources are found by `mimalloc-sys` in the following order:
1. The `MIMALLOC_SRC_DIR` environment variable.
2. A vendored `mimalloc/` directory next to `sys/Cargo.toml` (with `include/`, `src/`, and `LICENSE`).
3. The enclosing repository (`../../..` from `sys/`).
Within this repository, use a path or git dependency:
```toml
[dependencies]
mimalloc = { path = ".../mimalloc/contrib/rust" }
```
To publish crates based on these, copy `contrib/rust`, vendor the sources into
`sys/mimalloc/` (`cp -r ../../include ../../src ../../LICENSE sys/mimalloc/`), and rename both
packages (the names `mimalloc` and `mimalloc-sys` are already taken on crates.io). The
`mimalloc` crate depends on the exact version of `mimalloc-sys` as its fast paths depend
on internal entry points such as `mi_wmalloc_small` and `MI_SMALL_SIZE_MAX`.
## Performance tips
- Enable LTO so that allocations in other crates (and std) are inlined with a statically
known size and alignment (instead of going through the `__rust_alloc` shim):
```toml
[profile.release]
lto = "thin"
```
- Optimize mimalloc in debug builds too (the C library is compiled as part of `mimalloc-sys`):
```toml
[profile.dev.package.mimalloc-sys]
opt-level = 3
```
- Cross-language LTO can inline the allocation fast paths into Rust code: build with
clang (matching the LLVM version of `rustc`) using `CC=clang CFLAGS=-flto=thin` and
`RUSTFLAGS=-Clinker-plugin-lto` (see the `rustc` linker-plugin-lto documentation).
## Testing
```sh
cargo test --workspace
cargo test --release --features secure
cargo test --features debug,override
cargo +nightly test --features nightly
```
The tests also run as `test-rust` with `ctest` in a CMake build (if `cargo` is found),
using features that match the CMake configuration.