From dcf4952fef7c218ac9ab92f6512be1c16f776bdf Mon Sep 17 00:00:00 2001 From: BAder82t <41265463+BAder82t@users.noreply.github.com> Date: Thu, 18 Jun 2026 21:19:56 +0100 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20close=20issue=20#249=20=E2=80=94=20E?= =?UTF-8?q?mscripten=20OOM=20in=20CKKS=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #1179 added ClearBootstrapPrecom and ClearSchemeSwitchPrecom helpers but did not wire them into ReleaseAllContexts. This change completes that work, then re-enables the EVAL_FAST_ROTATION and CONTEXT_WITH_SERTYPE tests that PR #985 had to guard under !defined(__EMSCRIPTEN__) for the original std::bad_alloc failure. Library changes (no hot-path touched): - ReleaseAllContexts now walks every live context and calls ClearAllCKKSCaches before clearing the static maps. - Adds malloc_zone_pressure_relief(Apple) and _heapmin(MSVC) to AllocTrim. - Add ClearAllCKKSCaches convenience on CryptoContextImpl. - ClearBootstrapPrecom and ClearSchemeSwitchPrecom bodies made noexcept-safe so the walk does not throw on contexts that have not Enable(FHE)'d or Enable(SCHEMESWITCH)'d. Test changes: - Mid-test ClearAllCKKSCaches between JSON and BINARY rounds in UnitTestCKKSrnsSerialize. - UTCKKSCacheClear extends its WITH_TCM skip to __EMSCRIPTEN__ (the heap-probe helper has no emmalloc backend). - Remove the !defined(__EMSCRIPTEN__) guards around the affected rows. CI: - Add .github/workflows/emscripten.yml: builds with emsdk 3.1.59 and runs pke_tests/core_tests under node in two slices. Validated on macOS + WASM: - Native: pke_tests 1892/1892 pass, core_tests 158/158 pass. - WASM: CKKS slice 939/941 pass (2 heap-probe tests skipped), BFV+BGV+binfhe slice 324/324 pass, peak 1.6 GB. Closes #249. --- .github/workflows/emscripten.yml | 61 +++++++++++++++++++ src/core/lib/utils/memory.cpp | 23 ++++++- src/pke/include/cryptocontext.h | 16 +++-- src/pke/include/cryptocontextfactory.h | 4 ++ src/pke/include/schemebase/base-scheme.h | 8 +-- .../utckksrns/UnitTestCKKSCacheClear.cpp | 9 ++- .../unittest/utckksrns/UnitTestCKKSrns.cpp | 2 - .../utckksrns/UnitTestCKKSrnsSerialize.cpp | 3 +- 8 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/emscripten.yml diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml new file mode 100644 index 000000000..23f8a3629 --- /dev/null +++ b/.github/workflows/emscripten.yml @@ -0,0 +1,61 @@ +name: Emscripten + +# Builds with emcc and runs the WASM unit tests under node. Guards against +# re-introducing the cache-leak class that issue #249 tracked. + +on: + pull_request: + branches: [dev] + push: + branches: [dev, main] + workflow_dispatch: + +jobs: + build-and-test: + runs-on: ubuntu-22.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: { node-version: '20' } + + - name: Cache emsdk + id: cache-emsdk + uses: actions/cache@v4 + with: + path: ~/emsdk + key: emsdk-3.1.59 + + - name: Install emsdk + if: steps.cache-emsdk.outputs.cache-hit != 'true' + run: | + git clone --depth 1 --branch 3.1.59 https://github.com/emscripten-core/emsdk.git ~/emsdk + ~/emsdk/emsdk install 3.1.59 + ~/emsdk/emsdk activate 3.1.59 + + - name: Configure + run: | + source ~/emsdk/emsdk_env.sh + emcmake cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release -DBUILD_UNITTESTS=ON \ + -DBUILD_BENCHMARKS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_EXTRAS=OFF \ + -DBUILD_STATIC=ON -DBUILD_SHARED=OFF -DWITH_OPENMP=OFF -DNATIVE_SIZE=64 \ + -DCMAKE_EXE_LINKER_FLAGS="-s INITIAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4294967296 -s WASM=1 -s NODERAWFS=1 -s STACK_SIZE=5MB -s ASSERTIONS=0" + + - name: Build + run: | + source ~/emsdk/emsdk_env.sh + cmake --build build --target core_tests pke_tests -j2 + + - name: core_tests + working-directory: build/unittest + run: node --max-old-space-size=8192 ./core_tests.js --gtest_color=no + + # Split pke_tests across two processes — each stays well below the 4 GB WASM cap. + - name: pke_tests (CKKS + general) + working-directory: build/unittest + run: node --max-old-space-size=8192 ./pke_tests.js --gtest_filter='UTCKKSRNS*:UTCKKSCacheClear*:UTGENERAL*' --gtest_color=no + + - name: pke_tests (BFV + BGV + binfhe) + working-directory: build/unittest + run: node --max-old-space-size=8192 ./pke_tests.js --gtest_filter='UTBFV*:UTBGV*:UTBINFHE*' --gtest_color=no diff --git a/src/core/lib/utils/memory.cpp b/src/core/lib/utils/memory.cpp index 9d4c0f549..857c05d67 100644 --- a/src/core/lib/utils/memory.cpp +++ b/src/core/lib/utils/memory.cpp @@ -30,7 +30,12 @@ //================================================================================== #include "utils/memory.h" -#if defined(__GLIBC__) +#if defined(__APPLE__) + #include + #include +#elif defined(__GLIBC__) + #include +#elif defined(_MSC_VER) #include #endif @@ -67,9 +72,23 @@ namespace { // Not async-signal-safe and acquires allocator locks; call only from normal execution context, // never from a signal handler. Returns true if a trim was attempted, false on unsupported builds. bool AllocTrim() { -#if defined(__GLIBC__) +#if defined(__APPLE__) + vm_address_t* zones = nullptr; + unsigned count = 0; + if (malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count) == KERN_SUCCESS && zones) { + for (unsigned i = 0; i < count; ++i) { + auto* z = reinterpret_cast(zones[i]); + if (z) + malloc_zone_pressure_relief(z, 0); + } + } + return true; +#elif defined(__GLIBC__) malloc_trim(0); return true; +#elif defined(_MSC_VER) + _heapmin(); + return true; #else return false; #endif diff --git a/src/pke/include/cryptocontext.h b/src/pke/include/cryptocontext.h index 1831189a1..2cecc2ff1 100644 --- a/src/pke/include/cryptocontext.h +++ b/src/pke/include/cryptocontext.h @@ -626,16 +626,24 @@ class CryptoContextImpl : public Serializable { * @brief Clear CKKS bootstrap precomputations cached by this context's scheme. */ void ClearBootstrapPrecom() noexcept { - VerifyCKKSScheme(__func__); - m_scheme->ClearBootstrapPrecom(); + if (m_scheme) + m_scheme->ClearBootstrapPrecom(); } /** * @brief Clear CKKS/FHEW scheme-switch precomputations cached by this context's scheme. */ void ClearSchemeSwitchPrecom() noexcept { - VerifyCKKSScheme(__func__); - m_scheme->ClearSchemeSwitchPrecom(); + if (m_scheme) + m_scheme->ClearSchemeSwitchPrecom(); + } + + /** + * @brief Release all scheme-level caches (bootstrap + scheme-switch). + */ + void ClearAllCKKSCaches() noexcept { + ClearBootstrapPrecom(); + ClearSchemeSwitchPrecom(); } /** diff --git a/src/pke/include/cryptocontextfactory.h b/src/pke/include/cryptocontextfactory.h index e185006f6..e38cd2bd9 100644 --- a/src/pke/include/cryptocontextfactory.h +++ b/src/pke/include/cryptocontextfactory.h @@ -64,6 +64,10 @@ class CryptoContextFactory { public: static void ReleaseAllContexts() { + for (auto& cc : AllContexts) { + if (cc) + cc->ClearAllCKKSCaches(); + } if (!AllContexts.empty()) AllContexts[0]->ClearStaticMapsAndVectors(); AllContexts.clear(); diff --git a/src/pke/include/schemebase/base-scheme.h b/src/pke/include/schemebase/base-scheme.h index 528ac515b..9493ab997 100644 --- a/src/pke/include/schemebase/base-scheme.h +++ b/src/pke/include/schemebase/base-scheme.h @@ -1147,13 +1147,13 @@ class SchemeBase { } void ClearBootstrapPrecom() noexcept { - VerifyFHEEnabled(__func__); - m_FHE->ClearBootstrapPrecom(); + if (m_FHE) + m_FHE->ClearBootstrapPrecom(); } void ClearSchemeSwitchPrecom() noexcept { - VerifySchemeSwitchEnabled(__func__); - m_SchemeSwitch->ClearSchemeSwitchPrecom(); + if (m_SchemeSwitch) + m_SchemeSwitch->ClearSchemeSwitchPrecom(); } std::shared_ptr>> EvalBootstrapKeyGen(const PrivateKey privateKey, diff --git a/src/pke/unittest/utckksrns/UnitTestCKKSCacheClear.cpp b/src/pke/unittest/utckksrns/UnitTestCKKSCacheClear.cpp index bed0809d3..a5774e500 100644 --- a/src/pke/unittest/utckksrns/UnitTestCKKSCacheClear.cpp +++ b/src/pke/unittest/utckksrns/UnitTestCKKSCacheClear.cpp @@ -30,14 +30,13 @@ //================================================================================== #include "binfhecontext.h" +#include "gtest/gtest.h" #include "openfhe.h" #include "scheme/ckksrns/ckksrns-fhe.h" #include "scheme/ckksrns/ckksrns-schemeswitching.h" #include "scheme/scheme-swch-params.h" #include "utils/memory.h" -#include "gtest/gtest.h" - #include #if defined(__GLIBC__) @@ -116,9 +115,13 @@ CryptoContext MakeSchemeSwitchCC() { class UTCKKSCacheClear : public ::testing::Test { protected: -#if defined(WITH_TCM) +#if defined(WITH_TCM) || defined(__EMSCRIPTEN__) void SetUp() override { +#if defined(WITH_TCM) GTEST_SKIP() << "Heap usage checks are not stable with tcmalloc enabled"; +#else + GTEST_SKIP() << "Heap probe unavailable under Emscripten"; +#endif } #endif diff --git a/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp b/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp index 3e99c8d26..f04583dcd 100644 --- a/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp +++ b/src/pke/unittest/utckksrns/UnitTestCKKSrns.cpp @@ -398,7 +398,6 @@ static std::vector testCases = { { EVAL_FAST_ROTATION, "39", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FLEXIBLEAUTOEXT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, BATCH}, { EVAL_FAST_ROTATION, "40", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, HYBRID, FLEXIBLEAUTOEXT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, BATCH}, #endif -#if !defined(__EMSCRIPTEN__) { EVAL_FAST_ROTATION, "41", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, RING_DIM_HALF}, { EVAL_FAST_ROTATION, "42", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FIXEDAUTO, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, RING_DIM_HALF}, { EVAL_FAST_ROTATION, "43", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, HYBRID, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, RING_DIM_HALF}, @@ -527,7 +526,6 @@ static std::vector testCases = { // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode { SMALL_SCALING_MOD_SIZE, "01", {CKKSRNS_SCHEME, 32768, 19, 22, DFLT, DFLT, DFLT, DFLT, 23, DFLT, DFLT, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, { SMALL_SCALING_MOD_SIZE, "02", {CKKSRNS_SCHEME, 32768, 16, 50, DFLT, DFLT, DFLT, DFLT, 50, HEStd_NotSet, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, -#endif // ========================================== // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode, MultipartyMode, decryptionNoiseMode, ExecutionMode, NoiseEstimate, RegisterWordSize, compositeDegree, CKKSDataType { EVALCOMPLEX, "01", {CKKSRNS_SCHEME, RING_DIM, 7, DFLT, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, COMPLEX}, }, diff --git a/src/pke/unittest/utckksrns/UnitTestCKKSrnsSerialize.cpp b/src/pke/unittest/utckksrns/UnitTestCKKSrnsSerialize.cpp index ad91d6043..bbbf1b8d4 100644 --- a/src/pke/unittest/utckksrns/UnitTestCKKSrnsSerialize.cpp +++ b/src/pke/unittest/utckksrns/UnitTestCKKSrnsSerialize.cpp @@ -119,7 +119,6 @@ constexpr uint32_t BATCH = 16; // clang-format off static std::vector testCases = { // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode -#if !defined(__EMSCRIPTEN__) { CONTEXT_WITH_SERTYPE, "01", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, { CONTEXT_WITH_SERTYPE, "02", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FIXEDAUTO, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, { CONTEXT_WITH_SERTYPE, "03", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, HYBRID, FIXEDMANUAL, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, @@ -129,7 +128,6 @@ static std::vector testCases = { { CONTEXT_WITH_SERTYPE, "06", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, HYBRID, FLEXIBLEAUTO, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, { CONTEXT_WITH_SERTYPE, "07", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, BV, FLEXIBLEAUTOEXT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, { CONTEXT_WITH_SERTYPE, "08", {CKKSRNS_SCHEME, RING_DIM, MULT_DEPTH, SMODSIZE, DSIZE, BATCH, DFLT, DFLT, DFLT, HEStd_NotSet, HYBRID, FLEXIBLEAUTOEXT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT, DFLT}, }, -#endif #endif // ========================================== // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode @@ -189,6 +187,7 @@ class UTCKKSRNS_SER : public ::testing::TestWithParam { CryptoContext cc(UnitTestGenerateContext(testData.params)); UnitTestContextWithSertype(cc, SerType::JSON, "json"); + cc->ClearAllCKKSCaches(); UnitTestContextWithSertype(cc, SerType::BINARY, "binary"); } From 92b3837b80fd6eaad1c4a91746d5b4f3fba2770c Mon Sep 17 00:00:00 2001 From: Carlo Pascoe Date: Wed, 8 Jul 2026 16:03:31 -0400 Subject: [PATCH 2/2] add WITH_MALLOC_TUNING CMake option (default ON) Make the process-wide allocator tuning introduced in #1213 opt-out via a new build flag, since it changes malloc policy for the whole host application (freed memory is retained in-process, raising resident set size for workloads with large transient peaks). - Gate the glibc mallopt(M_MMAP_MAX=0, M_TRIM_THRESHOLD=-1) startup tuning behind WITH_MALLOC_TUNING and propagate the flag through config_core.h; build with -DWITH_MALLOC_TUNING=OFF to disable. - Document per-platform behavior in explicit branches: macOS libmalloc already retains freed memory (madvise-reusable pages plus the large cache), and the Windows process heap exposes no retention knob. - Rewrite the __APPLE__ branch of AllocTrim() to use the supported malloc_zone_pressure_relief(NULL, 0) all-zones call instead of the malloc_get_all_zones()/mach introspection API, dropping . - Docs: add the new flag and the previously undocumented options (GIT_SUBMOD_AUTO, WITH_COVTEST, WITH_NOISE_DEBUG, WITH_REDUCED_NOISE, USE_MACPORTS) to the CMake flags table, and fix stale defaults (WITH_BE2 is OFF; the default math backend is 4, not 2). --- CMakeLists.txt | 2 + configure/config_core.in | 1 + .../intro/installation/cmake_in_openfhe.rst | 44 +++++++++------- src/core/lib/utils/memory.cpp | 51 +++++++++++-------- 4 files changed, 58 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a3d5e4d3..5613a13de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ option(WITH_BE2 "Include MATHBACKEND 2 in build by setting WITH_BE2 to ON" option(WITH_BE4 "Include MATHBACKEND 4 in build by setting WITH_BE4 to ON" OFF ) option(WITH_NTL "Include MATHBACKEND 6 and NTL in build by setting WITH_NTL to ON" OFF ) option(WITH_TCM "Activate tcmalloc by setting WITH_TCM to ON" OFF ) +option(WITH_MALLOC_TUNING "Tune glibc malloc to retain freed memory for reuse" ON ) option(WITH_NATIVEOPT "Use machine-specific optimizations" OFF ) option(WITH_COVTEST "Turn on to enable coverage testing (can be used with g++ only)" OFF ) option(WITH_NOISE_DEBUG "Use only when running lattice estimator; not for production" OFF ) @@ -115,6 +116,7 @@ message(STATUS "WITH_BE2: ${WITH_BE2}") message(STATUS "WITH_BE4: ${WITH_BE4}") message(STATUS "WITH_NTL: ${WITH_NTL}") message(STATUS "WITH_TCM: ${WITH_TCM}") +message(STATUS "WITH_MALLOC_TUNING: ${WITH_MALLOC_TUNING}") message(STATUS "WITH_OPENMP: ${WITH_OPENMP}") message(STATUS "NATIVE_SIZE: ${NATIVE_SIZE}") message(STATUS "CKKS_M_FACTOR: ${CKKS_M_FACTOR}") diff --git a/configure/config_core.in b/configure/config_core.in index 21121c0e4..cd1ee8e17 100644 --- a/configure/config_core.in +++ b/configure/config_core.in @@ -10,6 +10,7 @@ #cmakedefine WITH_REDUCED_NOISE #cmakedefine WITH_NTL #cmakedefine WITH_TCM +#cmakedefine WITH_MALLOC_TUNING #cmakedefine WITH_OPENMP #cmakedefine WITH_NATIVEOPT diff --git a/docs/sphinx_rsts/intro/installation/cmake_in_openfhe.rst b/docs/sphinx_rsts/intro/installation/cmake_in_openfhe.rst index f85ffd687..6b03b4a75 100644 --- a/docs/sphinx_rsts/intro/installation/cmake_in_openfhe.rst +++ b/docs/sphinx_rsts/intro/installation/cmake_in_openfhe.rst @@ -81,28 +81,34 @@ Each of the options is enabled by saying ``-DOPTION_NAME=ON`` and is disabled by The table below shows the current list of options, definition for the option, and a default value. - ================== ===================================================================================================================================================================== ========== - OPTION_NAME Description Default - ================== ===================================================================================================================================================================== ========== - BUILD_UNITTESTS Set to ON to build unit tests for the library ON - BUILD_EXAMPLES Set to ON to build examples for the library ON - BUILD_BENCHMARKS Set to ON to build benchmarks for the library ON - BUILD_EXTRAS Set to ON to build extra examples for the library OFF - BUILD_SHARED Set to ON to include shared versions of the library ON - BUILD_STATIC Set to ON to include static versions of the library OFF - WITH_BE2 Include Backend 2 in build by setting WITH_BE2 to ON ON - WITH_BE4 Include Backend 4 in build by setting WITH_BE4 to ON ON - WITH_NTL Include Backend 6 and NTL in build by setting WITH_NTL to ON OFF - WITH_TCM Activate tcmalloc by setting WITH_TCM to ON OFF - WITH_OPENMP Use OpenMP to enable ON - WITH_NATIVEOPT Use machine-specific optimizations (major speedup for clang) OFF - NATIVE_SIZE Set default word size for native integer arithmetic to 64 or 128 bits 64 - CKKS_M_FACTOR Parameter used to strengthen the CKKS adversarial model in scenarios where decryption results are shared among multiple parties (See Security.md for more details) 1 - ================== ===================================================================================================================================================================== ========== + ==================== ===================================================================================================================================================================== ========== + OPTION_NAME Description Default + ==================== ===================================================================================================================================================================== ========== + BUILD_UNITTESTS Set to ON to build unit tests for the library ON + BUILD_EXAMPLES Set to ON to build examples for the library ON + BUILD_BENCHMARKS Set to ON to build benchmarks for the library ON + BUILD_EXTRAS Set to ON to build extra examples for the library OFF + BUILD_SHARED Set to ON to include shared versions of the library ON + BUILD_STATIC Set to ON to include static versions of the library OFF + GIT_SUBMOD_AUTO Automatically update git submodules at CMake configure time ON + WITH_BE2 Include Backend 2 in build by setting WITH_BE2 to ON OFF + WITH_BE4 Include Backend 4 in build by setting WITH_BE4 to ON (always ON when MATHBACKEND=4, the default) ON + WITH_NTL Include Backend 6 and NTL in build by setting WITH_NTL to ON OFF + WITH_TCM Activate tcmalloc by setting WITH_TCM to ON OFF + WITH_MALLOC_TUNING Tune glibc malloc to retain freed memory for reuse (higher performance, higher resident memory; see AllocTrim()) ON + WITH_OPENMP Use OpenMP to enable ON + WITH_NATIVEOPT Use machine-specific optimizations (major speedup for clang) OFF + WITH_COVTEST Turn on to enable coverage testing (can be used with g++ only) OFF + WITH_NOISE_DEBUG Use only when running the lattice estimator; not for production OFF + WITH_REDUCED_NOISE Enable reduced noise within HKS and BFV HPSPOVERQ modes OFF + USE_MACPORTS Use MacPorts installed packages (macOS) OFF + NATIVE_SIZE Set default word size for native integer arithmetic to 64 or 128 bits 64 + CKKS_M_FACTOR Parameter used to strengthen the CKKS adversarial model in scenarios where decryption results are shared among multiple parties (See Security.md for more details) 1 + ==================== ===================================================================================================================================================================== ========== .. note:: More Options will be added as development progresses -The default math backend for the OpenFHE build is Backend 2 (basic fixed-maximum-length big integers). This default can be changed on the CMake command line by setting the MATHBACKEND variable. For example, to select backend 6 (high performance fixed integers based on the GMP and NTL libraries), use ``-DMATHBACKEND=6`` on the CMake command line. +The default math backend for the OpenFHE build is Backend 4 (dynamically-sized big integers). This default can be changed on the CMake command line by setting the MATHBACKEND variable. For example, to select backend 6 (high performance fixed integers based on the GMP and NTL libraries), use ``-DMATHBACKEND=6`` on the CMake command line. Detecting Local Environments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/core/lib/utils/memory.cpp b/src/core/lib/utils/memory.cpp index 857c05d67..ca3ad29fc 100644 --- a/src/core/lib/utils/memory.cpp +++ b/src/core/lib/utils/memory.cpp @@ -28,10 +28,11 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //================================================================================== + +#include "config_core.h" #include "utils/memory.h" #if defined(__APPLE__) - #include #include #elif defined(__GLIBC__) #include @@ -41,22 +42,36 @@ namespace lbcrypto { -#if defined(__GLIBC__) -// Tune glibc malloc for FHE allocation patterns. Each homomorphic operation allocates and -// frees many large (~0.5-30 MB) polynomial coefficient buffers. By default glibc returns -// these freed buffers to the OS (brk heap-trim / munmap) and re-faults the pages on the next -// operation; under a single thread this page-fault / kernel-time overhead can dominate -// runtime. Keeping large allocations on the heap (M_MMAP_MAX=0) and never trimming it back to -// the OS (M_TRIM_THRESHOLD=-1) lets the buffers be reused, eliminating the churn. Runs once, -// process-wide, at library load. NOTE: this retains freed memory in-process, so workloads -// with large transient allocation peaks will show a higher resident set size. Use AllocTrim() -// after large allocations are freed if this becomes an issue. +#if defined(WITH_MALLOC_TUNING) +// Tune the process allocator to retain freed memory for reuse. OpenFHE operations allocate and +// free many large (~0.5-30 MB) buffers; on allocators that return freed buffers to the OS right +// away, re-faulting the pages on the next operation can dominate single-threaded runtime. Any +// tuning below runs once, process-wide, at library load. NOTE: retained memory shows up as a +// higher resident set size for workloads with large transient allocation peaks. Use AllocTrim() +// after large allocations are freed if this becomes an issue, or build with +// -DWITH_MALLOC_TUNING=OFF to disable this tuning entirely. namespace { + #if defined(__APPLE__) + // Nothing to tune: macOS libmalloc already retains freed memory in-process. Freed pages of + // small/medium allocations stay mapped and are marked reusable (the kernel reclaims them only + // under memory pressure), and freed large allocations are held in the default zone's cache. + // There is no public API to tune this behavior further; AllocTrim() releases these caches. + #elif defined(__GLIBC__) +// By default glibc returns freed large buffers to the OS (brk heap-trim / munmap) and re-faults +// the pages on the next operation. Keeping large allocations on the heap (M_MMAP_MAX=0) and +// never trimming it back to the OS (M_TRIM_THRESHOLD=-1) lets the buffers be reused, +// eliminating the churn. [[maybe_unused]] const bool ofheGlibcMallocTuned = []() noexcept { mallopt(M_MMAP_MAX, 0); mallopt(M_TRIM_THRESHOLD, -1); return true; }(); + #elif defined(_MSC_VER) + // Nothing to tune: the CRT heap is the Windows process heap, which serves allocations above + // roughly 0.5-1 MB directly via VirtualAlloc and returns them to the OS on free, with no + // supported process-wide switch to retain them. If this churn matters, link a caching + // allocator (e.g. mimalloc or tcmalloc) instead. + #endif } // namespace #endif @@ -73,15 +88,7 @@ namespace { // never from a signal handler. Returns true if a trim was attempted, false on unsupported builds. bool AllocTrim() { #if defined(__APPLE__) - vm_address_t* zones = nullptr; - unsigned count = 0; - if (malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count) == KERN_SUCCESS && zones) { - for (unsigned i = 0; i < count; ++i) { - auto* z = reinterpret_cast(zones[i]); - if (z) - malloc_zone_pressure_relief(z, 0); - } - } + malloc_zone_pressure_relief(nullptr, 0); return true; #elif defined(__GLIBC__) malloc_trim(0); @@ -94,8 +101,10 @@ bool AllocTrim() { #endif } +// secure_memset() overwrites memory with c and, unlike a plain std::memset() of an object about +// to be freed or go out of scope, is guaranteed not to be removed by dead-store elimination. void secure_memset(volatile void* mem, uint8_t c, size_t len) { - volatile uint8_t* ptr = (volatile uint8_t*)mem; + volatile uint8_t* ptr = static_cast(mem); for (size_t i = 0; i < len; ++i) *(ptr + i) = c; }