mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-20 05:59:32 +03:00
This commit is contained in:
parent
a09a64e29b
commit
f89ab5601d
2 changed files with 99 additions and 24 deletions
|
@ -323,6 +323,25 @@ mi_decl_export void mi_option_set(mi_option_t option, long value);
|
||||||
mi_decl_export void mi_option_set_default(mi_option_t option, long value);
|
mi_decl_export void mi_option_set_default(mi_option_t option, long value);
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum mi_mmap_flag_e {
|
||||||
|
// mmap wrapper may allocate from huge pages.
|
||||||
|
// mmap wrapper should not use huge pages if this flag not set.
|
||||||
|
mi_mmap_allow_large = 0x1,
|
||||||
|
|
||||||
|
// mmap wrapper must allocate from huge pages (mi_mmap_allow_large will be set as well).
|
||||||
|
mi_mmap_large_only = 0x2,
|
||||||
|
mi_mmap_commit = 0x4,
|
||||||
|
} mi_mmap_flag_t;
|
||||||
|
typedef unsigned int mi_mmap_flags_t;
|
||||||
|
|
||||||
|
typedef void* (mi_cdecl mi_mmap_fun)(void* addr, size_t size, size_t try_alignment, mi_mmap_flags_t mmap_flags, void* arg, bool* is_large);
|
||||||
|
mi_decl_export void mi_register_mmap_fun(mi_mmap_fun* callback, void* arg) mi_attr_noexcept;
|
||||||
|
|
||||||
|
// Returns true on error, false otherwise.
|
||||||
|
typedef bool (mi_cdecl mi_munmap_fun)(void* addr, size_t size, void* arg);
|
||||||
|
mi_decl_export void mi_register_munmap_fun(mi_munmap_fun* callback, void* arg) mi_attr_noexcept;
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------
|
||||||
// "mi" prefixed implementations of various posix, Unix, Windows, and C++ allocation functions.
|
// "mi" prefixed implementations of various posix, Unix, Windows, and C++ allocation functions.
|
||||||
// (This can be convenient when providing overrides of these functions as done in `mimalloc-override.h`.)
|
// (This can be convenient when providing overrides of these functions as done in `mimalloc-override.h`.)
|
||||||
|
|
102
src/os.c
102
src/os.c
|
@ -58,6 +58,12 @@ static size_t os_alloc_granularity = 4096;
|
||||||
// if non-zero, use large page allocation
|
// if non-zero, use large page allocation
|
||||||
static size_t large_os_page_size = 0;
|
static size_t large_os_page_size = 0;
|
||||||
|
|
||||||
|
static mi_mmap_fun* mmap_function;
|
||||||
|
static void* mmap_function_arg;
|
||||||
|
|
||||||
|
static mi_munmap_fun* munmap_function;
|
||||||
|
static void* munmap_function_arg;
|
||||||
|
|
||||||
// OS (small) page size
|
// OS (small) page size
|
||||||
size_t _mi_os_page_size() {
|
size_t _mi_os_page_size() {
|
||||||
return os_page_size;
|
return os_page_size;
|
||||||
|
@ -86,6 +92,16 @@ size_t _mi_os_good_alloc_size(size_t size) {
|
||||||
return _mi_align_up(size, align_size);
|
return _mi_align_up(size, align_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mi_register_mmap_fun(mi_mmap_fun* callback, void* arg) {
|
||||||
|
mmap_function = callback;
|
||||||
|
mmap_function_arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mi_register_munmap_fun(mi_munmap_fun* callback, void* arg) {
|
||||||
|
munmap_function = callback;
|
||||||
|
munmap_function_arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016.
|
// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016.
|
||||||
// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility)
|
// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility)
|
||||||
|
@ -97,6 +113,12 @@ typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*
|
||||||
static PVirtualAlloc2 pVirtualAlloc2 = NULL;
|
static PVirtualAlloc2 pVirtualAlloc2 = NULL;
|
||||||
static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL;
|
static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL;
|
||||||
|
|
||||||
|
static bool mi_win_munmap(void* addr, size_t size, void* arg)
|
||||||
|
{
|
||||||
|
UNUSED(arg); UNUSED(size);
|
||||||
|
return VirtualFree(addr, 0, MEM_RELEASE) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mi_win_enable_large_os_pages()
|
static bool mi_win_enable_large_os_pages()
|
||||||
{
|
{
|
||||||
if (large_os_page_size > 0) return true;
|
if (large_os_page_size > 0) return true;
|
||||||
|
@ -132,6 +154,9 @@ static bool mi_win_enable_large_os_pages()
|
||||||
return (ok!=0);
|
return (ok!=0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, mi_mmap_flags_t mmap_flags,
|
||||||
|
void* arg, bool* is_large);
|
||||||
|
|
||||||
void _mi_os_init(void) {
|
void _mi_os_init(void) {
|
||||||
// get the page size
|
// get the page size
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
|
@ -155,13 +180,30 @@ void _mi_os_init(void) {
|
||||||
if (mi_option_is_enabled(mi_option_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
|
if (mi_option_is_enabled(mi_option_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
|
||||||
mi_win_enable_large_os_pages();
|
mi_win_enable_large_os_pages();
|
||||||
}
|
}
|
||||||
|
munmap_function = mi_win_munmap;
|
||||||
|
mmap_function = mi_win_virtual_alloc;
|
||||||
}
|
}
|
||||||
#elif defined(__wasi__)
|
#elif defined(__wasi__)
|
||||||
|
static bool mi_wasi_munmap(void* addr, size_t size, void* arg)
|
||||||
|
{
|
||||||
|
UNUSED(addr); UNUSED(size); UNUSED(arg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void _mi_os_init() {
|
void _mi_os_init() {
|
||||||
os_page_size = 0x10000; // WebAssembly has a fixed page size: 64KB
|
os_page_size = 0x10000; // WebAssembly has a fixed page size: 64KB
|
||||||
os_alloc_granularity = 16;
|
os_alloc_granularity = 16;
|
||||||
|
munmap_function = mi_wasi_munmap;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
static bool mi_unix_munmap(void* addr, size_t size, void* arg)
|
||||||
|
{
|
||||||
|
UNUSED(arg);
|
||||||
|
return munmap(addr, size) == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, mi_mmap_flags_t mmap_flags, void* arg, bool* is_large);
|
||||||
|
|
||||||
void _mi_os_init() {
|
void _mi_os_init() {
|
||||||
// get the page size
|
// get the page size
|
||||||
long result = sysconf(_SC_PAGESIZE);
|
long result = sysconf(_SC_PAGESIZE);
|
||||||
|
@ -170,6 +212,8 @@ void _mi_os_init() {
|
||||||
os_alloc_granularity = os_page_size;
|
os_alloc_granularity = os_page_size;
|
||||||
}
|
}
|
||||||
large_os_page_size = 2*MiB; // TODO: can we query the OS for this?
|
large_os_page_size = 2*MiB; // TODO: can we query the OS for this?
|
||||||
|
munmap_function = mi_unix_munmap;
|
||||||
|
mmap_function = mi_unix_mmap;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -181,14 +225,7 @@ void _mi_os_init() {
|
||||||
static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats_t* stats)
|
static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats_t* stats)
|
||||||
{
|
{
|
||||||
if (addr == NULL || size == 0) return true; // || _mi_os_is_huge_reserved(addr)
|
if (addr == NULL || size == 0) return true; // || _mi_os_is_huge_reserved(addr)
|
||||||
bool err = false;
|
bool err = munmap_function(addr, size, munmap_function_arg);
|
||||||
#if defined(_WIN32)
|
|
||||||
err = (VirtualFree(addr, 0, MEM_RELEASE) == 0);
|
|
||||||
#elif defined(__wasi__)
|
|
||||||
err = 0; // WebAssembly's heap cannot be shrunk
|
|
||||||
#else
|
|
||||||
err = (munmap(addr, size) == -1);
|
|
||||||
#endif
|
|
||||||
if (was_committed) _mi_stat_decrease(&stats->committed, size);
|
if (was_committed) _mi_stat_decrease(&stats->committed, size);
|
||||||
_mi_stat_decrease(&stats->reserved, size);
|
_mi_stat_decrease(&stats->reserved, size);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -234,12 +271,19 @@ static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment
|
||||||
return VirtualAlloc(addr, size, flags, PAGE_READWRITE);
|
return VirtualAlloc(addr, size, flags, PAGE_READWRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags, bool large_only, bool allow_large, bool* is_large) {
|
static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, mi_mmap_flags_t mmap_flags,
|
||||||
mi_assert_internal(!(large_only && !allow_large));
|
void* arg, bool* is_large) {
|
||||||
|
mi_assert_internal((mmap_flags & (mi_mmap_allow_large | mi_mmap_large_only)) != mi_mmap_large_only);
|
||||||
static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
|
static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
|
||||||
void* p = NULL;
|
void* p = NULL;
|
||||||
|
bool large_only = (mmap_flags & mi_mmap_large_only) != 0;
|
||||||
|
bool allow_large = (mmap_flags & mi_mmap_allow_large) != 0;
|
||||||
|
int flags = MEM_RESERVE;
|
||||||
|
if (mmap_flags & mi_mmap_commit)
|
||||||
|
flags |= MEM_COMMIT;
|
||||||
|
|
||||||
if ((large_only || use_large_os_page(size, try_alignment))
|
if ((large_only || use_large_os_page(size, try_alignment))
|
||||||
&& allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) {
|
&& allow_large && (mmap_flags & mi_mmap_commit)) {
|
||||||
uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
|
uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
|
||||||
if (!large_only && try_ok > 0) {
|
if (!large_only && try_ok > 0) {
|
||||||
// if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive.
|
// if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive.
|
||||||
|
@ -302,7 +346,8 @@ static void* mi_unix_mmapx(void* addr, size_t size, size_t try_alignment, int pr
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, int protect_flags, bool large_only, bool allow_large, bool* is_large) {
|
static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, mi_mmap_flags_t mmap_flags, void* arg, bool* is_large) {
|
||||||
|
UNUSED(arg);
|
||||||
void* p = NULL;
|
void* p = NULL;
|
||||||
#if !defined(MAP_ANONYMOUS)
|
#if !defined(MAP_ANONYMOUS)
|
||||||
#define MAP_ANONYMOUS MAP_ANON
|
#define MAP_ANONYMOUS MAP_ANON
|
||||||
|
@ -310,6 +355,8 @@ static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, int pro
|
||||||
#if !defined(MAP_NORESERVE)
|
#if !defined(MAP_NORESERVE)
|
||||||
#define MAP_NORESERVE 0
|
#define MAP_NORESERVE 0
|
||||||
#endif
|
#endif
|
||||||
|
int protect_flags = mmap_flags & mi_mmap_commit ? (PROT_WRITE | PROT_READ) : PROT_NONE;
|
||||||
|
|
||||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
|
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
#if defined(MAP_ALIGNED) // BSD
|
#if defined(MAP_ALIGNED) // BSD
|
||||||
|
@ -329,6 +376,8 @@ static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, int pro
|
||||||
if (os_tag < 100 || os_tag > 255) os_tag = 100;
|
if (os_tag < 100 || os_tag > 255) os_tag = 100;
|
||||||
fd = VM_MAKE_TAG(os_tag);
|
fd = VM_MAKE_TAG(os_tag);
|
||||||
#endif
|
#endif
|
||||||
|
bool large_only = (mmap_flags & mi_mmap_large_only) != 0;
|
||||||
|
bool allow_large = (mmap_flags & mi_mmap_allow_large) != 0;
|
||||||
if ((large_only || use_large_os_page(size, try_alignment)) && allow_large) {
|
if ((large_only || use_large_os_page(size, try_alignment)) && allow_large) {
|
||||||
static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
|
static volatile _Atomic(uintptr_t) large_page_try_ok; // = 0;
|
||||||
uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
|
uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
|
||||||
|
@ -438,7 +487,12 @@ static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) {
|
||||||
static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
|
static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
|
||||||
mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
|
mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
|
||||||
if (size == 0) return NULL;
|
if (size == 0) return NULL;
|
||||||
if (!commit) allow_large = false;
|
mi_mmap_flags_t mmap_flags = 0;
|
||||||
|
if (commit) {
|
||||||
|
mmap_flags |= mi_mmap_commit;
|
||||||
|
if (allow_large)
|
||||||
|
mmap_flags |= mi_mmap_allow_large;
|
||||||
|
}
|
||||||
|
|
||||||
void* p = NULL;
|
void* p = NULL;
|
||||||
/*
|
/*
|
||||||
|
@ -452,15 +506,12 @@ static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, boo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
int flags = MEM_RESERVE;
|
p = mmap_function(NULL, size, try_alignment, mmap_flags, mmap_function_arg, is_large);
|
||||||
if (commit) flags |= MEM_COMMIT;
|
|
||||||
p = mi_win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large);
|
|
||||||
#elif defined(__wasi__)
|
#elif defined(__wasi__)
|
||||||
*is_large = false;
|
*is_large = false;
|
||||||
p = mi_wasm_heap_grow(size, try_alignment);
|
p = mi_wasm_heap_grow(size, try_alignment);
|
||||||
#else
|
#else
|
||||||
int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE);
|
p = mmap_function(NULL, size, try_alignment, mmap_flags, mmap_function_arg, is_large);
|
||||||
p = mi_unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large);
|
|
||||||
#endif
|
#endif
|
||||||
mi_stat_counter_increase(stats->mmap_calls, 1);
|
mi_stat_counter_increase(stats->mmap_calls, 1);
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
|
@ -476,7 +527,12 @@ static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, boo
|
||||||
static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
|
static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) {
|
||||||
mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0));
|
mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0));
|
||||||
mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
|
mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
|
||||||
if (!commit) allow_large = false;
|
mi_mmap_flags_t mmap_flags = 0;
|
||||||
|
if (commit) {
|
||||||
|
mmap_flags |= mi_mmap_commit;
|
||||||
|
if (allow_large)
|
||||||
|
mmap_flags |= mi_mmap_allow_large;
|
||||||
|
}
|
||||||
if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL;
|
if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL;
|
||||||
size = _mi_align_up(size, _mi_os_page_size());
|
size = _mi_align_up(size, _mi_os_page_size());
|
||||||
|
|
||||||
|
@ -496,8 +552,6 @@ static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit,
|
||||||
// retry this at most 3 times before giving up.
|
// retry this at most 3 times before giving up.
|
||||||
// (we can not decommit around the overallocation on Windows, because we can only
|
// (we can not decommit around the overallocation on Windows, because we can only
|
||||||
// free the original pointer, not one pointing inside the area)
|
// free the original pointer, not one pointing inside the area)
|
||||||
int flags = MEM_RESERVE;
|
|
||||||
if (commit) flags |= MEM_COMMIT;
|
|
||||||
for (int tries = 0; tries < 3; tries++) {
|
for (int tries = 0; tries < 3; tries++) {
|
||||||
// over-allocate to determine a virtual memory range
|
// over-allocate to determine a virtual memory range
|
||||||
p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats);
|
p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats);
|
||||||
|
@ -511,7 +565,7 @@ static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit,
|
||||||
// otherwise free and allocate at an aligned address in there
|
// otherwise free and allocate at an aligned address in there
|
||||||
mi_os_mem_free(p, over_size, commit, stats);
|
mi_os_mem_free(p, over_size, commit, stats);
|
||||||
void* aligned_p = mi_align_up_ptr(p, alignment);
|
void* aligned_p = mi_align_up_ptr(p, alignment);
|
||||||
p = mi_win_virtual_alloc(aligned_p, size, alignment, flags, false, allow_large, is_large);
|
p = mmap_function(aligned_p, size, alignment, mmap_flags, mmap_function_arg, is_large);
|
||||||
if (p == aligned_p) break; // success!
|
if (p == aligned_p) break; // success!
|
||||||
if (p != NULL) { // should not happen?
|
if (p != NULL) { // should not happen?
|
||||||
mi_os_mem_free(p, size, commit, stats);
|
mi_os_mem_free(p, size, commit, stats);
|
||||||
|
@ -891,10 +945,12 @@ static long mi_os_mbind(void* start, unsigned long len, unsigned long mode, cons
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) {
|
static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) {
|
||||||
mi_assert_internal(size%GiB == 0);
|
mi_assert_internal(size%GiB == 0);
|
||||||
bool is_large = true;
|
bool is_large = true;
|
||||||
void* p = mi_unix_mmap(addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large);
|
mi_mmap_flags_t mmap_flags = mi_mmap_commit | mi_mmap_allow_large | mi_mmap_large_only;
|
||||||
|
void* p = mmap_function(addr, size, MI_SEGMENT_SIZE, mmap_flags, mmap_function_arg, &is_large);
|
||||||
if (p == NULL) return NULL;
|
if (p == NULL) return NULL;
|
||||||
if (numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes
|
if (numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes
|
||||||
uintptr_t numa_mask = (1UL << numa_node);
|
uintptr_t numa_mask = (1UL << numa_node);
|
||||||
|
|
Loading…
Add table
Reference in a new issue