Compare commits

...

214 commits
v2.1.9 ... main

Author SHA1 Message Date
Daan
af21001f7a clarify to use as the PR branch 2025-04-02 12:17:45 -07:00
Daan
9f5a2969b8 clarify v3 tag 2025-04-02 12:08:26 -07:00
Daan
13cd3f7f05 Merge branch 'dev2' 2025-03-28 14:20:20 -07:00
Daan
94036de6fe Merge branch 'dev' into dev2 2025-03-28 14:19:07 -07:00
Daan
8a81fc73c8 update readme 2025-03-28 14:18:28 -07:00
Daan
401a043849 Merge branch 'dev' into dev2 2025-03-28 13:28:16 -07:00
Daan
f46c1d2624 update readme 2025-03-28 13:28:10 -07:00
Daan
b843fead22 update mimalloc-redirect to v1.3.3; fix issue #1049 2025-03-28 13:11:37 -07:00
Daan
1052c30f03 fix parenthesis in #if condition 2025-03-28 13:09:24 -07:00
Daan
eb992b1d19
Merge pull request #1050 from vfazio/vfazio-mi_yield_atomic-armeb
atomic: fix mi_atomic_yield for big-endian arm32
2025-03-28 13:07:25 -07:00
Vincent Fazio
23fbee7ec6 atomic: fix mi_atomic_yield for big-endian arm32
Previously, `mi_atomic_yield` would not be defined on ARM32 big-endian
architectures if they were pre-ARMv7.

Rework the #ifdef guard to be more readable and collapse the ARM guards
so both little and big endian are handled via the same mechanism.

