-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Native libraries use mimalloc as global allocator (#1249)
This PR moves the global allocator to MS's [mimalloc](https://github.com/microsoft/mimalloc). The performance gains are significant, and it adds `98KB` to the Python wheel and `215KB` to the native dylib used in the python wheel. This dependency adds `2.5s` to the build time. We can investigate wasm usage in the future, but this would likely require having a wasi-libc installation available for the build process to give mimalloc a wasi compatible libc to link against. Benchmarks on Apple M1 Max 64GB ``` Teleport evaluation time: [44.024 µs 44.043 µs 44.063 µs] change: [-21.789% -21.595% -21.420%] (p = 0.00 < 0.05) Performance has improved. Deutsch-Jozsa evaluation time: [3.0302 ms 3.0336 ms 3.0375 ms] change: [-23.878% -23.671% -23.493%] (p = 0.00 < 0.05) Performance has improved. Large file parity evaluation time: [28.956 ms 28.972 ms 28.989 ms] change: [-1.9178% -1.8288% -1.7431%] (p = 0.00 < 0.05) Performance has improved. Array append evaluation time: [306.58 µs 306.78 µs 307.02 µs] change: [-14.312% -14.068% -13.881%] (p = 0.00 < 0.05) Performance has improved. Array update evaluation time: [304.71 µs 304.84 µs 305.00 µs] change: [-10.777% -10.651% -10.537%] (p = 0.00 < 0.05) Performance has improved. Array literal evaluation time: [144.03 µs 144.32 µs 144.68 µs] change: [-39.800% -39.455% -39.171%] (p = 0.00 < 0.05) Performance has improved. Large nested iteration time: [30.418 ms 30.446 ms 30.476 ms] change: [-11.368% -11.252% -11.131%] (p = 0.00 < 0.05) Performance has improved. Large input file time: [15.869 ms 15.883 ms 15.900 ms] change: [-30.391% -30.267% -30.147%] (p = 0.00 < 0.05) Performance has improved. Standard library time: [9.5701 ms 9.5801 ms 9.5919 ms] change: [-28.527% -28.371% -28.224%] (p = 0.00 < 0.05) Performance has improved. ```
- Loading branch information
Showing
23 changed files
with
365 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,5 @@ __pycache__/ | |
/fuzz/artifacts | ||
/fuzz/coverage | ||
/fuzz/Cargo.lock | ||
.mypy_cache/ | ||
.pytest_cache/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "allocator" | ||
authors.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
edition.workspace = true | ||
license.workspace = true | ||
version.workspace = true | ||
|
||
[dependencies] | ||
mimalloc-sys = { path = "./mimalloc-sys" } | ||
|
||
[lints] | ||
workspace = true | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
|
||
cmake_minimum_required(VERSION 3.10.0) | ||
|
||
|
||
project(allocator_external) | ||
include(ExternalProject) | ||
|
||
ExternalProject_Add(mimalloc | ||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git | ||
GIT_TAG $ENV{ALLOCATOR_MIMALLOC_TAG} | ||
GIT_SHALLOW TRUE | ||
GIT_PROGRESS TRUE | ||
CONFIGURE_COMMAND "" | ||
BUILD_COMMAND "" | ||
INSTALL_COMMAND "" | ||
TEST_COMMAND "" | ||
USES_TERMINAL_DOWNLOAD TRUE | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[package] | ||
name = "mimalloc-sys" | ||
build = "build.rs" | ||
links = "mimalloc" | ||
authors.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
edition.workspace = true | ||
license.workspace = true | ||
version.workspace = true | ||
|
||
[dependencies] | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[build-dependencies] | ||
cmake = "0.1" | ||
cc = "1.0" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use std::boxed::Box; | ||
use std::env; | ||
use std::error::Error; | ||
use std::fs; | ||
use std::path::{Path, PathBuf}; | ||
|
||
use cmake::Config; | ||
|
||
// 1.8.2 | ||
//static ALLOCATOR_MIMALLOC_TAG: &str = "b66e3214d8a104669c2ec05ae91ebc26a8f5ab78"; | ||
// 2.1.2 | ||
static ALLOCATOR_MIMALLOC_TAG: &str = "43ce4bd7fd34bcc730c1c7471c99995597415488"; | ||
|
||
fn main() -> Result<(), Box<dyn Error>> { | ||
let dst = download_mimalloc()?; | ||
compile_mimalloc(&dst); | ||
println!("cargo:rerun-if-changed=build.rs"); | ||
println!("cargo:rerun-if-changed=CMakeLists.txt"); | ||
Ok(()) | ||
} | ||
|
||
// Compile mimalloc source code and link it to the crate. | ||
// The cc crate is used to compile the source code into a static library. | ||
// The cmake crate is used to download the source code and stage it in the build directory. | ||
// We don't use the cmake crate to compile the source code because the mimalloc build system | ||
// loads extra libraries, changes the name and path around, and does other things that are | ||
// difficult to handle. The cc crate is much simpler and more predictable. | ||
fn compile_mimalloc(dst: &Path) { | ||
let src_dir = dst | ||
.join("build") | ||
.join("mimalloc-prefix") | ||
.join("src") | ||
.join("mimalloc"); | ||
|
||
let mut build = cc::Build::new(); | ||
|
||
build.include(src_dir.join("include")); | ||
build.include(src_dir.join("src")); | ||
build.file(src_dir.join("src/static.c")); | ||
|
||
if build.get_compiler().is_like_msvc() { | ||
build.cpp(true); | ||
build.static_crt(true); | ||
} | ||
// turn off debug mode | ||
build.define("MI_DEBUG", "0"); | ||
|
||
// turning on optimizations doesn't seem to make a difference | ||
//build.opt_level(3); | ||
|
||
build.compile("mimalloc"); | ||
|
||
println!( | ||
"cargo:rustc-link-search=native={}", | ||
dst.join("lib").display() | ||
); | ||
println!("cargo:rustc-link-lib=static=mimalloc"); | ||
} | ||
|
||
// Use cmake to download mimalloc source code and stage | ||
// it in the build directory. | ||
fn download_mimalloc() -> Result<PathBuf, Box<dyn Error>> { | ||
let build_dir = get_build_dir()?; | ||
let mut config = Config::new(build_dir); | ||
|
||
config | ||
.no_build_target(true) | ||
.env("ALLOCATOR_MIMALLOC_TAG", ALLOCATOR_MIMALLOC_TAG) | ||
.very_verbose(true); | ||
|
||
let dst = config.build(); | ||
|
||
Ok(dst) | ||
} | ||
|
||
fn get_build_dir() -> Result<PathBuf, Box<dyn Error>> { | ||
let manifest_dir = env::var("CARGO_MANIFEST_DIR")?; | ||
let build_dir = PathBuf::from(manifest_dir.as_str()); | ||
let normalized_build_dir = fs::canonicalize(build_dir)?; | ||
Ok(normalized_build_dir) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use core::ffi::c_void; | ||
pub static MI_ALIGNMENT_MAX: usize = 1024 * 1024; // 1 MiB | ||
|
||
extern "C" { | ||
/// Allocate size bytes aligned by alignment. | ||
/// size: the number of bytes to allocate | ||
/// alignment: the minimal alignment of the allocated memory. Must be less than MI_ALIGNMENT_MAX | ||
/// returns: a pointer to the allocated memory, or null if out of memory. The returned pointer is aligned by alignment | ||
pub fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut c_void; | ||
pub fn mi_zalloc_aligned(size: usize, alignment: usize) -> *mut c_void; | ||
|
||
/// Free previously allocated memory. | ||
/// The pointer p must have been allocated before (or be nullptr). | ||
/// p: the pointer to the memory to free or nullptr | ||
pub fn mi_free(p: *mut c_void); | ||
pub fn mi_realloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void; | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn memory_can_be_allocated_and_freed() { | ||
let ptr = unsafe { mi_malloc_aligned(8, 8) }.cast::<u8>(); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
unsafe { mi_free(ptr.cast::<c_void>()) }; | ||
} | ||
|
||
#[test] | ||
fn memory_can_be_allocated_zeroed_and_freed() { | ||
let ptr = unsafe { mi_zalloc_aligned(8, 8) }.cast::<u8>(); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
unsafe { mi_free(ptr.cast::<c_void>()) }; | ||
} | ||
|
||
#[test] | ||
fn memory_can_be_reallocated_and_freed() { | ||
let ptr = unsafe { mi_malloc_aligned(8, 8) }.cast::<u8>(); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
let realloc_ptr = unsafe { mi_realloc_aligned(ptr.cast::<c_void>(), 8, 8) }.cast::<u8>(); | ||
assert!(!realloc_ptr.cast::<c_void>().is_null()); | ||
unsafe { mi_free(ptr.cast::<c_void>()) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#[cfg(not(target_family = "wasm"))] | ||
pub mod mimalloc; | ||
|
||
/// Declare a global allocator if the platform supports it. | ||
#[macro_export] | ||
macro_rules! assign_global { | ||
() => { | ||
#[cfg(not(target_family = "wasm"))] | ||
#[global_allocator] | ||
static GLOBAL: allocator::mimalloc::Mimalloc = allocator::mimalloc::Mimalloc; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use core::alloc::{GlobalAlloc, Layout}; | ||
use core::ffi::c_void; | ||
|
||
use mimalloc_sys::{mi_free, mi_malloc_aligned, mi_realloc_aligned, mi_zalloc_aligned}; | ||
|
||
pub struct Mimalloc; | ||
|
||
unsafe impl GlobalAlloc for Mimalloc { | ||
#[inline] | ||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||
debug_assert!(layout.align() < mimalloc_sys::MI_ALIGNMENT_MAX); | ||
mi_malloc_aligned(layout.size(), layout.align()).cast::<u8>() | ||
} | ||
|
||
#[inline] | ||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { | ||
mi_free(ptr.cast::<c_void>()); | ||
} | ||
|
||
#[inline] | ||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { | ||
debug_assert!(layout.align() < mimalloc_sys::MI_ALIGNMENT_MAX); | ||
mi_zalloc_aligned(layout.size(), layout.align()).cast::<u8>() | ||
} | ||
|
||
#[inline] | ||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { | ||
debug_assert!(layout.align() < mimalloc_sys::MI_ALIGNMENT_MAX); | ||
mi_realloc_aligned(ptr.cast::<c_void>(), new_size, layout.align()).cast::<u8>() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use std::error::Error; | ||
|
||
#[test] | ||
fn memory_can_be_allocated_and_freed() -> Result<(), Box<dyn Error>> { | ||
let layout = Layout::from_size_align(8, 8)?; | ||
let alloc = Mimalloc; | ||
|
||
unsafe { | ||
let ptr = alloc.alloc(layout); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
alloc.dealloc(ptr, layout); | ||
} | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn memory_can_be_alloc_zeroed_and_freed() -> Result<(), Box<dyn Error>> { | ||
let layout = Layout::from_size_align(8, 8)?; | ||
let alloc = Mimalloc; | ||
|
||
unsafe { | ||
let ptr = alloc.alloc_zeroed(layout); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
alloc.dealloc(ptr, layout); | ||
} | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn large_chunks_of_memory_can_be_allocated_and_freed() -> Result<(), Box<dyn Error>> { | ||
let layout = Layout::from_size_align(2 * 1024 * 1024 * 1024, 8)?; | ||
let alloc = Mimalloc; | ||
|
||
unsafe { | ||
let ptr = alloc.alloc(layout); | ||
assert!(!ptr.cast::<c_void>().is_null()); | ||
alloc.dealloc(ptr, layout); | ||
} | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.