|
| 1 | +[](https://crates.io/crates/rusty_alloc) |
| 2 | +[](https://docs.rs/rusty_alloc) |
| 3 | +[](https://github.com/remade-with-rust/rusty_alloc/actions) |
| 4 | +[](LICENSE) |
| 5 | +[](https://github.com/remade-with-rust) |
| 6 | + |
1 | 7 | # rusty_alloc |
2 | 8 |
|
3 | | -A pure-Rust remake of [mimalloc](https://github.com/microsoft/mimalloc) — the |
4 | | -v2.4.5 architecture (32 MiB segments, free-list-sharded pages, lock-free |
5 | | -cross-thread frees), rebuilt from the design rather than transliterated from |
6 | | -the C. |
| 9 | +A ground-up, pure-**Rust** general-purpose **allocator** — the mimalloc v2.4.5 |
| 10 | +architecture rebuilt from the design rather than transliterated from the C. No |
| 11 | +C in the dependency tree, permissive licence, and a safety property upstream |
| 12 | +does not offer. |
| 13 | + |
| 14 | +## ⚡ The headline |
7 | 15 |
|
8 | | -Part of the **[remade-with-rust](https://github.com/remade-with-rust)** |
9 | | -portfolio, and built to its principles: memory safety first, no C in the |
10 | | -product, general primitives over one-off fixes, and **measured, not vibed** — |
11 | | -which is why the performance section below spends as much space on what we |
12 | | -*haven't* established as on what we have. |
| 16 | +- **Parity with mimalloc on instructions retired**, and ~16% fewer than glibc, |
| 17 | + on real programs under `LD_PRELOAD`. |
| 18 | +- **A double free aborts instead of corrupting.** Upstream mimalloc accepts it |
| 19 | + silently in release builds; we detect it on both the local and the |
| 20 | + cross-thread path and abort, for a measured ~0.4%. |
| 21 | +- **~150 of mimalloc's ~157 `mi_*` entry points**, gated against the C |
| 22 | + implementation as a differential oracle on every change. |
| 23 | +- **Runs on WebAssembly** with no C toolchain and no emscripten. |
13 | 24 |
|
14 | 25 | > **Status: `0.1.0-alpha.1`.** The allocator is complete and gated. The |
15 | | -> performance *evidence* is not. Read |
16 | | -> [What is and isn't measured](#what-is-and-isnt-measured) before depending on |
17 | | -> this — the short version is that we count instructions, not seconds, and we |
18 | | -> make no speed claim. |
| 26 | +> performance *evidence* is not. See |
| 27 | +> [What is and isn't measured](#what-is-and-isnt-measured) — we count |
| 28 | +> instructions, not seconds, and make no speed claim. |
19 | 29 |
|
20 | | -## What it is |
| 30 | +## Performance (this machine, deterministic) |
21 | 31 |
|
22 | | -- **~150 of mimalloc's ~157 `mi_*` entry points**, semantics-for-semantics, |
23 | | - gated against the C implementation as a differential oracle. |
24 | | -- **A double free is detected and aborted, not silently accepted.** Upstream |
25 | | - mimalloc does not detect this in release builds. We do, at a measured cost of |
26 | | - ~0.4% on perl and ~0.2% on sqlite. Silently handing the same block to two |
27 | | - owners is the exact failure this project exists to prevent, so the check is |
28 | | - the point rather than an overhead. |
29 | | -- **No C anywhere in the product.** The C mimalloc in this repo is a |
30 | | - development-only oracle: never a dependency, never published. |
31 | | -- **Runs on WebAssembly** (`wasm32-unknown-unknown`) with no C toolchain and no |
32 | | - emscripten — `memory.grow`, single linear memory, gated by a self-test that |
33 | | - executes inside a real VM. |
| 32 | +Instructions retired under callgrind, x86-64 Linux, real programs via |
| 33 | +`LD_PRELOAD`. Repeats to 4–6 significant figures. |
34 | 34 |
|
35 | | -## What is and isn't measured |
| 35 | +| workload | vs mimalloc | vs glibc | |
| 36 | +|---|---:|---:| |
| 37 | +| lua | **0.99** | 0.84 | |
| 38 | +| perl | **1.01** | 0.83 | |
| 39 | +| sqlite | **1.00** | 1.00 | |
36 | 40 |
|
37 | | -### Measured — instructions retired |
| 41 | +**Method.** `bench/icount-arms.sh`. Instruction counts, not wall-clock — chosen |
| 42 | +deliberately, because this machine's timing noise floor is wider than the |
| 43 | +effect (see below). Counts are immune to scheduler, thermal and load artifacts; |
| 44 | +they are also *not* a measure of time. |
38 | 45 |
|
39 | | -Deterministic (callgrind), x86-64 Linux, `LD_PRELOAD` against the real programs: |
| 46 | +### What is and isn't measured |
40 | 47 |
|
41 | | -| workload | vs mimalloc | vs glibc | |
42 | | -|---|---:|---:| |
43 | | -| lua | 0.99 | 0.84 | |
44 | | -| perl | 1.01 | 0.83 | |
45 | | -| sqlite | 1.00 | 1.00 | |
| 48 | +**Wall-clock: measured, and it cannot resolve the difference.** |
| 49 | +`bench/wallclock.sh` runs pinned, ABBA-interleaved, N=31, microsecond timer, |
| 50 | +with a **null arm** — the same allocator compared against itself: |
| 51 | + |
| 52 | +| arm | median ratio | |
| 53 | +|---|---:| |
| 54 | +| **null (rusty_alloc vs ITSELF)** | **1.0117** | |
| 55 | +| perl, rusty_alloc vs mimalloc | 1.0009 | |
| 56 | +| sqlite, rusty_alloc vs mimalloc | 1.0091 | |
46 | 57 |
|
47 | | -Parity with mimalloc; roughly 16% fewer instructions than glibc. These repeat to |
48 | | -4–6 significant figures, so they are claims we can defend. |
| 58 | +The null arm is **1.17%** — wider than either effect. The honest reading is |
| 59 | +**"at parity, below measurement resolution"**. Reproduce on a quiet box with |
| 60 | +`N=31 bash bench/wallclock.sh`. |
49 | 61 |
|
50 | | -### NOT measured — and therefore not claimed |
| 62 | +**Not measured, and therefore not claimed:** |
51 | 63 |
|
52 | | -- **Wall-clock time.** See [Timing](#timing) below. We have run it; this machine |
53 | | - cannot resolve a difference this small, and we are not going to launder that |
54 | | - into a speed claim. |
55 | | -- **The full mimalloc-bench corpus.** Three workloads, not the suite. The |
| 64 | +- The full mimalloc-bench corpus. Three workloads, not the suite — the |
56 | 65 | project's own v1 gate (geomean within 10%, no bench >25% behind, RSS within |
57 | 66 | 15%) is **not yet demonstrated**. |
58 | | -- **RSS.** No systematic footprint sweep. |
59 | | -- **aarch64.** The code paths exist and compile. They have never been executed. |
| 67 | +- RSS. No systematic footprint sweep. |
| 68 | +- aarch64. Code paths exist and compile; they have never been executed. |
60 | 69 |
|
61 | 70 | There is no "faster than mimalloc" claim anywhere in this repository, because |
62 | 71 | the evidence for one does not exist yet. |
63 | 72 |
|
64 | | -### Timing |
| 73 | +## What is this? |
65 | 74 |
|
66 | | -`bench/wallclock.sh` runs pinned, ABBA-interleaved, microsecond-resolution |
67 | | -comparisons at N=31 with a **null arm** — the same allocator against itself. |
68 | | -The null arm is the floor: any delta smaller than it is noise, not a result. |
| 75 | +A reimplementation, not a binding. There are excellent mimalloc *bindings* for |
| 76 | +Rust; this is not one of them. Every line of the allocator is Rust, the C |
| 77 | +mimalloc in this repository is a development-only differential oracle, and it |
| 78 | +is never a dependency and never published. |
69 | 79 |
|
70 | | -Measured on the development machine (N=31, pinned, microsecond timer): |
| 80 | +`unsafe` is confined to the places an allocator genuinely needs it — the OS |
| 81 | +primitive layer, page and segment metadata, and the lock-free cross-thread |
| 82 | +protocol — with a stated invariant on every block, `unsafe_op_in_unsafe_fn` |
| 83 | +denied and `undocumented_unsafe_blocks` denied workspace-wide. |
71 | 84 |
|
72 | | -| arm | median ratio | |
73 | | -|---|---:| |
74 | | -| **null (rusty_alloc vs itself)** | **1.0117** | |
75 | | -| perl, rusty_alloc vs mimalloc | 1.0009 | |
76 | | -| sqlite, rusty_alloc vs mimalloc | 1.0091 | |
| 85 | +## The Remade With Rust ecosystem |
77 | 86 |
|
78 | | -The null arm is **1.17%** — wider than either measured effect. The same |
79 | | -allocator compared against itself differs by more than the difference we are |
80 | | -trying to detect, so the only honest reading is **"at parity, below measurement |
81 | | -resolution"**. That is not a hedge; it is what the instrument supports. |
82 | | -Reproduce it on a quiet box: |
| 87 | +| project | what | |
| 88 | +|---|---| |
| 89 | +| [rusty_alloc](https://github.com/remade-with-rust/rusty_alloc) | this — pure-Rust general-purpose allocator | |
| 90 | +| [rusty_h264](https://github.com/remade-with-rust/rusty_h264) | pure-Rust H.264 encoder and decoder | |
| 91 | +| [Mata Network](https://www.mata.network/) | the parent organisation | |
83 | 92 |
|
84 | | -```sh |
85 | | -N=31 bash bench/wallclock.sh |
| 93 | +## Features |
| 94 | + |
| 95 | +**Allocator core** — 32 MiB segments sliced into 64 KiB spans, free-list-sharded |
| 96 | +pages, the loom-verified four-state cross-thread free protocol, thread |
| 97 | +abandonment and adoption, first-class heaps, arenas, huge allocations, aligned |
| 98 | +allocation with interior-pointer recovery, and the full realloc family. |
| 99 | + |
| 100 | +**Safety** — double-free detection on both the owner and cross-thread paths; |
| 101 | +Miri-clean; a 640-thread churn probe; `debug_checks` for full invariant |
| 102 | +validation; `secure` for guard pages, encrypted free lists and guarded-object |
| 103 | +sampling. |
| 104 | + |
| 105 | +**Portability** — x86-64 and aarch64, Linux and Windows, plus |
| 106 | +`wasm32-unknown-unknown` via `memory.grow`. |
| 107 | + |
| 108 | +## Install |
| 109 | + |
| 110 | +```toml |
| 111 | +[dependencies] |
| 112 | +rusty_alloc-api = "0.1.0-alpha.1" |
86 | 113 | ``` |
87 | 114 |
|
88 | | -## Layout |
| 115 | +| crate | docs | what | |
| 116 | +|---|---|---| |
| 117 | +| [`rusty_alloc`](https://crates.io/crates/rusty_alloc) | [docs.rs](https://docs.rs/rusty_alloc) | allocator core | |
| 118 | +| [`rusty_alloc-api`](https://crates.io/crates/rusty_alloc-api) | [docs.rs](https://docs.rs/rusty_alloc-api) | safe Rust surface — start here | |
89 | 119 |
|
90 | | -| path | what | |
91 | | -|---|---| |
92 | | -| `crates/rusty_alloc` | allocator core — **published** | |
93 | | -| `crates/rusty_alloc_api` | safe Rust surface (`GlobalAlloc`, `Heap`, `Allocator`) — **published** | |
94 | | -| `crates/rusty_alloc_ffi` | `mi_*`-compatible C ABI (cdylib + staticlib) | |
95 | | -| `crates/rusty_alloc_override` | `malloc`/`free` interposition cdylib (LD_PRELOAD arm) | |
96 | | -| `crates/rusty_alloc_bench` | Tier-B harness + trace record/replay | |
97 | | -| `crates/rusty_alloc_wasm` | wasm self-test fixture | |
98 | | -| `oracle/mimalloc` | C mimalloc @ v2.4.5 — **dev-only oracle**, never a runtime dep | |
99 | | -| `corpus/mimalloc-bench` | the 1:1 benchmark corpus (submodule) | |
100 | | -| `docs/LEDGER.md` | one entry per milestone: numbers, method, and every revert | |
101 | | -| `docs/plans/rusty_alloc_v1.md` | plan of record — API inventory, gate ladder, roadmap | |
102 | | - |
103 | | -Only the two crates marked **published** go to crates.io. Everything else is |
104 | | -`publish = false`: harnesses, fixtures and native artifacts, not libraries. |
| 120 | +The FFI, LD_PRELOAD override, bench and wasm crates are `publish = false`: |
| 121 | +harnesses, fixtures and native artifacts, not libraries. |
105 | 122 |
|
106 | | -## Gates |
| 123 | +## Quick start |
| 124 | + |
| 125 | +```rust |
| 126 | +use rusty_alloc_api::RustyAlloc; |
107 | 127 |
|
108 | | -Every change runs: Windows + Linux test suites (all features), `clippy -D |
109 | | -warnings`, Miri, a 640-thread churn probe, a wasm VM self-test, and a |
110 | | -deterministic instruction-count A/B against the C oracle. `docs/LEDGER.md` |
111 | | -records what each milestone measured — **including the changes that were |
112 | | -reverted for being flat or slower**, which is most of them. |
| 128 | +#[global_allocator] |
| 129 | +static ALLOC: RustyAlloc = RustyAlloc; |
113 | 130 |
|
114 | | -## Building the oracle (contributors) |
| 131 | +fn main() { |
| 132 | + let v: Vec<u64> = (0..1_000).collect(); |
| 133 | + println!("{}", v.iter().sum::<u64>()); |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +## Architecture |
| 138 | + |
| 139 | +``` |
| 140 | +crates/rusty_alloc allocator core (published) |
| 141 | +crates/rusty_alloc_api safe Rust surface (published) |
| 142 | +crates/rusty_alloc_ffi mi_*-compatible C ABI |
| 143 | +crates/rusty_alloc_override malloc/free interposition cdylib |
| 144 | +crates/rusty_alloc_bench Tier-B harness + trace record/replay |
| 145 | +crates/rusty_alloc_wasm wasm self-test fixture |
| 146 | +oracle/mimalloc C mimalloc @ v2.4.5 — dev-only oracle |
| 147 | +corpus/mimalloc-bench the 1:1 benchmark corpus |
| 148 | +docs/LEDGER.md one entry per milestone: numbers, method, reverts |
| 149 | +docs/plans/rusty_alloc_v1.md plan of record — API inventory, gate ladder |
| 150 | +``` |
| 151 | + |
| 152 | +## Benchmarking |
115 | 153 |
|
116 | 154 | ```sh |
117 | 155 | git submodule update --init oracle/mimalloc corpus/mimalloc-bench |
118 | | -bash oracle/build.sh # builds mi / dmi / smi arms |
| 156 | +bash oracle/build.sh # build the C oracle arms |
| 157 | +bash bench/icount-arms.sh # deterministic instruction A/B |
| 158 | +N=31 bash bench/wallclock.sh # wall-clock, with a null arm |
| 159 | +bash bench/opscan.sh # per-operation scan vs mimalloc |
119 | 160 | ``` |
120 | 161 |
|
| 162 | +## Gates |
| 163 | + |
| 164 | +Every change runs Windows + Linux suites (all features), `clippy -D warnings`, |
| 165 | +Miri, a 640-thread churn probe, a wasm VM self-test, and a deterministic |
| 166 | +instruction A/B against the C oracle. [`docs/LEDGER.md`](docs/LEDGER.md) |
| 167 | +records what each milestone measured — **including the changes reverted for |
| 168 | +being flat or slower**, which is most of them. |
| 169 | + |
| 170 | +## Platform support |
| 171 | + |
| 172 | +| target | status | |
| 173 | +|---|---| |
| 174 | +| x86-64 Linux | tested; the LD_PRELOAD and measurement path | |
| 175 | +| x86-64 Windows | tested | |
| 176 | +| aarch64 | compiles; **never executed** | |
| 177 | +| wasm32-unknown-unknown | tested in a VM self-test | |
| 178 | + |
121 | 179 | ## License |
122 | 180 |
|
123 | | -MIT — see [LICENSE](LICENSE). Vendored dev-only dependencies keep their own |
124 | | -licenses; none of them ship. |
| 181 | +MIT — see [LICENSE](LICENSE). No GPL or LGPL anywhere in the tree. The vendored |
| 182 | +oracle and benchmark corpus are development-only, keep their own licences, and |
| 183 | +never ship. |
| 184 | + |
| 185 | +## About Mata Network |
| 186 | + |
| 187 | +rusty_alloc is part of the [remade-with-rust](https://github.com/remade-with-rust) |
| 188 | +portfolio from [Mata Network](https://www.mata.network/): foundational software |
| 189 | +rebuilt in Rust, memory-safe by construction, measured rather than asserted. |
0 commit comments