mimalloc icon indicating copy to clipboard operation
mimalloc copied to clipboard

Clarification of MI_OVERRIDE and mimalloc-new-delete.h

Open thehans opened this issue 3 years ago • 5 comments

Does MI_OVERRIDE=ON cmake option imply that new/delete are overridden in addition to the C malloc/etc interface?

If I compile with MI_OVERRIDE on Linux, I get "multiple definition" linker errors when I try to #include <mimalloc-new-delete.h>

Here are the messages from gcc-10:

/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `operator new(unsigned long, std::nothrow_t const&)':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc-override.c:190: multiple definition of `operator new(unsigned long, std::nothrow_t const&)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:31: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `operator new[](unsigned long, std::nothrow_t const&)':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc-override.c:191: multiple definition of `operator new[](unsigned long, std::nothrow_t const&)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:32: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_free':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:481: multiple definition of `operator delete[](void*)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:26: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_free':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:481: multiple definition of `operator delete(void*)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:25: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_free_size':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:576: multiple definition of `operator delete[](void*, unsigned long)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:36: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_free_size':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:576: multiple definition of `operator delete(void*, unsigned long)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:35: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_new':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:857: multiple definition of `operator new[](unsigned long)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:29: first defined here
/usr/bin/ld: submodules/mimalloc/libmimalloc-debug.a(alloc.c.o): in function `mi_new':
/home/hans/openscad_malloc/submodules/mimalloc/src/alloc.c:857: multiple definition of `operator new(unsigned long)'; CMakeFiles/OpenSCAD.dir/src/openscad.cc.o:/home/hans/openscad_malloc/submodules/mimalloc/include/mimalloc-new-delete.h:28: first defined here

(I got even stranger messages if I tried to build with clang-14, where it claimed they were redefined in boost/system/detail/std_interoperability.hpp which I assume was some kind of compiler bug since I saw no such definitions in that file)

Is the answer specific to target OS? (i.e. MI_OVERRIDE implies new/delete override, everywhere EXCEPT on Windows?) I'm wondering about this since MI_OVERRIDE defaults ON, and I see this preprocessor conditional in the tests: https://github.com/microsoft/mimalloc/blob/38a03229c89afbf2722e48c63da696bf11589dee/test/main-override.cpp#L17-L19

thehans avatar Jan 24 '22 00:01 thehans

Thanks @thehans for all your feedback!

I can see it is a bit confusing and will try to improve the documentation; However, it is partly confusing because there is inherent complexity in many ways to override malloc: use #defines (mimalloc-override.h), use static linking override (mimalloc-override.o), use dynamic override (e.g. LD_PRELOAD), use from C or C++, and the various ways the different OS's and linkers work. It would be great if there is was one standard way to do things but there just isn't and the right way depends the application.

Having said that:

  • MI_OVERRIDE=ON defines all the standard malloc api entry points, and also the C++ new/delete entry points. The only reason to disable this is either that you never use it (as the application calls the mi_ api's specifically), or when you linker would complain about multiple definitions (as on Windows without weak symbols).

  • On Unix systems, you can thus use static linking override, or LD_PRELOAD, such that all allocation goes via mimalloc.

  • However, if you compile mimalloc with MI_OVERRIDE=ON, and also include mimalloc-new-delete.h in your application you will now define the overrides for C++ new/delete twice (once in libmimalloc and once in your application) so that it is where your errors come from.

  • But on Windows, each DLL has its own namespace, and when overriding it uses dynamic patching which can only redirect basic malloc/free calls. That means that each C++ (application) module uses its own new/delete which eventually will call the (redirected) malloc/free. This turns out to be not as efficient as it can be and it is better to then overload the C++ new/delete to directly use the mimalloc ones where possible -- hence, on Windows with dynamic overriding, you should include mimalloc-new-delete.h if you can.

  • Of course, if Boost for example also overrides C++ new/delete then that is another conflict. Not sure if that is what you saw with clang-14.

  • And on macOS ...

As you can see, a lot of this is beyond the scope of mimalloc but more about linking C/C++ on various platforms. I will try if I can make this more clear with a nice diagram or something. The current goal is at least to make everything work out-of-the-box on the various platforms without needing special make flags etc.

daanx avatar Jan 24 '22 21:01 daanx

https://github.com/microsoft/mimalloc/issues/559#issuecomment-1916408486

yhyu13 avatar Jan 30 '24 09:01 yhyu13

* However, if you compile mimalloc with `MI_OVERRIDE=ON`, and also include `mimalloc-new-delete.h` in your application you will now define the overrides for C++ `new`/`delete` twice (once in `libmimalloc` and once in your application) so that it is where your errors come from.

* But on Windows, each DLL has its own namespace, and when overriding it uses dynamic patching which can only redirect basic `malloc`/`free` calls. That means that each C++ (application) module uses its own `new`/`delete` which eventually will call the (redirected) `malloc`/`free`. This turns out to be not as efficient as it can be and it is better to then overload the C++ `new`/`delete` to directly use the `mimalloc` ones where possible -- hence, on Windows with dynamic overriding, you should include `mimalloc-new-delete.h` if you can.

Hi @daanx, I realize this ticket is quite old (should it be closed?), however I'd like to ask a bit more about this. I'm using mimalloc 1.7.2 on Windows (yes, I realize it is quite old, but I'm not sure of whether that would have any impact on this particular issue).

  1. I've built mimalloc with MI_OVERRIDE=ON, and included mimalloc-new-delete.h, yet I don't see these double-definition errors. Am I doing something wrong?
  2. About the DLL namespaces: does this mean I should include mimalloc-new-delete.h on a single .cpp on each DLL as well as my main executable?
  3. Finally, is there any way to verify that new/delete have been overriden? I'm running a debug build with both MIMALLOC_VERBOSE and MIMALLOC_SHOW_STATS set, but I don't see anything that clearly indicates the situation for new/delete. If for example I use new to allocate a large buffer, I see the heap usage numbers increase in the verbose output even if I don't include mimalloc-new-delete.h.

Thanks.

martingalvan-volue avatar Mar 05 '25 15:03 martingalvan-volue

Hi

  1. I think you will not get link errors if you link with the dynamic C runtime so it is ok to not see link errors.
  2. Well, you can just use mimalloc-redirect.dll (with. mimalloc.dll) and everything will work. It is just more efficient to link directly to the mimalloc definitions if possible -- for example, by including mimalloc-new-delete.h in a single cpp file since you only need one definition in each namespace. So, yes, for best efficiency, you'd like a to include those new-delete definiitons (once) in each name space (= DLL).
  3. With MIMALLOC_VERBOSE=1 you'd like to see at the start that mimalloc is redirected. This will also redirect all new/delete calls (regardless if you include mimalloc-new-delete.h -- that is just for increased efficiency).

Hope this helps!

daanx avatar Mar 06 '25 00:03 daanx

Hi @daanx, thanks for the response. Should this issue be closed?

martingalvan-volue avatar Mar 06 '25 07:03 martingalvan-volue