Now, ARMv7+ will utilize `yield` while older targets will use `nop`
regardless of endianness.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
2025-03-28 08:04:07 -05:00
Daan
e1da869d9b Merge branch 'dev' into dev2 2025-03-25 16:06:14 -07:00
Daan
632eab958b fix for atomic_yield on arm 32-bit, issue #1046 2025-03-25 16:02:29 -07:00
Daan
881d5e3c06
Merge pull request #1047 from ognevny/patch-1
cmake: don't change properties of import lib on Windows/MinGW
2025-03-25 15:43:16 -07:00
Daan
246b3bc120
Merge pull request #1045 from SquallATF/mingw-fix
remove the `lib` prefix when enabling mimalloc-redirect for mingw
2025-03-25 15:42:53 -07:00
Maksim Bondarenkov
797ca19ba9
cmake: don't change properties of import lib on Windows/MinGW
CMake handles import lib for it automatically, and using `.dll.lib` extension is MSVC-specific hack
2025-03-24 08:35:15 +03:00
Peiyuan Song
34cc5c8fd9 remove the lib prefix when enabling mimalloc-redirect for mingw 2025-03-24 09:39:42 +08:00
Daan
9155fe98f1 Merge branch 'dev' into dev2 2025-03-21 20:07:29 -07:00
Daan
26b792d93b fix aligned malloc_requested statistic 2025-03-21 20:07:16 -07:00
Daan
ea2c7c6e56 merge from dev statistics update 2025-03-21 19:43:26 -07:00
Daan
a077311a5e improve tracking of malloc_requested count 2025-03-21 19:40:44 -07:00
Daan
d48bafe2bb print statistics nicer 2025-03-21 19:21:41 -07:00
Daan
02607f2b8d reduce test sizes for 32-bit 2025-03-21 17:22:36 -07:00
Daan
1463ead070 Merge branch 'dev' into dev2 2025-03-21 16:57:34 -07:00
Daan
6ed451c555 fix linux compile by including linux/prctl.h 2025-03-21 16:48:50 -07:00
Daan
01ee3568c1 name anonymous mmap address ranges for debugging on Linux (based on PR #1032 by @zhuker) 2025-03-21 16:19:54 -07:00
Daan
fdde679de1
Merge pull request #1040 from jbatez/dev
support MI_OPT_ARCH when using CMAKE_OSX_ARCHITECTURES with non-Apple Clang
2025-03-21 16:01:37 -07:00
Jo Bates
cf08c27d2b support MI_OPT_ARCH when using CMAKE_OSX_ARCHITECTURES with non-Apple Clang 2025-03-20 11:24:59 -07:00
Daan
891f9f4cf6 fix conflict marker (issue #1038) 2025-03-19 20:38:19 -07:00
Daan
e46877a5e1 merge from dev 2025-03-19 20:34:34 -07:00
Daan
660d749d77 do not default to MI_DEBUG=2 in release mode builds even when NDEBUG is not defined by defininig MI_BUILD_RELEASE (issue #1037) 2025-03-19 20:29:29 -07:00
Daan
7eafaa9685 fix visibility warning (issue #1031) 2025-03-19 20:22:02 -07:00
Daan
b2dcab58f7 fix assertion failure (issue #1031) 2025-03-19 20:20:36 -07:00
Daan
acba60a248 Merge branch 'dev' into dev2 2025-03-19 19:17:03 -07:00
Daan
afbc581f8d add Windows x86 to the build pipeline 2025-03-19 19:16:10 -07:00
Daan
1aa88e0d9a try to fix pipeline trigger 2025-03-19 19:11:38 -07:00
Daan
c49c8acfda Merge branch 'dev' into dev2 2025-03-19 19:06:28 -07:00
Daan
47bf3a5b1b potential fix for sporadic assertion failure on random returning 0 (issue #1039) 2025-03-19 19:06:21 -07:00
Daan
992ebd4eae Merge branch 'dev' into dev2 2025-03-19 18:50:59 -07:00
Daan
26fa8be427 improved accounting of committed bytes (issue #1035) 2025-03-19 18:50:53 -07:00
Daan
6e5ed1ea67 Merge branch 'dev' into dev2 2025-03-19 16:12:23 -07:00
Daan
9a35bca556 possible fix for wrong accounting of committed bytes (issue #1035) 2025-03-19 16:12:17 -07:00
Daan
3d6065f987 merge from dev 2025-03-10 14:21:50 -07:00
Daan Leijen
f11732acdf set default compilation to c++ mode on msvc 2025-03-10 12:39:09 -07:00
daanx
4aae566191 fix link error with msvc in C mode (issue #1030) 2025-03-10 12:17:46 -07:00
Daan
2b895f4e97 bump version to 2.2.3 for further development 2025-03-06 21:04:32 -08:00
Daan
2fc6b14bab bump version to 1.9.3 for further development 2025-03-06 21:03:51 -08:00
Daan
e112300059 Merge branch 'master' of https://github.com/microsoft/mimalloc 2025-03-06 20:59:26 -08:00
Daan
a62932135a merge from dev2: v2.2.2 2025-03-06 20:59:12 -08:00
Daan
f81bf1b31a Merge branch 'dev' into dev2 2025-03-06 20:56:33 -08:00
Daan
21f6edf9c6 update readme 2025-03-06 20:54:44 -08:00
Daan
2697d55fa5 Merge branch 'dev' into dev2 2025-03-06 20:41:05 -08:00
Daan
783c3ba486 improve QNX support 2025-03-06 20:40:52 -08:00
Daan
97d440b51d
Merge pull request #1019 from tkielan/dev
support building for QNX
2025-03-06 20:35:11 -08:00
Daan
26aade92cf merge from dev 2025-03-06 20:15:32 -08:00
Daan
9eac969ea5 improve atomic stat merging 2025-03-06 20:14:33 -08:00
Daan
a085c30555 update readme 2025-03-06 19:59:37 -08:00
Daan
bd4ee09dc6 Merge branch 'dev' into dev2 2025-03-06 19:52:19 -08:00
Daan
71c61c4b91 fix multi-threaded access in stats merging 2025-03-06 19:52:06 -08:00
Daan
0f60b12769 prepare readme for new release 2025-03-06 19:40:40 -08:00
Daan
a3bbb32ab1 Merge branch 'dev' into dev2 2025-03-06 19:21:45 -08:00
Daan
e1fde6b5ce update vcpkg file 2025-03-06 19:21:30 -08:00
Daan
ddff485628 Merge branch 'dev' into dev2 2025-03-06 19:11:32 -08:00
Daan
7d6304347e revert back to _WIN32_WINNT=x0600 as we dynamically check for GetPhysicalInstalledMemory now (issue #976) 2025-03-06 19:09:51 -08:00
daanx
5bed67cda9 Merge branch 'dev' into dev2 2025-03-05 16:09:43 -08:00
daanx
8f40bed086 fix erms detection 2025-03-05 15:48:57 -08:00
Daan Leijen
3e69b5323a Merge branch 'dev' into dev2 2025-03-05 15:37:54 -08:00
Daan Leijen
5c6ab532d9 make MI_OPT_ARCH by default OFF except for arm64 where we assume v8.1-a for fast atomics 2025-03-05 15:37:37 -08:00
Daan
2b9385911b Merge branch 'dev' into dev2 2025-03-05 09:59:45 -08:00
Daan
438903421a fix verbosity condition for printing options 2025-03-05 09:57:51 -08:00
Daan
346a5cb3ee Merge branch 'dev' into dev2 2025-03-05 09:56:17 -08:00
Daan
db831d4cf4 make mi_options_print, mi_arenas_print, and mi_stats_print independent of the vebosity setting 2025-03-03 16:23:28 -08:00
Daan
a7a3d317ef Merge branch 'dev' into dev2 2025-03-02 17:41:30 -08:00
Daan
b54a7d836c fix compilation error with MI_GUARDED 2025-03-02 17:41:13 -08:00
Daan Leijen
a24d71f374 fix compile warning 2025-03-02 17:10:24 -08:00
Daan Leijen
2d81b6fee9 Merge branch 'dev' into dev2 2025-03-02 17:06:35 -08:00
Daan Leijen
58d13f6a4f collect every N generic calls 2025-03-02 17:06:25 -08:00
Daan Leijen
4f1edad4fe add arena_purges stat 2025-03-02 16:36:16 -08:00
Daan Leijen
fcc2b561e9 merge new statistics from dev 2025-03-02 15:52:52 -08:00
Daan Leijen
c910750bbe fix mi_stat_get_json signature 2025-03-02 15:39:08 -08:00
Daan Leijen
b319156c4f fix test stress 2025-03-02 15:35:35 -08:00
Daan Leijen
5b685ef183 fix test stress 2025-03-02 15:35:12 -08:00
Daan Leijen
09ad6d2819 initial statistics api 2025-03-02 15:34:37 -08:00
Daan Leijen
1b749ea7d8 clean up statistics 2025-03-01 20:51:44 -08:00
Daan Leijen
cd6737a1ee merge from dev 2025-03-01 19:58:44 -08:00
Daan Leijen
bc5f636c5e nicer stats 2025-03-01 19:46:01 -08:00
daanx
4d727ee6e6 avoid pthread allocation size in mstress bench 2025-03-01 18:30:24 -08:00
Daan Leijen
18174400b2 fix stats_add condition 2025-03-01 18:04:34 -08:00
Daan Leijen
7758bb1067 merge from dev 2025-03-01 16:58:57 -08:00
Daan Leijen
56aba086ea remove superfluous stat fields (count and freed) 2025-03-01 16:52:48 -08:00
Daan Leijen
2e52ef2f69 Merge branch 'dev' into dev2 2025-03-01 16:29:45 -08:00
Daan Leijen
bdaeb1d469 remove stat_adjust 2025-03-01 16:29:28 -08:00
Daan Leijen
113800d1f0 remove status message 2025-03-01 14:52:37 -08:00
Daan Leijen
b0b9d9c22b add note that git rev dependency doesnt work 2025-03-01 14:51:27 -08:00
Daan Leijen
c2ae1c3539 fix git rev dependency in cmake configure 2025-03-01 14:31:24 -08:00
Daan Leijen
527cd05fec add git hash to compile defines 2025-03-01 14:29:23 -08:00
daanx
5f6ebb70fa add mi_options_print and mi_arenas_print 2025-02-28 17:30:41 -08:00
Daan
7a4d7b8d18 Merge branch 'dev' into dev2 2025-02-24 10:50:39 -08:00
Daan
a857a04803 fix misspelling (issue #1017, #1020) 2025-02-24 10:50:26 -08:00
Timothy Kielan
c8880e1ba0
support building for QNX 2025-02-21 15:16:22 +01:00
Daan
01afcaacf6 remove robustness claim for dynamic override on Windows (issue #1018) 2025-02-20 19:50:13 -08:00
Daan
b0c1765ab9 Merge branch 'dev' into dev2 2025-02-20 19:36:48 -08:00
daanx
aed71f8b32 prevent segment map overflow on arm32 (issue #1017) 2025-02-20 15:09:18 -08:00
Daan
566ab5038b do not use syscall on android (issue #1015) 2025-02-18 16:02:52 -08:00
Daan
3c13579fcf fix pre-processor overflow (issue #1017) 2025-02-18 15:55:45 -08:00
Daan
ac35aa168c consistent use of IN_LIST 2025-02-17 14:24:10 -08:00
Daan
8ed7b76adc add armv9 match 2025-02-17 14:16:56 -08:00
Daan
6caf1ab6be
Merge pull request #1012 from nmoinvaz/dev
CMake arch detection from CMAKE_OSX_ARCHITECTURES
2025-02-17 14:09:19 -08:00
Nathan Moinvaziri
1699b2a82e cmake arch detection from CMAKE_OSX_ARCHITECTURES (issue #1011) 2025-02-14 16:12:01 -08:00
Daan
26a0299537 Merge branch 'dev' into dev2 2025-02-13 11:12:40 -08:00
daanx
b357b80dfd update mimalloc-redirect to v1.3.2 2025-02-13 11:10:09 -08:00
Daan
8b49a9f49c remove deprecated ubuntu 20 from pipeline 2025-02-11 16:36:36 -08:00
Daan
b7779c7770 merge from dev 2025-02-11 16:30:03 -08:00
daanx
ece1defe5b parameter of clz/ctz is size_t 2025-02-11 14:35:21 -08:00
daanx
c62d276835 optimize mi_bin 2025-02-11 14:27:01 -08:00
Daan
482d8e1ae7 Merge branch 'dev' into dev2 2025-02-11 09:52:10 -08:00
daanx
d3897635ad fix compilation warning 2025-02-11 09:22:31 -08:00
daanx
0c8069adab use physical memory in kib to avoid overflow of size_t (issue #1010) 2025-02-11 09:18:23 -08:00
daanx
ec4aa62b65 use physical memory in kib to avoid overflow of size_t (issue #1010) 2025-02-11 09:12:29 -08:00
Daan
0d7956c7c2 Merge branch 'dev' into dev2 2025-02-10 20:55:41 -08:00
Daan
78dd3f0e38 add mi_is_redirected back (issue #993) 2025-02-10 20:55:30 -08:00
Daan
1d3d193561 make heap_main mi_decl_hidden 2025-02-10 20:37:01 -08:00
Daan
5ce6f9f407 Merge branch 'dev' into dev2 2025-02-10 20:29:46 -08:00
Daan
156687ac8b rename to mi_thread_set_in_threadpool 2025-02-10 20:26:19 -08:00
daanx
08ebe070a4 add mi_heap_set_in_threadpool 2025-02-10 20:11:52 -08:00
daanx
1d78531a64 update minject to take target arch into account (issue #997) 2025-02-10 20:02:02 -08:00
Daan
34a66514c0 Merge branch 'dev' into dev2 2025-02-09 19:27:07 -08:00
daanx
3e7875066b add dynamic override test on windows 2025-02-09 19:22:26 -08:00
Daan
84aadcb087 avoid link error with asan/c++ 2025-02-09 18:55:14 -08:00
Daan
8199a7f952 Merge branch 'dev' into dev2 2025-02-09 18:44:29 -08:00
Daan
62833be9eb disable dynamic override test on ubsan/tsan/asan 2025-02-09 18:44:17 -08:00
Daan
5f43fe91e0 Merge branch 'dev' into dev2 2025-02-09 18:34:53 -08:00
Daan
7dd598e0e8 add dynamic override test 2025-02-09 18:32:16 -08:00
Daan
06ced84829 Merge branch 'dev2' 2025-02-08 13:18:09 -08:00
Daan Leijen
f945d35d27 Merge branch 'dev' into dev2 2025-02-08 12:51:34 -08:00
Daan Leijen
5f9b42685e remove declspec(dllexport) from DllMain on Windows (issue #1008) 2025-02-08 12:51:06 -08:00
Daan
069279e3e7 improve cmake for multi-arch binaries on apple 2025-02-08 12:18:27 -08:00
Daan
65a89854d1 Merge branch 'dev' into dev2 2025-02-08 12:06:48 -08:00
Daan
0e9159e0bf add comment 2025-02-08 12:06:39 -08:00
Daan
cb2719cdb8
Merge pull request #999 from jbatez/dev
support MI_OPT_ARCH when targeting multiple CMAKE_OSX_ARCHITECTURES
2025-02-08 11:46:07 -08:00
Jo Bates
eb0081382b support MI_OPT_ARCH when targeting multiple CMAKE_OSX_ARCHITECTURES 2025-02-01 17:04:46 -08:00
Daan
90b7a694eb syntax error fix (#992) 2025-01-22 12:29:41 -08:00
Daan
5e71dfc336 check dynamically for getPhysicallyInstalledSystemMemory on windows (issue #992) 2025-01-22 12:03:58 -08:00
Daan
402a560c31 Merge branch 'dev' into dev2 2025-01-16 13:06:52 -08:00
Daan
81af010484 erge branch 'dev2' of /Volumes/T9/dev/mimalloc into dev2 2025-01-16 13:06:31 -08:00
daanx
14b4f674fa nicer figure 2025-01-15 12:33:11 -08:00
daanx
5e109f8b3f Merge branch 'dev' into dev2 2025-01-15 12:31:42 -08:00
daanx
2bc52557c5 add figure to readme for mimalloc-redirect 2025-01-15 12:31:35 -08:00
daanx
39b7af9d5f merge from dev 2025-01-15 12:12:24 -08:00
daanx
4a14c69554 disable using vcpkg in the VS projects 2025-01-15 12:11:52 -08:00
Daan
67f733281f merge from dev 2025-01-13 16:59:56 -08:00
Daan
97c304dc39 vcpkg: bump sha 2025-01-13 16:59:24 -08:00
Daan
866ce5b89d vcpkg: remove INTERFACE_INCLUDE_DIRECTORIES again 2025-01-13 16:57:59 -08:00
Daan
0ef19762fe bump vcpkg sha 2025-01-13 16:57:17 -08:00
Daan
e2db21e9ba remove INTERFACE_INCLUDE_DIRECTORIES 2025-01-13 16:55:56 -08:00
daanx
03e501bddb Merge branch 'dev' into dev2 2025-01-13 16:03:44 -08:00
daanx
bd8cdc912c add INTERFACE_INCLUDE_DIRECTORIES to vckpcg wrapper 2025-01-13 16:03:36 -08:00
daanx
2e597fbbf3 Merge branch 'dev' into dev2 2025-01-13 15:33:07 -08:00
daanx
af602df758 update readme to reflect name changes of the Windows export library to mimalloc.dll.lib 2025-01-13 15:32:52 -08:00
daanx
65c3a5c015 Merge branch 'dev' into dev2 2025-01-13 15:03:38 -08:00
daanx
7a27001483 update minject to use mimalloc.dll by default (issue #989) 2025-01-13 15:03:23 -08:00
daanx
8d520306b8 set pdb name on windows 2025-01-11 12:32:23 -08:00
daanx
24ec5978c8 use .dll.pdb for the dll debug file in Visual Studio 2025-01-10 10:59:44 -08:00
Daan
34b5d3c779 update vcpkg hash 2025-01-10 09:53:11 -08:00
Daan
191ea046e4 merge from dev 2025-01-10 09:50:30 -08:00
daanx
f4c3a9069a update minject -- possible fix for #986 2025-01-10 09:41:13 -08:00
daanx
11e64c5c42 update vcpkg instructions 2025-01-10 09:19:08 -08:00
Daan
be05b232e8 fix obj path on windows 2025-01-10 08:44:31 -08:00
Daan
4e5a6d2136 initial sources for vcpkg support 2025-01-09 23:29:57 -08:00
Daan
3f91c9f937 move docker files 2025-01-09 23:10:12 -08:00
daanx
0be6db0e28 comments 2025-01-09 21:06:38 -08:00
daanx
4fcf56af2e use consistent naming on Windows vs other platforms. Use mimalloc.dll.lib for the dll import library to avoid a clash with the static mimalloc.lib library 2025-01-09 20:57:08 -08:00
Daan Leijen
600ca88e87 fix library name for pkg config files (and to avoid extra patches in vcpkg) 2025-01-09 19:31:05 -08:00
daanx
432af2f304 use directly CMAKE_INSTALL_BIN/LIBDIR to avoid patch in vcpkg 2025-01-09 18:35:03 -08:00
Daan Leijen
fab7397e6b merge from dev 2025-01-07 19:34:24 -08:00
Daan Leijen
7fa3b41fc9 limit object size in pages to 1/8th 2025-01-07 19:30:52 -08:00
Daan Leijen
195249e6bf maintain precise try count for reclaim 2025-01-07 19:28:44 -08:00
Daan Leijen
24297c8dba Merge branch 'dev' into dev2 2025-01-07 19:20:10 -08:00
Daan Leijen
c4f96f7abc update proj 2025-01-07 19:20:04 -08:00
Daan Leijen
1f99d3d91b Merge branch 'dev' into dev2 2025-01-07 19:14:25 -08:00
Daan Leijen
a3b813c2bb update readme with clang-cl cmake instructions 2025-01-07 13:07:54 -08:00
daanx
248d8aad71 Merge branch 'dev' into dev2 2025-01-06 12:08:43 -08:00
daanx
1711a82756 add test for mimalloc override on Windows (issue #981) 2025-01-06 12:08:15 -08:00
Daan Leijen
5764845c4d fix build on msvc in C compilation; fix build with clang-cl on Windows 2025-01-06 10:27:46 -08:00
Daan Leijen
c23fbaa16a bump version for further development 2025-01-05 15:46:11 -08:00
Daan Leijen
9ec5da08b2 bump version for further development 2025-01-05 15:45:04 -08:00
Daan Leijen
83fd6e33fb Merge branch 'dev' of e:\dev\mimalloc3 into dev 2025-01-05 15:43:15 -08:00
daanx
53d8b771aa Merge branch 'dev' into dev2 2025-01-05 11:42:23 -08:00
daanx
aa8e8ab58d remove mi_debug_show_arenas parameter 2025-01-05 11:42:17 -08:00
daanx
4ded84afdc Merge branch 'dev' into dev2 2025-01-05 11:12:34 -08:00
daanx
de0324e1a7 return length from _mi_snprintf 2025-01-04 22:01:28 -08:00
daanx
8d8f2ad190 Merge branch 'dev' into dev2 2025-01-04 17:45:22 -08:00
daanx
a415940604 move singleton pages to the full queue at allocation time 2025-01-04 17:44:56 -08:00
Daan Leijen
ffdea63673 update readme 2025-01-03 20:34:14 -08:00
Daan Leijen
1d8348b411 Merge branch 'dev2' 2025-01-03 20:28:03 -08:00
Daan Leijen
c9b8b82bf6 merge from dev2 2025-01-03 18:22:26 -08:00
Daan
2765ec9302 Merge branch 'dev-slice' 2024-05-21 12:27:13 -07:00
Daan
a1b284de0a Merge branch 'dev-slice' 2024-05-13 10:18:56 -07:00
Daan
8b15203950 merge from dev-slice 2024-05-13 09:15:44 -07:00
Daan
229ec9cbdc merge from dev-slice v2.1.4 2024-04-22 11:10:56 -07:00
microsoft-github-policy-service[bot]
4e50d6714d
Auto merge mandatory file pr
This pr is auto merged as it contains a mandatory file and is opened for more than 10 days.
2023-06-12 18:55:34 +00:00
microsoft-github-policy-service[bot]
5c90133021
Microsoft mandatory file 2023-06-02 17:40:26 +00:00
daanx
2cbf68b5e7 Merge branch 'dev-slice' 2023-04-24 09:32:44 -07:00
Daan Leijen
5ac9e36ed6 Merge branch 'dev-slice' 2023-04-03 12:39:35 -07:00
Daan Leijen
acdd35290e Merge branch 'dev-slice' 2023-03-29 16:43:22 -07:00
Daan Leijen
70450b80d2 fix readme links 2023-03-29 16:40:50 -07:00
Daan Leijen
c2e5031710 merge from dev-slice 2023-03-29 16:33:27 -07:00
Daan Leijen
dd7348066f Merge branch 'dev-slice' 2022-12-23 13:35:58 -08:00
Daan Leijen
df6e288519 merge from dev-slice v2.0.9 2022-12-23 13:34:21 -08:00
Daan
9b558e2a07
Merge pull request #655 from rganesan/patch-1
Fix typo
2022-12-19 17:26:55 -08:00
Ganesan Rajagopal
aea0de4777
Fix typo 2022-12-03 16:27:33 +05:30
Daan
c7d4a099d9
Merge pull request #641 from ofek/patch-1
Fix typo
2022-11-07 18:00:10 -08:00
Ofek Lev
18a4b90501
Fix typo 2022-11-05 16:29:18 -04:00
Daan
0e3d543a13
Update readme.md 2022-11-03 17:11:21 -07:00
69 changed files with 1931 additions and 770 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
build
ide/vs20??/*.db
ide/vs20??/*.opendb
ide/vs20??/*.user
@ -8,3 +9,5 @@ docs/
*.zip
*.tar
*.gz
.vscode
.DS_STore

View file

@ -14,7 +14,7 @@ option(MI_TRACK_VALGRIND "Compile with Valgrind support (adds a small overhea
option(MI_TRACK_ASAN "Compile with address sanitizer support (adds a small overhead)" OFF)
option(MI_TRACK_ETW "Compile with Windows event tracing (ETW) support (adds a small overhead)" OFF)
option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF)
option(MI_OPT_ARCH "Only for optimized builds: turn on architecture specific optimizations (for arm64: '-march=armv8.1-a' (2016))" ON)
option(MI_OPT_ARCH "Only for optimized builds: turn on architecture specific optimizations (for arm64: '-march=armv8.1-a' (2016))" OFF)
option(MI_SEE_ASM "Generate assembly files" OFF)
option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON)
option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON)
@ -35,6 +35,10 @@ option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead
option(MI_NO_THP "Disable transparent huge pages support on Linux/Android for the mimalloc process only" OFF)
option(MI_EXTRA_CPPDEFS "Extra pre-processor definitions (use as `-DMI_EXTRA_CPPDEFS=\"opt1=val1;opt2=val2\"`)" "")
# negated options for vcpkg features
option(MI_NO_USE_CXX "Use plain C compilation (has priority over MI_USE_CXX)" OFF)
option(MI_NO_OPT_ARCH "Do not use architecture specific optimizations (like '-march=armv8.1-a' for example) (has priority over MI_OPT_ARCH)" OFF)
# deprecated options
option(MI_WIN_USE_FLS "Use Fiber local storage on Windows to detect thread termination (deprecated)" OFF)
option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF)
@ -74,6 +78,19 @@ else()
set(mi_defines "")
endif()
# pass git revision as a define
if(EXISTS "${CMAKE_SOURCE_DIR}/.git/index")
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} "describe" OUTPUT_VARIABLE mi_git_describe RESULT_VARIABLE mi_git_res ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(mi_git_res EQUAL "0")
list(APPEND mi_defines "MI_GIT_DESCRIBE=${mi_git_describe}")
# add to dependencies so we rebuild if the git head commit changes
set_property(GLOBAL APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/.git/index")
endif()
endif()
endif()
# -----------------------------------------------------------------------------
# Convenience: set default build type and compiler depending on the build directory
# -----------------------------------------------------------------------------
@ -99,13 +116,55 @@ if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$")
set(MI_SECURE "ON")
endif()
# Determine architecture
set(MI_OPT_ARCH_FLAGS "")
set(MI_ARCH "unknown")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|i[3456]86)$" OR CMAKE_GENERATOR_PLATFORM MATCHES "^(x86|Win32)$")
set(MI_ARCH "x86")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|x64|amd64|AMD64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "x64" OR "x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES) # must be before arm64
set(MI_ARCH "x64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|armv[89].?|ARM64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64" OR "arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
set(MI_ARCH "arm64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|armv[34567]|ARM)$")
set(MI_ARCH "arm32")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv|riscv32|riscv64)$")
if(CMAKE_SIZEOF_VOID_P==4)
set(MI_ARCH "riscv32")
else()
set(MI_ARCH "riscv64")
endif()
else()
set(MI_ARCH ${CMAKE_SYSTEM_PROCESSOR})
endif()
message(STATUS "Architecture: ${MI_ARCH}") # (${CMAKE_SYSTEM_PROCESSOR}, ${CMAKE_GENERATOR_PLATFORM}, ${CMAKE_GENERATOR})")
# negative overrides (mainly to support vcpkg features)
if(MI_NO_USE_CXX)
set(MI_USE_CXX "OFF")
endif()
if(MI_NO_OPT_ARCH)
set(MI_OPT_ARCH "OFF")
elseif(MI_ARCH STREQUAL "arm64")
set(MI_OPT_ARCH "ON") # enable armv8.1-a by default on arm64 unless MI_NO_OPT_ARCH is set
endif()
# -----------------------------------------------------------------------------
# Process options
# -----------------------------------------------------------------------------
if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
set(MI_CLANG_CL "ON")
endif()
# put -Wall early so other warnings can be disabled selectively
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang")
list(APPEND mi_cflags -Wall -Wextra -Wpedantic)
if (MI_CLANG_CL)
list(APPEND mi_cflags -W)
else()
list(APPEND mi_cflags -Wall -Wextra -Wpedantic)
endif()
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
list(APPEND mi_cflags -Wall -Wextra)
@ -118,14 +177,6 @@ if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel")
set(MI_USE_CXX "ON")
endif()
if(CMAKE_BUILD_TYPE MATCHES "Release|RelWithDebInfo")
if (NOT MI_OPT_ARCH)
message(STATUS "Architecture specific optimizations are disabled (MI_OPT_ARCH=OFF)")
endif()
else()
set(MI_OPT_ARCH OFF)
endif()
if(MI_OVERRIDE)
message(STATUS "Override standard malloc (MI_OVERRIDE=ON)")
if(APPLE)
@ -332,28 +383,6 @@ if(MI_WIN_USE_FIXED_TLS)
list(APPEND mi_defines MI_WIN_USE_FIXED_TLS=1)
endif()
# Determine architecture
set(MI_OPT_ARCH_FLAGS "")
set(MI_ARCH "unknown")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|i[3456]86)$" OR CMAKE_GENERATOR_PLATFORM MATCHES "^(x86|Win32)$")
set(MI_ARCH "x86")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|x64|amd64|AMD64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "x64") # must be before arm64
set(MI_ARCH "x64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|armv8.?|ARM64)$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
set(MI_ARCH "arm64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|armv[34567]|ARM)$")
set(MI_ARCH "arm32")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv|riscv32|riscv64)$")
if(CMAKE_SIZEOF_VOID_P==4)
set(MI_ARCH "riscv32")
else()
set(MI_ARCH "riscv64")
endif()
else()
set(MI_ARCH ${CMAKE_SYSTEM_PROCESSOR})
endif()
message(STATUS "Architecture: ${MI_ARCH}") # (${CMAKE_SYSTEM_PROCESSOR}, ${CMAKE_GENERATOR_PLATFORM}, ${CMAKE_GENERATOR})")
# Check /proc/cpuinfo for an SV39 MMU and limit the virtual address bits.
# (this will skip the aligned hinting in that case. Issue #939, #949)
if (EXISTS /proc/cpuinfo)
@ -371,7 +400,7 @@ endif()
# endif()
# Compiler flags
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU" AND NOT MI_CLANG_CL)
list(APPEND mi_cflags -Wno-unknown-pragmas -fvisibility=hidden)
if(NOT MI_USE_CXX)
list(APPEND mi_cflags -Wstrict-prototypes)
@ -385,7 +414,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "Intel")
list(APPEND mi_cflags -fvisibility=hidden)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku")
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku" AND NOT MI_CLANG_CL)
if(MI_LOCAL_DYNAMIC_TLS)
list(APPEND mi_cflags -ftls-model=local-dynamic)
else()
@ -401,16 +430,23 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM
if(MI_OVERRIDE)
list(APPEND mi_cflags -fno-builtin-malloc)
endif()
endif()
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku")
if(MI_OPT_ARCH)
if(MI_ARCH STREQUAL "arm64")
set(MI_OPT_ARCH_FLAGS "-march=armv8.1-a") # fast atomics
if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang" AND CMAKE_OSX_ARCHITECTURES) # to support multi-arch binaries (#999)
if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
list(APPEND MI_OPT_ARCH_FLAGS "-Xarch_arm64;-march=armv8.1-a")
endif()
elseif(MI_ARCH STREQUAL "arm64")
set(MI_OPT_ARCH_FLAGS "-march=armv8.1-a") # fast atomics
endif()
endif()
endif()
if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914)
list(APPEND mi_cflags /Zc:__cplusplus)
if(MI_OPT_ARCH)
if(MI_OPT_ARCH AND NOT MI_CLANG_CL)
if(MI_ARCH STREQUAL "arm64")
set(MI_OPT_ARCH_FLAGS "/arch:armv8.1") # fast atomics
endif()
@ -418,7 +454,7 @@ if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914)
endif()
if(MINGW)
add_definitions(-D_WIN32_WINNT=0x601) # issue #976
add_definitions(-D_WIN32_WINNT=0x600) # issue #976
endif()
if(MI_OPT_ARCH_FLAGS)
@ -464,13 +500,13 @@ else()
endif()
endif()
# -----------------------------------------------------------------------------
# Install and output names
# -----------------------------------------------------------------------------
# dynamic/shared library and symlinks always go to /usr/local/lib equivalent
set(mi_install_libdir "${CMAKE_INSTALL_LIBDIR}")
set(mi_install_bindir "${CMAKE_INSTALL_BINDIR}")
# we use ${CMAKE_INSTALL_BINDIR} and ${CMAKE_INSTALL_LIBDIR}.
# static libraries and object files, includes, and cmake config files
# are either installed at top level, or use versioned directories for side-by-side installation (default)
@ -484,19 +520,22 @@ else()
set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info
endif()
set(mi_basename "mimalloc")
set(mi_libname "mimalloc")
if(MI_SECURE)
set(mi_basename "${mi_basename}-secure")
set(mi_libname "${mi_libname}-secure")
endif()
if(MI_TRACK_VALGRIND)
set(mi_basename "${mi_basename}-valgrind")
set(mi_libname "${mi_libname}-valgrind")
endif()
if(MI_TRACK_ASAN)
set(mi_basename "${mi_basename}-asan")
set(mi_libname "${mi_libname}-asan")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC)
if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$"))
set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
list(APPEND mi_defines "MI_CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LC}") #todo: multi-config project needs $<CONFIG> ?
if(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")
list(APPEND mi_defines MI_BUILD_RELEASE)
else()
set(mi_libname "${mi_libname}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
endif()
if(MI_BUILD_SHARED)
@ -513,7 +552,7 @@ if(MI_BUILD_TESTS)
endif()
message(STATUS "")
message(STATUS "Library base name: ${mi_basename}")
message(STATUS "Library name : ${mi_libname}")
message(STATUS "Version : ${mi_version}.${mi_version_patch}")
message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}")
if(MI_USE_CXX)
@ -534,7 +573,7 @@ message(STATUS "")
# shared library
if(MI_BUILD_SHARED)
add_library(mimalloc SHARED ${mi_sources})
set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_basename} )
set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_libname} )
target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT)
target_compile_options(mimalloc PRIVATE ${mi_cflags} ${mi_cflags_dynamic})
target_link_libraries(mimalloc PRIVATE ${mi_libraries})
@ -542,15 +581,30 @@ if(MI_BUILD_SHARED)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_incdir}>
)
install(TARGETS mimalloc EXPORT mimalloc ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir})
if(WIN32 AND NOT MINGW)
# On windows, the import library name for the dll would clash with the static mimalloc.lib library
# so we postfix the dll import library with `.dll.lib` (and also the .pdb debug file)
set_property(TARGET mimalloc PROPERTY ARCHIVE_OUTPUT_NAME "${mi_libname}.dll" )
install(FILES "$<TARGET_FILE_DIR:mimalloc>/${mi_libname}.dll.lib" DESTINATION ${CMAKE_INSTALL_LIBDIR})
set_property(TARGET mimalloc PROPERTY PDB_NAME "${mi_libname}.dll")
# don't try to install the pdb since it may not be generated depending on the configuration
# install(FILES "$<TARGET_FILE_DIR:mimalloc>/${mi_libname}.dll.pdb" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(WIN32 AND MI_WIN_REDIRECT)
if(MINGW)
set_property(TARGET mimalloc PROPERTY PREFIX "")
endif()
# On windows, link and copy the mimalloc redirection dll too.
if(CMAKE_GENERATOR_PLATFORM STREQUAL "arm64ec")
set(MIMALLOC_REDIRECT_SUFFIX "-arm64ec")
elseif(MI_ARCH STREQUAL "x64")
set(MIMALLOC_REDIRECT_SUFFIX "")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
message(STATUS "Note: x64 code emulated on Windows for arm64 should use an arm64ec build of 'mimalloc-override.dll'")
message(STATUS " with 'mimalloc-redirect-arm64ec.dll'. See the 'bin\\readme.md' for more information.")
message(STATUS "Note: x64 code emulated on Windows for arm64 should use an arm64ec build of 'mimalloc.dll'")
message(STATUS " together with 'mimalloc-redirect-arm64ec.dll'. See the 'bin\\readme.md' for more information.")
endif()
elseif(MI_ARCH STREQUAL "x86")
set(MIMALLOC_REDIRECT_SUFFIX "32")
@ -558,20 +612,19 @@ if(MI_BUILD_SHARED)
set(MIMALLOC_REDIRECT_SUFFIX "-${MI_ARCH}") # -arm64 etc.
endif()
target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib)
target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) # the DLL import library
add_custom_command(TARGET mimalloc POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" $<TARGET_FILE_DIR:mimalloc>
COMMENT "Copy mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll to output directory")
install(FILES "$<TARGET_FILE_DIR:mimalloc>/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${mi_install_bindir})
install(FILES "$<TARGET_FILE_DIR:mimalloc>/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
install(TARGETS mimalloc EXPORT mimalloc ARCHIVE DESTINATION ${mi_install_libdir} RUNTIME DESTINATION ${mi_install_bindir} LIBRARY DESTINATION ${mi_install_libdir})
install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir})
endif()
# static library
if (MI_BUILD_STATIC)
add_library(mimalloc-static STATIC ${mi_sources})
set_property(TARGET mimalloc-static PROPERTY OUTPUT_NAME ${mi_libname})
set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags} ${mi_cflags_static})
@ -580,15 +633,6 @@ if (MI_BUILD_STATIC)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_incdir}>
)
if(WIN32)
# When building both static and shared libraries on Windows, a static library should use a
# different output name to avoid the conflict with the import library of a shared one.
string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename})
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name})
else()
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})
endif()
install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_objdir} LIBRARY)
install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir})
endif()
@ -597,6 +641,7 @@ endif()
install(FILES include/mimalloc.h DESTINATION ${mi_install_incdir})
install(FILES include/mimalloc-override.h DESTINATION ${mi_install_incdir})
install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_incdir})
install(FILES include/mimalloc-stats.h DESTINATION ${mi_install_incdir})
install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_cmakedir})
install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_cmakedir})
@ -613,12 +658,15 @@ if (MI_BUILD_OBJECT)
)
# Copy the generated object file (`static.o`) to the output directory (as `mimalloc.o`)
if(NOT WIN32)
if(CMAKE_GENERATOR MATCHES "^Visual Studio.*$")
set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/mimalloc-obj.dir/$<CONFIG>/static${CMAKE_C_OUTPUT_EXTENSION}")
else()
set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}")
set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_basename}${CMAKE_C_OUTPUT_EXTENSION}")
add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}")
add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out})
endif()
set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_libname}${CMAKE_C_OUTPUT_EXTENSION}")
add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}")
add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out})
# the following seems to lead to cmake warnings/errors on some systems, disable for now :-(
# install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_objdir})
@ -627,22 +675,23 @@ if (MI_BUILD_OBJECT)
# but that fails cmake versions less than 3.10 so we leave it as is for now
install(FILES ${mimalloc-obj-static}
DESTINATION ${mi_install_objdir}
RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} )
RENAME ${mi_libname}${CMAKE_C_OUTPUT_EXTENSION} )
endif()
# pkg-config file support
set(pc_libraries "")
set(mi_pc_libraries "")
foreach(item IN LISTS mi_libraries)
if(item MATCHES " *[-].*")
set(pc_libraries "${pc_libraries} ${item}")
set(mi_pc_libraries "${mi_pc_libraries} ${item}")
else()
set(pc_libraries "${pc_libraries} -l${item}")
set(mi_pc_libraries "${mi_pc_libraries} -l${item}")
endif()
endforeach()
include("cmake/JoinPaths.cmake")
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
join_paths(mi_pc_includedir "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(mi_pc_libdir "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
configure_file(mimalloc.pc.in mimalloc.pc @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc"
@ -657,15 +706,41 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc"
if (MI_BUILD_TESTS)
enable_testing()
# static link tests
foreach(TEST_NAME api api-fill stress)
add_executable(mimalloc-test-${TEST_NAME} test/test-${TEST_NAME}.c)
target_compile_definitions(mimalloc-test-${TEST_NAME} PRIVATE ${mi_defines})
target_compile_options(mimalloc-test-${TEST_NAME} PRIVATE ${mi_cflags})
target_include_directories(mimalloc-test-${TEST_NAME} PRIVATE include)
target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries})
if(MI_BUILD_SHARED AND (MI_TRACK_ASAN OR MI_DEBUG_TSAN OR MI_DEBUG_UBSAN))
target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries})
else()
target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc-static ${mi_libraries})
endif()
add_test(NAME test-${TEST_NAME} COMMAND mimalloc-test-${TEST_NAME})
endforeach()
# dynamic override test
if(MI_BUILD_SHARED AND NOT (MI_TRACK_ASAN OR MI_DEBUG_TSAN OR MI_DEBUG_UBSAN))
add_executable(mimalloc-test-stress-dynamic test/test-stress.c)
target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE ${mi_defines} "USE_STD_MALLOC=1")
if(WIN32)
target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE "MI_LINK_VERSION=1")
endif()
target_compile_options(mimalloc-test-stress-dynamic PRIVATE ${mi_cflags})
target_include_directories(mimalloc-test-stress-dynamic PRIVATE include)
target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # mi_version
if(WIN32)
add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_SHOW_STATS=1 $<TARGET_FILE:mimalloc-test-stress-dynamic>)
else()
if(APPLE)
set(LD_PRELOAD "DYLD_INSERT_LIBRARIES")
else()
set(LD_PRELOAD "LD_PRELOAD")
endif()
add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_SHOW_STATS=1 ${LD_PRELOAD}=$<TARGET_FILE:mimalloc> $<TARGET_FILE:mimalloc-test-stress-dynamic>)
endif()
endif()
endif()
# -----------------------------------------------------------------------------

View file

@ -7,9 +7,9 @@ trigger:
branches:
include:
- master
- dev
- dev2
- dev3
- dev2
- dev
tags:
include:
- v*
@ -34,6 +34,14 @@ jobs:
BuildType: secure
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON
MSBuildConfiguration: Release
Debug x86:
BuildType: debug
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -A Win32
MSBuildConfiguration: Debug
Release x86:
BuildType: release
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -A Win32
MSBuildConfiguration: Release
steps:
- task: CMake@1
inputs:
@ -241,49 +249,6 @@ jobs:
workingDirectory: $(BuildType)
displayName: CTest
- job:
displayName: Ubuntu 20.04
pool:
vmImage:
ubuntu-20.04
strategy:
matrix:
Debug:
CC: gcc
CXX: g++
BuildType: debug
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
Debug++:
CC: gcc
CXX: g++
BuildType: debug-cxx
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON
Debug Clang:
CC: clang
CXX: clang++
BuildType: debug-clang
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
Debug++ Clang:
CC: clang
CXX: clang++
BuildType: debug-clang-cxx
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON
Release Clang:
CC: clang
CXX: clang++
BuildType: release-clang
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
steps:
- task: CMake@1
inputs:
workingDirectory: $(BuildType)
cmakeArgs: .. $(cmakeExtraArgs)
- script: make -j$(nproc) -C $(BuildType)
displayName: Make
- script: ctest --verbose --timeout 240
workingDirectory: $(BuildType)
displayName: CTest
- job:
displayName: macOS 15 (Sequoia)
pool:

BIN
bin/mimalloc-redirect-arm64.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64ec.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64ec.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect32.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect32.lib Normal file → Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,15 +1,14 @@
# Windows Override
<span id="override_on_windows">Dynamically overriding on mimalloc on Windows</span>
is robust and has the particular advantage to be able to redirect all malloc/free calls
that go through the (dynamic) C runtime allocator, including those from other DLL's or
libraries. As it intercepts all allocation calls on a low level, it can be used reliably
on large programs that include other 3rd party components.
<span id="override_on_windows">We use a separate redirection DLL to override mimalloc on Windows</span>
such that we redirect all malloc/free calls that go through the (dynamic) C runtime allocator,
including those from other DLL's or libraries. As it intercepts all allocation calls on a low level,
it can be used on large programs that include other 3rd party components.
There are four requirements to make the overriding work well:
1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch).
2. Link your program explicitly with the `mimalloc.lib` export library for
2. Link your program explicitly with the `mimalloc.dll.lib` export library for
the `mimalloc.dll` -- which contains all mimalloc functionality.
To ensure the `mimalloc.dll` is actually loaded at run-time it is easiest
to insert some call to the mimalloc API in the `main` function, like `mi_version()`
@ -26,6 +25,23 @@ There are four requirements to make the overriding work well:
list of the final executable (so it can intercept all potential allocations).
You can use `minject -l <exe>` to check this if needed.
```csharp
┌──────────────┐
│ Your Program │
└────┬─────────┘
│ mi_version() ┌───────────────┐ ┌───────────────────────┐
├──────────────►│ mimalloc.dll ├────►│ mimalloc-redirect.dll │
│ └──────┬────────┘ └───────────────────────┘
│ ▼
│ malloc() etc. ┌──────────────┐
├──────────────►│ ucrtbase.dll │
│ └──────────────┘
└──────────────► ...
```
For best performance on Windows with C++, it
is also recommended to also override the `new`/`delete` operations (by including
[`mimalloc-new-delete.h`](../include/mimalloc-new-delete.h)

View file

@ -1,6 +1,6 @@
set(mi_version_major 2)
set(mi_version_minor 1)
set(mi_version_patch 9)
set(mi_version_minor 2)
set(mi_version_patch 3)
set(mi_version ${mi_version_major}.${mi_version_minor})
set(PACKAGE_VERSION ${mi_version})

View file

@ -0,0 +1,63 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO microsoft/mimalloc
HEAD_REF master
# The "REF" can be a commit hash, branch name (dev2), or a version (v2.2.1).
# REF "v${VERSION}"
REF e2db21e9ba9fb9172b7b0aa0fe9b8742525e8774
# The sha512 is the hash of the tar.gz bundle.
# (To get the sha512, run `vcpkg install mimalloc[override] --overlay-ports=<dir of this file>` and copy the sha from the error message.)
SHA512 8cbb601fdf8b46dd6a9c0d314d6da9d4960699853829e96d2470753867f90689fb4caeaf30d628943fd388670dc11902dbecc9cc7c329b99a510524a09bdb612
)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
c MI_NO_USE_CXX
guarded MI_GUARDED
secure MI_SECURE
override MI_OVERRIDE
optarch MI_OPT_ARCH
optsimd MI_OPT_SIMD
xmalloc MI_XMALLOC
asm MI_SEE_ASM
)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" MI_BUILD_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" MI_BUILD_SHARED)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
-DMI_USE_CXX=ON
-DMI_BUILD_TESTS=OFF
-DMI_BUILD_OBJECT=ON
-DMI_BUILD_STATIC=${MI_BUILD_STATIC}
-DMI_BUILD_SHARED=${MI_BUILD_SHARED}
-DMI_INSTALL_TOPLEVEL=ON
${FEATURE_OPTIONS}
)
vcpkg_cmake_install()
vcpkg_copy_pdbs()
file(COPY
"${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake"
"${CMAKE_CURRENT_LIST_DIR}/usage"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}"
)
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/mimalloc)
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
# todo: why is this needed?
vcpkg_replace_string(
"${CURRENT_PACKAGES_DIR}/include/mimalloc.h"
"!defined(MI_SHARED_LIB)"
"0 // !defined(MI_SHARED_LIB)"
)
endif()
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share")
vcpkg_fixup_pkgconfig()
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")

40
contrib/vcpkg/readme.md Normal file
View file

@ -0,0 +1,40 @@
# Vcpkg support
This directory is meant to provide the sources for the official [vcpkg port]
of mimalloc, but can also be used to override the official port with
your own variant.
For example, you can edit the [`portfile.cmake`](portfile.cmake)
to check out a specific commit, version, or branch of mimalloc, or set further options.
You can install such custom port as:
```sh
$ vcpkg install "mimalloc[override]" --recurse --overlay-ports=./contrib/vcpkg
```
This will also show the correct sha512 hash if you use a custom version.
Another way is to refer to the overlay from the [vcpkg-configuration.json](https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-configuration-json) file.
See also the vcpkg [documentation](https://learn.microsoft.com/en-us/vcpkg/produce/update-package-version) for more information.
# Using mimalloc from vcpkg
When using [cmake with vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/get-started?pivots=shell-powershell),
you can use mimalloc from the `CMakeLists.txt` as:
```cmake
find_package(mimalloc CONFIG REQUIRED)
target_link_libraries(main PRIVATE mimalloc)
```
See [`test/CMakeLists.txt](../../test/CMakeLists.txt) for more examples.
# Acknowledgements
The original port for vckpg was contributed by many people, including: @vicroms, @myd7349, @PhoubeHui, @LilyWangL,
@JonLiu1993, @RT2Code, Remy Tassoux, @wangao, @BillyONeal, @jiayuehua, @dg0yt, @gerar-ryan-immersaview, @nickdademo,
and @jimwang118 -- Thank you so much!
[vcpkg port]: https://github.com/microsoft/vcpkg/tree/master/ports/mimalloc

20
contrib/vcpkg/usage Normal file
View file

@ -0,0 +1,20 @@
Use the following CMake targets to import mimalloc:
find_package(mimalloc CONFIG REQUIRED)
target_link_libraries(main PRIVATE mimalloc)
And use mimalloc in your sources as:
#include <mimalloc.h>
#include <stdio.h>
int main(int argc, char** argv) {
int* p = mi_malloc_tp(int);
*p = mi_version();
printf("mimalloc version: %d\n", *p);
mi_free(p);
return 0;
}
When dynamically overriding on Windows, ensure `mimalloc.dll` is linked through some call to
mimalloc (e.g. `mi_version()`), and that the `mimalloc-redirect.dll` is in the same directory.
See https://github.com/microsoft/mimalloc/blob/dev/bin/readme.md for detailed information.

View file

@ -0,0 +1,20 @@
_find_package(${ARGS})
if(CMAKE_CURRENT_LIST_DIR STREQUAL "${MIMALLOC_CMAKE_DIR}/${MIMALLOC_VERSION_DIR}")
set(MIMALLOC_INCLUDE_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include")
# As in vcpkg.cmake
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
set(MIMALLOC_LIBRARY_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib")
else()
set(MIMALLOC_LIBRARY_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib")
endif()
set(MIMALLOC_OBJECT_DIR "${MIMALLOC_LIBRARY_DIR}")
set(MIMALLOC_TARGET_DIR "${MIMALLOC_LIBRARY_DIR}")
endif()
# vcpkg always configures either a static or dynamic library.
# ensure to always expose the mimalloc target as either the static or dynamic build.
if(TARGET mimalloc-static AND NOT TARGET mimalloc)
add_library(mimalloc INTERFACE IMPORTED)
set_target_properties(mimalloc PROPERTIES INTERFACE_LINK_LIBRARIES mimalloc-static)
endif()

45
contrib/vcpkg/vcpkg.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "mimalloc",
"version": "2.2.2",
"port-version": 1,
"description": "Compact general purpose allocator with excellent performance",
"homepage": "https://github.com/microsoft/mimalloc",
"license": "MIT",
"supports": "!uwp",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"features": {
"c": {
"description": "Use C11 compilation (this can still override new/delete)"
},
"override": {
"description": "Override the standard malloc/free interface"
},
"secure": {
"description": "Use full security mitigations (like guard pages and randomization)"
},
"guarded": {
"description": "Use build that support guard pages after objects controlled with MIMALLOC_GUARDED_SAMPLE_RATE"
},
"xmalloc": {
"description": "If out-of-memory, call abort() instead of returning NULL"
},
"optarch": {
"description": "Use architecture specific optimizations (on x64: '-march=haswell;-mavx2', on arm64: '-march=armv8.1-a')"
},
"optsimd": {
"description": "Allow use of SIMD instructions (avx2 or neon) (requires 'optarch' to be enabled)"
},
"asm": {
"description": "Generate assembly files"
}
}
}

View file

@ -120,49 +120,52 @@
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -422,12 +425,14 @@
<ClCompile Include="..\..\src\alloc-posix.c" />
<ClCompile Include="..\..\src\alloc.c" />
<ClCompile Include="..\..\src\arena-abandon.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\arena.c" />
<ClCompile Include="..\..\src\bitmap.c">
@ -481,6 +486,7 @@
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h" />
<ClInclude Include="..\..\include\mimalloc-new-delete.h" />
<ClInclude Include="..\..\include\mimalloc-stats.h" />
<ClInclude Include="..\..\include\mimalloc\atomic.h" />
<ClInclude Include="..\..\include\mimalloc\internal.h" />
<ClInclude Include="..\..\include\mimalloc\prim.h" />

View file

@ -16,9 +16,6 @@
<ClCompile Include="..\..\src\arena.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\arena-abandoned.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\bitmap.c">
<Filter>Sources</Filter>
</ClCompile>
@ -64,6 +61,9 @@
<ClCompile Include="..\..\src\stats.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\arena-abandon.c">
<Filter>Sources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\mimalloc\atomic.h">
@ -93,6 +93,9 @@
<ClInclude Include="..\..\include\mimalloc\prim.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="..\..\include\mimalloc-stats.h">
<Filter>Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Headers">

View file

@ -160,6 +160,9 @@
<TargetExt>.dll</TargetExt>
<TargetName>mimalloc</TargetName>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -180,6 +183,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)"</Command>
@ -208,6 +213,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)"</Command>
@ -236,6 +243,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64.dll" "$(OutputPath)"</Command>
@ -264,6 +273,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64ec.dll" "$(OutputPath)"</Command>
@ -296,6 +307,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)"</Command>
@ -328,6 +341,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)"</Command>
@ -361,6 +376,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64.dll" "$(OutputPath)"</Command>
@ -394,6 +411,8 @@
</ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
<ProgramDatabaseFile>$(OutDir)$(TargetName).dll.pdb</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect-arm64ec.dll" "$(OutputPath)"</Command>
@ -408,6 +427,7 @@
<ClInclude Include="..\..\include\mimalloc-etw.h" />
<ClInclude Include="..\..\include\mimalloc-new-delete.h" />
<ClInclude Include="..\..\include\mimalloc-override.h" />
<ClInclude Include="..\..\include\mimalloc-stats.h" />
<ClInclude Include="..\..\include\mimalloc\atomic.h" />
<ClInclude Include="..\..\include\mimalloc\internal.h" />
<ClInclude Include="..\..\include\mimalloc\prim.h" />
@ -438,15 +458,15 @@
</ClCompile>
<ClCompile Include="..\..\src\alloc-posix.c" />
<ClCompile Include="..\..\src\alloc.c" />
<ClCompile Include="..\..\src\arena-abandoned.c">
<ClCompile Include="..\..\src\arena-abandon.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\arena.c" />
<ClCompile Include="..\..\src\bitmap.c" />
@ -482,9 +502,6 @@
<ClCompile Include="..\..\src\segment.c" />
<ClCompile Include="..\..\src\stats.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\include\mimalloc-etw-gen.man" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -16,9 +16,6 @@
<ClCompile Include="..\..\src\arena.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\arena-abandoned.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\bitmap.c">
<Filter>Sources</Filter>
</ClCompile>
@ -61,6 +58,9 @@
<ClCompile Include="..\..\src\stats.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\..\src\arena-abandon.c">
<Filter>Sources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\mimalloc\atomic.h">
@ -96,6 +96,9 @@
<ClInclude Include="..\..\include\mimalloc\prim.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="..\..\include\mimalloc-stats.h">
<Filter>Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Headers">
@ -105,9 +108,4 @@
<UniqueIdentifier>{94b40bdc-a741-45dd-81aa-c05fabcd2970}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="..\..\include\mimalloc-etw-gen.man">
<Filter>Sources</Filter>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,355 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64EC">
<Configuration>Debug</Configuration>
<Platform>ARM64EC</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64EC">
<Configuration>Release</Configuration>
<Platform>ARM64EC</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{FEF7869F-750E-4C21-A04D-22707CC66879}</ProjectGuid>
<RootNamespace>mimalloc-test-override-dep</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>mimalloc-test-override-dep</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<EntryPointSymbol>
</EntryPointSymbol>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\test\main-override-dep.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\test\main-override-dep.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -148,6 +148,9 @@
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -347,6 +350,9 @@
<ProjectReference Include="mimalloc-override-dll.vcxproj">
<Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project>
</ProjectReference>
<ProjectReference Include="mimalloc-override-test-dep.vcxproj">
<Project>{fef7869f-750e-4c21-a04d-22707cc66879}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -148,6 +148,9 @@
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>

View file

@ -148,6 +148,9 @@
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>

View file

@ -148,6 +148,9 @@
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>

View file

@ -5,11 +5,13 @@ VisualStudioVersion = 17.12.35527.113
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-lib", "mimalloc-lib.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-static", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-dll", "mimalloc-override-dll.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-override-dep", "mimalloc-override-test-dep.vcxproj", "{FEF7869F-750E-4C21-A04D-22707CC66879}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-override", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}"
EndProject
@ -75,6 +77,22 @@ Global
{ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64
{ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32
{ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.Build.0 = Debug|ARM64EC
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32
{FEF7869F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64.ActiveCfg = Release|ARM64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64.Build.0 = Release|ARM64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.ActiveCfg = Release|ARM64EC
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|ARM64EC.Build.0 = Release|ARM64EC
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32
{FEF7869F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32
{FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.ActiveCfg = Debug|ARM64
{FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64.Build.0 = Debug|ARM64
{FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC

103
include/mimalloc-stats.h Normal file
View file

@ -0,0 +1,103 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2025, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
#pragma once
#ifndef MIMALLOC_STATS_H
#define MIMALLOC_STATS_H
#include <mimalloc.h>
#include <stdint.h>
#define MI_STAT_VERSION 1 // increased on every backward incompatible change
// count allocation over time
typedef struct mi_stat_count_s {
int64_t total; // total allocated
int64_t peak; // peak allocation
int64_t current; // current allocation
} mi_stat_count_t;
// counters only increase
typedef struct mi_stat_counter_s {
int64_t total; // total count
} mi_stat_counter_t;
#define MI_STAT_FIELDS() \
MI_STAT_COUNT(pages) /* count of mimalloc pages */ \
MI_STAT_COUNT(reserved) /* reserved memory bytes */ \
MI_STAT_COUNT(committed) /* committed bytes */ \
MI_STAT_COUNT(reset) /* reset bytes */ \
MI_STAT_COUNT(purged) /* purged bytes */ \
MI_STAT_COUNT(page_committed) /* committed memory inside pages */ \
MI_STAT_COUNT(pages_abandoned) /* abandonded pages count */ \
MI_STAT_COUNT(threads) /* number of threads */ \
MI_STAT_COUNT(malloc_normal) /* allocated bytes <= MI_LARGE_OBJ_SIZE_MAX */ \
MI_STAT_COUNT(malloc_huge) /* allocated bytes in huge pages */ \
MI_STAT_COUNT(malloc_requested) /* malloc requested bytes */ \
\
MI_STAT_COUNTER(mmap_calls) \
MI_STAT_COUNTER(commit_calls) \
MI_STAT_COUNTER(reset_calls) \
MI_STAT_COUNTER(purge_calls) \
MI_STAT_COUNTER(arena_count) /* number of memory arena's */ \
MI_STAT_COUNTER(malloc_normal_count) /* number of blocks <= MI_LARGE_OBJ_SIZE_MAX */ \
MI_STAT_COUNTER(malloc_huge_count) /* number of huge bloks */ \
MI_STAT_COUNTER(malloc_guarded_count) /* number of allocations with guard pages */ \
\
/* internal statistics */ \
MI_STAT_COUNTER(arena_rollback_count) \
MI_STAT_COUNTER(arena_purges) \
MI_STAT_COUNTER(pages_extended) /* number of page extensions */ \
MI_STAT_COUNTER(pages_retire) /* number of pages that are retired */ \
MI_STAT_COUNTER(page_searches) /* searches for a fresh page */ \
/* only on v1 and v2 */ \
MI_STAT_COUNT(segments) \
MI_STAT_COUNT(segments_abandoned) \
MI_STAT_COUNT(segments_cache) \
MI_STAT_COUNT(_segments_reserved) \
/* only on v3 */ \
MI_STAT_COUNTER(pages_reclaim_on_alloc) \
MI_STAT_COUNTER(pages_reclaim_on_free) \
MI_STAT_COUNTER(pages_reabandon_full) \
MI_STAT_COUNTER(pages_unabandon_busy_wait) \
// Define the statistics structure
#define MI_BIN_HUGE (73U) // see types.h
#define MI_STAT_COUNT(stat) mi_stat_count_t stat;
#define MI_STAT_COUNTER(stat) mi_stat_counter_t stat;
typedef struct mi_stats_s
{
int version;
MI_STAT_FIELDS()
// future extension
mi_stat_count_t _stat_reserved[4];
mi_stat_counter_t _stat_counter_reserved[4];
// size segregated statistics
mi_stat_count_t malloc_bins[MI_BIN_HUGE+1]; // allocation per size bin
mi_stat_count_t page_bins[MI_BIN_HUGE+1]; // pages allocated per size bin
} mi_stats_t;
#undef MI_STAT_COUNT
#undef MI_STAT_COUNTER
// Exported definitions
#ifdef __cplusplus
extern "C" {
#endif
mi_decl_export void mi_stats_get( size_t stats_size, mi_stats_t* stats ) mi_attr_noexcept;
mi_decl_export char* mi_stats_get_json( size_t buf_size, char* buf ) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL
#ifdef __cplusplus
}
#endif
#endif // MIMALLOC_STATS_H

View file

@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file
#ifndef MIMALLOC_H
#define MIMALLOC_H
#define MI_MALLOC_VERSION 219 // major + 2 digits minor
#define MI_MALLOC_VERSION 223 // major + 2 digits minor
// ------------------------------------------------------
// Compiler specific attributes
@ -149,12 +149,12 @@ typedef void (mi_cdecl mi_error_fun)(int err, void* arg);
mi_decl_export void mi_register_error(mi_error_fun* fun, void* arg);
mi_decl_export void mi_collect(bool force) mi_attr_noexcept;
mi_decl_export void mi_collect_reduce(size_t target_thread_owned) mi_attr_noexcept;
mi_decl_export int mi_version(void) mi_attr_noexcept;
mi_decl_export void mi_stats_reset(void) mi_attr_noexcept;
mi_decl_export void mi_stats_merge(void) mi_attr_noexcept;
mi_decl_export void mi_stats_print(void* out) mi_attr_noexcept; // backward compatibility: `out` is ignored and should be NULL
mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
mi_decl_export void mi_options_print(void) mi_attr_noexcept;
mi_decl_export void mi_process_init(void) mi_attr_noexcept;
mi_decl_export void mi_thread_init(void) mi_attr_noexcept;
@ -271,13 +271,14 @@ mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_block
mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept;
mi_decl_nodiscard mi_decl_export bool mi_is_redirected(void) mi_attr_noexcept;
mi_decl_export int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept;
mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept;
mi_decl_export int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept;
mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept;
mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept;
mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept;
mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept;
mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept;
mi_decl_export void mi_debug_show_arenas(bool show_inuse) mi_attr_noexcept;
mi_decl_export void mi_debug_show_arenas(void) mi_attr_noexcept;
mi_decl_export void mi_arenas_print(void) mi_attr_noexcept;
// Experimental: heaps associated with specific memory arena's
typedef int mi_arena_id_t;
@ -292,17 +293,26 @@ mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t a
#endif
// Experimental: allow sub-processes whose memory segments stay separated (and no reclamation between them)
// Used for example for separate interpreter's in one process.
// Experimental: allow sub-processes whose memory areas stay separated (and no reclamation between them)
// Used for example for separate interpreters in one process.
typedef void* mi_subproc_id_t;
mi_decl_export mi_subproc_id_t mi_subproc_main(void);
mi_decl_export mi_subproc_id_t mi_subproc_new(void);
mi_decl_export void mi_subproc_delete(mi_subproc_id_t subproc);
mi_decl_export void mi_subproc_add_current_thread(mi_subproc_id_t subproc); // this should be called right after a thread is created (and no allocation has taken place yet)
// Experimental: visit abandoned heap areas (from threads that have been terminated)
// Experimental: visit abandoned heap areas (that are not owned by a specific heap)
mi_decl_export bool mi_abandoned_visit_blocks(mi_subproc_id_t subproc_id, int heap_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg);
// Experimental: objects followed by a guard page.
// A sample rate of 0 disables guarded objects, while 1 uses a guard page for every object.
// A seed of 0 uses a random start point. Only objects within the size bound are eligable for guard pages.
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed);
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max);
// Experimental: communicate that the thread is part of a threadpool
mi_decl_export void mi_thread_set_in_threadpool(void) mi_attr_noexcept;
// Experimental: create a new heap with a specified heap tag. Set `allow_destroy` to false to allow the thread
// to reclaim abandoned memory (with a compatible heap_tag and arena_id) but in that case `mi_heap_destroy` will
// fall back to `mi_heap_delete`.
@ -310,12 +320,8 @@ mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new_ex(int heap_tag, bool al
// deprecated
mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept;
mi_decl_export void mi_collect_reduce(size_t target_thread_owned) mi_attr_noexcept;
// Experimental: objects followed by a guard page.
// A sample rate of 0 disables guarded objects, while 1 uses a guard page for every object.
// A seed of 0 uses a random start point. Only objects within the size bound are eligable for guard pages.
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed);
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max);
// ------------------------------------------------------
@ -380,6 +386,7 @@ typedef enum mi_option_e {
mi_option_guarded_sample_rate, // 1 out of N allocations in the min/max range will be guarded (=1000)
mi_option_guarded_sample_seed, // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0)
mi_option_target_segments_per_thread, // experimental (=0)
mi_option_generic_collect, // collect heaps every N (=10000) generic allocation calls
_mi_option_last,
// legacy option names
mi_option_large_os_pages = mi_option_allow_large_os_pages,

View file

@ -31,33 +31,33 @@ terms of the MIT license. A copy of the license can be found in the file
#if defined(__cplusplus)
// Use C++ atomics
#include <atomic>
#define _Atomic(tp) std::atomic<tp>
#define mi_atomic(name) std::atomic_##name
#define mi_memory_order(name) std::memory_order_##name
#if (__cplusplus >= 202002L) // c++20, see issue #571
#define MI_ATOMIC_VAR_INIT(x) x
#define _Atomic(tp) std::atomic<tp>
#define mi_atomic(name) std::atomic_##name
#define mi_memory_order(name) std::memory_order_##name
#if (__cplusplus >= 202002L) // c++20, see issue #571
#define MI_ATOMIC_VAR_INIT(x) x
#elif !defined(ATOMIC_VAR_INIT)
#define MI_ATOMIC_VAR_INIT(x) x
#define MI_ATOMIC_VAR_INIT(x) x
#else
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
#endif
#elif defined(_MSC_VER)
// Use MSVC C wrapper for C11 atomics
#define _Atomic(tp) tp
#define MI_ATOMIC_VAR_INIT(x) x
#define mi_atomic(name) mi_atomic_##name
#define mi_memory_order(name) mi_memory_order_##name
#define _Atomic(tp) tp
#define MI_ATOMIC_VAR_INIT(x) x
#define mi_atomic(name) mi_atomic_##name
#define mi_memory_order(name) mi_memory_order_##name
#else
// Use C11 atomics
#include <stdatomic.h>
#define mi_atomic(name) atomic_##name
#define mi_memory_order(name) memory_order_##name
#define mi_atomic(name) atomic_##name
#define mi_memory_order(name) memory_order_##name
#if (__STDC_VERSION__ >= 201710L) // c17, see issue #735
#define MI_ATOMIC_VAR_INIT(x) x
#define MI_ATOMIC_VAR_INIT(x) x
#elif !defined(ATOMIC_VAR_INIT)
#define MI_ATOMIC_VAR_INIT(x) x
#define MI_ATOMIC_VAR_INIT(x) x
#else
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
#endif
#endif
@ -129,6 +129,12 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub);
static inline int64_t mi_atomic_addi64_relaxed(volatile int64_t* p, int64_t add) {
return mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed));
}
static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) {
const int64_t add = mi_atomic_load_relaxed((_Atomic(int64_t)*)padd);
if (add != 0) {
mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed));
}
}
static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) {
int64_t current = mi_atomic_load_relaxed((_Atomic(int64_t)*)p);
while (current < x && !mi_atomic_cas_weak_release((_Atomic(int64_t)*)p, &current, x)) { /* nothing */ };
@ -260,6 +266,13 @@ static inline int64_t mi_atomic_addi64_relaxed(volatile _Atomic(int64_t)*p, int6
return current;
#endif
}
static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) {
const int64_t add = *padd;
if (add != 0) {
mi_atomic_addi64_relaxed((volatile _Atomic(int64_t)*)p, add);
}
}
static inline void mi_atomic_maxi64_relaxed(volatile _Atomic(int64_t)*p, int64_t x) {
int64_t current;
do {
@ -290,6 +303,7 @@ static inline bool mi_atomic_casi64_strong_acq_rel(volatile _Atomic(int64_t*)p,
#define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
#define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
#define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
#define mi_atomic_exchange_ptr_relaxed(tp,p,x) (tp*)mi_atomic_exchange_relaxed((_Atomic(uintptr_t)*)(p),(uintptr_t)x)
#define mi_atomic_exchange_ptr_release(tp,p,x) (tp*)mi_atomic_exchange_release((_Atomic(uintptr_t)*)(p),(uintptr_t)x)
#define mi_atomic_exchange_ptr_acq_rel(tp,p,x) (tp*)mi_atomic_exchange_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t)x)
@ -356,8 +370,9 @@ static inline void mi_atomic_yield(void) {
_mm_pause();
}
#elif (defined(__GNUC__) || defined(__clang__)) && \
(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \
defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)) || defined(__POWERPC__)
(defined(__x86_64__) || defined(__i386__) || \
defined(__aarch64__) || defined(__arm__) || \
defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__))
#if defined(__x86_64__) || defined(__i386__)
static inline void mi_atomic_yield(void) {
__asm__ volatile ("pause" ::: "memory");
@ -366,10 +381,16 @@ static inline void mi_atomic_yield(void) {
static inline void mi_atomic_yield(void) {
__asm__ volatile("wfe");
}
#elif (defined(__arm__) && __ARM_ARCH__ >= 7)
#elif defined(__arm__)
#if __ARM_ARCH >= 7
static inline void mi_atomic_yield(void) {
__asm__ volatile("yield" ::: "memory");
}
#else
static inline void mi_atomic_yield(void) {
__asm__ volatile ("nop" ::: "memory");
}
#endif
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__)
#ifdef __APPLE__
static inline void mi_atomic_yield(void) {
@ -380,10 +401,6 @@ static inline void mi_atomic_yield(void) {
__asm__ __volatile__ ("or 27,27,27" ::: "memory");
}
#endif
#elif defined(__armel__) || defined(__ARMEL__)
static inline void mi_atomic_yield(void) {
__asm__ volatile ("nop" ::: "memory");
}
#endif
#elif defined(__sun)
// Fallback for other archs

View file

@ -64,8 +64,8 @@ terms of the MIT license. A copy of the license can be found in the file
// "libc.c"
#include <stdarg.h>
void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args);
void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...);
int _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args);
int _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...);
char _mi_toupper(char c);
int _mi_strnicmp(const char* s, const char* t, size_t n);
void _mi_strlcpy(char* dest, const char* src, size_t dest_size);
@ -77,6 +77,7 @@ bool _mi_getenv(const char* name, char* result, size_t result_size);
// "options.c"
void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message);
void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...);
void _mi_message(const char* fmt, ...);
void _mi_warning_message(const char* fmt, ...);
void _mi_verbose_message(const char* fmt, ...);
void _mi_trace_message(const char* fmt, ...);
@ -126,6 +127,7 @@ bool _mi_os_has_virtual_reserve(void);
bool _mi_os_reset(void* addr, size_t size);
bool _mi_os_commit(void* p, size_t size, bool* is_zero);
bool _mi_os_commit_ex(void* addr, size_t size, bool* is_zero, size_t stat_size);
bool _mi_os_decommit(void* addr, size_t size);
bool _mi_os_protect(void* addr, size_t size);
bool _mi_os_unprotect(void* addr, size_t size);
@ -215,8 +217,8 @@ void _mi_deferred_free(mi_heap_t* heap, bool force);
void _mi_page_free_collect(mi_page_t* page,bool force);
void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page); // callback from segments
size_t _mi_bin_size(uint8_t bin); // for stats
uint8_t _mi_bin(size_t size); // for stats
size_t _mi_bin_size(size_t bin); // for stats
size_t _mi_bin(size_t size); // for stats
// "heap.c"
void _mi_heap_init(mi_heap_t* heap, mi_tld_t* tld, mi_arena_id_t arena_id, bool noreclaim, uint8_t tag);
@ -313,7 +315,7 @@ bool _mi_page_is_valid(mi_page_t* page);
#define MI_INIT64(x) MI_INIT32(x),MI_INIT32(x)
#define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x)
#define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x)
#define MI_INIT74(x) MI_INIT64(x),MI_INIT8(x),x(),x()
#include <string.h>
// initialize a local variable to zero; use memset as compilers optimize constant sized memset's
@ -922,21 +924,21 @@ static inline size_t _mi_os_numa_node_count(void) {
#include <limits.h> // LONG_MAX
#define MI_HAVE_FAST_BITSCAN
static inline size_t mi_clz(uintptr_t x) {
if (x==0) return MI_INTPTR_BITS;
#if (INTPTR_MAX == LONG_MAX)
return __builtin_clzl(x);
#else
return __builtin_clzll(x);
#endif
static inline size_t mi_clz(size_t x) {
if (x==0) return MI_SIZE_BITS;
#if (SIZE_MAX == ULONG_MAX)
return __builtin_clzl(x);
#else
return __builtin_clzll(x);
#endif
}
static inline size_t mi_ctz(uintptr_t x) {
if (x==0) return MI_INTPTR_BITS;
#if (INTPTR_MAX == LONG_MAX)
return __builtin_ctzl(x);
#else
return __builtin_ctzll(x);
#endif
static inline size_t mi_ctz(size_t x) {
if (x==0) return MI_SIZE_BITS;
#if (SIZE_MAX == ULONG_MAX)
return __builtin_ctzl(x);
#else
return __builtin_ctzll(x);
#endif
}
#elif defined(_MSC_VER)
@ -944,24 +946,24 @@ static inline size_t mi_ctz(uintptr_t x) {
#include <limits.h> // LONG_MAX
#include <intrin.h> // BitScanReverse64
#define MI_HAVE_FAST_BITSCAN
static inline size_t mi_clz(uintptr_t x) {
if (x==0) return MI_INTPTR_BITS;
static inline size_t mi_clz(size_t x) {
if (x==0) return MI_SIZE_BITS;
unsigned long idx;
#if (INTPTR_MAX == LONG_MAX)
_BitScanReverse(&idx, x);
#else
_BitScanReverse64(&idx, x);
#endif
return ((MI_INTPTR_BITS - 1) - idx);
#if (SIZE_MAX == ULONG_MAX)
_BitScanReverse(&idx, x);
#else
_BitScanReverse64(&idx, x);
#endif
return ((MI_SIZE_BITS - 1) - idx);
}
static inline size_t mi_ctz(uintptr_t x) {
if (x==0) return MI_INTPTR_BITS;
static inline size_t mi_ctz(size_t x) {
if (x==0) return MI_SIZE_BITS;
unsigned long idx;
#if (INTPTR_MAX == LONG_MAX)
_BitScanForward(&idx, x);
#else
_BitScanForward64(&idx, x);
#endif
#if (SIZE_MAX == ULONG_MAX)
_BitScanForward(&idx, x);
#else
_BitScanForward64(&idx, x);
#endif
return idx;
}
@ -1024,11 +1026,26 @@ static inline size_t mi_clz(size_t x) {
#endif
// "bit scan reverse": Return index of the highest bit (or MI_INTPTR_BITS if `x` is zero)
static inline size_t mi_bsr(uintptr_t x) {
return (x==0 ? MI_INTPTR_BITS : MI_INTPTR_BITS - 1 - mi_clz(x));
// "bit scan reverse": Return index of the highest bit (or MI_SIZE_BITS if `x` is zero)
static inline size_t mi_bsr(size_t x) {
return (x==0 ? MI_SIZE_BITS : MI_SIZE_BITS - 1 - mi_clz(x));
}
size_t _mi_popcount_generic(size_t x);
static inline size_t mi_popcount(size_t x) {
if (x<=1) return x;
if (x==SIZE_MAX) return MI_SIZE_BITS;
#if defined(__GNUC__)
#if (SIZE_MAX == ULONG_MAX)
return __builtin_popcountl(x);
#else
return __builtin_popcountll(x);
#endif
#else
return _mi_popcount_generic(x);
#endif
}
// ---------------------------------------------------------------------------------
// Provide our own `_mi_memcpy` for potential performance optimizations.

View file

@ -22,14 +22,14 @@ terms of the MIT license. A copy of the license can be found in the file
// OS memory configuration
typedef struct mi_os_mem_config_s {
size_t page_size; // default to 4KiB
size_t large_page_size; // 0 if not supported, usually 2MiB (4MiB on Windows)
size_t alloc_granularity; // smallest allocation size (usually 4KiB, on Windows 64KiB)
size_t physical_memory; // physical memory size
size_t virtual_address_bits; // usually 48 or 56 bits on 64-bit systems. (used to determine secure randomization)
bool has_overcommit; // can we reserve more memory than can be actually committed?
bool has_partial_free; // can allocated blocks be freed partially? (true for mmap, false for VirtualAlloc)
bool has_virtual_reserve; // supports virtual address space reservation? (if true we can reserve virtual address space without using commit or physical memory)
size_t page_size; // default to 4KiB
size_t large_page_size; // 0 if not supported, usually 2MiB (4MiB on Windows)
size_t alloc_granularity; // smallest allocation size (usually 4KiB, on Windows 64KiB)
size_t physical_memory_in_kib; // physical memory size in KiB
size_t virtual_address_bits; // usually 48 or 56 bits on 64-bit systems. (used to determine secure randomization)
bool has_overcommit; // can we reserve more memory than can be actually committed?
bool has_partial_free; // can allocated blocks be freed partially? (true for mmap, false for VirtualAlloc)
bool has_virtual_reserve; // supports virtual address space reservation? (if true we can reserve virtual address space without using commit or physical memory)
} mi_os_mem_config_t;
// Initialize
@ -124,7 +124,7 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap);
//-------------------------------------------------------------------
// Access to TLS (thread local storage) slots.
// We need fast access to both a unique thread id (in `free.c:mi_free`) and
// to a thread-local heap pointer (in `alloc.c:mi_malloc`).
// to a thread-local heap pointer (in `alloc.c:mi_malloc`).
// To achieve this we use specialized code for various platforms.
//-------------------------------------------------------------------

View file

@ -22,6 +22,7 @@ terms of the MIT license. A copy of the license can be found in the file
// --------------------------------------------------------------------------
#include <mimalloc-stats.h>
#include <stddef.h> // ptrdiff_t
#include <stdint.h> // uintptr_t, uint16_t, etc
#include "atomic.h" // _Atomic
@ -66,10 +67,10 @@ terms of the MIT license. A copy of the license can be found in the file
// #define MI_DEBUG 2 // + internal assertion checks
// #define MI_DEBUG 3 // + extensive internal invariant checking (cmake -DMI_DEBUG_FULL=ON)
#if !defined(MI_DEBUG)
#if !defined(NDEBUG) || defined(_DEBUG)
#define MI_DEBUG 2
#else
#if defined(MI_BUILD_RELEASE) || defined(NDEBUG)
#define MI_DEBUG 0
#else
#define MI_DEBUG 2
#endif
#endif
@ -197,14 +198,16 @@ typedef int32_t mi_ssize_t;
#define MI_SMALL_PAGE_SIZE (MI_ZU(1)<<MI_SMALL_PAGE_SHIFT)
#define MI_MEDIUM_PAGE_SIZE (MI_ZU(1)<<MI_MEDIUM_PAGE_SHIFT)
#define MI_SMALL_OBJ_SIZE_MAX (MI_SMALL_PAGE_SIZE/4) // 8KiB on 64-bit
#define MI_MEDIUM_OBJ_SIZE_MAX (MI_MEDIUM_PAGE_SIZE/4) // 128KiB on 64-bit
#define MI_SMALL_OBJ_SIZE_MAX (MI_SMALL_PAGE_SIZE/8) // 8 KiB on 64-bit
#define MI_MEDIUM_OBJ_SIZE_MAX (MI_MEDIUM_PAGE_SIZE/8) // 64 KiB on 64-bit
#define MI_MEDIUM_OBJ_WSIZE_MAX (MI_MEDIUM_OBJ_SIZE_MAX/MI_INTPTR_SIZE)
#define MI_LARGE_OBJ_SIZE_MAX (MI_SEGMENT_SIZE/2) // 16MiB on 64-bit
#define MI_LARGE_OBJ_SIZE_MAX (MI_SEGMENT_SIZE/2) // 16 MiB on 64-bit
#define MI_LARGE_OBJ_WSIZE_MAX (MI_LARGE_OBJ_SIZE_MAX/MI_INTPTR_SIZE)
// Maximum number of size classes. (spaced exponentially in 12.5% increments)
#define MI_BIN_HUGE (73U)
#if MI_BIN_HUGE != 73U
#error "mimalloc internal: expecting 73 bins"
#endif
#if (MI_MEDIUM_OBJ_WSIZE_MAX >= 655360)
#error "mimalloc internal: define more bins"
@ -559,6 +562,8 @@ struct mi_heap_s {
size_t page_count; // total number of pages in the `pages` queues.
size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues)
size_t page_retired_max; // largest retired index into the `pages` array.
long generic_count; // how often is `_mi_malloc_generic` called?
long generic_collect_count; // how often is `_mi_malloc_generic` called without collecting?
mi_heap_t* next; // list of heaps per thread
bool no_reclaim; // `true` if this heap should not reclaim abandoned pages
uint8_t tag; // custom tag, can be used for separating heaps based on the object types
@ -574,6 +579,60 @@ struct mi_heap_s {
};
// ------------------------------------------------------
// Sub processes do not reclaim or visit segments
// from other sub processes. These are essentially the
// static variables of a process.
// ------------------------------------------------------
struct mi_subproc_s {
_Atomic(size_t) abandoned_count; // count of abandoned segments for this sub-process
_Atomic(size_t) abandoned_os_list_count; // count of abandoned segments in the os-list
mi_lock_t abandoned_os_lock; // lock for the abandoned os segment list (outside of arena's) (this lock protect list operations)
mi_lock_t abandoned_os_visit_lock; // ensure only one thread per subproc visits the abandoned os list
mi_segment_t* abandoned_os_list; // doubly-linked list of abandoned segments outside of arena's (in OS allocated memory)
mi_segment_t* abandoned_os_list_tail; // the tail-end of the list
mi_memid_t memid; // provenance of this memory block
};
// ------------------------------------------------------
// Thread Local data
// ------------------------------------------------------
// A "span" is is an available range of slices. The span queues keep
// track of slice spans of at most the given `slice_count` (but more than the previous size class).
typedef struct mi_span_queue_s {
mi_slice_t* first;
mi_slice_t* last;
size_t slice_count;
} mi_span_queue_t;
#define MI_SEGMENT_BIN_MAX (35) // 35 == mi_segment_bin(MI_SLICES_PER_SEGMENT)
// Segments thread local data
typedef struct mi_segments_tld_s {
mi_span_queue_t spans[MI_SEGMENT_BIN_MAX+1]; // free slice spans inside segments
size_t count; // current number of segments;
size_t peak_count; // peak number of segments
size_t current_size; // current size of all segments
size_t peak_size; // peak size of all segments
size_t reclaim_count;// number of reclaimed (abandoned) segments
mi_subproc_t* subproc; // sub-process this thread belongs to.
mi_stats_t* stats; // points to tld stats
} mi_segments_tld_t;
// Thread local data
struct mi_tld_s {
unsigned long long heartbeat; // monotonic heartbeat count
bool recurse; // true if deferred was called; used to prevent infinite recursion.
mi_heap_t* heap_backing; // backing heap of this thread (cannot be deleted)
mi_heap_t* heaps; // list of heaps in this thread (so we can abandon all when the thread terminates)
mi_segments_tld_t segments; // segment tld
mi_stats_t stats; // statistics
};
// ------------------------------------------------------
// Debug
@ -609,10 +668,10 @@ void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line
#define mi_assert_expensive(x)
#endif
// ------------------------------------------------------
// Statistics
// ------------------------------------------------------
#ifndef MI_STAT
#if (MI_DEBUG>0)
#define MI_STAT 2
@ -621,59 +680,9 @@ void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line
#endif
#endif
typedef struct mi_stat_count_s {
int64_t allocated;
int64_t freed;
int64_t peak;
int64_t current;
} mi_stat_count_t;
typedef struct mi_stat_counter_s {
int64_t total;
int64_t count;
} mi_stat_counter_t;
typedef struct mi_stats_s {
mi_stat_count_t segments;
mi_stat_count_t pages;
mi_stat_count_t reserved;
mi_stat_count_t committed;
mi_stat_count_t reset;
mi_stat_count_t purged;
mi_stat_count_t page_committed;
mi_stat_count_t segments_abandoned;
mi_stat_count_t pages_abandoned;
mi_stat_count_t threads;
mi_stat_count_t normal;
mi_stat_count_t huge;
mi_stat_count_t large;
mi_stat_count_t malloc;
mi_stat_count_t segments_cache;
mi_stat_counter_t pages_extended;
mi_stat_counter_t mmap_calls;
mi_stat_counter_t commit_calls;
mi_stat_counter_t reset_calls;
mi_stat_counter_t purge_calls;
mi_stat_counter_t page_no_retire;
mi_stat_counter_t searches;
mi_stat_counter_t normal_count;
mi_stat_counter_t huge_count;
mi_stat_counter_t large_count;
mi_stat_counter_t arena_count;
mi_stat_counter_t arena_crossover_count;
mi_stat_counter_t arena_rollback_count;
mi_stat_counter_t guarded_alloc_count;
#if MI_STAT>1
mi_stat_count_t normal_bins[MI_BIN_HUGE+1];
#endif
} mi_stats_t;
// add to stat keeping track of the peak
void _mi_stat_increase(mi_stat_count_t* stat, size_t amount);
void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount);
// adjust stat in special cases to compensate for double counting
void _mi_stat_adjust_increase(mi_stat_count_t* stat, size_t amount);
void _mi_stat_adjust_decrease(mi_stat_count_t* stat, size_t amount);
// counters can just be increased
void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount);
@ -681,71 +690,18 @@ void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount);
#if (MI_STAT)
#define mi_stat_increase(stat,amount) _mi_stat_increase( &(stat), amount)
#define mi_stat_decrease(stat,amount) _mi_stat_decrease( &(stat), amount)
#define mi_stat_counter_increase(stat,amount) _mi_stat_counter_increase( &(stat), amount)
#define mi_stat_adjust_increase(stat,amount) _mi_stat_adjust_increase( &(stat), amount)
#define mi_stat_adjust_decrease(stat,amount) _mi_stat_adjust_decrease( &(stat), amount)
#define mi_stat_counter_increase(stat,amount) _mi_stat_counter_increase( &(stat), amount)
#else
#define mi_stat_increase(stat,amount) ((void)0)
#define mi_stat_decrease(stat,amount) ((void)0)
#define mi_stat_counter_increase(stat,amount) ((void)0)
#define mi_stat_adjuct_increase(stat,amount) ((void)0)
#define mi_stat_adjust_decrease(stat,amount) ((void)0)
#define mi_stat_counter_increase(stat,amount) ((void)0)
#endif
#define mi_heap_stat_counter_increase(heap,stat,amount) mi_stat_counter_increase( (heap)->tld->stats.stat, amount)
#define mi_heap_stat_increase(heap,stat,amount) mi_stat_increase( (heap)->tld->stats.stat, amount)
#define mi_heap_stat_decrease(heap,stat,amount) mi_stat_decrease( (heap)->tld->stats.stat, amount)
// ------------------------------------------------------
// Sub processes do not reclaim or visit segments
// from other sub processes
// ------------------------------------------------------
struct mi_subproc_s {
_Atomic(size_t) abandoned_count; // count of abandoned segments for this sub-process
_Atomic(size_t) abandoned_os_list_count; // count of abandoned segments in the os-list
mi_lock_t abandoned_os_lock; // lock for the abandoned os segment list (outside of arena's) (this lock protect list operations)
mi_lock_t abandoned_os_visit_lock; // ensure only one thread per subproc visits the abandoned os list
mi_segment_t* abandoned_os_list; // doubly-linked list of abandoned segments outside of arena's (in OS allocated memory)
mi_segment_t* abandoned_os_list_tail; // the tail-end of the list
mi_memid_t memid; // provenance of this memory block
};
// ------------------------------------------------------
// Thread Local data
// ------------------------------------------------------
// A "span" is is an available range of slices. The span queues keep
// track of slice spans of at most the given `slice_count` (but more than the previous size class).
typedef struct mi_span_queue_s {
mi_slice_t* first;
mi_slice_t* last;
size_t slice_count;
} mi_span_queue_t;
#define MI_SEGMENT_BIN_MAX (35) // 35 == mi_segment_bin(MI_SLICES_PER_SEGMENT)
// Segments thread local data
typedef struct mi_segments_tld_s {
mi_span_queue_t spans[MI_SEGMENT_BIN_MAX+1]; // free slice spans inside segments
size_t count; // current number of segments;
size_t peak_count; // peak number of segments
size_t current_size; // current size of all segments
size_t peak_size; // peak size of all segments
size_t reclaim_count;// number of reclaimed (abandoned) segments
mi_subproc_t* subproc; // sub-process this thread belongs to.
mi_stats_t* stats; // points to tld stats
} mi_segments_tld_t;
// Thread local data
struct mi_tld_s {
unsigned long long heartbeat; // monotonic heartbeat count
bool recurse; // true if deferred was called; used to prevent infinite recursion.
mi_heap_t* heap_backing; // backing heap of this thread (cannot be deleted)
mi_heap_t* heaps; // list of heaps in this thread (so we can abandon all when the thread terminates)
mi_segments_tld_t segments; // segment tld
mi_stats_t stats; // statistics
};
#define mi_heap_stat_adjust_decrease(heap,stat,amount) mi_stat_adjust_decrease( (heap)->tld->stats.stat, amount)
#endif

View file

@ -1,11 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
libdir=@libdir_for_pc_file@
includedir=@includedir_for_pc_file@
libdir=@mi_pc_libdir@
includedir=@mi_pc_includedir@
Name: @PROJECT_NAME@
Description: A compact general purpose allocator with excellent performance
Version: @PACKAGE_VERSION@
URL: https://github.com/microsoft/mimalloc/
Libs: -L${libdir} -lmimalloc
Libs.private: @pc_libraries@
Libs: -L${libdir} -l@mi_libname@
Libs.private: @mi_pc_libraries@
Cflags: -I${includedir}

View file

@ -12,15 +12,17 @@ is a general purpose allocator with excellent [performance](#performance) charac
Initially developed by Daan Leijen for the runtime systems of the
[Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages.
Latest release tag: `v2.1.9` (2025-01-03).
Latest v1 tag: `v1.8.9` (2024-01-03).
Latest release : `v3.0.3` (beta) (2025-03-28).
Latest v2 release: `v2.2.3` (2025-03-28).
Latest v1 release: `v1.9.3` (2024-03-28).
mimalloc is a drop-in replacement for `malloc` and can be used in other programs
without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as:
```
> LD_PRELOAD=/usr/lib/libmimalloc.so myprogram
```
It also includes a robust way to override the default allocator in [Windows](#override_on_windows). Notable aspects of the design include:
It also includes a way to dynamically override the default allocator in [Windows](#override_on_windows).
Notable aspects of the design include:
- __small and consistent__: the library is about 10k LOC using simple and
consistent data structures. This makes it very suitable
@ -70,22 +72,31 @@ Enjoy!
### Branches
* `master`: latest stable release (based on `dev2`).
* `dev`: development branch for mimalloc v1. Use this branch for submitting PR's.
* `master`: latest stable release (still based on `dev2`).
* `dev`: development branch for mimalloc v1. **Use this branch for submitting PR's**.
* `dev2`: development branch for mimalloc v2. This branch is downstream of `dev`
(and is essentially equal to `dev` except for `src/segment.c`). Uses larger sliced segments to manage
mimalloc pages what can reduce fragmentation.
* `dev3`: development branch for mimalloc v3-alpha. This branch is downstream of `dev`. This is still experimental,
but simplifies previous versions by having no segments any more. This improves sharing of memory
between threads, and on certain large workloads uses less memory with less fragmentation.
(and is essentially equal to `dev` except for `src/segment.c`). Uses larger sliced segments to manage
mimalloc pages that can reduce fragmentation.
* `dev3`: development branch for mimalloc v3-beta. This branch is downstream of `dev`. This version
simplifies the lock-free ownership of previous versions, has no thread-local segments any more.
This improves sharing of memory between threads, and on certain large workloads may use less memory
with less fragmentation.
### Releases
* 2025-01-03, `v1.8.9`, `v2.1.9`, `v3.0-alpha`: Interim release. Support Windows arm64. New [guarded](#guarded) build that can place OS
* 2025-03-28, `v1.9.3`, `v2.2.3`, `v3.0.3` (beta): Various small bug and build fixes, including:
fix arm32 pre v7 builds, fix mingw build, get runtime statistics, improve statistic commit counts,
fix execution on non BMI1 x64 systems.
* 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes.
Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`.
Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile.
Upgrade mimalloc-redirect to v1.3.2. `MI_OPT_ARCH` is off by default now but still assumes armv8.1-a on arm64
for fast atomic operations. Add QNX support.
* 2025-01-03, `v1.8.9`, `v2.1.9`, `v3.0.1-alpha`: Interim release. Support Windows arm64. New [guarded](#guarded) build that can place OS
guard pages behind objects to catch buffer overflows as they occur.
Many small fixes: build on Windows arm64, cygwin, riscV, and dragonfly; fix Windows static library initialization to account for
thread local destructors (in Rust/C++); macOS tag change; macOS TLS slot fix; improve stats;
consistent mimalloc.dll on Windows (instead of mimalloc-override.dll); fix mimalloc-redirect on Win11 H2;
consistent `mimalloc.dll` on Windows (instead of `mimalloc-override.dll`); fix mimalloc-redirect on Win11 H2;
add 0-byte to canary; upstream CPython fixes; reduce .bss size; allow fixed TLS slot on Windows for improved performance.
* 2024-05-21, `v1.8.7`, `v2.1.7`: Fix build issues on less common platforms. Started upstreaming patches
from the CPython [integration](https://github.com/python/cpython/issues/113141#issuecomment-2119255217). Upstream `vcpkg` patches.
@ -165,8 +176,8 @@ mimalloc is used in various large scale low-latency services and programs, for e
## Windows
Open `ide/vs2022/mimalloc.sln` in Visual Studio 2022 and build.
The `mimalloc` project builds a static library (in `out/msvc-x64`), while the
`mimalloc-override` project builds a DLL for overriding malloc
The `mimalloc-lib` project builds a static library (in `out/msvc-x64`), while the
`mimalloc-override-dll` project builds DLL for overriding malloc
in the entire program.
## Linux, macOS, BSD, etc.
@ -231,13 +242,21 @@ The cmake build type is specified when actually building, for example:
> cmake --build . --config=Release
```
## Single source
You can also install the [LLVM toolset](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-170#install-1)
on Windows to build with the `clang-cl` compiler directly:
```
> cmake ../.. -G "Visual Studio 17 2022" -T ClangCl
```
## Single Source
You can also directly build the single `src/static.c` file as part of your project without
needing `cmake` at all. Make sure to also add the mimalloc `include` directory to the include path.
# Using the library
# Using the Library
The preferred usage is including `<mimalloc.h>`, linking with
the shared- or static library, and using the `mi_malloc` API exclusively for allocation. For example,
@ -465,18 +484,17 @@ Note that certain security restrictions may apply when doing this from
the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash).
# Windows Override
### Dynamic Override on Windows
<span id="override_on_windows">Dynamically overriding on mimalloc on Windows</span>
is robust and has the particular advantage to be able to redirect all malloc/free calls
that go through the (dynamic) C runtime allocator, including those from other DLL's or
libraries. As it intercepts all allocation calls on a low level, it can be used reliably
on large programs that include other 3rd party components.
<span id="override_on_windows">We use a separate redirection DLL to override mimalloc on Windows</span>
such that we redirect all malloc/free calls that go through the (dynamic) C runtime allocator,
including those from other DLL's or libraries. As it intercepts all allocation calls on a low level,
it can be used on large programs that include other 3rd party components.
There are four requirements to make the overriding work well:
1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch).
2. Link your program explicitly with the `mimalloc.lib` export library for the `mimalloc.dll`.
2. Link your program explicitly with the `mimalloc.dll.lib` export library for the `mimalloc.dll`.
(which must be compiled with `-DMI_OVERRIDE=ON`, which is the default though).
To ensure the `mimalloc.dll` is actually loaded at run-time it is easiest
to insert some call to the mimalloc API in the `main` function, like `mi_version()`
@ -493,9 +511,8 @@ There are four requirements to make the overriding work well:
list of the final executable (so it can intercept all potential allocations).
You can use `minject -l <exe>` to check this if needed.
For best performance on Windows with C++, it
is also recommended to also override the `new`/`delete` operations (by including
[`mimalloc-new-delete.h`](include/mimalloc-new-delete.h)
For best performance on Windows with C++, it is also recommended to also override
the `new`/`delete` operations (by including [`mimalloc-new-delete.h`](include/mimalloc-new-delete.h)
a single(!) source file in your project).
The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic

View file

@ -191,9 +191,6 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t
const bool is_aligned = (((uintptr_t)page->free + offset) & align_mask)==0;
if mi_likely(is_aligned)
{
#if MI_STAT>1
mi_heap_stat_increase(heap, malloc, size);
#endif
void* p = (zero ? _mi_page_malloc_zeroed(heap,page,padsize) : _mi_page_malloc(heap,page,padsize)); // call specific page malloc for better codegen
mi_assert_internal(p != NULL);
mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);

View file

@ -30,6 +30,7 @@ terms of the MIT license. A copy of the license can be found in the file
// Note: in release mode the (inlined) routine is about 7 instructions with a single test.
extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept
{
mi_assert_internal(size >= MI_PADDING_SIZE);
mi_assert_internal(page->block_size == 0 /* empty heap */ || mi_page_block_size(page) >= size);
// check the free list
@ -83,11 +84,12 @@ extern inline void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_
#if (MI_STAT>0)
const size_t bsize = mi_page_usable_block_size(page);
if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) {
mi_heap_stat_increase(heap, normal, bsize);
mi_heap_stat_counter_increase(heap, normal_count, 1);
mi_heap_stat_increase(heap, malloc_normal, bsize);
mi_heap_stat_counter_increase(heap, malloc_normal_count, 1);
#if (MI_STAT>1)
const size_t bin = _mi_bin(bsize);
mi_heap_stat_increase(heap, normal_bins[bin], 1);
mi_heap_stat_increase(heap, malloc_bins[bin], 1);
mi_heap_stat_increase(heap, malloc_requested, size - MI_PADDING_SIZE);
#endif
}
#endif
@ -146,12 +148,6 @@ static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap,
void* const p = _mi_page_malloc_zero(heap, page, size + MI_PADDING_SIZE, zero);
mi_track_malloc(p,size,zero);
#if MI_STAT>1
if (p != NULL) {
if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); }
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
}
#endif
#if MI_DEBUG>3
if (p != NULL && zero) {
mi_assert_expensive(mi_mem_is_zero(p, size));
@ -188,12 +184,6 @@ extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool z
void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment); // note: size can overflow but it is detected in malloc_generic
mi_track_malloc(p,size,zero);
#if MI_STAT>1
if (p != NULL) {
if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); }
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
}
#endif
#if MI_DEBUG>3
if (p != NULL && zero) {
mi_assert_expensive(mi_mem_is_zero(p, size));
@ -640,7 +630,7 @@ static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size) {
// give up to place it right in front of the guard page if the offset is too large for unalignment
offset = MI_BLOCK_ALIGNMENT_MAX;
}
void* p = (uint8_t*)block + offset;
void* p = (uint8_t*)block + offset;
mi_track_align(block, p, offset, obj_size);
mi_track_mem_defined(block, sizeof(mi_block_t));
return p;
@ -662,13 +652,14 @@ mi_decl_restrict void* _mi_heap_malloc_guarded(mi_heap_t* heap, size_t size, boo
void* const p = mi_block_ptr_set_guarded(block, obj_size);
// stats
mi_track_malloc(p, size, zero);
mi_track_malloc(p, size, zero);
if (p != NULL) {
if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); }
#if MI_STAT>1
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
mi_heap_stat_adjust_decrease(heap, malloc_requested, req_size);
mi_heap_stat_increase(heap, malloc_requested, size);
#endif
_mi_stat_counter_increase(&heap->tld->stats.guarded_alloc_count, 1);
_mi_stat_counter_increase(&heap->tld->stats.malloc_guarded_count, 1);
}
#if MI_DEBUG>3
if (p != NULL && zero) {

View file

@ -259,7 +259,7 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t ar
// set the dirty bits (todo: no need for an atomic op here?)
if (arena->memid.initially_zero && arena->blocks_dirty != NULL) {
memid->initially_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL);
memid->initially_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL, NULL);
}
// set commit state
@ -271,10 +271,14 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t ar
// commit requested, but the range may not be committed as a whole: ensure it is committed now
memid->initially_committed = true;
bool any_uncommitted;
_mi_bitmap_claim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted);
size_t already_committed = 0;
_mi_bitmap_claim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted, &already_committed);
if (any_uncommitted) {
mi_assert_internal(already_committed < needed_bcount);
const size_t commit_size = mi_arena_block_size(needed_bcount);
const size_t stat_commit_size = commit_size - mi_arena_block_size(already_committed);
bool commit_zero = false;
if (!_mi_os_commit(p, mi_arena_block_size(needed_bcount), &commit_zero)) {
if (!_mi_os_commit_ex(p, commit_size, &commit_zero, stat_commit_size)) {
memid->initially_committed = false;
}
else {
@ -284,7 +288,14 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t ar
}
else {
// no need to commit, but check if already fully committed
memid->initially_committed = _mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index);
size_t already_committed = 0;
memid->initially_committed = _mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &already_committed);
if (!memid->initially_committed && already_committed > 0) {
// partially committed: as it will be committed at some time, adjust the stats and pretend the range is fully uncommitted.
mi_assert_internal(already_committed < needed_bcount);
_mi_stat_decrease(&_mi_stats_main.committed, mi_arena_block_size(already_committed));
_mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index);
}
}
return p;
@ -468,17 +479,19 @@ static void mi_arena_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks)
const size_t size = mi_arena_block_size(blocks);
void* const p = mi_arena_block_start(arena, bitmap_idx);
bool needs_recommit;
if (_mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx)) {
size_t already_committed = 0;
if (_mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx, &already_committed)) {
// all blocks are committed, we can purge freely
mi_assert_internal(already_committed == blocks);
needs_recommit = _mi_os_purge(p, size);
}
else {
// some blocks are not committed -- this can happen when a partially committed block is freed
// in `_mi_arena_free` and it is conservatively marked as uncommitted but still scheduled for a purge
// we need to ensure we do not try to reset (as that may be invalid for uncommitted memory),
// and also undo the decommit stats (as it was already adjusted)
// we need to ensure we do not try to reset (as that may be invalid for uncommitted memory).
mi_assert_internal(already_committed < blocks);
mi_assert_internal(mi_option_is_enabled(mi_option_purge_decommits));
needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, 0);
needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, mi_arena_block_size(already_committed));
}
// clear the purged blocks
@ -512,7 +525,7 @@ static void mi_arena_schedule_purge(mi_arena_t* arena, size_t bitmap_idx, size_t
else {
// already an expiration was set
}
_mi_bitmap_claim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx, NULL);
_mi_bitmap_claim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx, NULL, NULL);
}
}
@ -554,7 +567,8 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
// reset expire (if not already set concurrently)
mi_atomic_casi64_strong_acq_rel(&arena->purge_expire, &expire, (mi_msecs_t)0);
_mi_stat_counter_increase(&_mi_stats_main.arena_purges, 1);
// potential purges scheduled, walk through the bitmap
bool any_purged = false;
bool full_purge = true;
@ -607,7 +621,7 @@ static void mi_arenas_try_purge( bool force, bool visit_all )
// check if any arena needs purging?
const mi_msecs_t now = _mi_clock_now();
mi_msecs_t arenas_expire = mi_atomic_load_acquire(&mi_arenas_purge_expire);
mi_msecs_t arenas_expire = mi_atomic_loadi64_acquire(&mi_arenas_purge_expire);
if (!force && (arenas_expire == 0 || arenas_expire < now)) return;
const size_t max_arena = mi_atomic_load_acquire(&mi_arena_count);
@ -618,7 +632,7 @@ static void mi_arenas_try_purge( bool force, bool visit_all )
mi_atomic_guard(&purge_guard)
{
// increase global expire: at most one purge per delay cycle
mi_atomic_store_release(&mi_arenas_purge_expire, now + mi_arena_purge_delay());
mi_atomic_storei64_release(&mi_arenas_purge_expire, now + mi_arena_purge_delay());
size_t max_purge_count = (visit_all ? max_arena : 2);
bool all_visited = true;
for (size_t i = 0; i < max_arena; i++) {
@ -635,7 +649,7 @@ static void mi_arenas_try_purge( bool force, bool visit_all )
}
if (all_visited) {
// all arena's were visited and purged: reset global expire
mi_atomic_store_release(&mi_arenas_purge_expire, 0);
mi_atomic_storei64_release(&mi_arenas_purge_expire, 0);
}
}
}
@ -651,15 +665,16 @@ void _mi_arena_free(void* p, size_t size, size_t committed_size, mi_memid_t memi
if (p==NULL) return;
if (size==0) return;
const bool all_committed = (committed_size == size);
const size_t decommitted_size = (committed_size <= size ? size - committed_size : 0);
// need to set all memory to undefined as some parts may still be marked as no_access (like padding etc.)
mi_track_mem_undefined(p,size);
if (mi_memkind_is_os(memid.memkind)) {
// was a direct OS allocation, pass through
if (!all_committed && committed_size > 0) {
// if partially committed, adjust the committed stats (as `_mi_os_free` will increase decommit by the full size)
_mi_stat_decrease(&_mi_stats_main.committed, committed_size);
if (!all_committed && decommitted_size > 0) {
// if partially committed, adjust the committed stats (as `_mi_os_free` will decrease commit by the full size)
_mi_stat_increase(&_mi_stats_main.committed, decommitted_size);
}
_mi_os_free(p, size, memid);
}
@ -693,14 +708,14 @@ void _mi_arena_free(void* p, size_t size, size_t committed_size, mi_memid_t memi
mi_assert_internal(arena->blocks_purge != NULL);
if (!all_committed) {
// mark the entire range as no longer committed (so we recommit the full range when re-using)
// mark the entire range as no longer committed (so we will recommit the full range when re-using)
_mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx);
mi_track_mem_noaccess(p,size);
if (committed_size > 0) {
//if (committed_size > 0) {
// if partially committed, adjust the committed stats (is it will be recommitted when re-using)
// in the delayed purge, we now need to not count a decommit if the range is not marked as committed.
// in the delayed purge, we do no longer decrease the commit if the range is not marked entirely as committed.
_mi_stat_decrease(&_mi_stats_main.committed, committed_size);
}
//}
// note: if not all committed, it may be that the purge will reset/decommit the entire range
// that contains already decommitted parts. Since purge consistently uses reset or decommit that
// works (as we should never reset decommitted parts).
@ -904,7 +919,7 @@ int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noe
----------------------------------------------------------- */
static size_t mi_debug_show_bitmap(const char* prefix, const char* header, size_t block_count, mi_bitmap_field_t* fields, size_t field_count ) {
_mi_verbose_message("%s%s:\n", prefix, header);
_mi_message("%s%s:\n", prefix, header);
size_t bcount = 0;
size_t inuse_count = 0;
for (size_t i = 0; i < field_count; i++) {
@ -921,13 +936,14 @@ static size_t mi_debug_show_bitmap(const char* prefix, const char* header, size_
}
}
buf[MI_BITMAP_FIELD_BITS] = 0;
_mi_verbose_message("%s %s\n", prefix, buf);
_mi_message("%s %s\n", prefix, buf);
}
_mi_verbose_message("%s total ('x'): %zu\n", prefix, inuse_count);
_mi_message("%s total ('x'): %zu\n", prefix, inuse_count);
return inuse_count;
}
void mi_debug_show_arenas(bool show_inuse) mi_attr_noexcept {
void mi_debug_show_arenas(void) mi_attr_noexcept {
const bool show_inuse = true;
size_t max_arenas = mi_atomic_load_relaxed(&mi_arena_count);
size_t inuse_total = 0;
//size_t abandoned_total = 0;
@ -935,7 +951,7 @@ void mi_debug_show_arenas(bool show_inuse) mi_attr_noexcept {
for (size_t i = 0; i < max_arenas; i++) {
mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]);
if (arena == NULL) break;
_mi_verbose_message("arena %zu: %zu blocks of size %zuMiB (in %zu fields) %s\n", i, arena->block_count, MI_ARENA_BLOCK_SIZE / MI_MiB, arena->field_count, (arena->memid.is_pinned ? ", pinned" : ""));
_mi_message("arena %zu: %zu blocks of size %zuMiB (in %zu fields) %s\n", i, arena->block_count, MI_ARENA_BLOCK_SIZE / MI_MiB, arena->field_count, (arena->memid.is_pinned ? ", pinned" : ""));
if (show_inuse) {
inuse_total += mi_debug_show_bitmap(" ", "inuse blocks", arena->block_count, arena->blocks_inuse, arena->field_count);
}
@ -949,9 +965,14 @@ void mi_debug_show_arenas(bool show_inuse) mi_attr_noexcept {
// purge_total += mi_debug_show_bitmap(" ", "purgeable blocks", arena->block_count, arena->blocks_purge, arena->field_count);
//}
}
if (show_inuse) _mi_verbose_message("total inuse blocks : %zu\n", inuse_total);
//if (show_abandoned) _mi_verbose_message("total abandoned blocks: %zu\n", abandoned_total);
//if (show_purge) _mi_verbose_message("total purgeable blocks: %zu\n", purge_total);
if (show_inuse) _mi_message("total inuse blocks : %zu\n", inuse_total);
//if (show_abandoned) _mi_message("total abandoned blocks: %zu\n", abandoned_total);
//if (show_purge) _mi_message("total purgeable blocks: %zu\n", purge_total);
}
void mi_arenas_print(void) mi_attr_noexcept {
mi_debug_show_arenas();
}

View file

@ -81,7 +81,7 @@ inline bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, cons
// on to the next bit range
#ifdef MI_HAVE_FAST_BITSCAN
mi_assert_internal(mapm != 0);
const size_t shift = (count == 1 ? 1 : (MI_INTPTR_BITS - mi_clz(mapm) - bitidx));
const size_t shift = (count == 1 ? 1 : (MI_SIZE_BITS - mi_clz(mapm) - bitidx));
mi_assert_internal(shift > 0 && shift <= count);
#else
const size_t shift = 1;
@ -164,7 +164,7 @@ static bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size
return ((field & mask) == mask);
}
// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically.
// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically.
// Returns `true` if successful when all previous `count` bits were 0.
bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
const size_t idx = mi_bitmap_index_field(bitmap_idx);
@ -172,9 +172,9 @@ bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count
const size_t mask = mi_bitmap_mask_(count, bitidx);
mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
size_t expected = mi_atomic_load_relaxed(&bitmap[idx]);
do {
do {
if ((expected & mask) != 0) return false;
}
}
while (!mi_atomic_cas_strong_acq_rel(&bitmap[idx], &expected, expected | mask));
mi_assert_internal((expected & mask) == 0);
return true;
@ -212,7 +212,7 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit
if (initial == 0) return false;
if (initial >= count) return _mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx); // no need to cross fields (this case won't happen for us)
if (_mi_divide_up(count - initial, MI_BITMAP_FIELD_BITS) >= (bitmap_fields - idx)) return false; // not enough entries
// scan ahead
size_t found = initial;
size_t mask = 0; // mask bits for the final field
@ -260,7 +260,6 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit
} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
// claimed!
mi_stat_counter_increase(_mi_stats_main.arena_crossover_count,1);
*bitmap_idx = mi_bitmap_index_create(idx, initial_idx);
return true;
@ -370,7 +369,7 @@ bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t
// Set `count` bits at `bitmap_idx` to 1 atomically
// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero) {
bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero, size_t* already_set) {
size_t idx = mi_bitmap_index_field(bitmap_idx);
size_t pre_mask;
size_t mid_mask;
@ -378,28 +377,31 @@ bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t co
size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
bool all_zero = true;
bool any_zero = false;
size_t one_count = 0;
_Atomic(size_t)*field = &bitmap[idx];
size_t prev = mi_atomic_or_acq_rel(field++, pre_mask);
if ((prev & pre_mask) != 0) all_zero = false;
if ((prev & pre_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & pre_mask); }
if ((prev & pre_mask) != pre_mask) any_zero = true;
while (mid_count-- > 0) {
prev = mi_atomic_or_acq_rel(field++, mid_mask);
if ((prev & mid_mask) != 0) all_zero = false;
if ((prev & mid_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & mid_mask); }
if ((prev & mid_mask) != mid_mask) any_zero = true;
}
if (post_mask!=0) {
prev = mi_atomic_or_acq_rel(field, post_mask);
if ((prev & post_mask) != 0) all_zero = false;
if ((prev & post_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & post_mask); }
if ((prev & post_mask) != post_mask) any_zero = true;
}
if (pany_zero != NULL) { *pany_zero = any_zero; }
if (already_set != NULL) { *already_set = one_count; };
mi_assert_internal(all_zero ? one_count == 0 : one_count <= count);
return all_zero;
}
// Returns `true` if all `count` bits were 1.
// `any_ones` is `true` if there was at least one bit set to one.
static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones) {
static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones, size_t* already_set) {
size_t idx = mi_bitmap_index_field(bitmap_idx);
size_t pre_mask;
size_t mid_mask;
@ -407,30 +409,33 @@ static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_field
size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
bool all_ones = true;
bool any_ones = false;
size_t one_count = 0;
mi_bitmap_field_t* field = &bitmap[idx];
size_t prev = mi_atomic_load_relaxed(field++);
if ((prev & pre_mask) != pre_mask) all_ones = false;
if ((prev & pre_mask) != 0) any_ones = true;
if ((prev & pre_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & pre_mask); }
while (mid_count-- > 0) {
prev = mi_atomic_load_relaxed(field++);
if ((prev & mid_mask) != mid_mask) all_ones = false;
if ((prev & mid_mask) != 0) any_ones = true;
if ((prev & mid_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & mid_mask); }
}
if (post_mask!=0) {
prev = mi_atomic_load_relaxed(field);
if ((prev & post_mask) != post_mask) all_ones = false;
if ((prev & post_mask) != 0) any_ones = true;
if ((prev & post_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & post_mask); }
}
if (pany_ones != NULL) { *pany_ones = any_ones; }
if (already_set != NULL) { *already_set = one_count; }
mi_assert_internal(all_ones ? one_count == count : one_count < count);
return all_ones;
}
bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
return mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, NULL);
bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, size_t* already_set) {
return mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, NULL, already_set);
}
bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
bool any_ones;
mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, &any_ones);
mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, &any_ones, NULL);
return any_ones;
}

