Code Monkey home page Code Monkey logo

mimalloc's Introduction

mimalloc

 

mimalloc (pronounced "me-malloc") is a general purpose allocator with excellent performance characteristics. Initially developed by Daan Leijen for the runtime systems of the Koka and Lean languages.

Latest release tag: v2.1.4 (2024-04-22).
Latest v1 tag: v1.8.4 (2024-04-22).

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. Notable aspects of the design include:

  • small and consistent: the library is about 8k LOC using simple and consistent data structures. This makes it very suitable to integrate and adapt in other projects. For runtime systems it provides hooks for a monotonic heartbeat and deferred freeing (for bounded worst-case times with reference counting). Partly due to its simplicity, mimalloc has been ported to many systems (Windows, macOS, Linux, WASM, various BSD's, Haiku, MUSL, etc) and has excellent support for dynamic overriding. At the same time, it is an industrial strength allocator that runs (very) large scale distributed services on thousands of machines with excellent worst case latencies.
  • free list sharding: instead of one big free list (per size class) we have many smaller lists per "mimalloc page" which reduces fragmentation and increases locality -- things that are allocated close in time get allocated close in memory. (A mimalloc page contains blocks of one size class and is usually 64KiB on a 64-bit system).
  • free list multi-sharding: the big idea! Not only do we shard the free list per mimalloc page, but for each page we have multiple free lists. In particular, there is one list for thread-local free operations, and another one for concurrent free operations. Free-ing from another thread can now be a single CAS without needing sophisticated coordination between threads. Since there will be thousands of separate free lists, contention is naturally distributed over the heap, and the chance of contending on a single location will be low -- this is quite similar to randomized algorithms like skip lists where adding a random oracle removes the need for a more complex algorithm.
  • eager page purging: when a "page" becomes empty (with increased chance due to free list sharding) the memory is marked to the OS as unused (reset or decommitted) reducing (real) memory pressure and fragmentation, especially in long running programs.
  • secure: mimalloc can be built in secure mode, adding guard pages, randomized allocation, encrypted free lists, etc. to protect against various heap vulnerabilities. The performance penalty is usually around 10% on average over our benchmarks.
  • first-class heaps: efficiently create and use multiple heaps to allocate across different regions. A heap can be destroyed at once instead of deallocating each object separately.
  • bounded: it does not suffer from blowup [1], has bounded worst-case allocation times (wcat) (upto OS primitives), bounded space overhead (~0.2% meta-data, with low internal fragmentation), and has no internal points of contention using only atomic operations.
  • fast: In our benchmarks (see below), mimalloc outperforms other leading allocators (jemalloc, tcmalloc, Hoard, etc), and often uses less memory. A nice property is that it does consistently well over a wide range of benchmarks. There is also good huge OS page support for larger server programs.

The documentation gives a full overview of the API. You can read more on the design of mimalloc in the technical report which also has detailed benchmark results.

Enjoy!

Branches

  • master: latest stable release (based on dev-slice).
  • dev: development branch for mimalloc v1. Use this branch for submitting PR's.
  • dev-slice: development branch for mimalloc v2. This branch is downstream of dev (and is essentially equal to dev except for src/segment.c)

Releases

Note: the v2.x version has a different algorithm for managing internal mimalloc pages (as slices) that tends to use reduce memory usage and fragmentation compared to mimalloc v1.x (especially for large workloads). Should otherwise have similar performance (see below); please report if you observe any significant performance regression.

  • 2024-04-22, v1.8.4, v2.1.4: Fixes various bugs and build issues. Add MI_LIBC_MUSL cmake flag for musl builds. Free-ing code is refactored into a separate module (free.c). Mimalloc page info is simplified with the block size directly available (and new block_size_shift to improve aligned block free-ing). New approach to collection of abandoned segments: When a thread terminates the segments it owns are abandoned (containing still live objects) and these can be reclaimed by other threads. We no longer use a list of abandoned segments but this is now done using bitmaps in arena's which is more concurrent (and more aggressive). Abandoned memory can now also be reclaimed if a thread frees an object in an abandoned page (which can be disabled using mi_option_abandoned_reclaim_on_free). The option mi_option_max_segment_reclaim gives a maximum percentage of abandoned segments that can be reclaimed per try (=10%).

  • 2023-04-24, v1.8.2, v2.1.2: Fixes build issues on freeBSD, musl, and C17 (UE 5.1.1). Reduce code size/complexity by removing regions and segment-cache's and only use arenas with improved memory purging -- this may improve memory usage as well for larger services. Renamed options for consistency. Improved Valgrind and ASAN checking.

  • 2023-04-03, v1.8.1, v2.1.1: Fixes build issues on some platforms.

  • 2023-03-29, v1.8.0, v2.1.0: Improved support dynamic overriding on Windows 11. Improved tracing precision with asan and Valgrind, and added Windows event tracing ETW (contributed by Xinglong He). Created an OS abstraction layer to make it easier to port and separate platform dependent code (in src/prim). Fixed C++ STL compilation on older Microsoft C++ compilers, and various small bug fixes.

  • 2022-12-23, v1.7.9, v2.0.9: Supports building with asan and improved Valgrind support. Support arbitrary large alignments (in particular for std::pmr pools). Added C++ STL allocators attached to a specific heap (thanks @vmarkovtsev). Heap walks now visit all object (including huge objects). Support Windows nano server containers (by Johannes Schindelin,@dscho). Various small bug fixes.

  • 2022-11-03, v1.7.7, v2.0.7: Initial support for Valgrind for leak testing and heap block overflow detection. Initial support for attaching heaps to a speficic memory area (only in v2). Fix realloc behavior for zero size blocks, remove restriction to integral multiple of the alignment in alloc_align, improved aligned allocation performance, reduced contention with many threads on few processors (thank you @dposluns!), vs2022 support, support pkg-config, .

  • 2022-04-14, v1.7.6, v2.0.6: fix fallback path for aligned OS allocation on Windows, improve Windows aligned allocation even when compiling with older SDK's, fix dynamic overriding on macOS Monterey, fix MSVC C++ dynamic overriding, fix warnings under Clang 14, improve performance if many OS threads are created and destroyed, fix statistics for large object allocations, using MIMALLOC_VERBOSE=1 has no maximum on the number of error messages, various small fixes.

  • 2022-02-14, v1.7.5, v2.0.5 (alpha): fix malloc override on Windows 11, fix compilation with musl, potentially reduced committed memory, add bin/minject for Windows, improved wasm support, faster aligned allocation, various small fixes.

  • Older release notes

Special thanks to:

  • David Carlier (@devnexen) for his many contributions, and making mimalloc work better on many less common operating systems, like Haiku, Dragonfly, etc.
  • Mary Feofanova (@mary3000), Evgeniy Moiseenko, and Manuel Pöter (@mpoeter) for making mimalloc TSAN checkable, and finding memory model bugs using the genMC model checker.
  • Weipeng Liu (@pongba), Zhuowei Li, Junhua Wang, and Jakub Szymanski, for their early support of mimalloc and deployment at large scale services, leading to many improvements in the mimalloc algorithms for large workloads.
  • Jason Gibson (@jasongibson) for exhaustive testing on large scale workloads and server environments, and finding complex bugs in (early versions of) mimalloc.
  • Manuel Pöter (@mpoeter) and Sam Gross(@colesbury) for finding an ABA concurrency issue in abandoned segment reclamation. Sam also created the no GIL Python fork which uses mimalloc internally.

Usage

mimalloc is used in various large scale low-latency services and programs, for example:

Building

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 in the entire program.

macOS, Linux, BSD, etc.

We use cmake1 as the build system:

> mkdir -p out/release
> cd out/release
> cmake ../..
> make

This builds the library as a shared (dynamic) library (.so or .dylib), a static library (.a), and as a single object file (.o).

> sudo make install (install the library and header files in /usr/local/lib and /usr/local/include)

You can build the debug version which does many internal checks and maintains detailed statistics as:

> mkdir -p out/debug
> cd out/debug
> cmake -DCMAKE_BUILD_TYPE=Debug ../..
> make

This will name the shared library as libmimalloc-debug.so.

Finally, you can build a secure version that uses guard pages, encrypted free lists, etc., as:

> mkdir -p out/secure
> cd out/secure
> cmake -DMI_SECURE=ON ../..
> make

This will name the shared library as libmimalloc-secure.so. Use ccmake2 instead of cmake to see and customize all the available build options.

Notes:

  1. Install CMake: sudo apt-get install cmake
  2. Install CCMake: sudo apt-get install cmake-curses-gui

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

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,

> gcc -o myprogram -lmimalloc myfile.c

mimalloc uses only safe OS calls (mmap and VirtualAlloc) and can co-exist with other allocators linked to the same program. If you use cmake, you can simply use:

find_package(mimalloc 1.4 REQUIRED)

in your CMakeLists.txt to find a locally installed mimalloc. Then use either:

target_link_libraries(myapp PUBLIC mimalloc)

to link with the shared (dynamic) library, or:

target_link_libraries(myapp PUBLIC mimalloc-static)

to link with the static library. See test\CMakeLists.txt for an example.

For best performance in C++ programs, it is also recommended to override the global new and delete operators. For convenience, mimalloc provides mimalloc-new-delete.h which does this for you -- just include it in a single(!) source file in your project. In C++, mimalloc also provides the mi_stl_allocator struct which implements the std::allocator interface.

You can pass environment variables to print verbose messages (MIMALLOC_VERBOSE=1) and statistics (MIMALLOC_SHOW_STATS=1) (in the debug version):

> env MIMALLOC_SHOW_STATS=1 ./cfrac 175451865205073170563711388363

175451865205073170563711388363 = 374456281610909315237213 * 468551

heap stats:     peak      total      freed       unit
normal   2:    16.4 kb    17.5 mb    17.5 mb      16 b   ok
normal   3:    16.3 kb    15.2 mb    15.2 mb      24 b   ok
normal   4:      64 b      4.6 kb     4.6 kb      32 b   ok
normal   5:      80 b    118.4 kb   118.4 kb      40 b   ok
normal   6:      48 b       48 b       48 b       48 b   ok
normal  17:     960 b      960 b      960 b      320 b   ok

heap stats:     peak      total      freed       unit
    normal:    33.9 kb    32.8 mb    32.8 mb       1 b   ok
      huge:       0 b        0 b        0 b        1 b   ok
     total:    33.9 kb    32.8 mb    32.8 mb       1 b   ok
malloc requested:         32.8 mb

 committed:    58.2 kb    58.2 kb    58.2 kb       1 b   ok
  reserved:     2.0 mb     2.0 mb     2.0 mb       1 b   ok
     reset:       0 b        0 b        0 b        1 b   ok
  segments:       1          1          1
-abandoned:       0
     pages:       6          6          6
-abandoned:       0
     mmaps:       3
 mmap fast:       0
 mmap slow:       1
   threads:       0
   elapsed:     2.022s
   process: user: 1.781s, system: 0.016s, faults: 756, reclaims: 0, rss: 2.7 mb

The above model of using the mi_ prefixed API is not always possible though in existing programs that already use the standard malloc interface, and another option is to override the standard malloc interface completely and redirect all calls to the mimalloc library instead .

Environment Options

You can set further options either programmatically (using mi_option_set), or via environment variables:

  • MIMALLOC_SHOW_STATS=1: show statistics when the program terminates.
  • MIMALLOC_VERBOSE=1: show verbose messages.
  • MIMALLOC_SHOW_ERRORS=1: show error and warning messages.

Advanced options:

  • MIMALLOC_ARENA_EAGER_COMMIT=2: turns on eager commit for the large arenas (usually 1GiB) from which mimalloc allocates segments and pages. Set this to 2 (default) to only enable this on overcommit systems (e.g. Linux). Set this to 1 to enable explicitly on other systems as well (like Windows or macOS) which may improve performance (as the whole arena is committed at once). Note that eager commit only increases the commit but not the actual the peak resident set (rss) so it is generally ok to enable this.
  • MIMALLOC_PURGE_DELAY=N: the delay in N milli-seconds (by default 10) after which mimalloc will purge OS pages that are not in use. This signals to the OS that the underlying physical memory can be reused which can reduce memory fragmentation especially in long running (server) programs. Setting N to 0 purges immediately when a page becomes unused which can improve memory usage but also decreases performance. Setting N to a higher value like 100 can improve performance (sometimes by a lot) at the cost of potentially using more memory at times. Setting it to -1 disables purging completely.
  • MIMALLOC_PURGE_DECOMMITS=1: By default "purging" memory means unused memory is decommitted (MEM_DECOMMIT on Windows, MADV_DONTNEED (which decresease rss immediately) on mmap systems). Set this to 0 to instead "reset" unused memory on a purge (MEM_RESET on Windows, generally MADV_FREE (which does not decrease rss immediately) on mmap systems). Mimalloc generally does not "free" OS memory but only "purges" OS memory, in other words, it tries to keep virtual address ranges and decommits within those ranges (to make the underlying physical memory available to other processes).

Further options for large workloads and services:

  • MIMALLOC_USE_NUMA_NODES=N: pretend there are at most N NUMA nodes. If not set, the actual NUMA nodes are detected at runtime. Setting N to 1 may avoid problems in some virtual environments. Also, setting it to a lower number than the actual NUMA nodes is fine and will only cause threads to potentially allocate more memory across actual NUMA nodes (but this can happen in any case as NUMA local allocation is always a best effort but not guaranteed).
  • MIMALLOC_ALLOW_LARGE_OS_PAGES=1: use large OS pages (2 or 4MiB) when available; for some workloads this can significantly improve performance. When this option is disabled, it also disables transparent huge pages (THP) for the process (on Linux and Android). Use MIMALLOC_VERBOSE to check if the large OS pages are enabled -- usually one needs to explicitly give permissions for large OS pages (as on Windows and Linux). However, sometimes the OS is very slow to reserve contiguous physical memory for large OS pages so use with care on systems that can have fragmented memory (for that reason, we generally recommend to use MIMALLOC_RESERVE_HUGE_OS_PAGES instead whenever possible).
  • MIMALLOC_RESERVE_HUGE_OS_PAGES=N: where N is the number of 1GiB huge OS pages. This reserves the huge pages at startup and sometimes this can give a large (latency) performance improvement on big workloads. Usually it is better to not use MIMALLOC_ALLOW_LARGE_OS_PAGES=1 in combination with this setting. Just like large OS pages, use with care as reserving contiguous physical memory can take a long time when memory is fragmented (but reserving the huge pages is done at startup only once). Note that we usually need to explicitly give permission for huge OS pages (as on Windows and Linux)). With huge OS pages, it may be beneficial to set the setting MIMALLOC_EAGER_COMMIT_DELAY=N (N is 1 by default) to delay the initial N segments (of 4MiB) of a thread to not allocate in the huge OS pages; this prevents threads that are short lived and allocate just a little to take up space in the huge OS page area (which cannot be purged as huge OS pages are pinned to physical memory). The huge pages are usually allocated evenly among NUMA nodes. We can use MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N where N is the numa node (starting at 0) to allocate all the huge pages at a specific numa node instead.

