Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ option(MESHLIB_BUILD_EXTRA_IO_FORMATS "Build extra IO format support library" ON
option(MESHLIB_BUILD_MCP "Enable MCP server" ON)
option(MESHLIB_BUILD_GENERATED_C_BINDINGS "Build C bindings (assuming they are already generated)" OFF)

option(MESHLIB_USE_MIMALLOC "Use the mimalloc allocator for executables" ON)

option(MESHLIB_BUILD_MRCUDA "Build MRCuda library" ON)
option(MESHLIB_EXPERIMENTAL_HIP "(experimental) Use HIP toolkit for MRCuda library" OFF)
IF(MR_EMSCRIPTEN OR APPLE)
Expand Down
5 changes: 0 additions & 5 deletions cmake/Modules/ConfigureEmscripten.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS
"-s STACK_SIZE=1048576" # required for GDCM
)

option(MR_EMSCRIPTEN_MIMALLOC "Use mimalloc allocator (-s MALLOC=mimalloc) for Emscripten builds" ON)
IF(MR_EMSCRIPTEN_MIMALLOC)
set(MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS "${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} -s MALLOC=mimalloc")
ENDIF()

IF(MR_EMSCRIPTEN_SINGLETHREAD)
set(MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS "${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} -s ENVIRONMENT=web,node")
ELSE()
Expand Down
55 changes: 55 additions & 0 deletions cmake/Modules/Mimalloc.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# mr_enable_mimalloc(<target>) - enable the mimalloc allocator for an EXE target.
# EXE-only; call unconditionally (it branches internally). Gated by MESHLIB_USE_MIMALLOC.
# Verified by MRMesh.MimallocRedirectActive, which keys off the MR_MIMALLOC_ENABLED
# define this sets. MSBuild twin: MimallocRedirect.props.
function(mr_enable_mimalloc target)
if(NOT MESHLIB_USE_MIMALLOC)
return()
endif()

if(EMSCRIPTEN OR MR_EMSCRIPTEN)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep EMSCRIPTEN only.

target_link_options(${target} PRIVATE "-sMALLOC=mimalloc")
return()
endif()

if(WIN32)
# mimalloc.dll must be the FIRST PE import (prepend) so its redirect beats
# ucrtbase; /INCLUDE:mi_version forces the import.
find_package(mimalloc CONFIG REQUIRED)
get_target_property(_existing_libs ${target} LINK_LIBRARIES)
if(_existing_libs)
set_property(TARGET ${target} PROPERTY LINK_LIBRARIES mimalloc ${_existing_libs})
else()
set_property(TARGET ${target} PROPERTY LINK_LIBRARIES mimalloc)
endif()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't both branches functionally equivalent?

target_link_options(${target} PRIVATE "/INCLUDE:mi_version")
elseif(APPLE)
# macOS: static-link mimalloc via -force_load. brew's DYNAMIC mimalloc 3.x aborts
# at launch on macOS <= 13: an allocation during libSystem init hits mi_thread_init,
# which touches a thread-local before dyld can bootstrap it -> dyld abort. Static-
# linking ties mimalloc's init to this exe's own initializers (after libSystem),
# avoiding that early path.
find_package(mimalloc CONFIG QUIET)
if(TARGET mimalloc-static)
target_link_options(${target} PRIVATE "LINKER:-force_load,$<TARGET_FILE:mimalloc-static>")
else()
set(_mi_saved_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_library(MR_MIMALLOC_STATIC NAMES mimalloc REQUIRED)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mi_saved_suffixes})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the previous value be restored automatically since CMake functions create their own variable scopes?

target_link_options(${target} PRIVATE "LINKER:-force_load,${MR_MIMALLOC_STATIC}")
endif()
else()
# Linux: dynamic link engages the override (ubuntu24 via CONFIG; ubuntu22's
# libmimalloc-dev ships no config, so fall back to find_library).
find_package(mimalloc CONFIG QUIET)
if(TARGET mimalloc)
target_link_libraries(${target} PRIVATE mimalloc)
else()
find_library(MR_MIMALLOC_LIBRARY NAMES mimalloc REQUIRED)
target_link_libraries(${target} PRIVATE ${MR_MIMALLOC_LIBRARY})
endif()
endif()