View file

@ -111,9 +111,9 @@ bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t
// Set `count` bits at `bitmap_idx` to 1 atomically
// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero);
bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero, size_t* already_set);
bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, size_t* already_set);
bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
#endif

View file

@ -522,26 +522,24 @@ static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
// only maintain stats for smaller objects if requested
#if (MI_STAT>0)
static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) {
#if (MI_STAT < 2)
MI_UNUSED(block);
#endif
mi_heap_t* const heap = mi_heap_get_default();
const size_t bsize = mi_page_usable_block_size(page);
#if (MI_STAT>1)
const size_t usize = mi_page_usable_size_of(page, block);
mi_heap_stat_decrease(heap, malloc, usize);
#endif
// #if (MI_STAT>1)
// const size_t usize = mi_page_usable_size_of(page, block);
// mi_heap_stat_decrease(heap, malloc_requested, usize);
// #endif
if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) {
mi_heap_stat_decrease(heap, normal, bsize);
mi_heap_stat_decrease(heap, malloc_normal, bsize);
#if (MI_STAT > 1)
mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], 1);
mi_heap_stat_decrease(heap, malloc_bins[_mi_bin(bsize)], 1);
#endif
}
else if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
mi_heap_stat_decrease(heap, large, bsize);
}
//else if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
// mi_heap_stat_decrease(heap, malloc_large, bsize);
//}
else {
mi_heap_stat_decrease(heap, huge, bsize);
mi_heap_stat_decrease(heap, malloc_huge, bsize);
}
}
#else

View file

@ -166,7 +166,7 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
// collect abandoned segments (in particular, purge expired parts of segments in the abandoned segment list)
// note: forced purge can be quite expensive if many threads are created/destroyed so we do not force on abandonment
_mi_abandoned_collect(heap, collect == MI_FORCE /* force? */, &heap->tld->segments);
// if forced, collect thread data cache on program-exit (or shared library unload)
if (force && is_main_thread && mi_heap_is_backing(heap)) {
_mi_thread_data_collect(); // collect thread data cache
@ -174,6 +174,11 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
// collect arenas (this is program wide so don't force purges on abandonment of threads)
_mi_arenas_collect(collect == MI_FORCE /* force purge? */);
// merge statistics
if (collect <= MI_FORCE) {
mi_stats_merge();
}
}
void _mi_heap_collect_abandon(mi_heap_t* heap) {
@ -331,24 +336,25 @@ static bool _mi_heap_page_destroy(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_
// stats
const size_t bsize = mi_page_block_size(page);
if (bsize > MI_MEDIUM_OBJ_SIZE_MAX) {
if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
mi_heap_stat_decrease(heap, large, bsize);
}
else {
mi_heap_stat_decrease(heap, huge, bsize);
//if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
// mi_heap_stat_decrease(heap, malloc_large, bsize);
//}
//else
{
mi_heap_stat_decrease(heap, malloc_huge, bsize);
}
}
#if (MI_STAT)
#if (MI_STAT>0)
_mi_page_free_collect(page, false); // update used count
const size_t inuse = page->used;
if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
mi_heap_stat_decrease(heap, normal, bsize * inuse);
#if (MI_STAT>1)
mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], inuse);
#endif
mi_heap_stat_decrease(heap, malloc_normal, bsize * inuse);
#if (MI_STAT>1)
mi_heap_stat_decrease(heap, malloc_bins[_mi_bin(bsize)], inuse);
#endif
}
mi_heap_stat_decrease(heap, malloc, bsize * inuse); // todo: off for aligned blocks...
#endif
// mi_heap_stat_decrease(heap, malloc_requested, bsize * inuse); // todo: off for aligned blocks...
#endif
/// pretend it is all free now
mi_assert_internal(mi_page_thread_free(page) == NULL);
@ -564,7 +570,7 @@ void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page) {
static void mi_get_fast_divisor(size_t divisor, uint64_t* magic, size_t* shift) {
mi_assert_internal(divisor > 0 && divisor <= UINT32_MAX);
*shift = MI_INTPTR_BITS - mi_clz(divisor - 1);
*shift = MI_SIZE_BITS - mi_clz(divisor - 1);
*magic = ((((uint64_t)1 << 32) * (((uint64_t)1 << *shift) - divisor)) / divisor + 1);
}

View file

@ -67,29 +67,25 @@ const mi_page_t _mi_page_empty = {
QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \
QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 2) /* Full queue */ }
#define MI_STAT_COUNT_NULL() {0,0,0,0}
#define MI_STAT_COUNT_NULL() {0,0,0}
// Empty statistics
#if MI_STAT>1
#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) }
#else
#define MI_STAT_COUNT_END_NULL()
#endif
#define MI_STATS_NULL \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), \
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
{ 0, 0 }, { 0, 0 } \
MI_STAT_COUNT_END_NULL()
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
{ 0 }, { 0 }, { 0 }, { 0 }, \
{ 0 }, { 0 }, { 0 }, { 0 }, \
\
{ 0 }, { 0 }, { 0 }, { 0 }, { 0 }, \
MI_INIT4(MI_STAT_COUNT_NULL), \
{ 0 }, { 0 }, { 0 }, { 0 }, \
\
{ MI_INIT4(MI_STAT_COUNT_NULL) }, \
{ { 0 }, { 0 }, { 0 }, { 0 } }, \
\
{ MI_INIT74(MI_STAT_COUNT_NULL) }, \
{ MI_INIT74(MI_STAT_COUNT_NULL) }
// Empty slice span queues for every bin
@ -122,6 +118,7 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
{ {0}, {0}, 0, true }, // random
0, // page count
MI_BIN_FULL, 0, // page retired min/max
0, 0, // generic count
NULL, // next
false, // can reclaim
0, // tag
@ -141,7 +138,7 @@ mi_decl_cache_align static const mi_tld_t tld_empty = {
false,
NULL, NULL,
{ MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, &mi_subproc_default, tld_empty_stats }, // segments
{ MI_STATS_NULL } // stats
{ MI_STAT_VERSION, MI_STATS_NULL } // stats
};
mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
@ -151,13 +148,13 @@ mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
// the thread-local default heap for allocation
mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty;
extern mi_heap_t _mi_heap_main;
extern mi_decl_hidden mi_heap_t _mi_heap_main;
static mi_decl_cache_align mi_tld_t tld_main = {
0, false,
&_mi_heap_main, & _mi_heap_main,
{ MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, &mi_subproc_default, &tld_main.stats }, // segments
{ MI_STATS_NULL } // stats
{ MI_STAT_VERSION, MI_STATS_NULL } // stats
};
mi_decl_cache_align mi_heap_t _mi_heap_main = {
@ -170,6 +167,7 @@ mi_decl_cache_align mi_heap_t _mi_heap_main = {
{ {0x846ca68b}, {0}, 0, true }, // random
0, // page count
MI_BIN_FULL, 0, // page retired min/max
0, 0, // generic count
NULL, // next heap
false, // can reclaim
0, // tag
@ -182,7 +180,7 @@ mi_decl_cache_align mi_heap_t _mi_heap_main = {
bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.
mi_stats_t _mi_stats_main = { MI_STATS_NULL };
mi_stats_t _mi_stats_main = { MI_STAT_VERSION, MI_STATS_NULL };
#if MI_GUARDED
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed) {
@ -569,6 +567,9 @@ void _mi_heap_set_default_direct(mi_heap_t* heap) {
_mi_prim_thread_associate_default_heap(heap);
}
void mi_thread_set_in_threadpool(void) mi_attr_noexcept {
// nothing
}
// --------------------------------------------------------
// Run functions on process init/done, and thread init/done
@ -580,6 +581,11 @@ bool mi_decl_noinline _mi_preloading(void) {
return os_preloading;
}
// Returns true if mimalloc was redirected
mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
return _mi_is_redirected();
}
// Called once by the process loader from `src/prim/prim.c`
void _mi_process_load(void) {
mi_heap_main_init();
@ -616,7 +622,7 @@ static void mi_detect_cpu_features(void) {
int32_t cpu_info[4];
__cpuid(cpu_info, 7);
_mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see <https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features>
_mi_cpu_has_erms = ((cpu_info[2] & (1 << 9)) != 0); // bit 9 of ECX : see <https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features>
_mi_cpu_has_erms = ((cpu_info[1] & (1 << 9)) != 0); // bit 9 of EBX : see <https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features>
}
#else
static void mi_detect_cpu_features(void) {
@ -639,14 +645,6 @@ void mi_process_init(void) mi_attr_noexcept {
mi_detect_cpu_features();
_mi_os_init();
mi_heap_main_init();
#if MI_DEBUG
_mi_verbose_message("debug level : %d\n", MI_DEBUG);
#endif
_mi_verbose_message("secure level: %d\n", MI_SECURE);
_mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL);
#if MI_TSAN
_mi_verbose_message("thread santizer enabled\n");
#endif
mi_thread_init();
#if defined(_WIN32)

View file

@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file
// --------------------------------------------------------
// This module defines various std libc functions to reduce
// the dependency on libc, and also prevent errors caused
// the dependency on libc, and also prevent errors caused
// by some libc implementations when called before `main`
// executes (due to malloc redirection)
// --------------------------------------------------------
@ -83,7 +83,7 @@ bool _mi_getenv(const char* name, char* result, size_t result_size) {
// Define our own limited `_mi_vsnprintf` and `_mi_snprintf`
// This is mostly to avoid calling these when libc is not yet
// initialized (and to reduce dependencies)
//
//
// format: d i, p x u, s
// prec: z l ll L
// width: 10
@ -130,7 +130,7 @@ static void mi_out_alignright(char fill, char* start, size_t len, size_t extra,
}
static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char* end)
static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char* end)
{
if (x == 0 || base == 0 || base > 16) {
if (prefix != 0) { mi_outc(prefix, out, end); }
@ -144,8 +144,8 @@ static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char*
mi_outc((digit <= 9 ? '0' + digit : 'A' + digit - 10),out,end);
x = x / base;
}
if (prefix != 0) {
mi_outc(prefix, out, end);
if (prefix != 0) {
mi_outc(prefix, out, end);
}
size_t len = *out - start;
// and reverse in-place
@ -160,8 +160,8 @@ static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char*
#define MI_NEXTC() c = *in; if (c==0) break; in++;
void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
if (buf == NULL || bufsize == 0 || fmt == NULL) return;
int _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
if (buf == NULL || bufsize == 0 || fmt == NULL) return 0;
buf[bufsize - 1] = 0;
char* const end = buf + (bufsize - 1);
const char* in = fmt;
@ -181,7 +181,7 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
size_t width = 0;
char numtype = 'd';
char numplus = 0;
bool alignright = true;
bool alignright = true;
if (c == '+' || c == ' ') { numplus = c; MI_NEXTC(); }
if (c == '-') { alignright = false; MI_NEXTC(); }
if (c == '0') { fill = '0'; MI_NEXTC(); }
@ -191,7 +191,7 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
width = (10 * width) + (c - '0'); MI_NEXTC();
}
if (c == 0) break; // extra check due to while
}
}
if (c == 'z' || c == 't' || c == 'L') { numtype = c; MI_NEXTC(); }
else if (c == 'l') {
numtype = c; MI_NEXTC();
@ -265,11 +265,70 @@ void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
}
mi_assert_internal(out <= end);
*out = 0;
return (int)(out - buf);
}
void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) {
int _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
_mi_vsnprintf(buf, buflen, fmt, args);
const int written = _mi_vsnprintf(buf, buflen, fmt, args);
va_end(args);
return written;
}
#if MI_SIZE_SIZE == 4
#define mi_mask_even_bits32 (0x55555555)
#define mi_mask_even_pairs32 (0x33333333)
#define mi_mask_even_nibbles32 (0x0F0F0F0F)
// sum of all the bytes in `x` if it is guaranteed that the sum < 256!
static size_t mi_byte_sum32(uint32_t x) {
// perform `x * 0x01010101`: the highest byte contains the sum of all bytes.
x += (x << 8);
x += (x << 16);
return (size_t)(x >> 24);
}
static size_t mi_popcount_generic32(uint32_t x) {
// first count each 2-bit group `a`, where: a==0b00 -> 00, a==0b01 -> 01, a==0b10 -> 01, a==0b11 -> 10
// in other words, `a - (a>>1)`; to do this in parallel, we need to mask to prevent spilling a bit pair
// into the lower bit-pair:
x = x - ((x >> 1) & mi_mask_even_bits32);
// add the 2-bit pair results
x = (x & mi_mask_even_pairs32) + ((x >> 2) & mi_mask_even_pairs32);
// add the 4-bit nibble results
x = (x + (x >> 4)) & mi_mask_even_nibbles32;
// each byte now has a count of its bits, we can sum them now:
return mi_byte_sum32(x);
}
mi_decl_noinline size_t _mi_popcount_generic(size_t x) {
return mi_popcount_generic32(x);
}
#else
#define mi_mask_even_bits64 (0x5555555555555555)
#define mi_mask_even_pairs64 (0x3333333333333333)
#define mi_mask_even_nibbles64 (0x0F0F0F0F0F0F0F0F)
// sum of all the bytes in `x` if it is guaranteed that the sum < 256!
static size_t mi_byte_sum64(uint64_t x) {
x += (x << 8);
x += (x << 16);
x += (x << 32);
return (size_t)(x >> 56);
}
static size_t mi_popcount_generic64(uint64_t x) {
x = x - ((x >> 1) & mi_mask_even_bits64);
x = (x & mi_mask_even_pairs64) + ((x >> 2) & mi_mask_even_pairs64);
x = (x + (x >> 4)) & mi_mask_even_nibbles64;
return mi_byte_sum64(x);
}
mi_decl_noinline size_t _mi_popcount_generic(size_t x) {
return mi_popcount_generic64(x);
}
#endif