Use caution when using fork in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write for all pages in the original process including the huge OS pages. When any memory is now written in that area, the OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in large increments.

Secure Mode

mimalloc can be build in secure mode by using the -DMI_SECURE=ON flags in cmake. This build enables various mitigations to make mimalloc more robust against exploits. In particular:

  • All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow exploit cannot reach into the metadata).
  • All free list pointers are encoded with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption.
  • Double free's are detected (and ignored).
  • The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc from the OS are also address randomized.

As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees.

Debug Mode

When mimalloc is built using debug mode, various checks are done at runtime to catch development errors.

  • Statistics are maintained in detail for each object size. They can be shown using MIMALLOC_SHOW_STATS=1 at runtime.
  • All objects have padding at the end to detect (byte precise) heap block overflows.
  • Double free's, and freeing invalid heap pointers are detected.
  • Corrupted free-lists and some forms of use-after-free are detected.

Overriding Standard Malloc

Overriding the standard malloc (and new) can be done either dynamically or statically.

Dynamic override

This is the recommended way to override the standard malloc interface.

Dynamic Override on Linux, BSD

On these ELF-based systems we preload the mimalloc shared library so all calls to the standard malloc interface are resolved to the mimalloc library.

> env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram

You can set extra environment variables to check that mimalloc is running, like:

> env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram

or run with the debug version to get detailed statistics:

> env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram

Dynamic Override on MacOS

On macOS we can also preload the mimalloc shared library so all calls to the standard malloc interface are resolved to the mimalloc library.

> env DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram

Note that certain security restrictions may apply when doing this from the shell.

Dynamic Override on Windows

Dynamically overriding on mimalloc on Windows 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. There are four requirements to make the overriding work robustly:

  1. Use the C-runtime library as a DLL (using the /MD or /MDd switch).
  2. Link your program explicitly with mimalloc-override.dll library. To ensure the mimalloc-override.dll is loaded at run-time it is easiest to insert some call to the mimalloc API in the main function, like mi_version() (or use the /INCLUDE:mi_version switch on the linker). See the mimalloc-override-test project for an example on how to use this.
  3. The mimalloc-redirect.dll (or mimalloc-redirect32.dll) must be put in the same folder as the main mimalloc-override.dll at runtime (as it is a dependency of that DLL). The redirection DLL ensures that all calls to the C runtime malloc API get redirected to mimalloc functions (which reside in mimalloc-override.dll).
  4. Ensure the mimalloc-override.dll comes as early as possible in the import list of the final executable (so it can intercept all potential allocations).

For best performance on Windows with C++, it is also recommended to also override the new/delete operations (by including mimalloc-new-delete.h a single(!) source file in your project).

The environment variable MIMALLOC_DISABLE_REDIRECT=1 can be used to disable dynamic overriding at run-time. Use MIMALLOC_VERBOSE=1 to check if mimalloc was successfully redirected.

We cannot always re-link an executable with mimalloc-override.dll, and similarly, we cannot always ensure the the DLL comes first in the import table of the final executable. In many cases though we can patch existing executables without any recompilation if they are linked with the dynamic C runtime (ucrtbase.dll) -- just put the mimalloc-override.dll into the import table (and put mimalloc-redirect.dll in the same folder) Such patching can be done for example with CFF Explorer or the minject program.

