diff --git a/.github/workflows/reusable_compatibility.yml b/.github/workflows/reusable_compatibility.yml index 5503a8110..98eb223bb 100644 --- a/.github/workflows/reusable_compatibility.yml +++ b/.github/workflows/reusable_compatibility.yml @@ -95,10 +95,21 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build + # Exclude the test_jemalloc_pool test - + # TODO: add fix for that in v1.0.1 run: > UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose + ctest --verbose -E test_jemalloc_pool + + - name: Run EXCLUDED tests with filters + working-directory: ${{github.workspace}}/tag_version/build + # Exclude the jemallocPoolName test case of the test_jemalloc_pool test + # TODO: add fix for that in v1.0.1 + run: > + UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" + LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ + ./test/test_jemalloc_pool --gtest_filter="-*jemallocPoolName*" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -220,10 +231,22 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build + # Exclude the test_jemalloc_pool test - + # TODO: add fix for that in v1.0.1 run: | - $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" + $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" cp ${{github.workspace}}/latest_version/build/bin/Debug/umf.dll ${{github.workspace}}/tag_version/build/bin/Debug/umf.dll - ctest -C Debug --verbose + ctest -C Debug --verbose -E test_jemalloc_pool + + - name: Run EXCLUDED tests with filters + working-directory: ${{github.workspace}}/tag_version/build/ + # Exclude the jemallocPoolName test case of the test_jemalloc_pool test + # TODO: add fix for that in v1.0.1 + run: | + $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" + $env:Path = "${{github.workspace}}/tag_version/build/bin/Debug;${{env.VCPKG_BIN_PATH}};$env:Path" + cp ${{github.workspace}}/latest_version/build/bin/Debug/umf.dll ${{github.workspace}}/tag_version/build/bin/Debug/umf.dll + test/Debug/test_jemalloc_pool.exe --gtest_filter="-*jemallocPoolName*" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -361,10 +384,21 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build + # Exclude the test_jemalloc_pool test - + # TODO: add fix for that in v1.0.1 + run: > + UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" + LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ + ctest --verbose -E test_jemalloc_pool + + - name: Run EXCLUDED tests with filters + working-directory: ${{github.workspace}}/tag_version/build + # Exclude the jemallocPoolName test case of the test_jemalloc_pool test + # TODO: add fix for that in v1.0.1 run: > UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose + ./test/test_jemalloc_pool --gtest_filter="-*jemallocPoolName*" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e0eac132..cfec20fa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,14 @@ else() DEPENDS ${jemalloc_targ_SOURCE_DIR}/configure) if(NOT UMF_QEMU_BUILD) - set(MAKE_ARGUMENTS "-j$(nproc)") + if(CMAKE_GENERATOR STREQUAL "Ninja") + # While CMake is supposed to escape this in the generated build + # files, for some reason, it doesn't do so here. Until it's fixed, + # we just manually escape it for ninja. + set(MAKE_ARGUMENTS "-j$$(nproc)") + else() + set(MAKE_ARGUMENTS "-j$(nproc)") + endif() endif() add_custom_command( diff --git a/ChangeLog b/ChangeLog index 72b7fdec6..c55cb871e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Fri Aug 08 2025 Łukasz Stolarczuk + + * Version 1.0.1 + + This patch release contains following changes: + - make topology_init faster (#1469) + - verify if the provider supports the split operation (#1465) + - fix build failure when building for jemalloc with ninja (#1474) + Mon Jul 21 2025 Łukasz Stolarczuk * Version 1.0.0 diff --git a/src/libumf.c b/src/libumf.c index fb01eae57..9df7ee29c 100644 --- a/src/libumf.c +++ b/src/libumf.c @@ -74,7 +74,11 @@ umf_result_t umfInit(void) { if (TRACKER) { LOG_DEBUG("UMF library initialized"); } - +#if !defined(UMF_NO_HWLOC) + // some benchmarks uses multiple forks, and topology initialization is very slow + // so if we initialize topology before the first fork, we can get significant performance gain. + umfGetTopologyReduced(); +#endif return UMF_RESULT_SUCCESS; } diff --git a/src/memspaces/memspace_host_all.c b/src/memspaces/memspace_host_all.c index efcfa7ef4..06a07a660 100644 --- a/src/memspaces/memspace_host_all.c +++ b/src/memspaces/memspace_host_all.c @@ -37,7 +37,7 @@ static umf_result_t umfMemspaceHostAllCreate(umf_memspace_handle_t *hMemspace) { umf_result_t umf_ret = UMF_RESULT_SUCCESS; - hwloc_topology_t topology = umfGetTopology(); + hwloc_topology_t topology = umfGetTopologyReduced(); if (!topology) { // TODO: What would be an approrpiate err? return UMF_RESULT_ERROR_UNKNOWN; diff --git a/src/memtargets/memtarget_numa.c b/src/memtargets/memtarget_numa.c index a0a1e592a..d6431ef9f 100644 --- a/src/memtargets/memtarget_numa.c +++ b/src/memtargets/memtarget_numa.c @@ -218,7 +218,7 @@ static umf_result_t numa_get_capacity(void *memTarget, size_t *capacity) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; } - hwloc_topology_t topology = umfGetTopology(); + hwloc_topology_t topology = umfGetTopologyReduced(); if (!topology) { return UMF_RESULT_ERROR_NOT_SUPPORTED; } diff --git a/src/pool/pool_jemalloc.c b/src/pool/pool_jemalloc.c index abbf50d2b..343b30a28 100644 --- a/src/pool/pool_jemalloc.c +++ b/src/pool/pool_jemalloc.c @@ -12,6 +12,8 @@ #include #include "base_alloc_global.h" +#include "memory_provider_internal.h" +#include "provider_tracking.h" #include "utils_common.h" #include "utils_concurrency.h" #include "utils_log.h" @@ -272,8 +274,15 @@ static bool arena_extent_split(extent_hooks_t *extent_hooks, void *addr, jemalloc_memory_pool_t *pool = get_pool_by_arena_index(arena_ind); assert(pool); - return umfMemoryProviderAllocationSplit(pool->provider, addr, size, - size_a) != UMF_RESULT_SUCCESS; + + umf_result_t ret = + umfMemoryProviderAllocationSplit(pool->provider, addr, size, size_a); + if (ret != UMF_RESULT_SUCCESS) { + LOG_ERR("memory provider failed to split a memory region, while " + "jemalloc requires that"); + } + + return ret != UMF_RESULT_SUCCESS; } // arena_extent_merge - an extent merge function conforms to the extent_merge_t type and optionally @@ -424,11 +433,45 @@ static void *op_aligned_alloc(void *pool, size_t size, size_t alignment) { return ptr; } +// Verify if the memory provider supports the split() operation, +// because jemalloc pool requires that. +static umf_result_t verify_split(umf_memory_provider_handle_t provider) { + // Retrieve the upstream memory provider + umf_memory_provider_handle_t upstream_provider = NULL; + umfTrackingMemoryProviderGetUpstreamProvider( + umfMemoryProviderGetPriv(provider), &upstream_provider); + + size_t page_size = 0; + umf_result_t ret = + umfMemoryProviderGetMinPageSize(upstream_provider, NULL, &page_size); + if (ret != UMF_RESULT_SUCCESS) { + return ret; + } + + size_t size = 2 * page_size; // use double the page size for the split test + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfMemoryProviderAllocationSplit(upstream_provider, (void *)size, size, + page_size)) { + LOG_ERR("memory provider does not support the split operation, while " + "jemalloc pool requires that"); + return UMF_RESULT_ERROR_NOT_SUPPORTED; + } + + return UMF_RESULT_SUCCESS; +} + static umf_result_t op_initialize(umf_memory_provider_handle_t provider, const void *params, void **out_pool) { assert(provider); assert(out_pool); + // Verify if the memory provider supports the split() operation, + // because jemalloc pool requires that. + umf_result_t ret = verify_split(provider); + if (ret != UMF_RESULT_SUCCESS) { + return ret; + } + extent_hooks_t *pHooks = &arena_extent_hooks; size_t unsigned_size = sizeof(unsigned); int n_arenas_set_from_params = 0; diff --git a/src/provider/provider_os_memory.c b/src/provider/provider_os_memory.c index 5c79b0582..e984d8ee8 100644 --- a/src/provider/provider_os_memory.c +++ b/src/provider/provider_os_memory.c @@ -98,6 +98,7 @@ umf_result_t umfOsMemoryProviderParamsSetPartitions( #include "critnib.h" #include "libumf.h" #include "provider_os_memory_internal.h" +#include "topology.h" #include "utils_common.h" #include "utils_concurrency.h" #include "utils_log.h" @@ -629,27 +630,19 @@ static umf_result_t os_initialize(const void *params, void **provider) { memset(os_provider, 0, sizeof(*os_provider)); - int r = hwloc_topology_init(&os_provider->topo); - if (r) { - LOG_ERR("HWLOC topology init failed"); - ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; - goto err_free_os_provider; - } - - r = hwloc_topology_load(os_provider->topo); - if (r) { + os_provider->topo = umfGetTopologyReduced(); + if (!os_provider->topo) { os_store_last_native_error(UMF_OS_RESULT_ERROR_TOPO_DISCOVERY_FAILED, 0); LOG_ERR("HWLOC topology discovery failed"); ret = UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; - goto err_destroy_hwloc_topology; } os_provider->fd_offset_map = critnib_new(NULL, NULL); if (!os_provider->fd_offset_map) { LOG_ERR("creating file descriptor offset map failed"); ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; - goto err_destroy_hwloc_topology; + goto err_free_os_provider; } ret = translate_params(in_params, os_provider); @@ -692,8 +685,6 @@ static umf_result_t os_initialize(const void *params, void **provider) { free_bitmaps(os_provider); err_destroy_critnib: critnib_delete(os_provider->fd_offset_map); -err_destroy_hwloc_topology: - hwloc_topology_destroy(os_provider->topo); err_free_os_provider: umf_ba_global_free(os_provider); return ret; @@ -717,7 +708,7 @@ static umf_result_t os_finalize(void *provider) { if (os_provider->nodeset_str_buf) { umf_ba_global_free(os_provider->nodeset_str_buf); } - hwloc_topology_destroy(os_provider->topo); + umf_ba_global_free(os_provider); return UMF_RESULT_SUCCESS; } diff --git a/src/topology.c b/src/topology.c index eab7992ce..da7666f87 100644 --- a/src/topology.c +++ b/src/topology.c @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -13,7 +13,9 @@ #include "utils_log.h" static hwloc_topology_t topology = NULL; +static hwloc_topology_t topology_reduced = NULL; static UTIL_ONCE_FLAG topology_initialized = UTIL_ONCE_FLAG_INIT; +static UTIL_ONCE_FLAG topology_reduced_initialized = UTIL_ONCE_FLAG_INIT; void umfDestroyTopology(void) { if (topology) { @@ -24,22 +26,55 @@ void umfDestroyTopology(void) { memcpy(&topology_initialized, &is_initialized, sizeof(topology_initialized)); } + if (topology_reduced) { + hwloc_topology_destroy(topology_reduced); + + // portable version of "topology_initialized = UTIL_ONCE_FLAG_INIT;" + static UTIL_ONCE_FLAG is_initialized = UTIL_ONCE_FLAG_INIT; + memcpy(&topology_reduced_initialized, &is_initialized, + sizeof(topology_reduced_initialized)); + } } -static void umfCreateTopology(void) { - if (hwloc_topology_init(&topology)) { +static void umfCreateTopologyHelper(bool reduced, + hwloc_topology_t *topology_ptr) { + if (hwloc_topology_init(topology_ptr)) { LOG_ERR("Failed to initialize topology"); - topology = NULL; + *topology_ptr = NULL; return; } - if (hwloc_topology_load(topology)) { + if (reduced) { + // Set the topology to only include NUMA nodes and memory + // to improve performance of the topology load on large systems + if (hwloc_topology_set_all_types_filter(*topology_ptr, + HWLOC_TYPE_FILTER_KEEP_NONE)) { + LOG_ERR("Failed to set topology filter"); + hwloc_topology_destroy(*topology_ptr); + *topology_ptr = NULL; + return; + } + } + if (hwloc_topology_load(*topology_ptr)) { LOG_ERR("Failed to initialize topology"); - hwloc_topology_destroy(topology); - topology = NULL; + hwloc_topology_destroy(*topology_ptr); + *topology_ptr = NULL; } } +static void umfCreateTopology(void) { + umfCreateTopologyHelper(false, &topology); +} + +static void umfCreateTopologyReduced(void) { + umfCreateTopologyHelper(true, &topology_reduced); +} + +hwloc_topology_t umfGetTopologyReduced(void) { + utils_init_once(&topology_reduced_initialized, umfCreateTopologyReduced); + return topology_reduced; +} + hwloc_topology_t umfGetTopology(void) { utils_init_once(&topology_initialized, umfCreateTopology); return topology; diff --git a/src/topology.h b/src/topology.h index c20defda7..5d5982a0c 100644 --- a/src/topology.h +++ b/src/topology.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -17,6 +17,7 @@ extern "C" { #endif hwloc_topology_t umfGetTopology(void); +hwloc_topology_t umfGetTopologyReduced(void); void umfDestroyTopology(void); #ifdef __cplusplus diff --git a/test/common/provider.hpp b/test/common/provider.hpp index 6d42c59f2..e52dd614a 100644 --- a/test/common/provider.hpp +++ b/test/common/provider.hpp @@ -36,23 +36,23 @@ auto wrapProviderUnique(umf_memory_provider_handle_t hProvider) { typedef struct provider_base_t { umf_result_t initialize() noexcept { return UMF_RESULT_SUCCESS; }; umf_result_t alloc(size_t, size_t, void **) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t free([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t get_last_native_error(const char **, int32_t *) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t get_recommended_page_size([[maybe_unused]] size_t size, [[maybe_unused]] size_t *pageSize) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t get_min_page_size([[maybe_unused]] const void *ptr, [[maybe_unused]] size_t *pageSize) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t get_name(const char **name) noexcept { *name = "base"; @@ -60,45 +60,45 @@ typedef struct provider_base_t { } umf_result_t ext_purge_lazy([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_purge_force([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_allocation_merge([[maybe_unused]] void *lowPtr, [[maybe_unused]] void *highPtr, [[maybe_unused]] size_t totalSize) { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_allocation_split([[maybe_unused]] void *ptr, [[maybe_unused]] size_t totalSize, [[maybe_unused]] size_t firstSize) { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_get_ipc_handle_size([[maybe_unused]] size_t *size) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_get_ipc_handle([[maybe_unused]] const void *ptr, [[maybe_unused]] size_t size, [[maybe_unused]] void *providerIpcData) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_put_ipc_handle([[maybe_unused]] void *providerIpcData) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_open_ipc_handle([[maybe_unused]] void *providerIpcData, [[maybe_unused]] void **ptr) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } umf_result_t ext_close_ipc_handle([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) noexcept { - return UMF_RESULT_ERROR_UNKNOWN; + return UMF_RESULT_ERROR_NOT_SUPPORTED; } virtual ~provider_base_t() = default; } provider_base_t; diff --git a/test/pools/jemalloc_pool.cpp b/test/pools/jemalloc_pool.cpp index 97a2128f7..906aba763 100644 --- a/test/pools/jemalloc_pool.cpp +++ b/test/pools/jemalloc_pool.cpp @@ -192,18 +192,10 @@ TEST_F(test, jemallocPoolName) { umf_jemalloc_pool_params_handle_t params = nullptr; umf_result_t res = umfJemallocPoolParamsCreate(¶ms); EXPECT_EQ(res, UMF_RESULT_SUCCESS); - umf_memory_provider_handle_t provider_handle = nullptr; umf_memory_pool_handle_t pool = NULL; - struct memory_provider : public umf_test::provider_base_t {}; - umf_memory_provider_ops_t provider_ops = - umf_test::providerMakeCOps(); - auto providerUnique = - wrapProviderUnique(createProviderChecked(&provider_ops, nullptr)); - provider_handle = providerUnique.get(); - - res = - umfPoolCreate(umfJemallocPoolOps(), provider_handle, params, 0, &pool); + auto nullProvider = nullProviderCreate(); + res = umfPoolCreate(umfJemallocPoolOps(), nullProvider, params, 0, &pool); EXPECT_EQ(res, UMF_RESULT_SUCCESS); const char *name = nullptr; res = umfPoolGetName(pool, &name); @@ -211,5 +203,25 @@ TEST_F(test, jemallocPoolName) { EXPECT_STREQ(name, "jemalloc"); umfPoolDestroy(pool); + umfMemoryProviderDestroy(nullProvider); + umfJemallocPoolParamsDestroy(params); +} + +TEST_F(test, jemallocProviderDoesNotSupportSplit) { + umf_jemalloc_pool_params_handle_t params = nullptr; + umf_result_t res = umfJemallocPoolParamsCreate(¶ms); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t ba_provider; + umf_result_t ret = + umfMemoryProviderCreate(&BA_GLOBAL_PROVIDER_OPS, nullptr, &ba_provider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t pool = nullptr; + res = umfPoolCreate(umfJemallocPoolOps(), ba_provider, params, 0, &pool); + EXPECT_EQ(res, UMF_RESULT_ERROR_NOT_SUPPORTED); + EXPECT_EQ(pool, nullptr); + + umfMemoryProviderDestroy(ba_provider); umfJemallocPoolParamsDestroy(params); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy