Skip to main content

rustfs_mimalloc/
lib.rs

1//! High-performance [mimalloc](https://github.com/microsoft/mimalloc) V3 global allocator.
2//!
3//! ```rust
4//! use rustfs_mimalloc::MiMalloc;
5//!
6//! #[global_allocator]
7//! static GLOBAL: MiMalloc = MiMalloc;
8//! ```
9
10mod api;
11mod ffi;
12
13pub mod heap;
14
15pub use api::{ProcessInfo, set_current_thread_in_threadpool};
16
17use core::alloc::{GlobalAlloc, Layout};
18use core::ffi::c_void;
19
20/// The mimalloc global allocator.
21///
22/// Drop-in replacement for the system allocator. Always uses `mi_malloc_aligned`
23/// internally to guarantee correct alignment for all layouts.
24#[derive(Debug, Clone, Copy, Default)]
25pub struct MiMalloc;
26
27// ── GlobalAlloc: hot path — zero indirection ────────────────────────────────
28
29unsafe impl GlobalAlloc for MiMalloc {
30    #[inline(always)]
31    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
32        unsafe { rustfs_mimalloc_sys::mi_malloc_aligned(layout.size(), layout.align()) as *mut u8 }
33    }
34
35    #[inline(always)]
36    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
37        unsafe { rustfs_mimalloc_sys::mi_zalloc_aligned(layout.size(), layout.align()) as *mut u8 }
38    }
39
40    #[inline(always)]
41    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
42        unsafe { rustfs_mimalloc_sys::mi_free(ptr as *mut c_void) };
43    }
44
45    #[inline(always)]
46    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
47        unsafe {
48            rustfs_mimalloc_sys::mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align())
49                as *mut u8
50        }
51    }
52}
53
54// ── Tests ───────────────────────────────────────────────────────────────────
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use std::alloc::{GlobalAlloc, Layout};
60
61    #[global_allocator]
62    static GLOBAL: MiMalloc = MiMalloc;
63
64    #[test]
65    fn alloc_dealloc_roundtrip() {
66        let layout = Layout::from_size_align(64, 8).unwrap();
67        unsafe {
68            let ptr = GLOBAL.alloc(layout);
69            assert!(!ptr.is_null());
70            GLOBAL.dealloc(ptr, layout);
71        }
72    }
73
74    #[test]
75    fn alloc_zeroed_is_zero() {
76        let layout = Layout::from_size_align(256, 16).unwrap();
77        unsafe {
78            let ptr = GLOBAL.alloc_zeroed(layout);
79            assert!(!ptr.is_null());
80            assert!((0..256).all(|i| *ptr.add(i) == 0));
81            GLOBAL.dealloc(ptr, layout);
82        }
83    }
84
85    #[test]
86    fn realloc_preserves_content() {
87        let layout = Layout::from_size_align(64, 8).unwrap();
88        unsafe {
89            let ptr = GLOBAL.alloc(layout);
90            core::ptr::write_bytes(ptr, 0xAB, 64);
91            let new_ptr = GLOBAL.realloc(ptr, layout, 128);
92            assert!(!new_ptr.is_null());
93            assert!((0..64).all(|i| *new_ptr.add(i) == 0xAB));
94            GLOBAL.dealloc(new_ptr, Layout::from_size_align(128, 8).unwrap());
95        }
96    }
97
98    #[test]
99    fn alignment_respected() {
100        for align_pow in 0..=12 {
101            let align = 1usize << align_pow;
102            let layout = Layout::from_size_align(64, align).unwrap();
103            unsafe {
104                let ptr = GLOBAL.alloc(layout);
105                assert!(!ptr.is_null(), "align={align}");
106                assert_eq!(ptr as usize % align, 0, "align={align}");
107                GLOBAL.dealloc(ptr, layout);
108            }
109        }
110    }
111
112    #[test]
113    fn large_alignment_page_size() {
114        // Regression: mimalloc_rust#87 — 4096 alignment crashed
115        let layout = Layout::from_size_align(4096, 4096).unwrap();
116        for _ in 0..100 {
117            unsafe {
118                let ptr = GLOBAL.alloc(layout);
119                assert!(!ptr.is_null());
120                assert_eq!(ptr as usize % 4096, 0);
121                GLOBAL.dealloc(ptr, layout);
122            }
123        }
124    }
125
126    #[test]
127    fn vec_push_smoke() {
128        let v: Vec<i32> = (0..10_000).collect();
129        assert_eq!(v.len(), 10_000);
130        assert_eq!(v[9999], 9999);
131    }
132
133    #[test]
134    fn box_smoke() {
135        let b = Box::new(42u64);
136        assert_eq!(*b, 42);
137    }
138
139    #[test]
140    fn concurrent_alloc_free() {
141        use std::sync::{Arc, Barrier};
142        use std::thread;
143
144        let n = 8;
145        let barrier = Arc::new(Barrier::new(n));
146        let handles: Vec<_> = (0..n)
147            .map(|_| {
148                let b = barrier.clone();
149                thread::spawn(move || {
150                    b.wait();
151                    let layout = Layout::from_size_align(128, 16).unwrap();
152                    for _ in 0..1000 {
153                        unsafe {
154                            let ptr = GLOBAL.alloc(layout);
155                            assert!(!ptr.is_null());
156                            core::ptr::write_bytes(ptr, 0xCD, 128);
157                            GLOBAL.dealloc(ptr, layout);
158                        }
159                    }
160                })
161            })
162            .collect();
163        for h in handles {
164            h.join().unwrap();
165        }
166    }
167}