Static override

On Unix-like systems, you can also statically link with mimalloc to override the standard malloc interface. The recommended way is to link the final program with the mimalloc single object file (mimalloc.o). We use an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the mimalloc library, link it as the first object file. For example:

> gcc -o myprogram mimalloc.o  myfile1.c ...

Another way to override statically that works on all platforms, is to link statically to mimalloc (as shown in the introduction) and include a header file in each source file that re-defines malloc etc. to mi_malloc. This is provided by mimalloc-override.h. This only works reliably though if all sources are under your control or otherwise mixing of pointers from different heaps may occur!

Tools

Generally, we recommend using the standard allocator with memory tracking tools, but mimalloc can also be build to support the address sanitizer or the excellent Valgrind tool. Moreover, it can be build to support Windows event tracing (ETW). This has a small performance overhead but does allow detecting memory leaks and byte-precise buffer overflows directly on final executables. See also the test/test-wrong.c file to test with various tools.

Valgrind

To build with valgrind support, use the MI_TRACK_VALGRIND=ON cmake option:

> cmake ../.. -DMI_TRACK_VALGRIND=ON

This can also be combined with secure mode or debug mode. You can then run your programs directly under valgrind:

> valgrind <myprogram>

If you rely on overriding malloc/free by mimalloc (instead of using the mi_malloc/mi_free API directly), you also need to tell valgrind to not intercept those calls itself, and use:

> MIMALLOC_SHOW_STATS=1 valgrind  --soname-synonyms=somalloc=*mimalloc* -- <myprogram>

By setting the MIMALLOC_SHOW_STATS environment variable you can check that mimalloc is indeed used and not the standard allocator. Even though the Valgrind option is called --soname-synonyms, this also works when overriding with a static library or object file. Unfortunately, it is not possible to dynamically override mimalloc using LD_PRELOAD together with valgrind. See also the test/test-wrong.c file to test with valgrind.

Valgrind support is in its initial development -- please report any issues.

ASAN

To build with the address sanitizer, use the -DMI_TRACK_ASAN=ON cmake option:

> cmake ../.. -DMI_TRACK_ASAN=ON

This can also be combined with secure mode or debug mode. You can then run your programs as:'

> ASAN_OPTIONS=verbosity=1 <myprogram>

When you link a program with an address sanitizer build of mimalloc, you should generally compile that program too with the address sanitizer enabled. For example, assuming you build mimalloc in out/debug:

clang -g -o test-wrong -Iinclude test/test-wrong.c out/debug/libmimalloc-asan-debug.a -lpthread -fsanitize=address -fsanitize-recover=address

Since the address sanitizer redirects the standard allocation functions, on some platforms (macOSX for example) it is required to compile mimalloc with -DMI_OVERRIDE=OFF. Adress sanitizer support is in its initial development -- please report any issues.

ETW

Event tracing for Windows (ETW) provides a high performance way to capture all allocations though mimalloc and analyze them later. To build with ETW support, use the -DMI_TRACK_ETW=ON cmake option.

You can then capture an allocation trace using the Windows performance recorder (WPR), using the src/prim/windows/etw-mimalloc.wprp profile. In an admin prompt, you can use:

> wpr -start src\prim\windows\etw-mimalloc.wprp -filemode
> <my_mimalloc_program>
> wpr -stop <my_mimalloc_program>.etl

and then open <my_mimalloc_program>.etl in the Windows Performance Analyzer (WPA), or use a tool like TraceControl that is specialized for analyzing mimalloc traces.

Performance

Last update: 2021-01-30

We tested mimalloc against many other top allocators over a wide range of benchmarks, ranging from various real world programs to synthetic benchmarks that see how the allocator behaves under more extreme circumstances. In our benchmark suite, mimalloc outperforms other leading allocators (jemalloc, tcmalloc, Hoard, etc), and has a similar memory footprint. A nice property is that it does consistently well over the wide range of benchmarks.

General memory allocators are interesting as there exists no algorithm that is optimal -- for a given allocator one can usually construct a workload where it does not do so well. The goal is thus to find an allocation strategy that performs well over a wide range of benchmarks without suffering from (too much) underperformance in less common situations.

As always, interpret these results with care since some benchmarks test synthetic or uncommon situations that may never apply to your workloads. For example, most allocators do not do well on xmalloc-testN but that includes even the best industrial allocators like jemalloc and tcmalloc that are used in some of the world's largest systems (like Chrome or FreeBSD).

Also, the benchmarks here do not measure the behaviour on very large and long-running server workloads, or worst-case latencies of allocation. Much work has gone into mimalloc to work well on such workloads (for example, to reduce virtual memory fragmentation on long-running services) but such optimizations are not always reflected in the current benchmark suite.

We show here only an overview -- for more specific details and further benchmarks we refer to the technical report. The benchmark suite is automated and available separately as mimalloc-bench.

Benchmark Results on a 16-core AMD 5950x (Zen3)

Testing on the 16-core AMD 5950x processor at 3.4Ghz (4.9Ghz boost), with with 32GiB memory at 3600Mhz, running Ubuntu 20.04 with glibc 2.31 and GCC 9.3.0.

We measure three versions of mimalloc: the main version mi (tag:v1.7.0), the new v2.0 beta version as xmi (tag:v2.0.0), and the main version in secure mode as smi (tag:v1.7.0).

The other allocators are Google's tcmalloc (tc, tag:gperftools-2.8.1) used in Chrome, Facebook's jemalloc (je, tag:5.2.1) by Jason Evans used in Firefox and FreeBSD, the Intel thread building blocks allocator (tbb, tag:v2020.3), rpmalloc (rp,tag:1.4.1) by Mattias Jansson, the original scalable Hoard (git:d880f72) allocator by Emery Berger [1], the memory compacting Mesh (git:67ff31a) allocator by Bobby Powers et al [8], and finally the default system allocator (glibc, 2.31) (based on PtMalloc2).

Any benchmarks ending in N run on all 32 logical cores in parallel. Results are averaged over 10 runs and reported relative to mimalloc (where 1.2 means it took 1.2× longer to run). The legend also contains the overall relative score between the allocators where 100 points is the maximum if an allocator is fastest on all benchmarks.

The single threaded cfrac benchmark by Dave Barrett is an implementation of continued fraction factorization which uses many small short-lived allocations. All allocators do well on such common usage, where mimalloc is just a tad faster than tcmalloc and jemalloc.

The leanN program is interesting as a large realistic and concurrent workload of the Lean theorem prover compiling its own standard library, and there is a 13% speedup over tcmalloc. This is quite significant: if Lean spends 20% of its time in the allocator that means that mimalloc is 1.6× faster than tcmalloc here. (This is surprising as that is not measured in a pure allocation benchmark like alloc-test. We conjecture that we see this outsized improvement here because mimalloc has better locality in the allocation which improves performance for the other computations in a program as well).

The single threaded redis benchmark again show that most allocators do well on such workloads.

The larsonN server benchmark by Larson and Krishnan [2] allocates and frees between threads. They observed this behavior (which they call bleeding) in actual server applications, and the benchmark simulates this. Here, mimalloc is quite a bit faster than tcmalloc and jemalloc probably due to the object migration between different threads.

The mstressN workload performs many allocations and re-allocations, and migrates objects between threads (as in larsonN). However, it also creates and destroys the N worker threads a few times keeping some objects alive beyond the life time of the allocating thread. We observed this behavior in many larger server applications.

The rptestN benchmark by Mattias Jansson is a allocator test originally designed for rpmalloc, and tries to simulate realistic allocation patterns over multiple threads. Here the differences between allocators become more apparent.

The second benchmark set tests specific aspects of the allocators and shows even more extreme differences between them.

The alloc-test, by OLogN Technologies AG, is a very allocation intensive benchmark doing millions of allocations in various size classes. The test is scaled such that when an allocator performs almost identically on alloc-test1 as alloc-testN it means that it scales linearly.

The sh6bench and sh8bench benchmarks are developed by MicroQuill as part of SmartHeap. In sh6bench mimalloc does much better than the others (more than 2.5× faster than jemalloc). We cannot explain this well but believe it is caused in part by the "reverse" free-ing pattern in sh6bench. The sh8bench is a variation with object migration between threads; whereas tcmalloc did well on sh6bench, the addition of object migration causes it to be 10× slower than before.

The xmalloc-testN benchmark by Lever and Boreham [5] and Christian Eder, simulates an asymmetric workload where some threads only allocate, and others only free -- they observed this pattern in larger server applications. Here we see that the mimalloc technique of having non-contended sharded thread free lists pays off as it outperforms others by a very large margin. Only rpmalloc, tbb, and glibc also scale well on this benchmark.

The cache-scratch benchmark by Emery Berger [1], and introduced with the Hoard allocator to test for passive-false sharing of cache lines. With a single thread they all perform the same, but when running with multiple threads the potential allocator induced false sharing of the cache lines can cause large run-time differences. Crundal [6] describes in detail why the false cache line sharing occurs in the tcmalloc design, and also discusses how this can be avoided with some small implementation changes. Only the tbb, rpmalloc and mesh allocators also avoid the cache line sharing completely, while Hoard and glibc seem to mitigate the effects. Kukanov and Voss [7] describe in detail how the design of tbb avoids the false cache line sharing.

On a 36-core Intel Xeon

For completeness, here are the results on a big Amazon c5.18xlarge instance consisting of a 2×18-core Intel Xeon (Cascade Lake) at 3.4GHz (boost 3.5GHz) with 144GiB ECC memory, running Ubuntu 20.04 with glibc 2.31, GCC 9.3.0, and Clang 10.0.0. This time, the mimalloc allocators (mi, xmi, and smi) were compiled with the Clang compiler instead of GCC. The results are similar to the AMD results but it is interesting to see the differences in the larsonN, mstressN, and xmalloc-testN benchmarks.

Peak Working Set

The following figure shows the peak working set (rss) of the allocators on the benchmarks (on the c5.18xlarge instance).

Note that the xmalloc-testN memory usage should be disregarded as it allocates more the faster the program runs. Similarly, memory usage of larsonN, mstressN, rptestN and sh8bench can vary depending on scheduling and speed. Nevertheless, we hope to improve the memory usage on mstressN and rptestN (just as cfrac, larsonN and sh8bench have a small working set which skews the results).

References

  • [1] Emery D. Berger, Kathryn S. McKinley, Robert D. Blumofe, and Paul R. Wilson. Hoard: A Scalable Memory Allocator for Multithreaded Applications the Ninth International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS-IX). Cambridge, MA, November 2000. pdf

  • [2] P. Larson and M. Krishnan. Memory allocation for long-running server applications. In ISMM, Vancouver, B.C., Canada, 1998. pdf

  • [3] D. Grunwald, B. Zorn, and R. Henderson. Improving the cache locality of memory allocation. In R. Cartwright, editor, Proceedings of the Conference on Programming Language Design and Implementation, pages 177–186, New York, NY, USA, June 1993. pdf

  • [4] J. Barnes and P. Hut. A hierarchical O(n*log(n)) force-calculation algorithm. Nature, 324:446-449, 1986.

  • [5] C. Lever, and D. Boreham. Malloc() Performance in a Multithreaded Linux Environment. In USENIX Annual Technical Conference, Freenix Session. San Diego, CA. Jun. 2000. Available at https://github.com/kuszmaul/SuperMalloc/tree/master/tests

  • [6] Timothy Crundal. Reducing Active-False Sharing in TCMalloc. 2016. CS16S1 project at the Australian National University. pdf

  • [7] Alexey Kukanov, and Michael J Voss. The Foundations for Scalable Multi-Core Software in Intel Threading Building Blocks. Intel Technology Journal 11 (4). 2007

  • [8] Bobby Powers, David Tench, Emery D. Berger, and Andrew McGregor. Mesh: Compacting Memory Management for C/C++ In Proceedings of the 40th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI'19), June 2019, pages 333-–346.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

Older Release Notes

  • 2021-11-14, v1.7.3, v2.0.3 (beta): improved WASM support, improved macOS support and performance (including M1), improved performance for v2 for large objects, Python integration improvements, more standard installation directories, various small fixes.

  • 2021-06-17, v1.7.2, v2.0.2 (beta): support M1, better installation layout on Linux, fix thread_id on Android, prefer 2-6TiB area for aligned allocation to work better on pre-windows 8, various small fixes.

  • 2021-04-06, v1.7.1, v2.0.1 (beta): fix bug in arena allocation for huge pages, improved aslr on large allocations, initial M1 support (still experimental).

  • 2021-01-31, v2.0.0: beta release 2.0: new slice algorithm for managing internal mimalloc pages.

  • 2021-01-31, v1.7.0: stable release 1.7: support explicit user provided memory regions, more precise statistics, improve macOS overriding, initial support for Apple M1, improved DragonFly support, faster memcpy on Windows, various small fixes.

  • 2020-09-24, v1.6.7: stable release 1.6: using standard C atomics, passing tsan testing, improved handling of failing to commit on Windows, add mi_process_info api call.

  • 2020-08-06, v1.6.4: stable release 1.6: improved error recovery in low-memory situations, support for IllumOS and Haiku, NUMA support for Vista/XP, improved NUMA detection for AMD Ryzen, ubsan support.

  • 2020-05-05, v1.6.3: stable release 1.6: improved behavior in out-of-memory situations, improved malloc zones on macOS, build PIC static libraries by default, add option to abort on out-of-memory, line buffered statistics.

  • 2020-04-20, v1.6.2: stable release 1.6: fix compilation on Android, MingW, Raspberry, and Conda, stability fix for Windows 7, fix multiple mimalloc instances in one executable, fix strnlen overload, fix aligned debug padding.

  • 2020-02-17, v1.6.1: stable release 1.6: minor updates (build with clang-cl, fix alignment issue for small objects).

  • 2020-02-09, v1.6.0: stable release 1.6: fixed potential memory leak, improved overriding and thread local support on FreeBSD, NetBSD, DragonFly, and macOSX. New byte-precise heap block overflow detection in debug mode (besides the double-free detection and free-list corruption detection). Add nodiscard attribute to most allocation functions. Enable MIMALLOC_PAGE_RESET by default. New reclamation strategy for abandoned heap pages for better memory footprint.

  • 2020-02-09, v1.5.0: stable release 1.5: improved free performance, small bug fixes.

  • 2020-01-22, v1.4.0: stable release 1.4: improved performance for delayed OS page reset, more eager concurrent free, addition of STL allocator, fixed potential memory leak.

  • 2020-01-15, v1.3.0: stable release 1.3: bug fixes, improved randomness and stronger free list encoding in secure mode.

  • 2019-12-22, v1.2.2: stable release 1.2: minor updates.

  • 2019-11-22, v1.2.0: stable release 1.2: bug fixes, improved secure mode (free list corruption checks, double free mitigation). Improved dynamic overriding on Windows.

  • 2019-10-07, v1.1.0: stable release 1.1.

  • 2019-09-01, v1.0.8: pre-release 8: more robust windows dynamic overriding, initial huge page support.

  • 2019-08-10, v1.0.6: pre-release 6: various performance improvements.

mimalloc's People

Contributors

asl avatar barracuda156 avatar bmalrat avatar caixiangyue avatar carenas avatar cormacrelf avatar daanx avatar dervogel2020 avatar devnexen avatar dscho avatar haneefmubarak avatar isanych avatar jedisct1 avatar jmroot avatar jserv avatar kile0 avatar myd7349 avatar nico-abram avatar ohasanliuw avatar pitrou avatar qix- avatar res2k avatar rhermes avatar romange avatar sblondon avatar tiran avatar urshanselmann avatar viy2 avatar vmarkovtsev avatar xhochy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mimalloc's Issues

Segfault while building the Rust toolchain with a toolchain that uses mimalloc

So I'm trying to build a Rust toolchain with mimalloc that works here: rust-lang/rust#62340 , such that we can use the Rust toolchain compiler benchmark suite to test whether it would be worth it switching to mimalloc from jemalloc.

I think I've managed so far to link mimalloc properly, and built a toolchain that's statically linked with it and uses it. When that toolchain is used to build another toolchain, after building a chunk of it, it segfaults with: signal: 11, SIGSEGV: invalid memory reference. Target is x86_64-unknown-linux-gnu , and I'm using the master branch of mimalloc, via the mimalloc-sys bindings, which are here: https://github.com/gnzlbg/mimallocator/tree/master/mimalloc-sys

I'm building mimalloc following the steps from this script: https://github.com/gnzlbg/mimallocator/blob/master/mimalloc-sys/build.rs

Crash on run mimalloc-override-test

  • call stack
 	ntdll.dll!00000000773af3af()	Unknown
 	ntdll.dll!00000000773af9c6()	Unknown
 	ntdll.dll!00000000773b0592()	Unknown
 	ntdll.dll!00000000773b2204()	Unknown
 	ntdll.dll!000000007734d21c()	Unknown
 	ucrtbase.dll!000007fef636419b()	Unknown
>	mimalloc-override-test.exe!main() Line 29	C++
 	[External Code]	

version: 875ecbd
Description: Win7 x64 SP1 vs communtity 2017 15.9.11
Solution Config: Release x64
Uploading dump-and-pdb-files.zip…

Rights regarding the mimalloc logo

I would like to know the situation regarding the rights to use the mimalloc logo.
I would like to use it personally for my wrapper that I am creating to allow the use of mimalloc in the programming language D

Interlocked compile errors in C++ on Windows

Setting MI_USE_CXX to ON and compiling with MSC (latest version in 16.1.5 / 2019.1) a number of instances of this error occur:

C:\Users\Sean\Documents\Projects\test\mimalloc-src\include\mimalloc-atomic.h(68): error C2665: '_InterlockedIncrement': none of the 4 overloads could convert all the argument types
  C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\winbase.h(9283): note: could be 'unsigned __int64 _InterlockedIncrement(volatile unsigned __int64 *)'
  C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\winbase.h(9271): note: or       'unsigned long _InterlockedIncrement(volatile unsigned long *)'
  C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\winbase.h(9262): note: or       'unsigned int _InterlockedIncrement(volatile unsigned int *)'
  C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\winnt.h(2954): note: or       'LONG _InterlockedIncrement(volatile LONG *)'

