diff --git a/.gitlab/build_dane.yml b/.gitlab/build_dane.yml index 18f5ba1a9d..d0c893294b 100644 --- a/.gitlab/build_dane.yml +++ b/.gitlab/build_dane.yml @@ -59,6 +59,13 @@ dane-gcc_13_3_1-werror-src: EXTRA_CMAKE_OPTIONS: "-DENABLE_WARNINGS_AS_ERRORS:BOOL=ON" extends: .src_build_on_dane +dane-gcc_13_3_1-umpire_host_default-werror-src: + variables: + COMPILER: "gcc@13.3.1" + HOST_CONFIG: "dane-toss_4_x86_64_ib-${COMPILER}.cmake" + EXTRA_CMAKE_OPTIONS: "-DENABLE_WARNINGS_AS_ERRORS:BOOL=ON -DAXOM_DEFAULT_HOST_ALLOCATOR=UMPIRE_HOST" + extends: .src_build_on_dane + dane-sanitizers-gcc_13_3_1-src: variables: COMPILER: "gcc@13.3.1" diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index b38d9f3444..379d1dc7cc 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -18,6 +18,11 @@ The Axom project release numbers follow [Semantic Versioning](http://semver.org/ ## [Unreleased] - Release date yyyy-mm-dd +- Axom's host execution-space default allocator is now a configure-time policy. The default policy is malloc, regardless + of whether Axom is configured with Umpire enabled. Umpire builds may opt into the Umpire `HOST` resource with + `-DAXOM_DEFAULT_HOST_ALLOCATOR=UMPIRE_HOST`. Runtime per-use selection remains available through existing explicit + allocator-ID arguments. + ## [Version 0.15.0] - Release date 2026-08-28 ### Added diff --git a/src/axom/config.hpp.in b/src/axom/config.hpp.in index bec81ffb73..702670e4d6 100644 --- a/src/axom/config.hpp.in +++ b/src/axom/config.hpp.in @@ -81,6 +81,7 @@ * Compiler defines for library capabilities */ #cmakedefine AXOM_USE_UMPIRE_SHARED_MEMORY +#cmakedefine AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST /* * Compiler defines for third-party executables diff --git a/src/axom/core/ArrayBase.hpp b/src/axom/core/ArrayBase.hpp index 7b59b50897..a35a130b9f 100644 --- a/src/axom/core/ArrayBase.hpp +++ b/src/axom/core/ArrayBase.hpp @@ -911,7 +911,7 @@ struct DeviceStagingBuffer #if defined(AXOM_USE_CUDA) && defined(AXOM_USE_UMPIRE) if(m_deviceStage) { - int allocator_id = axom::detail::getAllocatorID(); + int allocator_id = axom::detail::getDefaultHostAllocatorID(); m_staging_buf = axom::allocate(nelems, allocator_id); if(read_from_data) { @@ -1018,7 +1018,7 @@ struct ArrayOps if constexpr(std::is_default_constructible_v) { #if defined(AXOM_USE_GPU) && defined(AXOM_USE_UMPIRE) - if(space != MemorySpace::Host) + if(space != MemorySpace::Host && space != MemorySpace::Malloc) { if constexpr(std::is_trivially_default_constructible_v) { @@ -1062,7 +1062,7 @@ struct ArrayOps void fill(T* array, IndexType begin, IndexType nelems, const T& value) { #if defined(AXOM_USE_GPU) && defined(AXOM_USE_UMPIRE) - if(space != MemorySpace::Host) + if(space != MemorySpace::Host && space != MemorySpace::Malloc) { if constexpr(std::is_trivially_copyable_v) { diff --git a/src/axom/core/examples/core_array_perf.cpp b/src/axom/core/examples/core_array_perf.cpp index 4ed0ba1dbe..c0a74f9d31 100644 --- a/src/axom/core/examples/core_array_perf.cpp +++ b/src/axom/core/examples/core_array_perf.cpp @@ -161,12 +161,10 @@ int allocatorIdFromPolicy(axom::runtime_policy::Policy policy) { AXOM_UNUSED_VAR(policy); #if defined(AXOM_USE_UMPIRE) - int allocatorID = policy == axom::runtime_policy::Policy::seq - ? axom::detail::getAllocatorID() - : + int allocatorID = + policy == axom::runtime_policy::Policy::seq ? axom::detail::getDefaultHostAllocatorID() : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) - policy == axom::runtime_policy::Policy::omp - ? axom::detail::getAllocatorID() + policy == axom::runtime_policy::Policy::omp ? axom::detail::getDefaultHostAllocatorID() : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) @@ -204,12 +202,20 @@ class MDMappingPerfTester { m_allocatorId = allocatorIdFromPolicy(params.runtimePolicy); #ifdef AXOM_USE_UMPIRE - umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); - umpire::Allocator allocator = rm.getAllocator(m_allocatorId); - std::cout << axom::fmt::format("Allocator id: {}, Umpire memory space {}", - m_allocatorId, - allocator.getName()) - << std::endl; + if(m_allocatorId == axom::MALLOC_ALLOCATOR_ID) + { + std::cout << axom::fmt::format("Allocator id: {}, malloc memory space", m_allocatorId) + << std::endl; + } + else + { + umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); + umpire::Allocator allocator = rm.getAllocator(m_allocatorId); + std::cout << axom::fmt::format("Allocator id: {}, Umpire memory space {}", + m_allocatorId, + allocator.getName()) + << std::endl; + } #else std::cout << axom::fmt::format("Allocator id: {}, default memory space", m_allocatorId) << std::endl; diff --git a/src/axom/core/execution/execution_space.hpp b/src/axom/core/execution/execution_space.hpp index f2cfd30fc9..03c12ac5dc 100644 --- a/src/axom/core/execution/execution_space.hpp +++ b/src/axom/core/execution/execution_space.hpp @@ -32,7 +32,7 @@ * with OpenMp, i.e., RAJA_ENABLE_OPENMP must be defined in the generated * RAJA/config.hpp. * - * The default memory allocator when using this execution space is HOST. + * The default memory allocator when using this execution space is malloc. * * When using this execution space, the data must reside on CPU/host memory. * diff --git a/src/axom/core/execution/internal/omp_exec.hpp b/src/axom/core/execution/internal/omp_exec.hpp index 3da39efb96..ad04b38b75 100644 --- a/src/axom/core/execution/internal/omp_exec.hpp +++ b/src/axom/core/execution/internal/omp_exec.hpp @@ -16,11 +16,6 @@ #error OMP_EXEC requires an OpenMP enabled RAJA #endif -// Umpire includes -#ifdef AXOM_USE_UMPIRE - #include "umpire/Umpire.hpp" -#endif - namespace axom { /*! @@ -41,10 +36,10 @@ struct execution_space using atomic_policy = RAJA::omp_atomic; using sync_policy = RAJA::omp_synchronize; -#ifdef AXOM_USE_UMPIRE +#ifdef AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST static constexpr MemorySpace memory_space = MemorySpace::Host; #else - static constexpr MemorySpace memory_space = MemorySpace::Dynamic; + static constexpr MemorySpace memory_space = MemorySpace::Malloc; #endif AXOM_HOST_DEVICE static constexpr bool async() noexcept { return false; } @@ -52,14 +47,7 @@ struct execution_space AXOM_HOST_DEVICE static constexpr bool onDevice() noexcept { return false; } AXOM_HOST_DEVICE static constexpr char* name() noexcept { return (char*)"[OMP_EXEC]"; } - static int allocatorID() noexcept - { -#ifdef AXOM_USE_UMPIRE - return axom::getUmpireResourceAllocatorID(umpire::resource::Host); -#else - return axom::getDefaultAllocatorID(); -#endif - } + static int allocatorID() noexcept { return axom::detail::getDefaultHostAllocatorID(); } AXOM_HOST_DEVICE static constexpr runtime_policy::Policy runtimePolicy() noexcept { return runtime_policy::Policy::omp; diff --git a/src/axom/core/execution/internal/seq_exec.hpp b/src/axom/core/execution/internal/seq_exec.hpp index ce9bac758b..cde15fb027 100644 --- a/src/axom/core/execution/internal/seq_exec.hpp +++ b/src/axom/core/execution/internal/seq_exec.hpp @@ -14,11 +14,6 @@ #include "RAJA/RAJA.hpp" #endif -// Umpire includes -#ifdef AXOM_USE_UMPIRE - #include "umpire/Umpire.hpp" -#endif - namespace axom { /*! @@ -51,10 +46,10 @@ struct execution_space using sync_policy = void; -#ifdef AXOM_USE_UMPIRE +#ifdef AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST static constexpr MemorySpace memory_space = MemorySpace::Host; #else - static constexpr MemorySpace memory_space = MemorySpace::Dynamic; + static constexpr MemorySpace memory_space = MemorySpace::Malloc; #endif AXOM_HOST_DEVICE static constexpr bool async() noexcept { return false; } @@ -62,14 +57,7 @@ struct execution_space AXOM_HOST_DEVICE static constexpr bool onDevice() noexcept { return false; } AXOM_HOST_DEVICE static constexpr char* name() noexcept { return (char*)"[SEQ_EXEC]"; } - static int allocatorID() noexcept - { -#ifdef AXOM_USE_UMPIRE - return axom::getUmpireResourceAllocatorID(umpire::resource::Host); -#else - return axom::getDefaultAllocatorID(); -#endif - } + static int allocatorID() noexcept { return axom::detail::getDefaultHostAllocatorID(); } AXOM_HOST_DEVICE static constexpr runtime_policy::Policy runtimePolicy() noexcept { return runtime_policy::Policy::seq; diff --git a/src/axom/core/memory_management.hpp b/src/axom/core/memory_management.hpp index 5f6fd8bd41..7debeb3bc1 100644 --- a/src/axom/core/memory_management.hpp +++ b/src/axom/core/memory_management.hpp @@ -157,15 +157,24 @@ inline void setDefaultAllocator(umpire::resource::MemoryResourceType resource_ty #endif /*! - * \brief Sets the default memory allocator to use. - * \param [in] allocId the Umpire allocator id + * \brief Sets the default memory allocator for the Umpire ResourceManager. + * \param [in] allocId the Axom allocator id * + * \note When Axom is compiled with Umpire and \a allocId is + * axom::MALLOC_ALLOCATOR_ID, this function sets Umpire's default + * allocator to its Host resource. * \note This function has no effect when Axom is not compiled with Umpire. */ inline void setDefaultAllocator(int allocId) { #ifdef AXOM_USE_UMPIRE umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); + if(allocId == MALLOC_ALLOCATOR_ID) + { + rm.setDefaultAllocator(rm.getAllocator(umpire::resource::Host)); + return; + } + umpire::Allocator allocator = rm.getAllocator(allocId); rm.setDefaultAllocator(allocator); #else @@ -188,6 +197,27 @@ inline int getDefaultAllocatorID() #endif } +namespace detail +{ +/*! + * \brief Returns the ID of the default host allocator. + * + * \note This is distinct from the current default allocator returned by + * axom::getDefaultAllocatorID(), which tracks Umpire's default allocator + * when Axom is configured with Umpire. + * + * \return ID of the default host allocator. + */ +inline int getDefaultHostAllocatorID() +{ +#if defined(AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST) + return getUmpireResourceAllocatorID(umpire::resource::Host); +#else + return MALLOC_ALLOCATOR_ID; +#endif +} +} // namespace detail + /*! * \brief Get the allocator id from which data has been allocated. * \return Allocator id. If Umpire doesn't have an allocator for the @@ -241,7 +271,7 @@ int getSharedMemoryAllocatorID(std::size_t minSegmentSize = 0); * \brief Allocates a chunk of memory of type T. * * \param [in] n the number of elements to allocate. - * \param [in] allocID the Umpire allocator to use (optional) + * \param [in] allocID the Axom/Umpire allocator to use (optional) * * \tparam T the type of pointer returned. * @@ -260,7 +290,7 @@ inline T* allocate(std::size_t n, int allocID = getDefaultAllocatorID()) noexcep * * \param [in] n the number of elements to allocate. * \param [in] name allocation name (must be non-empty for shared memory allocators) - * \param [in] allocID the Umpire allocator to use (optional) + * \param [in] allocID the Axom/Umpire allocator to use (optional) * * \return pointer to the new allocation or a nullptr if allocation failed. */ @@ -360,6 +390,11 @@ inline T* allocate(std::size_t n, int allocID) noexcept { const std::size_t numbytes = n * sizeof(T); + if(allocID == MALLOC_ALLOCATOR_ID) + { + return static_cast(std::malloc(numbytes)); + } + #ifdef AXOM_USE_UMPIRE if(umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); rm.isAllocator(allocID)) { @@ -368,11 +403,6 @@ inline T* allocate(std::size_t n, int allocID) noexcept } #endif - if(allocID == MALLOC_ALLOCATOR_ID) - { - return static_cast(std::malloc(numbytes)); - } - std::cerr << "Unrecognized allocator id " << allocID << std::endl; axom::utilities::processAbort(); @@ -384,6 +414,12 @@ inline T* allocate(std::size_t n, const std::string& name, int allocID) noexcept { const std::size_t numbytes = n * sizeof(T); + if(allocID == MALLOC_ALLOCATOR_ID) + { + AXOM_UNUSED_VAR(name); + return static_cast(std::malloc(numbytes)); + } + #ifdef AXOM_USE_UMPIRE if(umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); rm.isAllocator(allocID)) { @@ -393,12 +429,6 @@ inline T* allocate(std::size_t n, const std::string& name, int allocID) noexcept } #endif - if(allocID == MALLOC_ALLOCATOR_ID) - { - AXOM_UNUSED_VAR(name); - return static_cast(std::malloc(numbytes)); - } - std::cerr << "Unrecognized allocator id " << allocID << std::endl; axom::utilities::processAbort(); @@ -440,36 +470,28 @@ inline T* reallocate(T* pointer, std::size_t n, int allocID) noexcept #if defined(AXOM_USE_UMPIRE) umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); - if(rm.isAllocator(allocID)) + + if(pointer == nullptr) { - if(pointer == nullptr) - { - pointer = axom::allocate(n, allocID); - } - else - { - if(rm.hasAllocator(pointer)) - { - pointer = static_cast(rm.reallocate(pointer, numbytes)); - } - else - { - /* - * Reallocate from non-Umpire to Umpire, manually, using - * allocate, copy and deallocate. Because we don't know the - * current size, we first do a (extra) reallocate within the - * current space just so we have the size for the copy. - * Is there a better way? - */ - auto tmpPointer = std::realloc(pointer, numbytes); - pointer = axom::allocate(n, allocID); - copy(pointer, tmpPointer, numbytes); - deallocate(tmpPointer); - } - } + pointer = axom::allocate(n, allocID); return pointer; } + if(rm.hasAllocator(pointer)) + { + pointer = static_cast(rm.reallocate(pointer, numbytes)); + } + else + { + pointer = static_cast(std::realloc(pointer, numbytes)); + } + + // Consistently handle realloc(0) for std::realloc to match Umpire's behavior + if(n == 0 && pointer == nullptr) + { + pointer = axom::allocate(0, MALLOC_ALLOCATOR_ID); + } + #else if(allocID == MALLOC_ALLOCATOR_ID) @@ -547,13 +569,12 @@ inline void fill(void* dst, std::size_t n, const T& value) noexcept doHostFill = false; // Device memory: fill on host, then copy to device - const auto num_bytes = n * sizeof(T); - T* src = allocate(num_bytes, rm.getDefaultAllocator().getId()); + T* src = allocate(n, axom::detail::getDefaultHostAllocatorID()); for(std::size_t i = 0; i < n; ++i) { src[i] = value; } - rm.copy(dst, src, num_bytes); + axom::copy(dst, src, n * sizeof(T)); deallocate(src); } } diff --git a/src/axom/core/tests/core_execution_space.hpp b/src/axom/core/tests/core_execution_space.hpp index 749f953831..846e650fdf 100644 --- a/src/axom/core/tests/core_execution_space.hpp +++ b/src/axom/core/tests/core_execution_space.hpp @@ -118,6 +118,30 @@ TEST(core_execution_space, check_invalid) check_invalid(); } +//------------------------------------------------------------------------------ +TEST(core_execution_space, check_default_host_allocator) +{ + int expected_host_allocator = axom::MALLOC_ALLOCATOR_ID; + auto expected_host_memory_space = axom::MemorySpace::Malloc; +#if defined(AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST) + expected_host_allocator = axom::getUmpireResourceAllocatorID(umpire::resource::Host); + expected_host_memory_space = axom::MemorySpace::Host; +#endif + + EXPECT_EQ(expected_host_memory_space, axom::execution_space::memory_space); + EXPECT_EQ(expected_host_allocator, axom::execution_space::allocatorID()); + +#if defined(AXOM_USE_UMPIRE) + EXPECT_EQ(axom::getUmpireResourceAllocatorID(umpire::resource::Host), + axom::detail::getAllocatorID()); +#endif + +#if defined(AXOM_USE_OPENMP) && defined(AXOM_USE_RAJA) + EXPECT_EQ(expected_host_allocator, axom::execution_space::allocatorID()); + EXPECT_EQ(expected_host_memory_space, axom::execution_space::memory_space); +#endif +} + //============================================================================== // The following tests require RAJA and UMPIRE //============================================================================== @@ -130,7 +154,7 @@ TEST(core_execution_space, check_seq_exec) constexpr bool IS_ASYNC = false; constexpr bool ON_DEVICE = false; - int allocator_id = axom::getUmpireResourceAllocatorID(umpire::resource::Host); + int allocator_id = axom::execution_space::allocatorID(); check_execution_mappings 2022 RAJA::seq_exec, @@ -153,7 +177,7 @@ TEST(core_execution_space, check_omp_exec) constexpr bool IS_ASYNC = false; constexpr bool ON_DEVICE = false; - int allocator_id = axom::getUmpireResourceAllocatorID(umpire::resource::Host); + int allocator_id = axom::execution_space::allocatorID(); check_execution_mappings(N, srcAllocId); + EXPECT_EQ(axom::getAllocatorIDFromPointer(src), srcAllocId); axom::copy(src, origOnHost, N * sizeof(int)); int* dst = axom::reallocate(src, K, dstAllocId); + EXPECT_EQ(axom::getAllocatorIDFromPointer(dst), srcAllocId); axom::copy(tempOnHost, dst, N * sizeof(int)); diffCount = countDiffs(); EXPECT_EQ(diffCount, 0); src = axom::reallocate(dst, N, srcAllocId); + EXPECT_EQ(axom::getAllocatorIDFromPointer(src), srcAllocId); axom::copy(tempOnHost, src, N * sizeof(int)); diffCount = countDiffs(); EXPECT_EQ(diffCount, 0); diff --git a/src/axom/mir/examples/concentric_circles/runMIR.hpp b/src/axom/mir/examples/concentric_circles/runMIR.hpp index 7b979d3202..96c30d4675 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR.hpp +++ b/src/axom/mir/examples/concentric_circles/runMIR.hpp @@ -67,7 +67,9 @@ int installAllocator([[maybe_unused]] size_t initialPoolSizeBytes) int allocator_id = axom::execution_space::allocatorID(); #if defined(AXOM_USE_UMPIRE) auto& rm = umpire::ResourceManager::getInstance(); - umpire::Allocator allocator = rm.getAllocator(allocator_id); + umpire::Allocator allocator = allocator_id == axom::MALLOC_ALLOCATOR_ID + ? rm.getAllocator(umpire::resource::Host) + : rm.getAllocator(allocator_id); const std::string newName = allocator.getName() + "_POOL"; SLIC_INFO( @@ -211,7 +213,9 @@ int runMIR(const conduit::Node& hostMesh, const conduit::Node& options, conduit: try { auto& rm = umpire::ResourceManager::getInstance(); - umpire::Allocator allocator = rm.getAllocator(allocator_id); + umpire::Allocator allocator = allocator_id == axom::MALLOC_ALLOCATOR_ID + ? rm.getAllocator(umpire::resource::Host) + : rm.getAllocator(allocator_id); SLIC_INFO("Allocator Information:"); SLIC_INFO(axom::fmt::format("\tname: {}", allocator.getName())); SLIC_INFO(axom::fmt::format("\thighwatermark: {}", allocator.getHighWatermark())); diff --git a/src/axom/mir/examples/heavily_mixed/runMIR.hpp b/src/axom/mir/examples/heavily_mixed/runMIR.hpp index 0f7e85f9d1..0228a08efe 100644 --- a/src/axom/mir/examples/heavily_mixed/runMIR.hpp +++ b/src/axom/mir/examples/heavily_mixed/runMIR.hpp @@ -19,7 +19,9 @@ int installAllocator([[maybe_unused]] size_t initialPoolSizeBytes) int allocator_id = axom::execution_space::allocatorID(); #if defined(AXOM_USE_UMPIRE) auto& rm = umpire::ResourceManager::getInstance(); - umpire::Allocator allocator = rm.getAllocator(allocator_id); + umpire::Allocator allocator = allocator_id == axom::MALLOC_ALLOCATOR_ID + ? rm.getAllocator(umpire::resource::Host) + : rm.getAllocator(allocator_id); const std::string newName = allocator.getName() + "_POOL"; SLIC_INFO( @@ -141,7 +143,9 @@ int runMIR(const conduit::Node& hostMesh, const conduit::Node& options, conduit: try { auto& rm = umpire::ResourceManager::getInstance(); - umpire::Allocator allocator = rm.getAllocator(allocator_id); + umpire::Allocator allocator = allocator_id == axom::MALLOC_ALLOCATOR_ID + ? rm.getAllocator(umpire::resource::Host) + : rm.getAllocator(allocator_id); SLIC_INFO("Allocator Information:"); SLIC_INFO(axom::fmt::format("\tname: {}", allocator.getName())); SLIC_INFO(axom::fmt::format("\thighwatermark: {}", allocator.getHighWatermark())); diff --git a/src/axom/primal/tests/primal_intersect.cpp b/src/axom/primal/tests/primal_intersect.cpp index 66fd2c2bca..12c02bc44b 100644 --- a/src/axom/primal/tests/primal_intersect.cpp +++ b/src/axom/primal/tests/primal_intersect.cpp @@ -2592,9 +2592,8 @@ void check_plane_bb_intersect() // Determine new allocator (for CUDA or HIP policy, set to device) umpire::Allocator allocator = - (axom::execution_space::onDevice() - ? rm.getAllocator(umpire::resource::Device) - : rm.getAllocator(axom::execution_space::allocatorID())); + (axom::execution_space::onDevice() ? rm.getAllocator(umpire::resource::Device) + : rm.getAllocator(umpire::resource::Host)); // Set new default to device axom::setDefaultAllocator(allocator.getId()); @@ -2673,9 +2672,8 @@ void check_plane_seg_intersect() // Determine new allocator (for CUDA or HIP policy, set to device) umpire::Allocator allocator = - (axom::execution_space::onDevice() - ? rm.getAllocator(umpire::resource::Device) - : rm.getAllocator(axom::execution_space::allocatorID())); + (axom::execution_space::onDevice() ? rm.getAllocator(umpire::resource::Device) + : rm.getAllocator(umpire::resource::Host)); // Set new default to device axom::setDefaultAllocator(allocator.getId()); @@ -2762,9 +2760,8 @@ void check_segment_segment_intersect_policy() const int current_allocator = axom::getDefaultAllocatorID(); umpire::Allocator allocator = - (axom::execution_space::onDevice() - ? rm.getAllocator(umpire::resource::Device) - : rm.getAllocator(axom::execution_space::allocatorID())); + (axom::execution_space::onDevice() ? rm.getAllocator(umpire::resource::Device) + : rm.getAllocator(umpire::resource::Host)); axom::setDefaultAllocator(allocator.getId()); diff --git a/src/axom/quest/MarchingCubes.cpp b/src/axom/quest/MarchingCubes.cpp index 4a0d4f855e..3c1e82391b 100644 --- a/src/axom/quest/MarchingCubes.cpp +++ b/src/axom/quest/MarchingCubes.cpp @@ -195,14 +195,8 @@ void MarchingCubes::populateContourMesh(axom::mint::UnstructuredMesh() -#else - axom::detail::getAllocatorID() -#endif - : m_allocatorID; + const int hostAllocatorId = + hostAndInternalMemoriesAreSeparate ? axom::detail::getDefaultHostAllocatorID() : m_allocatorID; if(hostAndInternalMemoriesAreSeparate) { diff --git a/src/axom/quest/examples/quest_marching_cubes_example.cpp b/src/axom/quest/examples/quest_marching_cubes_example.cpp index 3c19f9734f..5c98329bab 100644 --- a/src/axom/quest/examples/quest_marching_cubes_example.cpp +++ b/src/axom/quest/examples/quest_marching_cubes_example.cpp @@ -1611,10 +1611,9 @@ int allocatorIdToTest(axom::runtime_policy::Policy policy) //--------------------------------------------------------------------------- // Memory resource. For testing, choose device memory if appropriate. //--------------------------------------------------------------------------- - int allocatorID = - policy == RuntimePolicy::seq ? axom::detail::getAllocatorID() : + int allocatorID = policy == RuntimePolicy::seq ? axom::detail::getDefaultHostAllocatorID() : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) - policy == RuntimePolicy::omp ? axom::detail::getAllocatorID() + policy == RuntimePolicy::omp ? axom::detail::getDefaultHostAllocatorID() : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) diff --git a/src/axom/sidre/core/Buffer.cpp b/src/axom/sidre/core/Buffer.cpp index 8fb9f2f958..f90cc31393 100644 --- a/src/axom/sidre/core/Buffer.cpp +++ b/src/axom/sidre/core/Buffer.cpp @@ -11,6 +11,7 @@ #include // Sidre project headers +#include "ConduitMemory.hpp" #include "Group.hpp" #include "View.hpp" @@ -22,17 +23,28 @@ namespace sidre { /*! * \brief Helper function. If allocatorID is a valid umpire allocator ID then - * return it. Otherwise return the ID of the default allocator. + * return it. Otherwise return the ID of the configured default host allocator. */ int getValidAllocatorID(int allocID) { if(allocID == INVALID_ALLOCATOR_ID) { - allocID = getDefaultAllocatorID(); + allocID = axom::detail::getDefaultHostAllocatorID(); } return allocID; } +int getBufferAllocatorID(const Node& node, void* data_ptr) +{ + if(data_ptr != nullptr) + { + return axom::getAllocatorIDFromPointer(data_ptr); + } + + const int allocID = ConduitMemory::conduitAllocIdToAxom(node.allocator()); + return getValidAllocatorID(allocID); +} + /* ************************************************************************* * @@ -82,7 +94,9 @@ Buffer* Buffer::allocate(int allocID) if(data != nullptr) { - m_node.set_external(DataType(m_node.dtype()), data); + DataType dtype(m_node.dtype()); + m_node.set_allocator(ConduitMemory::axomAllocIdToConduit(allocID)); + m_node.set_external(dtype, data); } return this; } @@ -134,15 +148,17 @@ Buffer* Buffer::reallocate(IndexType num_elems) } void* old_data_ptr = getVoidPtr(); + const int allocID = getBufferAllocatorID(m_node, old_data_ptr); DataType dtype(m_node.dtype()); dtype.set_number_of_elements(num_elems); IndexType new_size = dtype.strided_bytes(); - void* new_data_ptr = axom::reallocate(static_cast(old_data_ptr), new_size); + void* new_data_ptr = axom::reallocate(static_cast(old_data_ptr), new_size, allocID); if(num_elems == 0 || new_data_ptr != nullptr) { m_node.reset(); + m_node.set_allocator(ConduitMemory::axomAllocIdToConduit(allocID)); m_node.set_external(dtype, new_data_ptr); } else diff --git a/src/axom/sidre/core/Group.cpp b/src/axom/sidre/core/Group.cpp index c9e35ac0f5..b8182c516d 100644 --- a/src/axom/sidre/core/Group.cpp +++ b/src/axom/sidre/core/Group.cpp @@ -17,6 +17,7 @@ #include "axom/core/Macros.hpp" #include "axom/core/MapCollection.hpp" #include "axom/core/Path.hpp" +#include "axom/core/memory_management.hpp" // Sidre headers #include "Buffer.hpp" @@ -1084,10 +1085,8 @@ Group* Group::createGroup(const std::string& path, bool is_list, bool accept_exi return nullptr; } - SLIC_ASSERT(group->getDefaultArrayAllocatorID() == m_default_array_alloc_id); - SLIC_ASSERT(group->getDefaultTupleAllocatorID() == m_default_tuple_alloc_id); - new_group->setDefaultArrayAllocator(m_default_array_alloc_id); - new_group->setDefaultTupleAllocator(m_default_tuple_alloc_id); + new_group->setDefaultArrayAllocator(group->getDefaultArrayAllocatorID()); + new_group->setDefaultTupleAllocator(group->getDefaultTupleAllocatorID()); return group->attachGroup(new_group); } @@ -1115,11 +1114,8 @@ Group* Group::createUnnamedGroup(bool is_list) return nullptr; } -#ifdef AXOM_USE_UMPIRE - // Why only do this with Umpire? BTNG. - new_group->setDefaultArrayAllocator(getDefaultArrayAllocator()); - new_group->setDefaultTupleAllocator(getDefaultTupleAllocator()); -#endif + new_group->setDefaultArrayAllocator(getDefaultArrayAllocatorID()); + new_group->setDefaultTupleAllocator(getDefaultTupleAllocatorID()); return attachGroup(new_group); } @@ -2290,13 +2286,8 @@ Group::Group(const std::string& name, DataStore* datastore, bool is_list) , m_is_list(is_list) , m_view_coll(nullptr) , m_group_coll(nullptr) -#ifdef AXOM_USE_UMPIRE - , m_default_array_alloc_id(axom::getDefaultAllocatorID()) - , m_default_tuple_alloc_id(axom::getDefaultAllocatorID()) -#else - , m_default_array_alloc_id(axom::MALLOC_ALLOCATOR_ID) - , m_default_tuple_alloc_id(axom::MALLOC_ALLOCATOR_ID) -#endif + , m_default_array_alloc_id(axom::detail::getDefaultHostAllocatorID()) + , m_default_tuple_alloc_id(axom::detail::getDefaultHostAllocatorID()) { if(is_list) { diff --git a/src/axom/sidre/tests/sidre_buffer.cpp b/src/axom/sidre/tests/sidre_buffer.cpp index 860781677a..8642b5b54b 100644 --- a/src/axom/sidre/tests/sidre_buffer.cpp +++ b/src/axom/sidre/tests/sidre_buffer.cpp @@ -404,10 +404,14 @@ class UmpireTest : public ::testing::TestWithParam } DataStore ds; - umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); int allocID; }; +void checkPointerAllocatorID(void* ptr, int expectedAllocID) +{ + ASSERT_EQ(expectedAllocID, axom::getAllocatorIDFromPointer(ptr)); +} + //------------------------------------------------------------------------------ TEST_P(UmpireTest, allocate) { @@ -418,7 +422,7 @@ TEST_P(UmpireTest, allocate) buff->describe(INT_ID, SIZE); buff->allocate(allocID); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -426,7 +430,7 @@ TEST_P(UmpireTest, allocate) Buffer* buff = ds.createBuffer(); buff->allocate(INT_ID, SIZE, allocID); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -434,7 +438,7 @@ TEST_P(UmpireTest, allocate) Buffer* buff = ds.createBuffer(INT_ID, SIZE); buff->allocate(allocID); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } } @@ -444,13 +448,14 @@ TEST_P(UmpireTest, allocate_default) constexpr int SIZE = 100; axom::setDefaultAllocator(allocID); + const int defaultHostAllocatorID = axom::detail::getDefaultHostAllocatorID(); { Buffer* buff = ds.createBuffer(); buff->describe(INT_ID, SIZE); buff->allocate(); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), defaultHostAllocatorID); buff->deallocate(); } @@ -458,7 +463,7 @@ TEST_P(UmpireTest, allocate_default) Buffer* buff = ds.createBuffer(); buff->allocate(INT_ID, SIZE); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), defaultHostAllocatorID); buff->deallocate(); } @@ -466,7 +471,7 @@ TEST_P(UmpireTest, allocate_default) Buffer* buff = ds.createBuffer(INT_ID, SIZE); buff->allocate(); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), defaultHostAllocatorID); buff->deallocate(); } } @@ -488,7 +493,7 @@ TEST_P(UmpireTest, reallocate) buff->allocate(allocID); buff->reallocate(2 * SIZE); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -498,7 +503,7 @@ TEST_P(UmpireTest, reallocate) buff->allocate(INT_ID, SIZE, allocID); buff->reallocate(2 * SIZE); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -507,7 +512,7 @@ TEST_P(UmpireTest, reallocate) buff->allocate(allocID); buff->reallocate(2 * SIZE); - ASSERT_EQ(allocID, rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } } @@ -533,7 +538,7 @@ TEST_P(UmpireTest, reallocate_zero) buff->reallocate(0); buff->reallocate(SIZE); - ASSERT_EQ(axom::getDefaultAllocatorID(), rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -544,7 +549,7 @@ TEST_P(UmpireTest, reallocate_zero) buff->reallocate(0); buff->reallocate(SIZE); - ASSERT_EQ(axom::getDefaultAllocatorID(), rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } @@ -554,7 +559,7 @@ TEST_P(UmpireTest, reallocate_zero) buff->reallocate(0); buff->reallocate(SIZE); - ASSERT_EQ(axom::getDefaultAllocatorID(), rm.getAllocator(buff->getVoidPtr()).getId()); + checkPointerAllocatorID(buff->getVoidPtr(), allocID); buff->deallocate(); } } diff --git a/src/axom/sidre/tests/sidre_buffer_unit.cpp b/src/axom/sidre/tests/sidre_buffer_unit.cpp index ee364820bb..209e3df337 100644 --- a/src/axom/sidre/tests/sidre_buffer_unit.cpp +++ b/src/axom/sidre/tests/sidre_buffer_unit.cpp @@ -21,6 +21,7 @@ using axom::sidre::View; using axom::sidre::getTypeID; #include +#include //------------------------------------------------------------------------------ @@ -92,6 +93,12 @@ void verifyAllocatedBuffer(Buffer* buf, DataTypeId tid, int eltsize, int eltcoun EXPECT_NE(static_cast(nullptr), buf->getVoidPtr()); } +void verifyBufferAllocatorID(Buffer* buf, int allocID) +{ + ASSERT_TRUE(buf->isAllocated()); + EXPECT_EQ(allocID, axom::getAllocatorIDFromPointer(buf->getVoidPtr())); +} + // Test describe methods TEST(sidre_buffer, buffer_describe) { @@ -282,6 +289,60 @@ TEST(sidre_buffer, buffer_allocate) delete ds; } +TEST(sidre_buffer, buffer_default_host_allocator) +{ + const DataTypeId tid = getTypeID(SIDRE_INT_ID); + constexpr int ELT_SIZE = sizeof(int); + constexpr int ELT_COUNT = 10; + const int defaultHostAllocatorID = axom::detail::getDefaultHostAllocatorID(); + + DataStore* ds = new DataStore(); + + Buffer* allocatedBuf = ds->createBuffer(tid, ELT_COUNT); + allocatedBuf->allocate(); + verifyAllocatedBuffer(allocatedBuf, tid, ELT_SIZE, ELT_COUNT); + verifyBufferAllocatorID(allocatedBuf, defaultHostAllocatorID); + + Buffer* reallocatedBuf = ds->createBuffer(tid, ELT_COUNT); + reallocatedBuf->reallocate(ELT_COUNT); + verifyAllocatedBuffer(reallocatedBuf, tid, ELT_SIZE, ELT_COUNT); + verifyBufferAllocatorID(reallocatedBuf, defaultHostAllocatorID); + + delete ds; +} + +TEST(sidre_buffer, buffer_reallocate_preserves_allocator) +{ + const DataTypeId tid = getTypeID(SIDRE_INT_ID); + constexpr int ELT_SIZE = sizeof(int); + constexpr int ELT_COUNT = 10; + + std::vector allocIds(1, axom::MALLOC_ALLOCATOR_ID); +#ifdef AXOM_USE_UMPIRE + allocIds.push_back(axom::getUmpireResourceAllocatorID(umpire::resource::Host)); +#endif + + DataStore* ds = new DataStore(); + + for(int allocID : allocIds) + { + Buffer* buf = ds->createBuffer(tid, ELT_COUNT); + buf->allocate(allocID); + verifyAllocatedBuffer(buf, tid, ELT_SIZE, ELT_COUNT); + verifyBufferAllocatorID(buf, allocID); + + buf->reallocate(0); + verifyAllocatedBuffer(buf, tid, ELT_SIZE, 0); + verifyBufferAllocatorID(buf, allocID); + + buf->reallocate(ELT_COUNT); + verifyAllocatedBuffer(buf, tid, ELT_SIZE, ELT_COUNT); + verifyBufferAllocatorID(buf, allocID); + } + + delete ds; +} + // Specifically test for successful reallocation-to-zero-elements TEST(sidre_databuffer, buffer_reallocate_zero_elements) { diff --git a/src/axom/sidre/tests/sidre_group.cpp b/src/axom/sidre/tests/sidre_group.cpp index d2dc061fe9..fafb45545a 100644 --- a/src/axom/sidre/tests/sidre_group.cpp +++ b/src/axom/sidre/tests/sidre_group.cpp @@ -4114,6 +4114,47 @@ TEST(sidre_group, get_data_info) delete ds; } +//------------------------------------------------------------------------------ +// Default allocator tests: +//------------------------------------------------------------------------------ + +TEST(sidre_group, default_allocator_getters_on_fresh_datastore) +{ + DataStore ds; + Group* root = ds.getRoot(); + const int defaultHostAllocatorID = axom::detail::getDefaultHostAllocatorID(); + + EXPECT_EQ(defaultHostAllocatorID, root->getDefaultArrayAllocatorID()); + EXPECT_EQ(defaultHostAllocatorID, root->getDefaultTupleAllocatorID()); + EXPECT_EQ(defaultHostAllocatorID, root->getDefaultAllocatorID()); +} + +TEST(sidre_group, default_allocator_getter_after_exec_space_id) +{ + DataStore ds; + Group* root = ds.getRoot(); + const int seqAllocID = axom::execution_space::allocatorID(); + root->setDefaultArrayAllocator(seqAllocID); + + EXPECT_EQ(seqAllocID, root->getDefaultArrayAllocatorID()); +} + +#ifdef AXOM_USE_UMPIRE +TEST(sidre_group, default_allocator_getters_with_umpire_id) +{ + auto& rm = umpire::ResourceManager::getInstance(); + const int hostId = rm.getAllocator(umpire::resource::Host).getId(); + + DataStore ds; + Group* root = ds.getRoot(); + root->setDefaultArrayAllocator(hostId); + root->setDefaultTupleAllocator(hostId); + + EXPECT_NO_THROW({ EXPECT_EQ(hostId, root->getDefaultArrayAllocator().getId()); }); + EXPECT_NO_THROW({ EXPECT_EQ(hostId, root->getDefaultTupleAllocator().getId()); }); +} +#endif + //------------------------------------------------------------------------------ #ifdef AXOM_USE_UMPIRE @@ -4140,14 +4181,63 @@ TEST_P(UmpireTest, root_default_allocator) axom::setDefaultAllocator(allocID); DataStore dsPrime; - ASSERT_EQ(dsPrime.getRoot()->getDefaultAllocatorID(), allocID); + ASSERT_EQ(dsPrime.getRoot()->getDefaultAllocatorID(), axom::detail::getDefaultHostAllocatorID()); +} + +TEST_P(UmpireTest, allocate_default_host_allocator) +{ + // In this test: + // + // allocID is an Umpire allocator ID used to change Umpire's default via + // axom::setDefaultAllocator(allocID). + // + // defaultHostAllocatorID is the configured Axom host default allocator + // + // Thus, a new Datastore root Group ignores the Umpire default and uses the + // configured host default instead. + // + // In general, allocID is not tied to the Axom configured default host allocator: + // + // - When AXOM_DEFAULT_HOST_ALLOCATOR=MALLOC, defaultHostAllocatorID == axom::MALLOC_ALLOCATOR_ID, + // and allocID is an Umpire allocator ID. + // - When AXOM_DEFAULT_HOST_ALLOCATOR=UMPIRE_HOST, they match only when allocID is Umpire Host. + // They differ for Pinned, Device, Unified, etc. in GPU-enabled builds + + axom::setDefaultAllocator(allocID); + + DataStore dsPrime; + Group* rootPrime = dsPrime.getRoot(); + const int defaultHostAllocatorID = axom::detail::getDefaultHostAllocatorID(); + + { + View* view = rootPrime->createViewAndAllocate("v", INT_ID, SIZE); + + ASSERT_EQ(defaultHostAllocatorID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); + rootPrime->destroyViewAndData("v"); + } + + { + IndexType shape[] = {1, SIZE, 1}; + View* view = rootPrime->createViewWithShapeAndAllocate("v", INT_ID, 3, shape); + + ASSERT_EQ(defaultHostAllocatorID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); + rootPrime->destroyViewAndData("v"); + } + + { + DataType dtype = conduit::DataType::default_dtype(INT_ID); + dtype.set_number_of_elements(SIZE); + View* view = rootPrime->createViewAndAllocate("v", dtype); + + ASSERT_EQ(defaultHostAllocatorID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); + rootPrime->destroyViewAndData("v"); + } } TEST_P(UmpireTest, get_set_allocator) { int defaultAllocatorID = axom::getDefaultAllocatorID(); - ASSERT_EQ(root->getDefaultAllocator().getId(), defaultAllocatorID); - ASSERT_EQ(root->getDefaultAllocatorID(), defaultAllocatorID); + ASSERT_EQ(root->getDefaultAllocatorID(), axom::detail::getDefaultHostAllocatorID()); root->setDefaultAllocator(allocID); defaultAllocatorID = axom::getDefaultAllocatorID(); diff --git a/src/axom/sidre/tests/sidre_view.cpp b/src/axom/sidre/tests/sidre_view.cpp index 6224ff3e3b..379aa608a2 100644 --- a/src/axom/sidre/tests/sidre_view.cpp +++ b/src/axom/sidre/tests/sidre_view.cpp @@ -2439,10 +2439,60 @@ TEST_P(UmpireTest, allocate_default) } } +//------------------------------------------------------------------------------ +TEST_P(UmpireTest, allocate_default_host_allocator) +{ + // In this test: + // + // allocID is an Umpire allocator ID used to change Umpire's default via + // axom::setDefaultAllocator(allocID). + // + // defaultHostAllocatorID is the configured Axom host default allocator + // + // Thus, a new Datastore root Group ignores the Umpire default and uses the + // configured host default instead. + // + // In general, allocID is not tied to the Axom configured default host allocator: + // + // - When AXOM_DEFAULT_HOST_ALLOCATOR=MALLOC, defaultHostAllocatorID == axom::MALLOC_ALLOCATOR_ID, + // and allocID is an Umpire allocator ID. + // - When AXOM_DEFAULT_HOST_ALLOCATOR=UMPIRE_HOST, they match only when allocID is Umpire Host. + // They differ for Pinned, Device, Unified, etc. in GPU-enabled builds + + axom::setDefaultAllocator(allocID); + + DataStore dsPrime; + Group* rootPrime = dsPrime.getRoot(); + + const int defaultHostAllocatorID = axom::detail::getDefaultHostAllocatorID(); + + { + View* view = rootPrime->createView("v"); + view->allocate(INT_ID, SIZE); + + ASSERT_EQ(defaultHostAllocatorID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); + rootPrime->destroyViewAndData("v"); + } + + { + View* view = rootPrime->createView("v"); + DataType dtype = conduit::DataType::default_dtype(INT_ID); + dtype.set_number_of_elements(SIZE); + view->allocate(dtype); + + ASSERT_EQ(defaultHostAllocatorID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); + rootPrime->destroyViewAndData("v"); + } +} + //------------------------------------------------------------------------------ TEST_P(UmpireTest, reallocate) { #if defined(AXOM_USE_GPU) && defined(UMPIRE_ENABLE_CONST) + // Constant memory is meant for read-only GPU constant storage and generally + // cannot support the same runtime reallocation/write semantics as ordinary memory. + // So we return early and skip the test in this case instead of asserting + // behavior that Sidre cannot promise. if(allocID == axom::getUmpireResourceAllocatorID(umpire::resource::Constant)) { return; @@ -2488,7 +2538,7 @@ TEST_P(UmpireTest, reallocate_zero) view->reallocate(0); view->reallocate(SIZE); - ASSERT_EQ(axom::getDefaultAllocatorID(), rm.getAllocator(view->getVoidPtr()).getId()); + ASSERT_EQ(allocID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); root->destroyViewAndData("v"); } @@ -2501,7 +2551,7 @@ TEST_P(UmpireTest, reallocate_zero) view->reallocate(0); view->reallocate(SIZE); - ASSERT_EQ(axom::getDefaultAllocatorID(), rm.getAllocator(view->getVoidPtr()).getId()); + ASSERT_EQ(allocID, axom::getAllocatorIDFromPointer(view->getVoidPtr())); root->destroyViewAndData("v"); } diff --git a/src/cmake/AxomConfig.cmake b/src/cmake/AxomConfig.cmake index fea39381cb..ec9e264346 100644 --- a/src/cmake/AxomConfig.cmake +++ b/src/cmake/AxomConfig.cmake @@ -63,6 +63,23 @@ foreach(option MPI3) endif() endforeach() +string(TOUPPER "${AXOM_DEFAULT_HOST_ALLOCATOR}" _axom_default_host_allocator_upper) +if(_axom_default_host_allocator_upper STREQUAL "MALLOC") + set(AXOM_DEFAULT_HOST_ALLOCATOR "MALLOC" CACHE STRING "Default host allocator: MALLOC or UMPIRE_HOST" FORCE) + unset(AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST) +elseif(_axom_default_host_allocator_upper STREQUAL "UMPIRE_HOST") + if(NOT AXOM_USE_UMPIRE) + message(FATAL_ERROR + "AXOM_DEFAULT_HOST_ALLOCATOR=UMPIRE_HOST requires Axom to be configured with Umpire.") + endif() + set(AXOM_DEFAULT_HOST_ALLOCATOR "UMPIRE_HOST" CACHE STRING "Default host allocator: MALLOC or UMPIRE_HOST" FORCE) + set(AXOM_DEFAULT_HOST_ALLOCATOR_USES_UMPIRE_HOST TRUE) +else() + message(FATAL_ERROR + "Invalid value for AXOM_DEFAULT_HOST_ALLOCATOR. Must be 'MALLOC' or 'UMPIRE_HOST'; " + "was '${AXOM_DEFAULT_HOST_ALLOCATOR}'") +endif() + # Handle paths convert_to_native_escaped_file_path(${PROJECT_SOURCE_DIR} AXOM_SRC_DIR_NATIVE) convert_to_native_escaped_file_path(${PROJECT_BINARY_DIR} AXOM_BIN_DIR_NATIVE) diff --git a/src/cmake/AxomOptions.cmake b/src/cmake/AxomOptions.cmake index 3be6be1b51..0d2d30b8af 100644 --- a/src/cmake/AxomOptions.cmake +++ b/src/cmake/AxomOptions.cmake @@ -25,6 +25,8 @@ option(AXOM_ENABLE_SPARSEHASH "Enables Sparsehash." ON) option(AXOM_ENABLE_ALL_COMPONENTS "Enables all components by default" ON) option(AXOM_USE_64BIT_INDEXTYPE "Use 64-bit integers for axom::IndexType" ON) +set(AXOM_DEFAULT_HOST_ALLOCATOR "MALLOC" CACHE STRING "Default host allocator: MALLOC or UMPIRE_HOST") +set_property(CACHE AXOM_DEFAULT_HOST_ALLOCATOR PROPERTY STRINGS "MALLOC" "UMPIRE_HOST") # When enabled (default), Sidre will serialize tuple views of size 1 with state="SCALAR" # in its I/O metadata for compatibility with downstream readers (e.g. VisIt's Blueprint database plugin). diff --git a/src/cmake/axom-config.cmake.in b/src/cmake/axom-config.cmake.in index 66987f6603..4d9e0cb399 100644 --- a/src/cmake/axom-config.cmake.in +++ b/src/cmake/axom-config.cmake.in @@ -77,6 +77,7 @@ if(NOT AXOM_FOUND) # Configuration for Axom compiler defines set(AXOM_DEBUG_DEFINE "@AXOM_DEBUG_DEFINE@") set(AXOM_DEBUG_DEFINE_STRING "@AXOM_DEBUG_DEFINE_STRING@") + set(AXOM_DEFAULT_HOST_ALLOCATOR "@AXOM_DEFAULT_HOST_ALLOCATOR@") # Component-specific options set(AXOM_SIDRE_IO_USE_SCALAR_STATE_STRING "@AXOM_SIDRE_IO_USE_SCALAR_STATE_STRING@") diff --git a/src/docs/sphinx/quickstart_guide/config_build.rst b/src/docs/sphinx/quickstart_guide/config_build.rst index 87e1c27b20..6b00b982e9 100644 --- a/src/docs/sphinx/quickstart_guide/config_build.rst +++ b/src/docs/sphinx/quickstart_guide/config_build.rst @@ -409,6 +409,11 @@ Axom build options, compiler support, and parallelism | | | but this can be overridden by setting | | | | `AXOM_DEBUG_DEFINE` to `ON` or `OFF` | +------------------------------+---------+----------------------------------------+ +| AXOM_DEFAULT_HOST_ALLOCATOR | MALLOC | Controls the allocator used by default | +| | | for host execution spaces. Valid | +| | | values are `MALLOC` and `UMPIRE_HOST`. | +| | | `UMPIRE_HOST` requires Umpire. | ++------------------------------+---------+----------------------------------------+ | ENABLE_ALL_WARNINGS | ON | Enable extra compiler warnings | | | | in all build targets | +------------------------------+---------+----------------------------------------+