mimalloc2/
lib.rs

1#![no_std]
2
3extern crate libc;
4extern crate libmimalloc_sys as ffi;
5
6use core::ffi::c_void;
7use core::alloc::{GlobalAlloc, Layout};
8
9use libmimalloc_sys::*;
10
11
12// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
13#[cfg(all(any(
14    target_arch = "x86",
15    target_arch = "arm",
16    target_arch = "mips",
17    target_arch = "powerpc",
18    target_arch = "powerpc64",
19    target_arch = "asmjs",
20    target_arch = "wasm32"
21)))]
22const MIN_ALIGN: usize = 8;
23
24#[cfg(all(any(
25    target_arch = "x86_64",
26    target_arch = "aarch64",
27    target_arch = "mips64",
28    target_arch = "s390x",
29    target_arch = "sparc64"
30)))]
31const MIN_ALIGN: usize = 16;
32
33/// Drop-in mimalloc global allocator.
34///
35/// ## Usage
36/// ```rust,ignore
37/// use mimalloc::MiMalloc;
38///
39/// #[global_allocator]
40/// static GLOBAL: MiMalloc = MiMalloc;
41/// ```
42pub struct MiMalloc;
43
44#[cfg(target_os = "linux")]
45const ONE_G: usize = 1073741824; // 1GB
46
47#[cfg(target_os = "linux")]
48#[inline]
49fn hugepage_align(layout: Layout) -> usize {
50    let mut len_bytes = layout.size();
51    // NOTE: 确保尺寸大小向 1G 对齐。
52    let rem = len_bytes % ONE_G;
53    if rem != 0 {
54        len_bytes += ONE_G - rem;
55    }
56    debug_assert_eq!(len_bytes % ONE_G, 0);
57
58    len_bytes
59}
60
61#[cfg(target_os = "linux")]
62#[inline]
63unsafe fn hugepage_alloc_zeroed(layout: Layout) -> *mut u8 {
64    // Example Code:
65    // https://github.com/torvalds/linux/blob/master/tools/testing/selftests/vm/map_hugetlb.c
66    let len_bytes = hugepage_align(layout);
67    
68    // NOTE: flag `MAP_ANONYMOUS` 指示返回的内存区域会使用 0 来填充。
69    const FLAGS: libc::c_int         = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_HUGETLB;
70    const PROTECT_FLAGS: libc::c_int = libc::PROT_READ | libc::PROT_WRITE; // protect_flags
71    
72    let addr = core::ptr::null_mut();
73    let ptr: *mut libc::c_void = libc::mmap(addr, len_bytes, PROTECT_FLAGS, FLAGS, -1, 0);
74    if ptr == libc::MAP_FAILED {
75        // NOTE: 巨页分配失败,使用下面的传统的分配逻辑。
76        //       如果巨页数量足够多的话,没必要记录错误信息,
77        //       以防止后续巨页分配尝试。
78        return core::ptr::null_mut();
79    } else {
80        return ptr as *mut u8;
81    }
82}
83
84#[cfg(target_os = "linux")]
85#[inline]
86unsafe fn hugepage_dealloc(ptr: *mut u8, layout: Layout) -> libc::c_int {
87    let len_bytes = hugepage_align(layout);
88    // NOTE: `0` is success, other is failed.
89    libc::munmap(ptr as *mut libc::c_void, len_bytes)
90}
91
92unsafe impl GlobalAlloc for MiMalloc {
93    #[inline]
94    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
95        #[cfg(target_os = "linux")]
96        {
97            if layout.size() >= ONE_G {
98                let ptr = hugepage_alloc_zeroed(layout);
99                if !ptr.is_null() {
100                    return ptr;
101                }
102            }
103        }
104
105        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
106            mi_malloc(layout.size()) as *mut u8
107        } else {
108            #[cfg(target_os = "macos")]
109            if layout.align() > (1 << 31) {
110                return core::ptr::null_mut();
111            }
112
113            mi_malloc_aligned(layout.size(), layout.align()) as *mut u8
114        }
115    }
116
117    #[inline]
118    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
119        #[cfg(target_os = "linux")]
120        {
121            if layout.size() >= ONE_G {
122                let ptr = hugepage_alloc_zeroed(layout);
123                if !ptr.is_null() {
124                    return ptr;
125                }
126            }
127        }
128
129        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
130            mi_zalloc(layout.size()) as *mut u8
131        } else {
132            #[cfg(target_os = "macos")]
133            if layout.align() > (1 << 31) {
134                return core::ptr::null_mut();
135            }
136
137            mi_zalloc_aligned(layout.size(), layout.align()) as *mut u8
138        }
139    }
140
141    #[inline]
142    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
143        #[cfg(target_os = "linux")]
144        {
145            if layout.size() >= ONE_G {
146                let ret = hugepage_dealloc(ptr, layout);
147                if ret == 0 {
148                    return ();
149                }
150            }
151        }
152
153        mi_free(ptr as *mut c_void);
154    }
155
156    #[inline]
157    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
158        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
159            mi_realloc(ptr as *mut c_void, new_size) as *mut u8
160        } else {
161            mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align()) as *mut u8
162        }
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn it_frees_allocated_memory() {
172        unsafe {
173            let layout = Layout::from_size_align(8, 8).unwrap();
174            let alloc = MiMalloc;
175
176            let ptr = alloc.alloc(layout);
177            alloc.dealloc(ptr, layout);
178        }
179    }
180
181    #[test]
182    fn it_frees_allocated_big_memory() {
183        unsafe {
184            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
185            let alloc = MiMalloc;
186
187            let ptr = alloc.alloc(layout);
188            alloc.dealloc(ptr, layout);
189        }
190    }
191
192    #[test]
193    fn it_frees_zero_allocated_memory() {
194        unsafe {
195            let layout = Layout::from_size_align(8, 8).unwrap();
196            let alloc = MiMalloc;
197
198            let ptr = alloc.alloc_zeroed(layout);
199            alloc.dealloc(ptr, layout);
200        }
201    }
202
203    #[test]
204    fn it_frees_zero_allocated_big_memory() {
205        unsafe {
206            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
207            let alloc = MiMalloc;
208
209            let ptr = alloc.alloc_zeroed(layout);
210            alloc.dealloc(ptr, layout);
211        }
212    }
213
214    #[test]
215    fn it_frees_reallocated_memory() {
216        unsafe {
217            let layout = Layout::from_size_align(8, 8).unwrap();
218            let alloc = MiMalloc;
219
220            let ptr = alloc.alloc(layout);
221            let ptr = alloc.realloc(ptr, layout, 16);
222            alloc.dealloc(ptr, layout);
223        }
224    }
225
226    #[test]
227    fn it_frees_reallocated_big_memory() {
228        unsafe {
229            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
230            let alloc = MiMalloc;
231
232            let ptr = alloc.alloc(layout);
233            let ptr = alloc.realloc(ptr, layout, 2 << 20);
234            alloc.dealloc(ptr, layout);
235        }
236    }
237}