I haven't dug too deep, but looking in the Win10 SDK's copy of WinBase.h I'm only seeing overloads for unsigned types. This issue might then be more of an issue with the Win10 SDK and not actually the compiler, though I didn't get these errors when compiling with the C compiler.

There's also a few instances of:

C:\Users\Sean\Documents\Projects\test\mimalloc-src\src\alloc.c(114): error C2679: binary '=': no operator found which takes a right-hand operand of type 'volatile mi_thread_free_t' (or there is no acceptable conversion)
  C:\Users\Sean\Documents\Projects\test\mimalloc-src\include\mimalloc-types.h(142): note: could be 'mi_thread_free_u &mi_thread_free_u::operator =(mi_thread_free_u &&)'
  C:\Users\Sean\Documents\Projects\test\mimalloc-src\include\mimalloc-types.h(142): note: or       'mi_thread_free_u &mi_thread_free_u::operator =(const mi_thread_free_u &)'
  C:\Users\Sean\Documents\Projects\test\mimalloc-src\src\alloc.c(114): note: while trying to match the argument list '(mi_thread_free_t, volatile mi_thread_free_t)'

All of these look like issues with (missing) volatile reference overloads in mimalloc C++ wrappers, I think, though I haven't really looked into this one at all.

OSX debugging

The problem does not occur on linux.

https://github.com/microsoft/mimalloc/blob/master/src/alloc.c#L212

(lldb) r
Process 93543 launched: './bin/arangodbtests' (x86_64)
mimalloc: option 'secure': 0
mimalloc: option 'pool_commit': 0
mimalloc: process init: 0x7fffc980c3c0
mimalloc: debug level : 1
mimalloc: option 'show_errors': 1
mimalloc: error: trying to mi_free a pointer that does not point to a valid heap space: 0x111040000
mimalloc: assertion failed: at "/Users/jenkins/arangodb/3rdParty/mimalloc/src/options.c":121, _mi_error_message
  assertion: "false"
Process 93543 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007fffc0a21d42 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fffc0a21d42 <+10>: jae    0x7fffc0a21d4c            ; <+20>
    0x7fffc0a21d44 <+12>: movq   %rax, %rdi
    0x7fffc0a21d47 <+15>: jmp    0x7fffc0a1acaf            ; cerror_nocancel
    0x7fffc0a21d4c <+20>: retq   
Target 0: (arangodbtests) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fffc0a21d42 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fffc0b0f457 libsystem_pthread.dylib`pthread_kill + 90
    frame #2: 0x00007fffc0987420 libsystem_c.dylib`abort + 129
    frame #3: 0x00000001055e339f arangodbtests`_mi_assert_fail(assertion="false", fname="/Users/jenkins/arangodb/3rdParty/mimalloc/src/options.c", line=121, func="_mi_error_message") at options.c:136
    frame #4: 0x00000001055e3e24 arangodbtests`_mi_error_message(fmt="trying to mi_free a pointer that does not point to a valid heap space: %p\n") at options.c:121
    frame #5: 0x00000001055dfa53 arangodbtests`mi_free(p=0x0000000111040000) at alloc.c:212
    frame #6: 0x00000001055dfbc5 arangodbtests`_ZdlPv(p=0x0000000111040000) at alloc-override.c:90
    frame #7: 0x0000000105573910 arangodbtests`testing::internal::MakeAndRegisterTestInfo(test_case_name="ActiveFailover", name="creating_a_job_should_create_a_job_in_todo", type_param=0x0000000000000000, value_param=0x0000000000000000, code_location=(file = "/Users/jenkins/arangodb/tests/Agency/ActiveFailoverTest.cpp", line = 113), fixture_class_id=0x0000000109537728, set_up_tc=(arangodbtests`testing::Test::SetUpTestCase() at gtest.h:20190), tear_down_tc=(arangodbtests`testing::Test::TearDownTestCase() at gtest.h:20198), factory=0x0000000111020000)(), void (*)(), testing::internal::TestFactoryBase*) at gtest-all.cc:4053
    frame #8: 0x00000001000ae474 arangodbtests`::__cxx_global_var_init.29() at ActiveFailoverTest.cpp:113
    frame #9: 0x00000001000aee64 arangodbtests`_GLOBAL__sub_I_ActiveFailoverTest.cpp at ActiveFailoverTest.cpp:0
    frame #10: 0x000000010f80ca1b dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 385
    frame #11: 0x000000010f80cc1e dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
    frame #12: 0x000000010f8084aa dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 338
    frame #13: 0x000000010f807524 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 138
    frame #14: 0x000000010f8075b9 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 75
    frame #15: 0x000000010f7f947a dyld`dyld::initializeMainExecutable() + 195
    frame #16: 0x000000010f7fd8c6 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3966
    frame #17: 0x000000010f7f8249 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 470
    frame #18: 0x000000010f7f8036 dyld`_dyld_start + 54
(lldb) 

In order to reproduce you could build the tests in this branch:
https://github.com/arangodb/arangodb/tree/feature/mimalloc

Make aligned heap alloc public

currently mi_heap_malloc_zero_aligned_at is not on the public api, and there is no aligned heap api's, it should be fairly simple and useful to have this as a public api right?

I can make a PR if wanted, I just got curious why it isn't so already.

Debug assertions are not silent

Enabling -DMI_DEBUG=3 produces output even when no errors are encountered. The library should only print when there is something to print.

Prefix CMake variables/options

The CMakeLists.txt currently unprefixed variables like SECURE or OVERRIDE. This can be messy and problematic when mimalloc is added to a project via add_subdirectory. Library projects like this ideally should use prefixed variables, e.g. MIMALLOC_SECURE rather than SECURE, so that amalgamations of multiple third-party dependencies used in larger monolithic projects don't end up with name conflicts or messy configuration pages.

mi_rezalloc() zero initialization is unsafe

In _mi_realloc_zero() and _mi_heap_malloc_zero() only the requested size and not the possibly larger allocated size is initialized to zero.

This may lead to uninitialized 'new' memory from the applications point of view in a following call to _mi_realloc_zero(), where the 'fits' test using mi_usable_size(p) and the memcpy(newp, p, ...) relies on a defined state of the previously allocated and not only the previously requested memory range.

Also take into account, that using mi_rezalloc() on a pointer from a call to mi_malloc() is legal but would lead to uninitialized memory in the range from the requested size to the allocated size too. It seems to me that the rezalloc feature requires to store the requested size or at least to zero out the implicit allocated range form requested size to allocated size in all allocations.

I wonder if this is why posix doesn't specify rezalloc().


On my windows machine this code sample can reproduce the problem:

int main()
{
  //  mi_rezalloc() is not safe!

  //  test precondition: first allocation on page!

  //  force allocating the last block in the page next, so it will be 'recycled' immediately...
  for (int idx = 0; idx < 127; ++idx)
    ::mi_free(::mi_malloc(128));

  //  force known state of 'uninitialized' memory (by mi_malloc debug initialization or explicit memset)
  auto uninitialized = static_cast< unsigned char* >(::mi_malloc(128));
  assert(mi_usable_size(uninitialized) == 128);
  ::memset(uninitialized, 0xD0, 128);
  ::mi_free(uninitialized);

  //  this initialized allocation recycles the uninitialized pointer (if not, adapt forcing loop above or use a different size class...)
  auto initialized = static_cast< unsigned char* >(::mi_rezalloc(nullptr, 121));
  assert(mi_usable_size(initialized) == 128);
  assert(uninitialized == initialized);
  assert(initialized[  0] ==    0); // access in requested range; mimalloc has initialized this!
  assert(initialized[120] ==    0); // access in requested range; mimalloc has initialized this!
  assert(initialized[121] == 0xd0); // access in verified usable range; mimalloc has not initialized this!
  initialized[  0] = 'A';
  initialized[120] = 'Z';

  //  grow with initialization! remember what the api documentation says:
  //  > If the newsize is larger than the original allocated size of p, the extra bytes are initialized to zero.
  initialized = static_cast< unsigned char* >(::mi_rezalloc(initialized, 122));
  assert(mi_usable_size(initialized) == 128);
  assert(uninitialized == initialized);
  assert(initialized[  0] ==  'A'); // access in requested old range; mimalloc has not re-initialized this!
  assert(initialized[120] ==  'Z'); // access in requested old range; mimalloc has not re-initialized this!
  assert(initialized[121] == 0x00); // access in requested initialized new memory; mimalloc has not initialized this!
}

Sized deallocation API

There does not appear to be a sized-deallocation API (e.g. free_(void*, size_t)) where the user promises that the size passed is the size hint in range [requested_size, alloction_size] where allocation size is the maximum of mi_good_size and mi_usable_size.

large page support on windows?

It appears that there's no option to turn on large page support on windows?

Searched the code base, VirtualAlloc was called without MEM_LARGE_PAGES.

Any future plan to support it? It was said to significantly reduce TLB misses and may lead to notable gains in memory intensive scenarios.

Statistics are produced even when MI_STAT=0

I'm compiling the static library manually as follows:

"/usr/bin/clang" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC"
"-g" "-fno-omit-frame-pointer" "--target=x86_64-apple-darwin" "-I" 
"mimalloc/include" "-I" "mimalloc/src" "-std=gnu11" "-Wall" "-Wextra" 
"-Wno-unknown-pragmas" "-ftls-model=initial-exec" "-DMI_STATIC_LIB" 
"-DMI_DEBUG=3" "-DMI_SECURE=2" "-DMI_STAT=0" "-o" 
"target/debug/build/mimalloc-sys-329fad58db2cfb59/out/mimalloc/src/static.o" 
"-c" "mimalloc/src/static.c"

Note that MI_STAT=0. Still, i'm getting output of the form:

mimalloc: option 'page_reset': 0
mimalloc: option 'show_stats': 0
heap stats:     peak      total      freed       unit      count  
   elapsed:     0.003 s
   process: user: 0.004 s, system: 0.004 s, faults: 0, reclaims: 707, rss: 2.0 mb
mimalloc: process done: 0x104de25c0

Is _MBCS required?

The default solution file on Windows/VS20XX compiles the code as Multi Byte Character Set. This appears to have been done quite deliberately.

Is this really necessary or can the code also be compiled as UNICODE, which would be preferable.

Windows CMake+Ninja build warning due to duplicate basename

Building a monolithic project with CMake+Ninja (default behaviour in Visual Studio these days) that adds mimalloc via add_subdirectory:

ninja : warning : multiple rules generate lib/mimalloc-debug.lib. builds involving this target will not be correct; continuing anyway [-w dupbuild=warn]

The problem appears to be that both the shared and static versions of the library end up creating a mimalloc-static.lib file (the import library for the DLL and the static library itself). This is because both libraries set:

set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})

Which causes both the DLL's import library and the static library's name to be identical.

#############################################
# Link the shared library bin\mimalloc-debug.dll

build bin\mimalloc-debug.dll lib\mimalloc-debug.lib: C_SHARED_LIBRARY_LINKER__mimalloc
  ...stuff...
  TARGET_FILE = bin\mimalloc-debug.dll
  TARGET_IMPLIB = lib\mimalloc-debug.lib
  TARGET_PDB = bin\mimalloc-debug.pdb
#############################################
# Link the static library lib\mimalloc-debug.lib

build lib\mimalloc-debug.lib: C_STATIC_LIBRARY_LINKER__mimalloc-static
  ...stuff...
  TARGET_FILE = lib\mimalloc-debug.lib
  TARGET_PDB = lib\mimalloc-debug.pdb

Library versioning

The cmake install script should install the shared library to libmimalloc.so.<ABI version> and point a symlink to libmimalloc.so, instead it just installs to the latter. It should also add execution bit to it (chmod 755 instead of 644).

Why #pragma once?

Why you write #pragma once in these header files?

I think we can eliminate it because first it works only on Windows and second there is no reason to use it because we already have header guards:

#ifndef __MIMALLOC_X_H
#define __MIMALLOC_X_H

'reserved' and 'committed' stats don't appear to match their descriptions

Thank you for making this library available! I'm enjoying playing around with it.

In Windows, the flags MEM_COMMIT and MEM_RESERVE are passed together to VirtualAlloc, yet the 'committed' heap stat does not equal 'reserved'. It seems like the library's intention is for those stats to have the equivalent API semantics of 'backed by physical' vs 'reserved virtual address space'.

The same bug appears to apply to the other platforms, though I'm not as familiar with mmap and could be wrong.

Allocation overriding on Windows with CMake

The file alloc-override-win.c is never included or built on Windows when using CMake. It appears that the file is only referenced at all in the provided .vcxproj files, which are not of help to parent CMake projects that are building via add_subdirectory (and may not even be using MSBuild anyway but rather Ninja, which is also the default CMake build system in Visual Studio itself these days).

please release prebuilt binaries for windows and linux

Hi there,

For the linux side of things making the .a/.so should be straighforward enough.
For the windows side of things however, it would have been nice to release some pre-build binaries for windows 10 to things out with.

Also, it would have been nice to simply msbuild.exe ide/vs2017/mimalloc.sln
giving examples to build it at the command line with different mstools versions.
How do we query for the available install mstools versions we have to pass it at the msbuild command line and force it to use that?

Thank you for listening.

Compiling with Mingw needs fixes

Compiling with Mingw gives two errors:
[1]

/at/rust/projects/mimalloc/target/x86_64-pc-windows-gnu/debug/build/mimalloc-sys-49105590189f0a7b/out/build/mimalloc/src/alloc.c:416:13: error: ‘EINVAL’ undeclared (first use in this function); did you mean ‘WINVER’?
     errno = EINVAL; return NULL;
             ^~~~~~
             WINVER
/at/rust/projects/mimalloc/target/x86_64-pc-windows-gnu/debug/build/mimalloc-sys-49105590189f0a7b/out/build/mimalloc/src/alloc.c:416:13: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [CMakeFiles/mimalloc-static.dir/src/alloc.c.obj] Error 1

It may be fixed with add

#ifndef EINVAL
#define EINVAL 22
#endif

in the alloc.c. I took the code from alloc-override.c.

[2]

/at/rust/projects/mimalloc/target/x86_64-pc-windows-gnu/debug/build/mimalloc-sys-49105590189f0a7b/out/build/mimalloc/src/init.c:388:12: fatal error: Windows.h: No such file or directory
   #include <Windows.h>
            ^~~~~~~~~~~

If the header <Windows.h> is the same as <windows.h> then it my be fixed with rename the header to <windows.h> in the init.c.

With these two fixes and with this patch (#22) I can compile mimalloc with Mingw on Linux.

Document interaction of MI_OVERRIDE / MI_INTERPOSE options with static and dynamic builds

IIUC if I compile src/static.c and statically link it to my binary, all calls to malloc and similar APIs will be overriden by mimalloc. Is this correct on all platforms ? (Windows, Linux, MacOSX, Android, FreeBSD, OpenBSD, NetBSD, etc.) ?

That is, it doesn't matter whether I define MI_OVERRIDE / MI_INTERSPERSE for this configuration ?

It would be worth it to document the CMake options somewhere, explaining specifically what they do, and how they interact with the different ways in which the library can be built.

Expose standard C and POSIX APIs even without override

It would be great if the library could always expose a standard C and POSIX API, e.g., for example, right now, mi_aligned_alloc and mi_posix_memalign are missing.

Ideally, they would always be available. This would allow writing a wrapper that uses the standard C names, e.g., aligned_alloc, and just always calls mi_aligned_alloc internally, instead of having some logic to do different things depending on MI_OVERRIDE.

Added support for mimalloc to pt::pector

I've added support for mimalloc to pt::pector. pt::pector is a 'better' std::vector (not always) written by Adrien Guinet, who has dropped of the radar, I hope he's doing well. So I have forked that project and I'm doing some minor maintenance. Its license is LGPL (but header only, so I don't really know what that means). pt::pector intends to be a better std::vector. To that purpose (a.o.) a pt::malloc_allocator was added to pt::pector, in order to benefit from std::realloc. This allowed for marginal improvements over std::vector. Now I've added a pt::mimalloc_allocator, so, same thing, just replaced std::malloc and friends with mi_malloc and its girl-friends. This has had a dramatic change in the resulting benchmark numbers.

What I'm doing in the bench-marking is to emplace_back() on a vector of vector-type containers, randomly selected and of random length within a range. The ranges are then iteratively increased and the length of the vectors increases as well. It sounds more complicated than it is, the code is here.

I'm testing this both with VS2019 and with Clang/LLVM-9.0 (trunk) on Windows 10-1903 x64, Intel Ci3 5005U. For VC with LTCG and for Clang with Thin LTO.

Results for VS2019:

06/28/19 12:00:18
Running Y:\REPOS\podder\x64\Release\benchmark.exe
Run on (4 X 1995 MHz CPU s)
CPU Caches:
  L1 Data 32K (x2)
  L1 Instruction 32K (x2)
  L2 Unified 262K (x2)
  L3 Unified 3145K (x1)
