Skip to main content

Module mimalloc

Module mimalloc 

Source
Expand description

mimalloc is a general-purpose allocator that serves each size class from thread-local pages carved out of arenas of operating system memory, returns an object freed by any thread to the page it came from, and purges freed arena memory back to the operating system after a delay.

This module installs mimalloc as the global allocator through MiMalloc, a GlobalAlloc whose Config lives in the binary and is applied by the first allocation. Effective reports the options in force and Stats reports allocator statistics.

§Configuration timing

mimalloc creates its first arena at the first allocation, which happens before main on most targets. MiMalloc applies its Config on that allocation, so the purge policy is in force before any memory is freed and a reservation is the first arena threads search. A reservation requested through the environment (MIMALLOC_RESERVE_OS_MEMORY or MIMALLOC_RESERVE_HUGE_OS_PAGES) is made at load and comes first.

§Linking

The crate declares links = "mimalloc", so Cargo rejects a dependency graph in which another package, such as libmimalloc-sys, declares the same key.

Enable the local-dynamic-tls feature when the crate is built into a shared library that is loaded with dlopen. Without it, mimalloc uses initial-exec thread-local storage on targets other than musl. That storage comes from a static block the loader sizes at startup, and a library loaded later may not fit in it. The feature also has mimalloc free what memory it can when the library is unloaded.

§Page geometry

mimalloc carves arenas into slices and derives its page sizes from the slice. Slices form chunks, and chunks form the metadata groups of an arena. One group looks like this with the default slice on 64-bit targets:

one metadata group: 8 chunks, 256 MiB
        |<----- ARENA_MAX_OBJECT_SIZE: 7 chunks, 224 MiB ------>|
+-------+-------+-------+-------+-------+---------------+-------+
|chunk 0|chunk 1|chunk 2|chunk 3|chunk 4|page of its own|chunk 7|
+-------+-------+-------+-------+-------+---------------+-------+
|       |
|       '-------------------------------------------------------.
|                                                               |
+--------+--------+---------+-----------------+-----------------+
|  page  | small  | medium  |      large      |                 |
|metadata|1 slice |8 slices |    64 slices    |       ...       |
|  table | 64 KiB | 512 KiB |      4 MiB      |                 |
+--------+--------+---------+-----------------+-----------------+
         |<------>| ARENA_SLICE_SIZE
|<----------- ARENA_CHUNK_SIZE: 512 slices, 32 MiB ------------>|

Objects up to LARGE_OBJECT_LIMIT come from thread-local pages at the cost of a free-list operation. Larger objects get a page of their own, which costs an arena operation per allocation and free.

The crate’s large-objects-* features raise the limit at build time by scaling the slice (ARENA_SLICE_SIZE) and with it every page. The geometry applies to the whole process, so only binaries should enable them. The largest enabled feature wins.

§Large objects

Where an allocation lives depends on its size and alignment:

allocation (size, alignment)
  |
  +-- alignment >= metadata group (256 MiB) ---> null
  |
  +-- alignment > ARENA_SLICE_SIZE (64 KiB) ---> page of its own,
  |                                              mapped from the OS
  v
size <= LARGE_OBJECT_LIMIT (512 KiB)?
  | yes                             | no
  v                                 v
shared page of its size class       page of its own
  |                                 |
  '----------------+----------------'
                   v
page <= Config::arena_max_object_size?
  | yes                             | no
  v                                 v
arena -- no room, no new arena ---> OS mapping

A shared page outlives its objects for reuse. A page of its own is sized to the object rounded up (by as much as 4 MiB for the largest), and freeing the object releases it. The exception is a small object aligned beyond a slice and freed by another thread, which holds its page until the owning thread collects.

An arena keeps a released page until the Purge policy purges it. A page spanning several chunks (see ARENA_CHUNK_SIZE) needs that many consecutive free chunks, so a fragmented arena can make mimalloc reserve a new arena or, failing that, map the page from the operating system. Such a page is unmapped on release, so each allocation pays for system calls and fresh page faults.

Pages above Config::arena_max_object_size are mapped from the operating system the same way. The limit defaults to and is capped at ARENA_MAX_OBJECT_SIZE, the largest page an arena can hold. A limit below the largest shared page also maps the shared pages of larger size classes from the operating system. Those pages are still kept for reuse.

Alignments of a whole metadata group or more are refused with null, which a GlobalAlloc caller reports as an allocation error.

§Example

use commonware_allocator::mimalloc::{Config, MiMalloc, Purge, Reserve, Status};
use std::time::Duration;

#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc::new(Config {
    purge: Purge::After(Duration::from_secs(10)),
    reserve: Some(Reserve::pinned(2 << 30)),
    ..Config::DEFAULT
});

fn main() {
    assert!(commonware_allocator::mimalloc::is_global_allocator());
    println!("{:?}", Status::read());
}

Modules§

metrics
Allocator statistics exported as metrics.

Structs§

Config
Configuration applied by MiMalloc on its first allocation.
Effective
The options mimalloc is running with, whether set by a Config, the environment, or its defaults.
MiMalloc
mimalloc as a #[global_allocator] with a Config applied on the first allocation.
Reserve
Memory reserved from the operating system before the first allocation.
Stats
Process-wide allocator statistics, excluding allocations that C libraries make through the system allocator.
Status
Process-wide configuration state.

Enums§

Purge
When memory freed back to an arena is purged (decommitted) to the operating system.
ReserveOutcome
Outcome of the reservation requested by Config::reserve.

Constants§

ARENA_CHUNK_SIZE
Size of an arena chunk, the unit reservations are rounded down to (see Reserve::bytes). It is 512 slices, or 32 MiB at the default slice on 64-bit targets.
ARENA_MAX_OBJECT_SIZE
Largest page an arena can hold, and so the cap on Config::arena_max_object_size. mimalloc keeps page metadata at the start of every metadata group, so a page spans at most the group’s other chunks: seven chunks, or 224 MiB at the default slice on 64-bit targets.
ARENA_SLICE_SIZE
Size of an arena slice, the unit mimalloc carves arenas into and the smallest page size.
LARGE_OBJECT_LIMIT
Largest allocation served from a shared thread-local page. It is eight slices: 512 KiB by default, or the size named by an enabled large-objects-* feature. Larger objects get a page of their own, which costs an arena operation per allocation and free.
MAX_PURGE_DELAY
Longest purge delay Purge::After applies (about 24 days). mimalloc stores the delay in milliseconds in a C long, which is 32 bits on some platforms.

Functions§

good_size
Returns the block size mimalloc allocates for size bytes: its size class, or above the largest class, size rounded up to an OS page. The page serving such a block may be larger.
is_global_allocator
Returns whether the global allocator is this crate’s mimalloc, installed directly through MiMalloc or wrapped by another allocator. It checks a probe allocation against mimalloc’s heap region.
version
Returns the linked mimalloc version as major * 10000 + minor * 100 + patch.