View file

@ -162,6 +162,7 @@ static mi_option_desc_t options[_mi_option_last] =
UNINIT, MI_OPTION(guarded_sample_rate)}, // 1 out of N allocations in the min/max range will be guarded (=4000)
{ 0, UNINIT, MI_OPTION(guarded_sample_seed)},
{ 0, UNINIT, MI_OPTION(target_segments_per_thread) }, // abandon segments beyond this point, or 0 to disable.
{ 10000, UNINIT, MI_OPTION(generic_collect) }, // collect heaps every N (=10000) generic allocation calls
};
static void mi_option_init(mi_option_desc_t* desc);
@ -176,11 +177,6 @@ void _mi_options_init(void) {
for(int i = 0; i < _mi_option_last; i++ ) {
mi_option_t option = (mi_option_t)i;
long l = mi_option_get(option); MI_UNUSED(l); // initialize
// if (option != mi_option_verbose)
{
mi_option_desc_t* desc = &options[option];
_mi_verbose_message("option '%s': %ld %s\n", desc->name, desc->value, (mi_option_has_size_in_kib(option) ? "KiB" : ""));
}
}
mi_max_error_count = mi_option_get(mi_option_max_errors);
mi_max_warning_count = mi_option_get(mi_option_max_warnings);
@ -191,7 +187,50 @@ void _mi_options_init(void) {
_mi_warning_message("option 'allow_large_os_pages' is disabled to allow for guarded objects\n");
}
}
_mi_verbose_message("guarded build: %s\n", mi_option_get(mi_option_guarded_sample_rate) != 0 ? "enabled" : "disabled");
#endif
if (mi_option_is_enabled(mi_option_verbose)) { mi_options_print(); }
}
#define mi_stringifyx(str) #str // and stringify
#define mi_stringify(str) mi_stringifyx(str) // expand
void mi_options_print(void) mi_attr_noexcept
{
// show version
const int vermajor = MI_MALLOC_VERSION/100;
const int verminor = (MI_MALLOC_VERSION%100)/10;
const int verpatch = (MI_MALLOC_VERSION%10);
_mi_message("v%i.%i.%i%s%s (built on %s, %s)\n", vermajor, verminor, verpatch,
#if defined(MI_CMAKE_BUILD_TYPE)
", " mi_stringify(MI_CMAKE_BUILD_TYPE)
#else
""
#endif
,
#if defined(MI_GIT_DESCRIBE)
", git " mi_stringify(MI_GIT_DESCRIBE)
#else
""
#endif
, __DATE__, __TIME__);
// show options
for (int i = 0; i < _mi_option_last; i++) {
mi_option_t option = (mi_option_t)i;
long l = mi_option_get(option); MI_UNUSED(l); // possibly initialize
mi_option_desc_t* desc = &options[option];
_mi_message("option '%s': %ld %s\n", desc->name, desc->value, (mi_option_has_size_in_kib(option) ? "KiB" : ""));
}
// show build configuration
_mi_message("debug level : %d\n", MI_DEBUG );
_mi_message("secure level: %d\n", MI_SECURE );
_mi_message("mem tracking: %s\n", MI_TRACK_TOOL);
#if MI_GUARDED
_mi_message("guarded build: %s\n", mi_option_get(mi_option_guarded_sample_rate) != 0 ? "enabled" : "disabled");
#endif
#if MI_TSAN
_mi_message("thread santizer enabled\n");
#endif
}
@ -442,6 +481,13 @@ static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix
}
}
void _mi_message(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args);
va_end(args);
}
void _mi_trace_message(const char* fmt, ...) {
if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher
va_list args;

View file

@ -18,17 +18,17 @@ terms of the MIT license. A copy of the license can be found in the file
----------------------------------------------------------- */
#ifndef MI_DEFAULT_VIRTUAL_ADDRESS_BITS
#if MI_INTPTR_SIZE < 8
#define MI_DEFAULT_VIRTUAL_ADDRESS_BITS 32
#define MI_DEFAULT_VIRTUAL_ADDRESS_BITS 32
#else
#define MI_DEFAULT_VIRTUAL_ADDRESS_BITS 48
#define MI_DEFAULT_VIRTUAL_ADDRESS_BITS 48
#endif
#endif
#ifndef MI_DEFAULT_PHYSICAL_MEMORY
#ifndef MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB
#if MI_INTPTR_SIZE < 8
#define MI_DEFAULT_PHYSICAL_MEMORY 4*MI_GiB
#define MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB 4*MI_MiB // 4 GiB
#else
#define MI_DEFAULT_PHYSICAL_MEMORY 32*MI_GiB
#define MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB 32*MI_MiB // 32 GiB
#endif
#endif
@ -36,7 +36,7 @@ static mi_os_mem_config_t mi_os_mem_config = {
4096, // page size
0, // large page size (usually 2MiB)
4096, // allocation granularity
MI_DEFAULT_PHYSICAL_MEMORY,
MI_DEFAULT_PHYSICAL_MEMORY_IN_KIB,
MI_DEFAULT_VIRTUAL_ADDRESS_BITS,
true, // has overcommit? (if true we use MAP_NORESERVE on mmap systems)
false, // can we partially free allocated blocks? (on mmap systems we can free anywhere in a mapped range, but on Windows we must free the entire span)

View file

@ -57,27 +57,23 @@ static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) {
// Returns MI_BIN_HUGE if the size is too large.
// We use `wsize` for the size in "machine word sizes",
// i.e. byte size == `wsize*sizeof(void*)`.
static inline uint8_t mi_bin(size_t size) {
static inline size_t mi_bin(size_t size) {
size_t wsize = _mi_wsize_from_size(size);
uint8_t bin;
if (wsize <= 1) {
bin = 1;
#if defined(MI_ALIGN4W)
if mi_likely(wsize <= 4) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#if defined(MI_ALIGN4W)
else if (wsize <= 4) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
#elif defined(MI_ALIGN2W)
if mi_likely(wsize <= 8) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#elif defined(MI_ALIGN2W)
else if (wsize <= 8) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
#else
if mi_likely(wsize <= 8) {
return (wsize == 0 ? 1 : wsize);
}
#else
else if (wsize <= 8) {
bin = (uint8_t)wsize;
}
#endif
else if (wsize > MI_MEDIUM_OBJ_WSIZE_MAX) {
bin = MI_BIN_HUGE;
#endif
else if mi_unlikely(wsize > MI_MEDIUM_OBJ_WSIZE_MAX) {
return MI_BIN_HUGE;
}
else {
#if defined(MI_ALIGN4W)
@ -85,15 +81,14 @@ static inline uint8_t mi_bin(size_t size) {
#endif
wsize--;
// find the highest bit
uint8_t b = (uint8_t)mi_bsr(wsize); // note: wsize != 0
const size_t b = (MI_SIZE_BITS - 1 - mi_clz(wsize)); // note: wsize != 0
// and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
// - adjust with 3 because we use do not round the first 8 sizes
// which each get an exact bin
bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;
mi_assert_internal(bin < MI_BIN_HUGE);
const size_t bin = ((b << 2) + ((wsize >> (b - 2)) & 0x03)) - 3;
mi_assert_internal(bin > 0 && bin < MI_BIN_HUGE);
return bin;
}
mi_assert_internal(bin > 0 && bin <= MI_BIN_HUGE);
return bin;
}
@ -102,11 +97,11 @@ static inline uint8_t mi_bin(size_t size) {
Queue of pages with free blocks
----------------------------------------------------------- */
uint8_t _mi_bin(size_t size) {
size_t _mi_bin(size_t size) {
return mi_bin(size);
}
size_t _mi_bin_size(uint8_t bin) {
size_t _mi_bin_size(size_t bin) {
return _mi_heap_empty.pages[bin].block_size;
}
@ -145,10 +140,15 @@ static inline bool mi_page_is_large_or_huge(const mi_page_t* page) {
return (mi_page_block_size(page) > MI_MEDIUM_OBJ_SIZE_MAX || mi_page_is_huge(page));
}
static size_t mi_page_bin(const mi_page_t* page) {
const size_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : (mi_page_is_huge(page) ? MI_BIN_HUGE : mi_bin(mi_page_block_size(page))));
mi_assert_internal(bin <= MI_BIN_FULL);
return bin;
}
static mi_page_queue_t* mi_heap_page_queue_of(mi_heap_t* heap, const mi_page_t* page) {
mi_assert_internal(heap!=NULL);
uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : (mi_page_is_huge(page) ? MI_BIN_HUGE : mi_bin(mi_page_block_size(page))));
mi_assert_internal(bin <= MI_BIN_FULL);
const size_t bin = mi_page_bin(page);
mi_page_queue_t* pq = &heap->pages[bin];
mi_assert_internal((mi_page_block_size(page) == pq->block_size) ||
(mi_page_is_large_or_huge(page) && mi_page_queue_is_huge(pq)) ||
@ -189,7 +189,7 @@ static inline void mi_heap_queue_first_update(mi_heap_t* heap, const mi_page_que
}
else {
// find previous size; due to minimal alignment upto 3 previous bins may need to be skipped
uint8_t bin = mi_bin(size);
size_t bin = mi_bin(size);
const mi_page_queue_t* prev = pq - 1;
while( bin == mi_bin(prev->block_size) && prev > &heap->pages[0]) {
prev--;
@ -322,8 +322,8 @@ static void mi_page_queue_enqueue_from_ex(mi_page_queue_t* to, mi_page_queue_t*
page->prev = to->first;
page->next = next;
to->first->next = page;
if (next != NULL) {
next->prev = page;
if (next != NULL) {
next->prev = page;
}
else {
to->last = page;

View file

@ -291,6 +291,7 @@ static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size
mi_assert_internal(full_block_size >= block_size);
mi_page_init(heap, page, full_block_size, heap->tld);
mi_heap_stat_increase(heap, pages, 1);
mi_heap_stat_increase(heap, page_bins[mi_page_bin(page)], 1);
if (pq != NULL) { mi_page_queue_push(heap, pq, page); }
mi_assert_expensive(_mi_page_is_valid(page));
return page;
@ -412,7 +413,7 @@ void _mi_page_force_abandon(mi_page_t* page) {
// ensure this page is no longer in the heap delayed free list
_mi_heap_delayed_free_all(heap);
// We can still access the page meta-info even if it is freed as we ensure
// We can still access the page meta-info even if it is freed as we ensure
// in `mi_segment_force_abandon` that the segment is not freed (yet)
if (page->capacity == 0) return; // it may have been freed now
@ -438,14 +439,14 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) {
// no more aligned blocks in here
mi_page_set_has_aligned(page, false);
mi_heap_t* heap = mi_page_heap(page);
// remove from the page list
// (no need to do _mi_heap_delayed_free first as all blocks are already free)
mi_heap_t* heap = mi_page_heap(page);
mi_segments_tld_t* segments_tld = &heap->tld->segments;
mi_page_queue_remove(pq, page);
// and free it
mi_heap_stat_decrease(heap, page_bins[mi_page_bin(page)], 1);
mi_page_set_heap(page,NULL);
_mi_segment_page_free(page, force, segments_tld);
}
@ -477,7 +478,7 @@ void _mi_page_retire(mi_page_t* page) mi_attr_noexcept {
const size_t bsize = mi_page_block_size(page);
if mi_likely( /* bsize < MI_MAX_RETIRE_SIZE && */ !mi_page_queue_is_special(pq)) { // not full or huge queue?
if (pq->last==page && pq->first==page) { // the only page in the queue?
mi_stat_counter_increase(_mi_stats_main.page_no_retire,1);
mi_stat_counter_increase(_mi_stats_main.pages_retire,1);
page->retire_expire = (bsize <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4);
mi_heap_t* heap = mi_page_heap(page);
mi_assert_internal(pq >= heap->pages);
@ -814,7 +815,7 @@ static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* p
page = next;
} // for each page
mi_heap_stat_counter_increase(heap, searches, count);
mi_heap_stat_counter_increase(heap, page_searches, count);
// set the page to the best candidate
if (page_candidate != NULL) {
@ -936,13 +937,14 @@ static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size, size_t
}
const size_t bsize = mi_page_usable_block_size(page); // note: not `mi_page_block_size` to account for padding
if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
mi_heap_stat_increase(heap, large, bsize);
mi_heap_stat_counter_increase(heap, large_count, 1);
/*if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
mi_heap_stat_increase(heap, malloc_large, bsize);
mi_heap_stat_counter_increase(heap, malloc_large_count, 1);
}
else {
mi_heap_stat_increase(heap, huge, bsize);
mi_heap_stat_counter_increase(heap, huge_count, 1);
else */
{
_mi_stat_increase(&heap->tld->stats.malloc_huge, bsize);
_mi_stat_counter_increase(&heap->tld->stats.malloc_huge_count, 1);
}
}
return page;
@ -987,11 +989,23 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_al
}
mi_assert_internal(mi_heap_is_initialized(heap));
// call potential deferred free routines
_mi_deferred_free(heap, false);
// do administrative tasks every N generic mallocs
if mi_unlikely(++heap->generic_count >= 100) {
heap->generic_collect_count += heap->generic_count;
heap->generic_count = 0;
// call potential deferred free routines
_mi_deferred_free(heap, false);
// free delayed frees from other threads (but skip contended ones)
_mi_heap_delayed_free_partial(heap);
// free delayed frees from other threads (but skip contended ones)
_mi_heap_delayed_free_partial(heap);
// collect every once in a while (10000 by default)
const long generic_collect = mi_option_get_clamp(mi_option_generic_collect, 1, 1000000L);
if (heap->generic_collect_count >= generic_collect) {
heap->generic_collect_count = 0;
mi_heap_collect(heap, false /* force? */);
}
}
// find (or allocate) a page of the right size
mi_page_t* page = mi_find_page(heap, size, huge_alignment);
@ -1010,14 +1024,20 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_al
mi_assert_internal(mi_page_block_size(page) >= size);
// and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc)
void* p;
if mi_unlikely(zero && mi_page_is_huge(page)) {
// note: we cannot call _mi_page_malloc with zeroing for huge blocks; we zero it afterwards in that case.
void* p = _mi_page_malloc(heap, page, size);
p = _mi_page_malloc(heap, page, size);
mi_assert_internal(p != NULL);
_mi_memzero_aligned(p, mi_page_usable_block_size(page));
return p;
}
else {
return _mi_page_malloc_zero(heap, page, size, zero);
p = _mi_page_malloc_zero(heap, page, size, zero);
mi_assert_internal(p != NULL);
}
// move singleton pages to the full queue
if (page->reserved == page->used) {
mi_page_to_full(page, mi_page_queue_of(page));
}
return p;
}

View file

@ -31,11 +31,12 @@ terms of the MIT license. A copy of the license can be found in the file
#if defined(__linux__)
#include <features.h>
#include <linux/prctl.h> // PR_SET_VMA
//#if defined(MI_NO_THP)
#include <sys/prctl.h> // THP disable
#include <sys/prctl.h> // THP disable
//#endif
#if defined(__GLIBC__)
#include <linux/mman.h> // linux mmap flags
#include <linux/mman.h> // linux mmap flags
#else
#include <sys/mman.h>
#endif
@ -57,12 +58,19 @@ terms of the MIT license. A copy of the license can be found in the file
#include <sys/sysctl.h>
#endif
#if defined(__linux__) || defined(__FreeBSD__)
#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__)
#define MI_HAS_SYSCALL_H
#include <sys/syscall.h>
#endif
#if !defined(MADV_DONTNEED) && defined(POSIX_MADV_DONTNEED) // QNX
#define MADV_DONTNEED POSIX_MADV_DONTNEED
#endif
#if !defined(MADV_FREE) && defined(POSIX_MADV_FREE) // QNX
#define MADV_FREE POSIX_MADV_FREE
#endif
//------------------------------------------------------------------------------------
// Use syscalls for some primitives to allow for libraries that override open/read/close etc.
// and do allocation themselves; using syscalls prevents recursion when mimalloc is
@ -142,8 +150,9 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config )
config->alloc_granularity = (size_t)psize;
#if defined(_SC_PHYS_PAGES)
long pphys = sysconf(_SC_PHYS_PAGES);
if (pphys > 0 && (size_t)pphys < (SIZE_MAX/(size_t)psize)) {
config->physical_memory = (size_t)pphys * (size_t)psize;
const size_t psize_in_kib = (size_t)psize / MI_KiB;
if (psize_in_kib > 0 && pphys > 0 && (size_t)pphys <= (SIZE_MAX/psize_in_kib)) {
config->physical_memory_in_kib = (size_t)pphys * psize_in_kib;
}
#endif
}
@ -189,20 +198,32 @@ int _mi_prim_free(void* addr, size_t size ) {
static int unix_madvise(void* addr, size_t size, int advice) {
#if defined(__sun)
int res = madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520)
#elif defined(__QNX__)
int res = posix_madvise(addr, size, advice);
#else
int res = madvise(addr, size, advice);
#endif
return (res==0 ? 0 : errno);
}
static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) {
static void* unix_mmap_prim(void* addr, size_t size, int protect_flags, int flags, int fd) {
void* p = mmap(addr, size, protect_flags, flags, fd, 0 /* offset */);
#if (defined(__linux__) && defined(PR_SET_VMA))
if (p!=MAP_FAILED && p!=NULL) {
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, size, "mimalloc");
}
#endif
return p;
}
static void* unix_mmap_prim_aligned(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) {
MI_UNUSED(try_alignment);
void* p = NULL;
#if defined(MAP_ALIGNED) // BSD
if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) {
size_t n = mi_bsr(try_alignment);
if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB
p = mmap(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd, 0);
p = unix_mmap_prim(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd);
if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) {
int err = errno;
_mi_trace_message("unable to directly request aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, addr);
@ -213,7 +234,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p
}
#elif defined(MAP_ALIGN) // Solaris
if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) {
p = mmap((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd, 0); // addr parameter is the required alignment
p = unix_mmap_prim((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd); // addr parameter is the required alignment
if (p!=MAP_FAILED) return p;
// fall back to regular mmap
}
@ -223,7 +244,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p
if (addr == NULL) {
void* hint = _mi_os_get_aligned_hint(try_alignment, size);
if (hint != NULL) {
p = mmap(hint, size, protect_flags, flags, fd, 0);
p = unix_mmap_prim(hint, size, protect_flags, flags, fd);
if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) {
#if MI_TRACK_ENABLED // asan sometimes does not instrument errno correctly?
int err = 0;
@ -238,7 +259,7 @@ static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int p
}
#endif
// regular mmap
p = mmap(addr, size, protect_flags, flags, fd, 0);
p = unix_mmap_prim(addr, size, protect_flags, flags, fd);
if (p!=MAP_FAILED) return p;
// failed to allocate
return NULL;
@ -309,7 +330,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec
if (large_only || lflags != flags) {
// try large OS page allocation
*is_large = true;
p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd);
p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, lflags, lfd);
#ifdef MAP_HUGE_1GB
if (p == NULL && (lflags & MAP_HUGE_1GB) == MAP_HUGE_1GB) {
mi_huge_pages_available = false; // don't try huge 1GiB pages again
@ -317,7 +338,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec
_mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno);
}
lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB);
p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd);
p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, lflags, lfd);
}
#endif
if (large_only) return p;
@ -330,7 +351,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec
// regular allocation
if (p == NULL) {
*is_large = false;
p = unix_mmap_prim(addr, size, try_alignment, protect_flags, flags, fd);
p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, flags, fd);
if (p != NULL) {
#if defined(MADV_HUGEPAGE)
// Many Linux systems don't allow MAP_HUGETLB but they support instead

View file

@ -62,6 +62,9 @@ static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL;
static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL;
static PGetNumaProcessorNode pGetNumaProcessorNode = NULL;
// Available after Windows XP
typedef BOOL (__stdcall *PGetPhysicallyInstalledSystemMemory)( PULONGLONG TotalMemoryInKilobytes );
//---------------------------------------------
// Enable large page support dynamically (if possible)
//---------------------------------------------
@ -120,16 +123,10 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config )
if (si.dwAllocationGranularity > 0) { config->alloc_granularity = si.dwAllocationGranularity; }
// get virtual address bits
if ((uintptr_t)si.lpMaximumApplicationAddress > 0) {
const size_t vbits = MI_INTPTR_BITS - mi_clz((uintptr_t)si.lpMaximumApplicationAddress);
const size_t vbits = MI_SIZE_BITS - mi_clz((uintptr_t)si.lpMaximumApplicationAddress);
config->virtual_address_bits = vbits;
}
// get physical memory
ULONGLONG memInKiB = 0;
if (GetPhysicallyInstalledSystemMemory(&memInKiB)) {
if (memInKiB > 0 && memInKiB < (SIZE_MAX / MI_KiB)) {
config->physical_memory = memInKiB * MI_KiB;
}
}
// get the VirtualAlloc2 function
HINSTANCE hDll;
hDll = LoadLibrary(TEXT("kernelbase.dll"));
@ -152,8 +149,19 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config )
pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx");
pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx");
pGetNumaProcessorNode = (PGetNumaProcessorNode)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNode");
// Get physical memory (not available on XP, so check dynamically)
PGetPhysicallyInstalledSystemMemory pGetPhysicallyInstalledSystemMemory = (PGetPhysicallyInstalledSystemMemory)(void (*)(void))GetProcAddress(hDll,"GetPhysicallyInstalledSystemMemory");
if (pGetPhysicallyInstalledSystemMemory != NULL) {
ULONGLONG memInKiB = 0;
if ((*pGetPhysicallyInstalledSystemMemory)(&memInKiB)) {
if (memInKiB > 0 && memInKiB <= SIZE_MAX) {
config->physical_memory_in_kib = (size_t)memInKiB;
}
}
}
FreeLibrary(hDll);
}
// Enable large/huge OS page support?
if (mi_option_is_enabled(mi_option_allow_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
win_enable_large_os_pages(&config->large_page_size);
}
@ -173,7 +181,7 @@ int _mi_prim_free(void* addr, size_t size ) {
// In mi_os_mem_alloc_aligned the fallback path may have returned a pointer inside
// the memory region returned by VirtualAlloc; in that case we need to free using
// the start of the region.
MEMORY_BASIC_INFORMATION info = { 0 };
MEMORY_BASIC_INFORMATION info; _mi_memzero_var(info);
VirtualQuery(addr, &info, sizeof(info));
if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)MI_SEGMENT_SIZE) {
errcode = 0;
@ -635,7 +643,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) {
}
else if (reason==DLL_THREAD_DETACH && !_mi_is_redirected()) {
_mi_thread_done(NULL);
}
}
}
@ -643,7 +651,7 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) {
#define MI_PRIM_HAS_PROCESS_ATTACH 1
// Windows DLL: easy to hook into process_init and thread_done
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
mi_win_main((PVOID)inst,reason,reserved);
return TRUE;
}

View file

@ -143,13 +143,17 @@ void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* ctx_new) {
uintptr_t _mi_random_next(mi_random_ctx_t* ctx) {
mi_assert_internal(mi_random_is_initialized(ctx));
#if MI_INTPTR_SIZE <= 4
return chacha_next32(ctx);
#elif MI_INTPTR_SIZE == 8
return (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx));
#else
# error "define mi_random_next for this platform"
#endif
uintptr_t r;
do {
#if MI_INTPTR_SIZE <= 4
r = chacha_next32(ctx);
#elif MI_INTPTR_SIZE == 8
r = (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx));
#else
# error "define mi_random_next for this platform"
#endif
} while (r==0);
return r;
}
@ -163,7 +167,7 @@ uintptr_t _mi_os_random_weak(uintptr_t extra_seed) {
x ^= _mi_prim_clock_now();
// and do a few randomization steps
uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1;
for (uintptr_t i = 0; i < max; i++) {
for (uintptr_t i = 0; i < max || x==0; i++, x++) {
x = _mi_random_shuffle(x);
}
mi_assert_internal(x != 0);
@ -179,7 +183,7 @@ static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) {
if (!use_weak) { _mi_warning_message("unable to use secure randomness\n"); }
#endif
uintptr_t x = _mi_os_random_weak(0);
for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words.
for (size_t i = 0; i < 8; i++, x++) { // key is eight 32-bit words.
x = _mi_random_shuffle(x);
((uint32_t*)key)[i] = (uint32_t)x;
}

View file

@ -28,8 +28,14 @@ terms of the MIT license. A copy of the license can be found in the file
#define MI_SEGMENT_MAP_PART_SIZE (MI_INTPTR_SIZE*MI_KiB - 128) // 128 > sizeof(mi_memid_t) !
#define MI_SEGMENT_MAP_PART_BITS (8*MI_SEGMENT_MAP_PART_SIZE)
#define MI_SEGMENT_MAP_PART_ENTRIES (MI_SEGMENT_MAP_PART_SIZE / MI_INTPTR_SIZE)
#define MI_SEGMENT_MAP_PART_BIT_SPAN (MI_SEGMENT_ALIGN)
#define MI_SEGMENT_MAP_PART_BIT_SPAN (MI_SEGMENT_ALIGN) // memory area covered by 1 bit
#if (MI_SEGMENT_MAP_PART_BITS < (MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_BIT_SPAN)) // prevent overflow on 32-bit (issue #1017)
#define MI_SEGMENT_MAP_PART_SPAN (MI_SEGMENT_MAP_PART_BITS * MI_SEGMENT_MAP_PART_BIT_SPAN)
#else
#define MI_SEGMENT_MAP_PART_SPAN MI_SEGMENT_MAP_MAX_ADDRESS
#endif
#define MI_SEGMENT_MAP_MAX_PARTS ((MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_SPAN) + 1)
// A part of the segment map.
@ -52,7 +58,7 @@ static mi_segmap_part_t* mi_segment_map_index_of(const mi_segment_t* segment, bo
mi_segmap_part_t* part = mi_atomic_load_ptr_relaxed(mi_segmap_part_t, &mi_segment_map[segindex]);
// allocate on demand to reduce .bss footprint
if (part == NULL) {
if mi_unlikely(part == NULL) {
if (!create_on_demand) return NULL;
mi_memid_t memid;
part = (mi_segmap_part_t*)_mi_os_alloc(sizeof(mi_segmap_part_t), &memid);

View file

@ -155,7 +155,7 @@ size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx) {
Each thread owns its own segments.
Currently we have:
- small pages (64KiB)
- small pages (64KiB)
- medium pages (512KiB)
- large pages (4MiB),
- huge segments have 1 page in one segment that can be larger than `MI_SEGMENT_SIZE`.
@ -975,7 +975,7 @@ static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t
mi_assert_internal(segment != NULL);
mi_assert_internal(segment->next == NULL);
mi_assert_internal(segment->used == 0);
// in `mi_segment_force_abandon` we set this to true to ensure the segment's memory stays valid
if (segment->dont_free) return;
@ -1000,7 +1000,7 @@ static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t
mi_assert_internal(page_count == 2); // first page is allocated by the segment itself
// stats
_mi_stat_decrease(&tld->stats->page_committed, mi_segment_info_size(segment));
// _mi_stat_decrease(&tld->stats->page_committed, mi_segment_info_size(segment));
// return it to the OS
mi_segment_os_free(segment, tld);
@ -1367,6 +1367,7 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slice
}
else {
// otherwise, push on the visited list so it gets not looked at too quickly again
max_tries++; // don't count this as a try since it was not suitable
mi_segment_try_purge(segment, false /* true force? */); // force purge if needed as we may not visit soon again
_mi_arena_segment_mark_abandoned(segment);
}

View file

@ -30,55 +30,25 @@ static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) {
{
// add atomically (for abandoned pages)
int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount);
// if (stat == &_mi_stats_main.committed) { mi_assert_internal(current + amount >= 0); };
mi_atomic_maxi64_relaxed(&stat->peak, current + amount);
if (amount > 0) {
mi_atomic_addi64_relaxed(&stat->allocated,amount);
}
else {
mi_atomic_addi64_relaxed(&stat->freed, -amount);
mi_atomic_addi64_relaxed(&stat->total,amount);
}
}
else {
// add thread local
stat->current += amount;
if (stat->current > stat->peak) stat->peak = stat->current;
if (amount > 0) {
stat->allocated += amount;
}
else {
stat->freed += -amount;
}
}
}
// Adjust stats to compensate; for example before committing a range,
// first adjust downwards with parts that were already committed so
// we avoid double counting.
static void mi_stat_adjust(mi_stat_count_t* stat, int64_t amount) {
if (amount == 0) return;
if mi_unlikely(mi_is_in_main(stat))
{
// adjust atomically
mi_atomic_addi64_relaxed(&stat->current, amount);
mi_atomic_addi64_relaxed(&stat->allocated, amount);
mi_atomic_addi64_relaxed(&stat->freed, amount);
}
else {
// don't affect the peak
stat->current += amount;
// add to both
stat->allocated += amount;
stat->freed += amount;
if (stat->current > stat->peak) { stat->peak = stat->current; }
if (amount > 0) { stat->total += amount; }
}
}
void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) {
if (mi_is_in_main(stat)) {
mi_atomic_addi64_relaxed( &stat->count, 1 );
mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount );
}
else {
stat->count++;
stat->total += amount;
}
}
@ -91,73 +61,66 @@ void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) {
mi_stat_update(stat, -((int64_t)amount));
}
void _mi_stat_adjust_increase(mi_stat_count_t* stat, size_t amount) {
mi_stat_adjust(stat, (int64_t)amount);
static void mi_stat_adjust(mi_stat_count_t* stat, int64_t amount) {
if (amount == 0) return;
if mi_unlikely(mi_is_in_main(stat))
{
// adjust atomically
mi_atomic_addi64_relaxed(&stat->current, amount);
mi_atomic_addi64_relaxed(&stat->total,amount);
}
else {
// adjust local
stat->current += amount;
stat->total += amount;
}
}
void _mi_stat_adjust_decrease(mi_stat_count_t* stat, size_t amount) {
mi_stat_adjust(stat, -((int64_t)amount));
}
// must be thread safe as it is called from stats_merge
static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) {
static void mi_stat_count_add_mt(mi_stat_count_t* stat, const mi_stat_count_t* src) {
if (stat==src) return;
if (src->allocated==0 && src->freed==0) return;
mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit);
mi_atomic_addi64_relaxed( &stat->current, src->current * unit);
mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit);
// peak scores do not work across threads..
mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit);
mi_atomic_void_addi64_relaxed(&stat->total, &src->total);
mi_atomic_void_addi64_relaxed(&stat->current, &src->current);
// peak scores do really not work across threads .. we just add them
mi_atomic_void_addi64_relaxed( &stat->peak, &src->peak);
// or, take the max?
// mi_atomic_maxi64_relaxed(&stat->peak, src->peak);
}
static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) {
static void mi_stat_counter_add_mt(mi_stat_counter_t* stat, const mi_stat_counter_t* src) {
if (stat==src) return;
mi_atomic_addi64_relaxed( &stat->total, src->total * unit);
mi_atomic_addi64_relaxed( &stat->count, src->count * unit);
mi_atomic_void_addi64_relaxed(&stat->total, &src->total);
}
#define MI_STAT_COUNT(stat) mi_stat_count_add_mt(&stats->stat, &src->stat);
#define MI_STAT_COUNTER(stat) mi_stat_counter_add_mt(&stats->stat, &src->stat);
// must be thread safe as it is called from stats_merge
static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
if (stats==src) return;
mi_stat_add(&stats->segments, &src->segments,1);
mi_stat_add(&stats->pages, &src->pages,1);
mi_stat_add(&stats->reserved, &src->reserved, 1);
mi_stat_add(&stats->committed, &src->committed, 1);
mi_stat_add(&stats->reset, &src->reset, 1);
mi_stat_add(&stats->purged, &src->purged, 1);
mi_stat_add(&stats->page_committed, &src->page_committed, 1);
mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
mi_stat_add(&stats->threads, &src->threads, 1);
// copy all fields
MI_STAT_FIELDS()
mi_stat_add(&stats->malloc, &src->malloc, 1);
mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
mi_stat_add(&stats->normal, &src->normal, 1);
mi_stat_add(&stats->huge, &src->huge, 1);
mi_stat_add(&stats->large, &src->large, 1);
mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1);
mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1);
mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1);
mi_stat_counter_add(&stats->reset_calls, &src->reset_calls, 1);
mi_stat_counter_add(&stats->purge_calls, &src->purge_calls, 1);
mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
mi_stat_counter_add(&stats->searches, &src->searches, 1);
mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1);
mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1);
mi_stat_counter_add(&stats->large_count, &src->large_count, 1);
mi_stat_counter_add(&stats->guarded_alloc_count, &src->guarded_alloc_count, 1);
#if MI_STAT>1
#if MI_STAT>1
for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) {
mi_stat_add(&stats->normal_bins[i], &src->normal_bins[i], 1);
}
mi_stat_count_add_mt(&stats->malloc_bins[i], &src->malloc_bins[i]);
}
#endif
for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
mi_stat_count_add_mt(&stats->page_bins[i], &src->page_bins[i]);
}
#endif
}
#undef MI_STAT_COUNT
#undef MI_STAT_COUNTER
/* -----------------------------------------------------------
Display statistics
----------------------------------------------------------- */
@ -208,26 +171,26 @@ static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64
if (unit != 0) {
if (unit > 0) {
mi_print_amount(stat->peak, unit, out, arg);
mi_print_amount(stat->allocated, unit, out, arg);
mi_print_amount(stat->freed, unit, out, arg);
mi_print_amount(stat->total, unit, out, arg);
// mi_print_amount(stat->freed, unit, out, arg);
mi_print_amount(stat->current, unit, out, arg);
mi_print_amount(unit, 1, out, arg);
mi_print_count(stat->allocated, unit, out, arg);
mi_print_count(stat->total, unit, out, arg);
}
else {
mi_print_amount(stat->peak, -1, out, arg);
mi_print_amount(stat->allocated, -1, out, arg);
mi_print_amount(stat->freed, -1, out, arg);
mi_print_amount(stat->total, -1, out, arg);
// mi_print_amount(stat->freed, -1, out, arg);
mi_print_amount(stat->current, -1, out, arg);
if (unit == -1) {
_mi_fprintf(out, arg, "%24s", "");
}
else {
mi_print_amount(-unit, 1, out, arg);
mi_print_count((stat->allocated / -unit), 0, out, arg);
mi_print_count((stat->total / -unit), 0, out, arg);
}
}
if (stat->allocated > stat->freed) {
if (stat->current != 0) {
_mi_fprintf(out, arg, " ");
_mi_fprintf(out, arg, (notok == NULL ? "not all freed" : notok));
_mi_fprintf(out, arg, "\n");
@ -238,7 +201,7 @@ static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64
}
else {
mi_print_amount(stat->peak, 1, out, arg);
mi_print_amount(stat->allocated, 1, out, arg);
mi_print_amount(stat->total, 1, out, arg);
_mi_fprintf(out, arg, "%11s", " "); // no freed
mi_print_amount(stat->current, 1, out, arg);
_mi_fprintf(out, arg, "\n");
@ -255,6 +218,13 @@ static void mi_stat_peak_print(const mi_stat_count_t* stat, const char* msg, int
_mi_fprintf(out, arg, "\n");
}
static void mi_stat_total_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) {
_mi_fprintf(out, arg, "%10s:", msg);
_mi_fprintf(out, arg, "%12s", " "); // no peak
mi_print_amount(stat->total, unit, out, arg);
_mi_fprintf(out, arg, "\n");
}
static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
_mi_fprintf(out, arg, "%10s:", msg);
mi_print_amount(stat->total, -1, out, arg);
@ -263,7 +233,7 @@ static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg
static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) {
const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count));
const int64_t avg_tens = (stat->total == 0 ? 0 : (stat->total*10 / stat->total));
const long avg_whole = (long)(avg_tens/10);
const long avg_frac1 = (long)(avg_tens%10);
_mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1);
@ -271,7 +241,7 @@ static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char*
static void mi_print_header(mi_output_fun* out, void* arg ) {
_mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count ");
_mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "current ", "block ", "total# ");
}
#if MI_STAT>1
@ -279,7 +249,7 @@ static void mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, const c
bool found = false;
char buf[64];
for (size_t i = 0; i <= max; i++) {
if (bins[i].allocated > 0) {
if (bins[i].total > 0) {
found = true;
int64_t unit = _mi_bin_size((uint8_t)i);
_mi_snprintf(buf, 64, "%s %3lu", fmt, (long)i);
@ -340,44 +310,44 @@ static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0)
// and print using that
mi_print_header(out,arg);
#if MI_STAT>1
mi_stats_print_bins(stats->normal_bins, MI_BIN_HUGE, "normal",out,arg);
mi_stats_print_bins(stats->malloc_bins, MI_BIN_HUGE, "bin",out,arg);
#endif
#if MI_STAT
mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg);
mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg);
mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg);
mi_stat_count_t total = { 0,0,0,0 };
mi_stat_add(&total, &stats->normal, 1);
mi_stat_add(&total, &stats->large, 1);
mi_stat_add(&total, &stats->huge, 1);
mi_stat_print(&total, "total", 1, out, arg);
mi_stat_print(&stats->malloc_normal, "binned", (stats->malloc_normal_count.total == 0 ? 1 : -1), out, arg);
// mi_stat_print(&stats->malloc_large, "large", (stats->malloc_large_count.total == 0 ? 1 : -1), out, arg);
mi_stat_print(&stats->malloc_huge, "huge", (stats->malloc_huge_count.total == 0 ? 1 : -1), out, arg);
mi_stat_count_t total = { 0,0,0 };
mi_stat_count_add_mt(&total, &stats->malloc_normal);
// mi_stat_count_add(&total, &stats->malloc_large);
mi_stat_count_add_mt(&total, &stats->malloc_huge);
mi_stat_print_ex(&total, "total", 1, out, arg, "");
#endif
#if MI_STAT>1
mi_stat_print(&stats->malloc, "malloc req", 1, out, arg);
mi_stat_total_print(&stats->malloc_requested, "malloc req", 1, out, arg);
_mi_fprintf(out, arg, "\n");
#endif
mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, "");
mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, "");
mi_stat_peak_print(&stats->reset, "reset", 1, out, arg );
mi_stat_peak_print(&stats->purged, "purged", 1, out, arg );
mi_stat_print(&stats->page_committed, "touched", 1, out, arg);
mi_stat_print_ex(&stats->page_committed, "touched", 1, out, arg, "");
mi_stat_print(&stats->segments, "segments", -1, out, arg);
mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg);
mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg);
mi_stat_print(&stats->pages, "pages", -1, out, arg);
mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg);
mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg);
mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg);
mi_stat_counter_print(&stats->pages_retire, "-retire", out, arg);
mi_stat_counter_print(&stats->arena_count, "arenas", out, arg);
mi_stat_counter_print(&stats->arena_crossover_count, "-crossover", out, arg);
// mi_stat_counter_print(&stats->arena_crossover_count, "-crossover", out, arg);
mi_stat_counter_print(&stats->arena_rollback_count, "-rollback", out, arg);
mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg);
mi_stat_counter_print(&stats->commit_calls, "commits", out, arg);
mi_stat_counter_print(&stats->reset_calls, "resets", out, arg);
mi_stat_counter_print(&stats->purge_calls, "purges", out, arg);
mi_stat_counter_print(&stats->guarded_alloc_count, "guarded", out, arg);
mi_stat_counter_print(&stats->malloc_guarded_count, "guarded", out, arg);
mi_stat_print(&stats->threads, "threads", -1, out, arg);
mi_stat_counter_print_avg(&stats->searches, "searches", out, arg);
mi_stat_counter_print_avg(&stats->page_searches, "searches", out, arg);
_mi_fprintf(out, arg, "%10s: %5zu\n", "numa nodes", _mi_os_numa_node_count());
size_t elapsed;
@ -496,3 +466,164 @@ mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, s
if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit;
if (page_faults!=NULL) *page_faults = pinfo.page_faults;
}
// --------------------------------------------------------
// Return statistics
// --------------------------------------------------------
void mi_stats_get(size_t stats_size, mi_stats_t* stats) mi_attr_noexcept {
if (stats == NULL || stats_size == 0) return;
_mi_memzero(stats, stats_size);
const size_t size = (stats_size > sizeof(mi_stats_t) ? sizeof(mi_stats_t) : stats_size);
_mi_memcpy(stats, &_mi_stats_main, size);
stats->version = MI_STAT_VERSION;
}
// --------------------------------------------------------
// Statics in json format
// --------------------------------------------------------
typedef struct mi_heap_buf_s {
char* buf;
size_t size;
size_t used;
bool can_realloc;
} mi_heap_buf_t;
static bool mi_heap_buf_expand(mi_heap_buf_t* hbuf) {
if (hbuf==NULL) return false;
if (hbuf->buf != NULL && hbuf->size>0) {
hbuf->buf[hbuf->size-1] = 0;
}
if (hbuf->size > SIZE_MAX/2 || !hbuf->can_realloc) return false;
const size_t newsize = (hbuf->size == 0 ? 2*MI_KiB : 2*hbuf->size);
char* const newbuf = (char*)mi_rezalloc(hbuf->buf, newsize);
if (newbuf == NULL) return false;
hbuf->buf = newbuf;
hbuf->size = newsize;
return true;
}
static void mi_heap_buf_print(mi_heap_buf_t* hbuf, const char* msg) {
if (msg==NULL || hbuf==NULL) return;
if (hbuf->used + 1 >= hbuf->size && !hbuf->can_realloc) return;
for (const char* src = msg; *src != 0; src++) {
char c = *src;
if (hbuf->used + 1 >= hbuf->size) {
if (!mi_heap_buf_expand(hbuf)) return;
}
mi_assert_internal(hbuf->used < hbuf->size);
hbuf->buf[hbuf->used++] = c;
}
mi_assert_internal(hbuf->used < hbuf->size);
hbuf->buf[hbuf->used] = 0;
}
static void mi_heap_buf_print_count_bin(mi_heap_buf_t* hbuf, const char* prefix, mi_stat_count_t* stat, size_t bin, bool add_comma) {
const size_t binsize = _mi_bin_size(bin);
const size_t pagesize = (binsize <= MI_SMALL_OBJ_SIZE_MAX ? MI_SMALL_PAGE_SIZE :
(binsize <= MI_MEDIUM_OBJ_SIZE_MAX ? MI_MEDIUM_PAGE_SIZE :
#if MI_LARGE_PAGE_SIZE
(binsize <= MI_LARGE_OBJ_SIZE_MAX ? MI_LARGE_PAGE_SIZE : 0)
#else
0
#endif
));
char buf[128];
_mi_snprintf(buf, 128, "%s{ \"total\": %lld, \"peak\": %lld, \"current\": %lld, \"block_size\": %zu, \"page_size\": %zu }%s\n", prefix, stat->total, stat->peak, stat->current, binsize, pagesize, (add_comma ? "," : ""));
buf[127] = 0;
mi_heap_buf_print(hbuf, buf);
}
static void mi_heap_buf_print_count(mi_heap_buf_t* hbuf, const char* prefix, mi_stat_count_t* stat, bool add_comma) {
char buf[128];
_mi_snprintf(buf, 128, "%s{ \"total\": %lld, \"peak\": %lld, \"current\": %lld }%s\n", prefix, stat->total, stat->peak, stat->current, (add_comma ? "," : ""));
buf[127] = 0;
mi_heap_buf_print(hbuf, buf);
}
static void mi_heap_buf_print_count_value(mi_heap_buf_t* hbuf, const char* name, mi_stat_count_t* stat) {
char buf[128];
_mi_snprintf(buf, 128, " \"%s\": ", name);
buf[127] = 0;
mi_heap_buf_print(hbuf, buf);
mi_heap_buf_print_count(hbuf, "", stat, true);
}
static void mi_heap_buf_print_value(mi_heap_buf_t* hbuf, const char* name, int64_t val) {
char buf[128];
_mi_snprintf(buf, 128, " \"%s\": %lld,\n", name, val);
buf[127] = 0;
mi_heap_buf_print(hbuf, buf);
}
static void mi_heap_buf_print_size(mi_heap_buf_t* hbuf, const char* name, size_t val, bool add_comma) {
char buf[128];
_mi_snprintf(buf, 128, " \"%s\": %zu%s\n", name, val, (add_comma ? "," : ""));
buf[127] = 0;
mi_heap_buf_print(hbuf, buf);
}
static void mi_heap_buf_print_counter_value(mi_heap_buf_t* hbuf, const char* name, mi_stat_counter_t* stat) {
mi_heap_buf_print_value(hbuf, name, stat->total);
}
#define MI_STAT_COUNT(stat) mi_heap_buf_print_count_value(&hbuf, #stat, &stats->stat);
#define MI_STAT_COUNTER(stat) mi_heap_buf_print_counter_value(&hbuf, #stat, &stats->stat);
char* mi_stats_get_json(size_t output_size, char* output_buf) mi_attr_noexcept {
mi_heap_buf_t hbuf = { NULL, 0, 0, true };
if (output_size > 0 && output_buf != NULL) {
_mi_memzero(output_buf, output_size);
hbuf.buf = output_buf;
hbuf.size = output_size;
hbuf.can_realloc = false;
}
else {
if (!mi_heap_buf_expand(&hbuf)) return NULL;
}
mi_heap_buf_print(&hbuf, "{\n");
mi_heap_buf_print_value(&hbuf, "version", MI_STAT_VERSION);
mi_heap_buf_print_value(&hbuf, "mimalloc_version", MI_MALLOC_VERSION);
// process info
mi_heap_buf_print(&hbuf, " \"process\": {\n");
size_t elapsed;
size_t user_time;
size_t sys_time;
size_t current_rss;
size_t peak_rss;
size_t current_commit;
size_t peak_commit;
size_t page_faults;
mi_process_info(&elapsed, &user_time, &sys_time, &current_rss, &peak_rss, &current_commit, &peak_commit, &page_faults);
mi_heap_buf_print_size(&hbuf, "elapsed_msecs", elapsed, true);
mi_heap_buf_print_size(&hbuf, "user_msecs", user_time, true);
mi_heap_buf_print_size(&hbuf, "system_msecs", sys_time, true);
mi_heap_buf_print_size(&hbuf, "page_faults", page_faults, true);
mi_heap_buf_print_size(&hbuf, "rss_current", current_rss, true);
mi_heap_buf_print_size(&hbuf, "rss_peak", peak_rss, true);
mi_heap_buf_print_size(&hbuf, "commit_current", current_commit, true);
mi_heap_buf_print_size(&hbuf, "commit_peak", peak_commit, false);
mi_heap_buf_print(&hbuf, " },\n");
// statistics
mi_stats_t* stats = &_mi_stats_main;
MI_STAT_FIELDS()
// size bins
mi_heap_buf_print(&hbuf, " \"malloc_bins\": [\n");
for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
mi_heap_buf_print_count_bin(&hbuf, " ", &stats->malloc_bins[i], i, i!=MI_BIN_HUGE);
}
mi_heap_buf_print(&hbuf, " ],\n");
mi_heap_buf_print(&hbuf, " \"page_bins\": [\n");
for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
mi_heap_buf_print_count_bin(&hbuf, " ", &stats->page_bins[i], i, i!=MI_BIN_HUGE);
}
mi_heap_buf_print(&hbuf, " ]\n");
mi_heap_buf_print(&hbuf, "}\n");
return hbuf.buf;
}

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.18)
project(mimalloc-test C CXX)
set(CMAKE_C_STANDARD 11)
@ -16,10 +16,12 @@ if (NOT CMAKE_BUILD_TYPE)
endif()
# Import mimalloc (if installed)
find_package(mimalloc 2.0 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH)
find_package(mimalloc 2.2 CONFIG REQUIRED)
message(STATUS "Found mimalloc installed at: ${MIMALLOC_LIBRARY_DIR} (${MIMALLOC_VERSION_DIR})")
# overriding with a dynamic library
# link with a dynamic shared library
# use `LD_PRELOAD` to actually override malloc/free at runtime with mimalloc
add_executable(dynamic-override main-override.c)
target_link_libraries(dynamic-override PUBLIC mimalloc)
@ -29,9 +31,9 @@ target_link_libraries(dynamic-override-cxx PUBLIC mimalloc)
# overriding with a static object file works reliable as the symbols in the
# object file have priority over those in library files
add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc.o)
add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc${CMAKE_C_OUTPUT_EXTENSION})
target_include_directories(static-override-obj PUBLIC ${MIMALLOC_INCLUDE_DIR})
target_link_libraries(static-override-obj PUBLIC pthread)
target_link_libraries(static-override-obj PUBLIC mimalloc-static)
# overriding with a static library works too if using the `mimalloc-override.h`