target_compile_definitions(${target} PRIVATE MR_MIMALLOC_ENABLED)
endfunction()
28 changes: 0 additions & 28 deletions cmake/Modules/MimallocRedirect.cmake

This file was deleted.

1 change: 1 addition & 0 deletions requirements/macos.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libharu
libpng
libtiff
libzip
mimalloc
ninja
nlohmann-json
opencascade
Expand Down
1 change: 1 addition & 0 deletions requirements/ubuntu.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libgtk-3-dev
libhidapi-dev
libhpdf-dev
libjsoncpp-dev
libmimalloc-dev
libocct-data-exchange-dev
libpython3-dev
libspdlog-dev
Expand Down
1 change: 1 addition & 0 deletions requirements/vcpkg-linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ libe57format
libharu
libjpeg-turbo
libzip
mimalloc[override]
opencascade-minimal
openctm
openvdb
Expand Down
4 changes: 2 additions & 2 deletions scripts/build_source.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ if [ "${MR_EMSCRIPTEN}" == "ON" ]; then
EMSCRIPTEN_ROOT="${EMSDK}/upstream/emscripten"

[[ ${MR_EMSCRIPTEN_SIMD:=} ]] || export MR_EMSCRIPTEN_SIMD=1
[[ ${MR_EMSCRIPTEN_MIMALLOC:=} ]] || export MR_EMSCRIPTEN_MIMALLOC=1
[[ ${MESHLIB_USE_MIMALLOC:=} ]] || export MESHLIB_USE_MIMALLOC=1
MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} \
-D CMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake \
-D CMAKE_FIND_ROOT_PATH=${PWD} \
-D MR_EMSCRIPTEN=1 \
-D MR_EMSCRIPTEN_SINGLETHREAD=${MR_EMSCRIPTEN_SINGLETHREAD} \
-D MR_EMSCRIPTEN_WASM64=${MR_EMSCRIPTEN_WASM64} \
-D MR_EMSCRIPTEN_SIMD=${MR_EMSCRIPTEN_SIMD} \
-D MR_EMSCRIPTEN_MIMALLOC=${MR_EMSCRIPTEN_MIMALLOC} \
-D MESHLIB_USE_MIMALLOC=${MESHLIB_USE_MIMALLOC} \
"
fi

Expand Down
6 changes: 2 additions & 4 deletions source/MRTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ ELSE()
)
ENDIF()

if(WIN32)
include(MimallocRedirect)
mr_win32_enable_mimalloc_redirect(${PROJECT_NAME})
endif()
include(Mimalloc)
mr_enable_mimalloc(${PROJECT_NAME})

