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