----------------------------------------------------------------------------------------------------------------------------------
Benchmark                                                                                        Time             CPU   Iterations
----------------------------------------------------------------------------------------------------------------------------------
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_mean                       747 ns          802 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_median                     746 ns          809 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_stddev                    10.9 ns         26.7 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_mean                       871 ns          848 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_median                     873 ns          889 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_stddev                    3.91 ns         91.2 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_mean                       874 ns          907 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_median                     871 ns          893 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_stddev                    8.87 ns         70.2 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_mean                      873 ns          834 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_median                    871 ns          830 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_stddev                   9.96 ns         38.4 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_mean                      951 ns          977 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_median                    948 ns          991 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_stddev                   17.9 ns         63.4 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_mean                     1275 ns         1231 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_median                   1270 ns         1235 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_stddev                   19.4 ns         79.2 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_mean                    1866 ns         1836 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_median                  1834 ns         1859 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_stddev                   107 ns         78.5 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_mean                    2652 ns         2576 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_median                  2570 ns         2539 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_stddev                   226 ns          201 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_mean                    4005 ns         3838 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_median                  3980 ns         3838 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_stddev                   302 ns          291 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_mean                    6496 ns         6719 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_median                  6501 ns         6563 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_stddev                   367 ns          442 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_mean              770 ns          750 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_median            766 ns          759 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_stddev           9.64 ns         74.0 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_mean              866 ns          885 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_median            866 ns          907 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_stddev           1.75 ns         74.5 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_mean              859 ns          859 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_median            860 ns          859 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_stddev           2.75 ns         46.0 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_mean             870 ns          931 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_median           870 ns          935 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_stddev          1.54 ns         20.9 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_mean             923 ns          977 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_median           921 ns         1004 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_stddev          7.22 ns         78.9 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_mean            1164 ns         1193 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_median          1166 ns         1224 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_stddev          9.91 ns         70.4 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_mean           1607 ns         1656 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_median         1607 ns         1679 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_stddev         5.47 ns          137 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_mean           2000 ns         1978 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_median         2001 ns         1904 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_stddev         8.88 ns          228 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_mean           2777 ns         2920 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_median         2767 ns         2905 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_stddev         32.1 ns          156 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_mean           4755 ns         4883 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_median         4752 ns         4883 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_stddev         58.6 ns          114 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_mean            408 ns          432 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_median          407 ns          430 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_stddev         3.49 ns         25.7 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_mean            416 ns          430 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_median          417 ns          425 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_stddev         1.58 ns         25.5 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_mean            435 ns          475 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_median          434 ns          477 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_stddev         3.69 ns         46.5 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_mean           463 ns          445 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_median         464 ns          452 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_stddev        2.66 ns         48.1 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_mean           530 ns          509 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_median         525 ns          502 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_stddev        10.9 ns         58.6 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_mean           606 ns          645 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_median         607 ns          637 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_stddev        1.27 ns         65.2 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_mean          798 ns          792 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_median        783 ns          795 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_stddev       34.9 ns         41.7 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_mean         1078 ns         1130 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_median       1077 ns         1109 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_stddev       4.59 ns         80.1 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_mean         1652 ns         1611 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_median       1653 ns         1592 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_stddev       8.64 ns          180 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_mean         2931 ns         2965 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_median       2934 ns         2965 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_stddev       15.1 ns         90.1 ns            4

And with Clang the results are [very different, but still putting mipector (the one backed by mimalloc) in the lead]:

06/28/19 12:04:41
Running podder-benchmark.exe
Run on (4 X 1995 MHz CPU s)
CPU Caches:
  L1 Data 32K (x2)
  L1 Instruction 32K (x2)
  L2 Unified 262K (x2)
  L3 Unified 3145K (x1)
----------------------------------------------------------------------------------------------------------------------------------
Benchmark                                                                                        Time             CPU   Iterations
----------------------------------------------------------------------------------------------------------------------------------
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_mean                       710 ns          698 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_median                     707 ns          715 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/23/8192/repeats:4_stddev                    6.30 ns         88.9 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_mean                       710 ns          725 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_median                     710 ns          732 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/32/8192/repeats:4_stddev                    2.46 ns         19.7 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_mean                       723 ns          774 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_median                     725 ns          781 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/64/8192/repeats:4_stddev                    6.81 ns         47.7 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_mean                      745 ns          734 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_median                    745 ns          719 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/128/8192/repeats:4_stddev                   7.13 ns         44.2 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_mean                      792 ns          802 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_median                    794 ns          816 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/256/8192/repeats:4_stddev                   8.04 ns         33.2 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_mean                     1052 ns         1011 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_median                   1057 ns         1018 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/512/8192/repeats:4_stddev                   12.1 ns         47.7 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_mean                    1532 ns         1583 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_median                  1518 ns         1573 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/1024/8192/repeats:4_stddev                  43.8 ns         85.1 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_mean                    1933 ns         2026 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_median                  1933 ns         2026 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/2048/8192/repeats:4_stddev                  53.8 ns         63.0 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_mean                    2597 ns         2667 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_median                  2625 ns         2651 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/4096/8192/repeats:4_stddev                   108 ns          147 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_mean                    3843 ns         3578 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_median                  3828 ns         3432 ns            4
bm_emplace_back_random<std::vector<std::uint8_t>>/8192/8192/repeats:4_stddev                  51.7 ns          293 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_mean              765 ns          764 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_median            765 ns          746 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_stddev           3.74 ns         40.1 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_mean              790 ns          766 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_median            784 ns          727 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_stddev           19.5 ns         83.7 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_mean              803 ns          813 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_median            801 ns          766 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_stddev           7.03 ns          129 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_mean             877 ns          886 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_median           844 ns          865 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_stddev          73.8 ns         86.4 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_mean             910 ns          949 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_median           912 ns          893 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_stddev          5.99 ns          133 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_mean            1164 ns         1248 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_median          1165 ns         1224 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_stddev          10.7 ns          176 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_mean           1701 ns         1822 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_median         1701 ns         1822 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_stddev         8.86 ns          179 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_mean           2207 ns         2225 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_median         2210 ns         2225 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_stddev         21.1 ns          169 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_mean           3127 ns         3058 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_median         3128 ns         3040 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_stddev         7.67 ns         70.1 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_mean           5252 ns         4778 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_median         5256 ns         4743 ns            4
bm_emplace_back_random<pector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_stddev          138 ns          562 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_mean            412 ns          377 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_median          412 ns          374 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/23/8192/repeats:4_stddev         1.15 ns         53.8 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_mean            421 ns          415 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_median          421 ns          413 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/32/8192/repeats:4_stddev         1.44 ns         26.5 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_mean            443 ns          439 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_median          442 ns          454 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/64/8192/repeats:4_stddev         2.38 ns         42.0 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_mean           484 ns          502 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_median         484 ns          495 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/128/8192/repeats:4_stddev       0.331 ns         52.2 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_mean           552 ns          559 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_median         552 ns          578 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/256/8192/repeats:4_stddev        1.31 ns         39.1 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_mean           657 ns          698 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_median         657 ns          705 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/512/8192/repeats:4_stddev        2.61 ns         41.1 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_mean          866 ns          848 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_median        868 ns          858 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/1024/8192/repeats:4_stddev       5.89 ns         71.5 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_mean         1249 ns         1311 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_median       1250 ns         1283 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/2048/8192/repeats:4_stddev       4.56 ns         96.7 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_mean         1995 ns         2053 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_median       1998 ns         2066 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/4096/8192/repeats:4_stddev       13.5 ns         50.1 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_mean         3626 ns         3721 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_median       3618 ns         3721 ns            4
bm_emplace_back_random<mipector<std::uint8_t, std::int64_t>>/8192/8192/repeats:4_stddev       46.5 ns          133 ns            4

Just sharing, time for a better vector has never looked better than now! Thanks for this great allocator.

Add CMake option to choose building SHARED vs STATIC

This is related to outstanding PR: #22

Ping @myd7349

I hit this issue last night as well, so I'm glad someone is already working on it, but I don't think that change is necessary or the best approach.

I believe the root cause here is that there's currently no way to choose between the SHARED and the STATIC builds. The CMakeLists.txt currently builds both no matter what which causes a problem in this case. It's also generally undesirable in a lot of other cases. The suggestion is simply to add the choice of SHARED vs STATIC vs BOTH for the consumer via a CMake option(s), and raise the CMake error when compiler is MSVC, OVERRIDE is ON, and MI_BUILD_STATIC is ON.

It's a pretty common convention to use options to expose the shared/static choice to consumers:
https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html

Note: Libraries such as this should not use the BUILD_SHARED_LIBS global value, but instead should use their own specific option name such as MI_BUILD_SHARED and/or MI_BUILD_STATIC. There are a few ways to model that, depending on whether or not BUILD_BOTH is really a valid and necessary scenario.

Regarding PR #22
It is intended to workaround this issue via conditionally forcing of CMake options, which is generally confusing to consumers who explicitly request OVERRIDE ON and get something built with OVERRIDE OFF. For this reason, it's considered by many to be an anti-pattern. Instead, it's usually preferable to explicitly raise an error in the build system for invalid configurations. The pre-processor error below is currently doing that job successfully:

#if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL))

However, this pre-processor approach has the disadvantage of being declared far away from where options are declared and used in CMakeLists.txt, which makes it impossible to see that it is part of a build-level invariant when looking at the CMakeLists.txt. There are comments in various parts of documentation, but the suggestion to declare the invariant in CMakeLists.txt has obvious advantages.

Regarding Visual Studio Solution versus CMake:
The current situation is that VS Solution intends to be the supported/promoted way to build on Windows, so nobody probably encountered this issue while testing for Windows. However, we don't want to use the VS Solution, we want to build with CMake on all platforms, and we are planning on submitting future PR's to make the CMake output equivalent to the VS Solution on Windows eventually.

`mi_heap_visit_blocks` callback is missing explicit calling convention

The callback mi_block_visit_fun is missing a calling convention specification (e.g. __cdecl or the like), which can cause linkage problems when mixing a mimalloc library compiled with one ABI but linked into a target that uses a different default ABI (e.g. using the /Gv switch that can be common in performance-oriented codebases).

Its standard practice in Windows code to always tag a calling convention on callback function declarations to avoid this issue. Windows headers use the CALLBACK macro for this purpose though specifying a new macro for mimalloc's purpose would suffice too so the code remains portable.