View file

@ -0,0 +1,15 @@
// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc.
// This is imported by the `mimalloc-test-override` project.
#include <string>
#include "main-override-dep.h"
std::string TestAllocInDll::GetString()
{
char* test = new char[128];
memset(test, 0, 128);
const char* t = "test";
memcpy(test, t, 4);
std::string r = test;
delete[] test;
return r;
}

11
test/main-override-dep.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc.
// This is imported by the `mimalloc-test-override` project.
#include <string>
class TestAllocInDll
{
public:
__declspec(dllexport) std::string GetString();
};

View file

@ -32,7 +32,10 @@ static void test_manage_os_memory(void);
int main() {
mi_version();
mi_stats_reset();
test_manage_os_memory();
// mi_bins();
// test_manage_os_memory();
// test_large_pages();
// detect double frees and heap corruption
// double_free1();
@ -40,7 +43,7 @@ int main() {
// corrupt_free();
// block_overflow1();
// block_overflow2();
test_canary_leak();
// test_canary_leak();
// test_aslr();
// invalid_free();
// test_reserved();
@ -397,31 +400,34 @@ static inline size_t _mi_wsize_from_size(size_t size) {
return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
}
// #define MI_ALIGN2W
// Return the bin for a given field size.
// Returns MI_BIN_HUGE if the size is too large.
// We use `wsize` for the size in "machine word sizes",
// i.e. byte size == `wsize*sizeof(void*)`.
extern inline uint8_t _mi_bin8(size_t size) {
size_t wsize = _mi_wsize_from_size(size);
uint8_t bin;
if (wsize <= 1) {
static inline size_t mi_bin(size_t wsize) {
// size_t wsize = _mi_wsize_from_size(size);
// size_t bin;
/*if (wsize <= 1) {
bin = 1;
}
*/
#if defined(MI_ALIGN4W)
else if (wsize <= 4) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
if (wsize <= 4) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#elif defined(MI_ALIGN2W)
else if (wsize <= 8) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
if (wsize <= 8) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#else
else if (wsize <= 8) {
bin = (uint8_t)wsize;
if (wsize <= 8) {
return (wsize == 0 ? 1 : wsize);
}
#endif
else if (wsize > MI_LARGE_WSIZE_MAX) {
bin = MI_BIN_HUGE;
return MI_BIN_HUGE;
}
else {
#if defined(MI_ALIGN4W)
@ -429,15 +435,17 @@ extern inline uint8_t _mi_bin8(size_t size) {
#endif
wsize--;
// find the highest bit
uint8_t b = mi_bsr32((uint32_t)wsize);
const size_t b = _mi_bsr(wsize); // note: wsize != 0
// and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
// - adjust with 3 because we use do not round the first 8 sizes
// which each get an exact bin
bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;
const size_t bin = ((b << 2) + ((wsize >> (b - 2)) & 0x03)) - 3;
assert(bin > 0 && bin < MI_BIN_HUGE);
return bin;
}
return bin;
}
static inline uint8_t _mi_bin4(size_t size) {
size_t wsize = _mi_wsize_from_size(size);
uint8_t bin;
@ -493,7 +501,7 @@ static size_t _mi_binx8(size_t bsize) {
}
static inline size_t mi_bin(size_t wsize) {
static inline size_t mi_binx(size_t wsize) {
uint8_t bin;
if (wsize <= 1) {
bin = 1;

View file

@ -9,16 +9,11 @@
#include <vector>
#include <future>
#include <iostream>
#include <thread>
//#include <mimalloc.h>
#include <assert.h>
#ifdef _WIN32
#include <mimalloc-new-delete.h>
#endif
#ifdef _WIN32
#include <windows.h>
static void msleep(unsigned long msecs) { Sleep(msecs); }
#else
@ -45,12 +40,20 @@ static void test_thread_local(); // issue #944
static void test_mixed1(); // issue #942
static void test_stl_allocators();
#if x_WIN32
#include "main-override-dep.h"
static void test_dep(); // issue #981: test overriding in another DLL
#else
static void test_dep() { };
#endif
int main() {
mi_stats_reset(); // ignore earlier allocations
various_tests();
test_mixed1();
test_dep();
//test_std_string();
//test_thread_local();
// heap_thread_free_huge();
@ -114,8 +117,10 @@ static void various_tests() {
t->~Test();
delete[] tbuf;
#if _WIN32
const char* ptr = ::_Getdays(); // test _base overrid
free((void*)ptr);
#endif
}
class Static {
@ -144,6 +149,16 @@ static bool test_stl_allocator1() {
struct some_struct { int i; int j; double z; };
#if x_WIN32
static void test_dep()
{
TestAllocInDll t;
std::string s = t.GetString();
}
#endif
static bool test_stl_allocator2() {
std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
@ -404,7 +419,7 @@ void large_alloc(void)
// issue #372
static void fail_aslr() {
size_t sz = (4ULL << 40); // 4TiB
size_t sz = (size_t)(4ULL << 40); // 4TiB
void* p = malloc(sz);
printf("pointer p: %p: area up to %p\n", p, (uint8_t*)p + sz);
*(int*)0x5FFFFFFF000 = 0; // should segfault

View file

@ -203,7 +203,11 @@ int main(void) {
CHECK_BODY("malloc-aligned9") { // test large alignments
bool ok = true;
void* p[8];
size_t sizes[8] = { 8, 512, 1024 * 1024, MI_BLOCK_ALIGNMENT_MAX, MI_BLOCK_ALIGNMENT_MAX + 1, 2 * MI_BLOCK_ALIGNMENT_MAX, 8 * MI_BLOCK_ALIGNMENT_MAX, 0 };
size_t sizes[8] = { 8, 512, 1024 * 1024, MI_BLOCK_ALIGNMENT_MAX, MI_BLOCK_ALIGNMENT_MAX + 1,
#if SIZE_MAX > UINT32_MAX
2 * MI_BLOCK_ALIGNMENT_MAX, 8 * MI_BLOCK_ALIGNMENT_MAX,
#endif
0 };
for (int i = 0; i < 28 && ok; i++) {
int align = (1 << i);
for (int j = 0; j < 8 && ok; j++) {

View file

@ -61,6 +61,7 @@ static bool main_participates = false; // main thread participates as a
#define custom_free(p) free(p)
#else
#include <mimalloc.h>
#include <mimalloc-stats.h>
#define custom_calloc(n,s) mi_calloc(n,s)
#define custom_realloc(p,s) mi_realloc(p,s)
#define custom_free(p) mi_free(p)
@ -116,7 +117,7 @@ static void* alloc_items(size_t items, random_t r) {
else if (chance(10, r) && allow_large_objects) items *= 1000; // 0.1% huge
else items *= 100; // 1% large objects;
}
if (items == 40) items++; // pthreads uses that size for stack increases
if (items>=32 && items<=40) items*=2; // pthreads uses 320b allocations (this shows that more clearly in the stats)
if (use_one_size > 0) items = (use_one_size / sizeof(uintptr_t));
if (items==0) items = 1;
uintptr_t* p = (uintptr_t*)custom_calloc(items,sizeof(uintptr_t));
@ -268,16 +269,23 @@ static void test_leak(void) {
}
#endif
#if defined(USE_STD_MALLOC) && defined(MI_LINK_VERSION)
#ifdef __cplusplus
extern "C"
#endif
int mi_version(void);
#endif
int main(int argc, char** argv) {
#ifdef MI_LINK_VERSION
mi_version();
#endif
#ifdef HEAP_WALK
mi_option_enable(mi_option_visit_abandoned);
#endif
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
mi_option_set(mi_option_arena_reserve, 32 * 1024 /* in kib = 32MiB */);
#endif
#ifndef USE_STD_MALLOC
mi_stats_reset();
#endif
// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
if (argc >= 2) {
@ -299,6 +307,11 @@ int main(int argc, char** argv) {
allow_large_objects = true;
}
printf("Using %d threads with a %d%% load-per-thread and %d iterations %s\n", THREADS, SCALE, ITER, (allow_large_objects ? "(allow large objects)" : ""));
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
mi_stats_reset();
#endif
//mi_reserve_os_memory(1024*1024*1024ULL, false, true);
//int res = mi_reserve_huge_os_pages(4,1);
//printf("(reserve huge: %i\n)", res);
@ -310,9 +323,9 @@ int main(int argc, char** argv) {
//mi_reserve_os_memory(512ULL << 20, true, true);
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
mi_stats_reset();
#endif
#endif
#ifdef STRESS
test_stress();
@ -322,11 +335,16 @@ int main(int argc, char** argv) {
#ifndef USE_STD_MALLOC
#ifndef NDEBUG
mi_debug_show_arenas(true);
mi_debug_show_arenas();
mi_collect(true);
#endif
char* json = mi_stats_get_json(0, NULL);
if (json != NULL) {
fputs(json,stderr);
mi_free(json);
}
#endif
mi_stats_print(NULL);
#endif
mi_stats_print(NULL);
//bench_end_program();
return 0;
}