1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
[]
= "sefer-alloc"
= "0.2.1"
= "2021"
# Matches the resocks5 ecosystem floor; the core is plain safe Rust and will
# build on much older toolchains, but we pin a known-good MSRV from day one.
= "1.88"
= "MIT OR Apache-2.0"
= "A safe-by-construction, 100% Rust memory toolkit (no C/C++ libraries — no libnuma/mimalloc/jemalloc/snmalloc/tcmalloc): a single-threaded handle store (Region<T>) and a drop-in #[global_allocator] (SeferAlloc) over one verified segment substrate, with #![forbid(unsafe_code)] at the top."
= "README.md"
= "https://github.com/PHPCraftdream/sefer-alloc"
= "https://github.com/PHPCraftdream/sefer-alloc"
= "https://docs.rs/sefer-alloc"
= ["allocator", "arena", "generational", "handle", "lock-free"]
= ["data-structures", "memory-management", "concurrency", "no-std"]
# Workspace root. `sefer-alloc` is the root crate AND the workspace; the
# extracted, independently-publishable building blocks live under `crates/`:
# - sefer-region (crates/region) — typed handle store (Handle<T>/Region<T>)
# #![forbid(unsafe_code)]
# - aligned-vmem (crates/vmem) — OS virtual-memory aperture
# #![allow(unsafe_code)], single-purpose, audit-friendly
# - numa-shim (crates/numa) — dependency-free NUMA detection + binding
# #![allow(unsafe_code)], single-purpose, audit-friendly
# - malloc-bench-rs (crates/malloc-bench) — portable GlobalAlloc benchmark harness
# standalone, not in sefer-alloc runtime dep tree
# Each is a real crates.io crate someone can `cargo add` on its own; sefer-alloc
# re-exports sefer-region's surface for backward compatibility, and delegates its
# OS-unsafe seams to aligned-vmem and numa-shim (thin interop wrappers).
[]
= ["crates/vmem", "crates/numa", "crates/malloc-bench", "crates/region"]
= "2"
[]
# `loom` is a build-time cfg (set via `RUSTFLAGS="--cfg loom"`) that gates the
# loom model-check harness. Declare it so `unexpected cfg` warnings are silent.
= { = "warn", = ['cfg(loom)'] }
[]
# The single-threaded core (`Region<T>` / `Handle<T>`) needs only `alloc` and
# builds under `no_std`. `std` (default-on) is required only for `SyncRegion`
# (it wraps `std::sync::RwLock`) and the concurrent/byte tiers. Disable default
# features to get the bare `no_std` + `alloc` core.
= ["std"]
= ["sefer-region/std"]
# Phase 3b: lock-free reads. The concurrent tier is opt-in; it requires `std`
# (its deps need it) and pulls in only `arc-swap` + `crossbeam-epoch` beyond the
# core.
# - 3b-I (`arc-swap`): RCU with page-granularity copy-on-write — zero `unsafe`
# of our own.
# - 3b-II (`crossbeam-epoch`): per-slot atomics + epoch reclamation — the single
# confined `unsafe` Hand organ (src/concurrent/hand.rs), the only module with
# `#![allow(unsafe_code)]`. See docs/PLAN.md.
= ["std", "dep:arc-swap", "dep:crossbeam-epoch"]
# Phase 7c: thread-per-core pinning. Additive over `experimental` — pulls in
# `core_affinity` (a safe wrapper over the OS affinity syscalls; zero `unsafe`
# of our own) and exposes an explicit shard-binding API plus a thread-per-core
# runner on `ShardedRegion`. The default and `experimental` builds do NOT pull
# `core_affinity`: this feature is purely opt-in. See docs/PLAN.md §7c.
= ["experimental", "dep:core_affinity"]
# Phase 8: the segment substrate + self-hosted metadata — the foundation of a
# real allocator (the Membrane Inversion). OS-backed SEGMENT-aligned spans via a
# confined `unsafe` aperture (`mmap`/`VirtualAlloc`, NO `std::alloc` anywhere),
# a self-hosted `SegmentTable` carved from the first primordial segment,
# per-segment `PageMap` / `BinTable`, and single-threaded
# `alloc`/`dealloc`/`realloc`/`alloc_zeroed`. The alloc path is provably
# reentrancy-free (M5): no `Vec`/`Box`/`HashSet`/`std::alloc`/`format!` is
# reachable — all metadata self-hosts in segment memory. This is single-threaded
# correctness-first; per-thread heaps + concurrency are Phases 9–10. Introduces
# two confined-`unsafe` modules — `alloc_core::os` (the OS aperture) and
# `alloc_core::node` (the intrusive free-list node seam, the generalized `hand`
# discipline) — so the crate switches to `deny(unsafe_code)` with this feature
# on; `unsafe` outside those modules stays a hard error. Requires `std` (the OS
# syscalls are std-gated). See `docs/ALLOC_PLAN.md` §5 P8 and §6.
= ["std", "dep:aligned-vmem"]
# Phase 9: per-thread heap + intrusive free lists -- the lock-free fast path.
# A thread allocates from its own heap with no lock and no atomic on the common
# case. Single-owner still (cross-thread free is Phase 10). Builds on the
# Phase 8 segment substrate (`alloc-core`). Requires `std` (TLS).
= ["alloc-core"]
# Phase 10: opt-in cross-thread free. Enables the lock-free Treiber stack
# (`ThreadFreeStack`) for cross-thread `dealloc`, owner stamping on segment
# headers, and the `dealloc_any_thread` entry point. Sound across thread death
# via abandonment-leak (segments and the Treiber head are leaked on `Heap::drop`
# so late cross-thread frees never touch unmapped memory). Full abandoned-heap
# adoption (reclaiming leaked segments) is Phase 11. Without this feature, the
# `alloc` feature provides the Phase 9 single-thread-only allocator (sound:
# single owner, no cross-thread refs). Requires `alloc` (and thus `alloc-core`).
= ["alloc"]
# Phase 11: the drop-in `GlobalAlloc` face -- `SeferAlloc`. Installs over the
# per-thread `Heap` substrate via the TLS `with_heap` binding (allocation-free
# lazy init). The `unsafe impl GlobalAlloc` is the single new `unsafe` surface
# of this phase -- it is the documented malloc-face seam (the trait is `unsafe`;
# the body delegates to the already-proven safe `Heap` entry points). Every
# entry point is no-panic (a panic in a global allocator aborts the process):
# checked branches return null on OOM, never panic/abort. Reentrancy-free (M5)
# is upheld structurally: no `Vec`/`Box`/`HashSet`/`std::alloc`/`format!` on the
# path -- the TLS init is the Phase 9 lazy allocation-free discipline. Requires
# `alloc` (and thus `alloc-core`). Add `alloc-xthread` alongside it for the
# multi-thread face (cross-thread `dealloc` via the Phase 10 Treiber stack).
= ["alloc"]
# Phase 35: M6 decommit — return the payload pages of an empty small segment to
# the OS (Win `VirtualFree MEM_DECOMMIT` / unix `madvise MADV_DONTNEED`) when its
# live-block count drops to zero, recommitting on first reuse. Additive over
# `alloc-core`: it adds an owner-only `live_count` field to the segment header
# (the field is present in EVERY build's layout so the byte layout is stable;
# its mutation/read is gated on this feature) and wires the decommit/reset/
# recommit policy into the own-thread free + cross-thread reclaim + carve paths,
# all under `#[cfg(feature = "alloc-decommit")]`. With this feature OFF the
# default build is byte-for-byte unchanged. NO new dependency and NO new `unsafe`
# site — the OS seam (`os::decommit_pages`/`recommit_pages`) already exists; the
# new bookkeeping is plain safe arithmetic through the `node` seam. M6 is
# decommit-safe WITHOUT crossbeam-epoch / M11 — see the safety argument at the
# decommit point in `alloc_core` and `docs/PHASE35_DECOMMIT_DESIGN.md` §1.
# Requires `alloc-core`. Default OFF.
= ["alloc-core"]
# Convenience alias: the recommended feature set for long-running multi-thread
# workloads (DBMS-style, async runtimes, anything that creates and frees many
# segments over time). Without `alloc-decommit` the per-process SegmentTable
# (1024 slots, append-only) is the hard ceiling on cumulative segment
# allocations; `alloc-decommit` lets empty segments be recycled (#60),
# lifting that ceiling. Add this feature for any long-running deployment.
= ["alloc-global"]
= ["alloc-global", "alloc-xthread", "alloc-decommit", "fastbin"]
# NUMA-aware segment reservation: when a new segment is reserved, pages are
# steered to the NUMA node of the calling thread
# (Linux: mbind(2), Windows: VirtualAllocExNuma).
# macOS: no-op (no public NUMA API). Default OFF. Requires `alloc-core`.
= ["alloc-core", "dep:numa-shim"]
[]
# Phase 1: typed handle store (Region<T> + Handle<T> + SyncRegion<T>), extracted
# from sefer-alloc into a standalone, independently-publishable crate alongside
# aligned-vmem / numa-shim / malloc-bench-rs. sefer-alloc re-exports its surface
# for backward compatibility — existing users of `use sefer_alloc::{Region,
# Handle, SyncRegion}` continue to work unchanged. New consumers who want ONLY
# the handle store (no allocator stack) should depend on `sefer-region` directly.
#
# `default-features = false`: sefer-region gates its std-dependent SyncRegion on
# its own `std` feature, which we re-enable via our own `std` feature so the
# no_std core compiles without it and the default build gets SyncRegion as before.
= { = "crates/region", = "0.1", = false }
# Phase 3b-I: atomic snapshot publication for lock-free reads. Optional — only
# the `experimental` concurrent tier pulls it in. Its internal `unsafe` is the
# dependency's, not ours; our crate stays `#![forbid(unsafe_code)]` with it on.
= { = "1", = true }
# Phase 3b-II: epoch-based memory reclamation for the per-slot-atomics lock-free
# read tier. Optional — only pulled in by `experimental`. Its `unsafe` is the
# dependency's; OUR `unsafe` is confined to src/concurrent/hand.rs (the single
# documented `#![allow(unsafe_code)]` module) and is wrapped in a safe-to-use
# `AtomicSlot<T>` API. See docs/PLAN.md.
= { = "0.9", = true }
# Phase 7c: OS thread-to-core pinning for the thread-per-core runner. A safe
# wrapper over `sched_setaffinity` (Linux) / `SetThreadAffinityMask` (Windows)
# / `thread_policy_set` (macOS). Optional — only the `pinning` feature pulls it
# in. Pinning is best-effort on some OSes; the runner still runs correctly if
# the host refuses the affinity. See docs/PLAN.md §7c.
= { = "0.8", = true }
# Phase 8 (alloc-core) OS aperture: SEGMENT-aligned anonymous virtual memory,
# page-level decommit/recommit. Extracted from sefer-alloc into a standalone
# crate so it can be used and published independently. Pulled in ONLY by the
# `alloc-core` feature (and every feature that depends on it).
= { = "crates/vmem", = "0.1", = true }
# Phase 78 (numa-aware) NUMA seam: dependency-free NUMA detection + binding,
# extracted from sefer-alloc into a standalone crate so it can be published
# independently. Pulled in ONLY by the `numa-aware` feature.
= { = "crates/numa", = "0.1", = ["vmem-integration"], = true }
[]
# Phase 2 dependency: the locality bench compares `Region` against raw
# `slotmap::SlotMap` / `DenseSlotMap`. slotmap is no longer a direct production
# dep (it lives in `sefer-region`), but the bench still needs it for the head-
# to-head numbers. Dev-only.
= "1"
# Phase 0 dependency: property-based differential testing against a reference
# model. This is the heart of the verification-first build — see docs/PLAN.md.
= "1"
# Task #52: tokio burn-in example — async runtime driving SeferAlloc as
# #[global_allocator]. Minimal feature set: rt-multi-thread (multi-thread
# scheduler), macros (#[tokio::main]), sync (mpsc channel), time (sleep /
# timeout / interval). No fs/net/signal/process — not needed for the harness.
= { = "1", = ["rt-multi-thread", "macros", "sync", "time"] }
# Phase 2: criterion benches that empirically choose the single-threaded
# backing container (SlotMap vs DenseSlotMap vs HashMap vs Vec<Box<T>>) across
# the iterate / lookup / churn axes. See docs/BENCHMARKS.md for the verdict.
= "0.5"
# Phase 9: mimalloc for head-to-head benchmarking of the per-thread heap's
# alloc/dealloc hot path vs a state-of-the-art allocator. Dev-only — NOT a
# runtime dependency.
= "0.1"
# Phase 3b-II: loom model-checks the publication PROTOCOL (generation/value
# ordering) of the epoch tier. loom and crossbeam-epoch do NOT compose (loom
# replaces the allocator and atomics), so the loom harness models a bare
# AtomicU32 generation + AtomicPtr value publication using loom's own atomics,
# NOT crossbeam. Reclamation correctness rests on the crate + miri, not loom.
# Built only under `cfg(loom)` (`RUSTFLAGS="--cfg loom"` / `--cfg loom`).
[]
= "0.7"
[[]]
= "locality"
= false
# Phase 7a: sharded write-scaling bench — `ShardedRegion` vs `SyncRegion` /
# `Arc<Mutex<Region>>` across thread counts. Quick criterion profile. Requires
# `experimental` (for `ShardedRegion`) and the default `std` (for `SyncRegion`).
[[]]
= "sharded_write"
= false
= ["experimental"]
# Phase 7c: pinned thread-per-core write bench — `ShardedRegion` writes with
# each worker pinned to a core AND explicitly bound to the matching shard, vs
# the unpinned 7a path. Quick criterion profile. Honest verdict: the win is
# workload-dependent (only manifests when the working set is cache-hot and
# shard-local); see the bench's module docs. Requires `pinning` (which brings
# `experimental` + `core_affinity`).
[[]]
= "pinned_write"
= false
= ["pinning"]
# Phase 9: per-thread heap alloc/dealloc hot path vs mimalloc and the system
# allocator. Quick criterion profile (sample_size 10, short warm-up). Honest
# verdict in docs/HEAP_BENCH.md.
[[]]
= "heap_alloc"
= false
= ["alloc"]
# Phase 11: `SeferAlloc` installed as `#[global_allocator]` vs `mimalloc` and
# the system allocator, exercising real Rust allocation patterns (Vec/Box churn).
# Quick criterion profile (sample_size 10, short warm-up). Honest verdict in
# docs/ALLOC_BENCH.md. Requires `alloc-global` (which brings `alloc`).
[[]]
= "global_alloc"
= false
= ["alloc-global"]
# Task #54: large-block and realloc-heavy profiling — SeferAlloc vs mimalloc
# vs System across three groups: single-shot alloc+free of multi-MiB blocks,
# geometric grow via GlobalAlloc::realloc, and realloc under adversarial
# neighbour pressure. Quick criterion profile. Requires `alloc-global`.
[[]]
= "large_realloc"
= false
= ["alloc-global"]
# Phase 11: minimal example installing `SeferAlloc` as the `#[global_allocator]`.
# Gated so the default build does not try to compile it without the feature.
[[]]
= "global_allocator"
= ["alloc-global"]
# Phase 13.7: multi-thread macro-benchmark (larson + mstress sweep, T=1/2/4).
# The file is `#![cfg]`-gated on alloc-global + alloc-xthread; without this
# registration `cargo build` / `cargo test` (without `--tests`) tries to compile
# it under the default feature set and fails with E0601 (no `main`).
[[]]
= "malloc_macro"
= ["alloc-global", "alloc-xthread"]
# Soak harness: high-load multi-thread stability test (32/64/128 threads, hours).
# NOT a throughput benchmark — a correctness + stability harness that detects
# panics, deadlocks, and accounting mismatches at scale.
# Run: `cargo run --release --example soak_xthread --features "alloc-global alloc-xthread"`
# Configure via SEFER_SOAK_THREADS=N and SEFER_SOAK_SECONDS=N (default: avail_par × 5s).
[[]]
= "soak_xthread"
= ["alloc-global", "alloc-xthread"]
# Task #53: RSS-probe harness — measures resident-set-size and in-flight block
# count under asymmetric cross-thread free pressure (producers allocate only;
# 1 consumer frees only). Designed to stress RemoteFreeRing and show the
# alloc-decommit recovery ratio. NOT a throughput benchmark.
# Run: `cargo run --release --example rss_probe --features "alloc-global alloc-xthread"`
# With decommit: add `alloc-decommit` to the features list.
# Tune via env: SEFER_RSS_PRODUCERS, SEFER_RSS_SECONDS, SEFER_RSS_LARGE_FRACTION.
[[]]
= "rss_probe"
= ["alloc-global", "alloc-xthread"]
# Task #62 §A-1: cross-thread free micro-bench — direct push→drain of the
# RemoteFreeRing via AllocCore test seams (dbg_push_to_ring +
# dbg_drain_all_rings), no tokio, no channels. Produces a low-noise flamegraph
# where criterion KDE overhead should be ≤ 50% (vs 84–85% in §1/§3 profiles).
# Documented in docs/PROFILE_FLAMEGRAPHS.md §6.
[[]]
= "heap_xthread"
= false
= ["alloc-core", "alloc-xthread"]
# Task #62 §A-2: synthetic async-like allocation pattern — mixed-size
# alloc/dealloc/realloc (row buffers, accumulators, medium blobs) via
# SeferAlloc::GlobalAlloc called directly. No tokio runtime noise, no
# channels, no spawns. Single-threaded DBMS-pipeline simulation.
# Documented in docs/PROFILE_FLAMEGRAPHS.md §6.
[[]]
= "heap_async_pattern"
= false
= ["alloc-global"]
# Task #52: tokio async burn-in — SeferAlloc as #[global_allocator] under a
# real tokio multi-thread runtime with a DBMS-pipeline-like async workload.
# All tokio-internal allocations also go through SeferAlloc.
# Run: `SEFER_BURNIN_SECONDS=10 SEFER_TOKIO_WORKERS=4 SEFER_BURNIN_TASKS=256 \
# cargo run --release --example tokio_burn_in \
# --features "alloc-global alloc-xthread"`
[[]]
= "tokio_burn_in"
= ["alloc-global", "alloc-xthread"]
# ── §0 — Flamegraph profiling: debug-info is OPT-IN, NOT persistent ──────────
#
# Persistent `debug = true` in [profile.release] / [profile.bench] slows linking
# and inflates `.rlib` / rmeta sizes noticeably for every `cargo build --release`
# (even non-profiling runs). We therefore do NOT add a [profile.*] section.
#
# Instead: pass the env var once, at profiling time:
#
# CARGO_PROFILE_BENCH_DEBUG=line-tables-only \
# cargo build --bench heap_xthread \
# --features "alloc-core alloc-xthread" --profile bench \
# CARGO_TARGET_DIR=/tmp/sefer-fg-v3a
#
# CARGO_PROFILE_RELEASE_DEBUG=line-tables-only \
# cargo build --release --example malloc_macro \
# --features "alloc-global alloc-xthread" \
# CARGO_TARGET_DIR=/tmp/sefer-fg-v3a
#
# `line-tables-only` gives adequate function names + file/line for flamegraphs
# without the full DWARF variable info that `true`/`full` would add.
# Documented in docs/PROFILE_FLAMEGRAPHS.md §0.