The idea is that this function can be declared in mimalloc.h using this specifier and then any user code that is defining a callback for mi_heap_visit_blocks would use the same specifier macro for the callback declaration, guaranteeing compatibility even if the default calling conventions differ.

Some performance issues with mimalloc

Hi, I tried mimalloc in ClickHouse and faced some (I hope so) interesting issues.

The slowdown against default jemalloc was big -- the query processing is approximately two times slower than usual.

I looked around and have some questions with examples of why something is happening.

First of all, everything is done under Linux x86-64.

The example

#include <memory>

int main() {
    std::unique_ptr<int[]> a(new int[1ull << 30]);
    return 0;
}

With standard allocator, I have only 1 mmap and 1 munmap which is pretty cool and expected because allocation is huge.

strace -fe mmap,munmap ./test
...
mmap(NULL, 4294971392, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c63616000
munmap(0x7f5c63616000, 4294971392)      = 0

With mimalloc, I have 6 mmaps and munmaps with rather big ones that are doubled.

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8f24999000
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8f24996000
munmap(0x7f8f24e71000, 193620)          = 0
mmap(NULL, 4194304, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8f24596000
munmap(0x7f8f24596000, 4194304)         = 0
mmap(NULL, 8388608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8f24196000
munmap(0x7f8f24196000, 2531328)         = 0
munmap(0x7f8f24800000, 1662976)         = 0
mmap(NULL, 4294967504, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8e243ff000
munmap(0x7f8e243ff000, 4294967504)      = 0
mmap(NULL, 4299161808, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8e23fff000
munmap(0x7f8e23fff000, 4096)            = 0
munmap(0x7f8f24001000, 4186320)         = 0
munmap(0x7f8e24000000, 4294967504)      = 0

Then I read some code and saw the https://github.com/microsoft/mimalloc/blob/master/src/segment.c#L290 and https://github.com/microsoft/mimalloc/blob/master/src/os.c#L284 that wants 4mb alignment and it is highly unlikely for Linux to have such address after mmap syscall. So we do munmap once, then use the slow method to mmap once and munmap two times more because we don't want to store mmap useless regions https://github.com/microsoft/mimalloc/blob/master/src/os.c#L245. I am not sure this behavior is somehow optimal (at least with Linux we should not wait for 4mb alignment after mmap).

And is there any deallocated regions reuse? Because I tried such code below and saw a lot of mmap and munmap calls (for each construction and destruction many times).

#include <memory>
#include <thread>
#include <vector>

void Foo() {
    for (size_t i = 0; i < 10000; ++i) {
        std::unique_ptr<int[]> a(new int[1ull << 18]);
    }
}

int main() {
    std::vector<std::thread> thrs;
    for (size_t i = 0; i < 10; ++i) {
        thrs.emplace_back(Foo);
    }
    for (auto&& thr : thrs) {
        thr.join();
    }
    return 0;
}

With jemalloc and default allocator such code works almost immediately

time ./test                                                     
./test  0.02s user 0.00s system 205% cpu 0.011 total

With mimalloc it is extremely slow

time LD_PRELOAD=mimalloc/build/libmimalloc.so ./test
LD_PRELOAD=mimalloc/build/libmimalloc.so ./test  0.42s user 4.62s system 164% cpu 3.055 total

And such usage of the allocator is common, for example, in server applications, when you accept the query, process it and then again accept the query, memory reuse can help to avoid syscall penalty.

So the question is -- what are the best practices of using mimalloc? :)

Warning C4133

I had already spotted this in dev, but now it's in master:

os.c(96): warning C4133: 'function': incompatible types - from 'char [15]' to 'LPCWSTR'
os.c(113): warning C4133: 'function': incompatible types - from 'char [22]' to 'LPCWSTR'

A narrow c-string is consumed by a function that expects a wide-string (when compiled with _UNICODE).

To fix this warning, the most simple is to use the TEXT() macro:

hDll = LoadLibrary(TEXT("kernelbase.dll")); // 96

and

ok = LookupPrivilegeValue(NULL, TEXT("SeLockMemoryPrivilege"), &tp.Privileges[0].Luid); // 113

This will then also work correctly with _MBCS.

GetProcAddress (line 99) doesn't need this, as GetProcAddress consumes a narrow string (https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress), just for the purpose of consistency and added fun ;-) .

WebAssembly support?

Is WebAssembly a target mimalloc may eventually support?

WebAssembly doesn't have mmap(); only a single, linear memory segment that can grow. Allocating memory from the host must be done in a way similar to sbrk(2) as in the good old days.

Is it something mimalloc could do?

Azure Pipelines does not run any tests

AFAICT Azure Pipelines only checks that the library builds, but it does not run any tests.

Changes to the library should IMO be gated on all tests passing, and probably on checking that running the test suite of some multi-threaded allocation heavy program with a statically and dynamically linked mimalloc also passes.

Tests

After instinctively running make test, I discovered that the main cmake targets don't include tests.

The tests in ./test are rather ... sparse.

Seems like this might be an good thing to work on in the future. ;)

All default heaps walk

Is there any plans to have a way to walk all per thread default heaps? There seems that all stats and walk functions work either in the current thread heap or on a passed on heap and no way to access all heaps.

Support for destroying heap from different thread

While destroying a heap from a thread that is different from the heap's creator, the mi_segment_is_valid assertion under _mi_segment_page_free fails. What would be required to add support for destroying heaps from arbitrary threads?

C++ operator `new` on Windows

While #85 makes this somewhat moot for me right now, it appears that overriding operator new in C++ on Windows is incomplete in mimalloc.

There's an #if 0 around the code in alloc-override-win.c that appears to be necessary to patch the operators. It's not documented why that's there.

Further, not all modern overloads of new are patched as of C++17. For example, there does not appear to be any patching for the std::align_val_t overloads, despite the mimalloc documentation.

Setup Github Releases

When pulling in sources to build and package this library, the ideal case is for the library authors to use Github releases with periodic versioned releases. Currently we're pulling from Master and pinning to commits, but that is sub-optimal. It doesn't even need to be a 1.0 release, it could be some 0.1.0 pre-release.

Support a mi_stats_print interface similar to jemalloc

Passing a FILE* to mi_stats_print is very difficult from non console applications. je_malloc has a very simple interface to pass a function that receives a pointer to a buffer and that seems much better for all the cases were writing to a file is complicated. It could by default have a function that writes to stderr for simplicity.

feature request: support tool to instrument existing windows 10 .exe's to use mimalloc

For certain desireable .exe's we don't have the sources.
That means we can't use the windows import libraries to link mimalloc.lib into the target .exe on windows 10.

feature request:
https://github.com/stevemk14ebr/PolyHook
https://easyhook.github.io/
are tools that patch at runtime to hook up the existing dll calls within a running binary, but that's not something I would prefer to do.
In linux land we have patchelf/prelink which can change the search path and the dll's the linux binary is attempting to use AFTER BUILD TIME. It's an patch/instrumentation tool. I was looking for a similar tool for windows 10 binaries. Is there such a beast? If not could the Microsoft team develop/provide one? Thank you for listening.

Build fails with Error evaluating generator expression: $<TARGET_OBJECTS:mimalloc-obj>

It is not possible to build master (or the newest release 1.0.3) using cmake.

Full output:

elszben@elx78373ycn:~/Downloads/mimalloc-1.0.3$ cmake .
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- No build type selected, default to *** Release ***
-- Override standard malloc (MI_OVERRIDE=ON)
-- Output library name : mimalloc
-- Installation directory: lib/mimalloc-1.0
-- Configuring done
CMake Error:
Error evaluating generator expression:

$<TARGET_OBJECTS:mimalloc-obj>

The evaluation of the TARGET_OBJECTS generator expression is only suitable
for consumption by CMake. It is not suitable for writing out elsewhere.

-- Generating done
-- Build files have been written to: /home/elszben/Downloads/mimalloc-1.0.3

Any idea how to fix it?

Thanks in advance.

Fails to compile on ARM Linux

On Arch Linux, ARM 32 bit:

 $ cmake ../../
-- The C compiler identification is GNU 8.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- No build type selected, default to *** Release ***
-- Override standard malloc (OVERRIDE=ON)
-- Output library name   : mimalloc
-- Installation directory: lib/mimalloc-1.0
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jrh/Projects/mimalloc/out/release
$ make
Scanning dependencies of target mimalloc-static
[  4%] Building C object CMakeFiles/mimalloc-static.dir/src/stats.c.o
In file included from /home/jrh/Projects/mimalloc/src/stats.c:8:
/home/jrh/Projects/mimalloc/include/mimalloc-internal.h: In function ‘_mi_thread_id’:
/home/jrh/Projects/mimalloc/include/mimalloc-internal.h:312:22: error: ‘_mi_backing_heap’ undeclared (first use in this function); did you mean ‘_mi_bin_size’?
   return (uintptr_t)&_mi_backing_heap;
                      ^~~~~~~~~~~~~~~~
                      _mi_bin_size
/home/jrh/Projects/mimalloc/include/mimalloc-internal.h:312:22: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [CMakeFiles/mimalloc-static.dir/build.make:63: CMakeFiles/mimalloc-static.dir/src/stats.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:73: CMakeFiles/mimalloc-static.dir/all] Error 2
make: *** [Makefile:130: all] Error 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.