add_test(
NAME ${PROJECT_NAME}
Expand Down
41 changes: 30 additions & 11 deletions source/MRTest/MRMimallocRedirectTests.cpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
#include <gtest/gtest.h>

#ifdef _WIN32
#if defined( MR_MIMALLOC_ENABLED )

#include <MRMesh/MRMesh.h>
#include <MRMesh/MRCube.h>

#include <cstdlib>
#include <string_view>

// mimalloc.h declares these as `bool`. Matching the actual return type is
// critical: MSVC returns `bool` in the low byte of EAX, so reading the call
// site as `int` picks up garbage in the upper 24 bits (e.g. -980287487).
// Local Debug happened to zero those bits; Release CI didn't.
extern "C" bool mi_is_redirected();
// Declared here to avoid <mimalloc.h>. Return type MUST be bool, not int: MSVC
// returns bool in AL, so an int read picks up garbage in the upper bits (broke Release CI).
extern "C" bool mi_is_in_heap_region( const void* p );
#ifdef _WIN32
extern "C" bool mi_is_redirected();
#endif

namespace MR
{

// Verifies mimalloc's transparent CRT-allocator redirect engaged for this EXE.
// Wired in MeshLib/source/MimallocRedirect.props (MSBuild) and
// MeshLib/cmake/Modules/MimallocRedirect.cmake (CMake). Skipped when
// MIMALLOC_DISABLE_REDIRECT=1 (mimalloc's own runtime kill-switch).
// Confirms mimalloc services real allocations, not just that the lib is linked.
// Wired by Mimalloc.cmake (CMake) / MimallocRedirect.props (MSBuild) via the
// MR_MIMALLOC_ENABLED define. Each allocation below must land in a mimalloc region:
// C malloc, C++ operator new, and an allocation made INSIDE MRMesh (cross-library -
// the real goal, what the process-wide override must reach, esp. macOS two-level ns).
// On Windows also asserts the redirect engaged; MIMALLOC_DISABLE_REDIRECT=1 skips that.
TEST( MRMesh, MimallocRedirectActive )
{
#ifdef _WIN32
if ( const char* disable = std::getenv( "MIMALLOC_DISABLE_REDIRECT" );
disable && std::string_view( disable ) == "1" )
{
GTEST_SKIP() << "MIMALLOC_DISABLE_REDIRECT=1; redirect intentionally disabled.";
}

EXPECT_TRUE( mi_is_redirected() );
#endif

// C malloc, in this executable
void* p = std::malloc( 64 );
ASSERT_NE( p, nullptr );
EXPECT_TRUE( mi_is_in_heap_region( p ) );
std::free( p );

// C++ operator new, in this executable
int* q = new int[16];
EXPECT_TRUE( mi_is_in_heap_region( q ) );
delete[] q;

// cross-library: makeCube() fills points via operator new compiled into MRMesh,
// so its buffer must be a mimalloc region too (proves the override reaches the libs)
const Mesh cube = makeCube();
ASSERT_NE( cube.points.data(), nullptr );
EXPECT_TRUE( mi_is_in_heap_region( cube.points.data() ) );
}

} // namespace MR

#endif // _WIN32
#endif // MR_MIMALLOC_ENABLED
6 changes: 2 additions & 4 deletions source/MRViewerApp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ ELSE()
)
ENDIF()

if(WIN32)
include(MimallocRedirect)
mr_win32_enable_mimalloc_redirect(${PROJECT_NAME})
endif()
include(Mimalloc)
mr_enable_mimalloc(${PROJECT_NAME})

install(
TARGETS ${PROJECT_NAME}
Expand Down
11 changes: 9 additions & 2 deletions source/MimallocRedirect.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
against either vcpkg snapshot without per-runner edits.

Verified by MRTest's MRMesh.MimallocRedirectActive unit test (gated by
MIMALLOC_DISABLE_REDIRECT env var). See also MimallocRedirect.cmake for the
CMake equivalent.
MIMALLOC_DISABLE_REDIRECT env var). See also Mimalloc.cmake (mr_enable_mimalloc).
-->
<PropertyGroup>
<MimallocDebugLib Condition="Exists('$(VcpkgCurrentInstalledDir)\debug\lib\mimalloc-debug.dll.lib')">mimalloc-debug.dll.lib</MimallocDebugLib>
Expand All @@ -32,12 +31,20 @@
<MimallocReleaseLib Condition="'$(MimallocReleaseLib)'=='' AND Exists('$(VcpkgCurrentInstalledDir)\lib\mimalloc.lib')">mimalloc.lib</MimallocReleaseLib>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<!-- Marks this EXE mimalloc-linked so the engagement test compiles (cf. Mimalloc.cmake). -->
<PreprocessorDefinitions>MR_MIMALLOC_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies>$(MimallocDebugLib);%(AdditionalDependencies)</AdditionalDependencies>
<ForceSymbolReferences>mi_version;%(ForceSymbolReferences)</ForceSymbolReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<!-- Marks this EXE mimalloc-linked so the engagement test compiles (cf. Mimalloc.cmake). -->
<PreprocessorDefinitions>MR_MIMALLOC_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies>$(MimallocReleaseLib);%(AdditionalDependencies)</AdditionalDependencies>
<ForceSymbolReferences>mi_version;%(ForceSymbolReferences)</ForceSymbolReferences>
Expand Down
Loading