From ce3af2c46b8b6466b240ba5529b48f0ad5b6e0b7 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 14 Feb 2024 16:32:18 -0800 Subject: [PATCH] Remove duplicated memory_resource_tests (#1451) During the initial introduction of `async_resource_ref` we duplicated all tests that take a `device_memory_resource*`. Now that we have already some experience with running it in production and are moving more of the interfaces to `async resource_ref` remove those duplicated tests. closes #1450 Authors: - Michael Schellenberger Costa (https://github.com/miscco) Approvers: - Mark Harris (https://github.com/harrism) - Bradley Dice (https://github.com/bdice) URL: https://github.com/rapidsai/rmm/pull/1451 --- tests/CMakeLists.txt | 7 - tests/mr/device/mr_multithreaded_tests.cpp | 286 ------------------ .../mr/device/mr_ref_multithreaded_tests.cpp | 70 +++++ tests/mr/device/mr_ref_test.hpp | 20 ++ tests/mr/device/mr_ref_tests.cpp | 37 +++ tests/mr/device/mr_test.hpp | 285 ----------------- tests/mr/device/mr_tests.cpp | 129 -------- tests/mr/device/thrust_allocator_tests.cu | 4 +- tests/mr/host/mr_tests.cpp | 256 ---------------- 9 files changed, 129 insertions(+), 965 deletions(-) delete mode 100644 tests/mr/device/mr_multithreaded_tests.cpp delete mode 100644 tests/mr/device/mr_test.hpp delete mode 100644 tests/mr/device/mr_tests.cpp delete mode 100644 tests/mr/host/mr_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a3d493e40..0d0561098 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -122,10 +122,6 @@ endfunction() # test sources -# device mr tests -ConfigureTest(DEVICE_MR_TEST mr/device/mr_tests.cpp mr/device/mr_multithreaded_tests.cpp GPUS 1 - PERCENT 90) - # device mr_ref tests ConfigureTest(DEVICE_MR_REF_TEST mr/device/mr_ref_tests.cpp mr/device/mr_ref_multithreaded_tests.cpp GPUS 1 PERCENT 100) @@ -163,9 +159,6 @@ ConfigureTest(ALIGNED_TEST mr/device/aligned_mr_tests.cpp) # limiting adaptor tests ConfigureTest(LIMITING_TEST mr/device/limiting_mr_tests.cpp) -# host mr tests -ConfigureTest(HOST_MR_TEST mr/host/mr_tests.cpp) - # host mr_ref tests ConfigureTest(HOST_MR_REF_TEST mr/host/mr_ref_tests.cpp) diff --git a/tests/mr/device/mr_multithreaded_tests.cpp b/tests/mr/device/mr_multithreaded_tests.cpp deleted file mode 100644 index 113b59e8f..000000000 --- a/tests/mr/device/mr_multithreaded_tests.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (c) 2020-2021, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mr_test.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace rmm::test { -namespace { - -struct mr_test_mt : public mr_test {}; - -INSTANTIATE_TEST_CASE_P(MultiThreadResourceTests, - mr_test_mt, - ::testing::Values(mr_factory{"CUDA", &make_cuda}, -#ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT - mr_factory{"CUDA_Async", &make_cuda_async}, -#endif - mr_factory{"Managed", &make_managed}, - mr_factory{"Pool", &make_pool}, - mr_factory{"Arena", &make_arena}, - mr_factory{"Binning", &make_binning}), - [](auto const& info) { return info.param.name; }); - -template -void spawn_n(std::size_t num_threads, Task task, Arguments&&... args) -{ - std::vector threads; - threads.reserve(num_threads); - for (std::size_t i = 0; i < num_threads; ++i) { - threads.emplace_back(std::thread(task, std::forward(args)...)); - } - - for (auto& thread : threads) { - thread.join(); - } -} - -template -void spawn(Task task, Arguments&&... args) -{ - spawn_n(4, task, std::forward(args)...); -} - -TEST(DefaultTest, UseCurrentDeviceResource_mt) { spawn(test_get_current_device_resource); } - -TEST(DefaultTest, CurrentDeviceResourceIsCUDA_mt) -{ - spawn([]() { - EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); - EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); - }); -} - -TEST(DefaultTest, GetCurrentDeviceResource_mt) -{ - spawn([]() { - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource(); - EXPECT_NE(nullptr, mr); - EXPECT_TRUE(mr->is_equal(rmm::mr::cuda_memory_resource{})); - }); -} - -TEST_P(mr_test_mt, SetCurrentDeviceResource_mt) -{ - // single thread changes default resource, then multiple threads use it - - rmm::mr::device_memory_resource* old = rmm::mr::set_current_device_resource(this->mr.get()); - EXPECT_NE(nullptr, old); - - spawn([mr = this->mr.get()]() { - EXPECT_EQ(mr, rmm::mr::get_current_device_resource()); - test_get_current_device_resource(); // test allocating with the new default resource - }); - - // setting default resource w/ nullptr should reset to initial - rmm::mr::set_current_device_resource(nullptr); - EXPECT_TRUE(old->is_equal(*rmm::mr::get_current_device_resource())); -} - -TEST_P(mr_test_mt, SetCurrentDeviceResourcePerThread_mt) -{ - int num_devices{}; - RMM_CUDA_TRY(cudaGetDeviceCount(&num_devices)); - - std::vector threads; - threads.reserve(num_devices); - for (int i = 0; i < num_devices; ++i) { - threads.emplace_back(std::thread{[mr = this->mr.get()](auto dev_id) { - RMM_CUDA_TRY(cudaSetDevice(dev_id)); - rmm::mr::device_memory_resource* old = - rmm::mr::set_current_device_resource(mr); - EXPECT_NE(nullptr, old); - // initial resource for this device should be CUDA mr - EXPECT_TRUE(old->is_equal(rmm::mr::cuda_memory_resource{})); - // get_current_device_resource should equal the resource we - // just set - EXPECT_EQ(mr, rmm::mr::get_current_device_resource()); - // Setting current dev resource to nullptr should reset to - // cuda MR and return the MR we previously set - old = rmm::mr::set_current_device_resource(nullptr); - EXPECT_NE(nullptr, old); - EXPECT_EQ(old, mr); - EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal( - rmm::mr::cuda_memory_resource{})); - }, - i}); - } - - for (auto& thread : threads) { - thread.join(); - } -} - -TEST_P(mr_test_mt, AllocateDefaultStream) -{ - spawn(test_various_allocations, this->mr.get(), rmm::cuda_stream_view{}); -} - -TEST_P(mr_test_mt, AllocateOnStream) -{ - spawn(test_various_allocations, this->mr.get(), this->stream.view()); -} - -TEST_P(mr_test_mt, RandomAllocationsDefaultStream) -{ - spawn(test_random_allocations, - this->mr.get(), - default_num_allocations, - default_max_size, - rmm::cuda_stream_view{}); -} - -TEST_P(mr_test_mt, RandomAllocationsStream) -{ - spawn(test_random_allocations, - this->mr.get(), - default_num_allocations, - default_max_size, - this->stream.view()); -} - -TEST_P(mr_test_mt, MixedRandomAllocationFreeDefaultStream) -{ - spawn( - test_mixed_random_allocation_free, this->mr.get(), default_max_size, rmm::cuda_stream_view{}); -} - -TEST_P(mr_test_mt, MixedRandomAllocationFreeStream) -{ - spawn(test_mixed_random_allocation_free, this->mr.get(), default_max_size, this->stream.view()); -} - -void allocate_loop(rmm::mr::device_memory_resource* mr, - std::size_t num_allocations, - std::list& allocations, - std::mutex& mtx, - std::condition_variable& allocations_ready, - cudaEvent_t& event, - rmm::cuda_stream_view stream) -{ - constexpr std::size_t max_size{1_MiB}; - - std::default_random_engine generator; - std::uniform_int_distribution size_distribution(1, max_size); - - for (std::size_t i = 0; i < num_allocations; ++i) { - std::size_t size = size_distribution(generator); - void* ptr = mr->allocate(size, stream); - { - std::lock_guard lock(mtx); - RMM_CUDA_TRY(cudaEventRecord(event, stream.value())); - allocations.emplace_back(ptr, size); - } - allocations_ready.notify_one(); - } - // Work around for threads going away before cudaEvent has finished async processing - cudaEventSynchronize(event); -} - -void deallocate_loop(rmm::mr::device_memory_resource* mr, - std::size_t num_allocations, - std::list& allocations, - std::mutex& mtx, - std::condition_variable& allocations_ready, - cudaEvent_t& event, - rmm::cuda_stream_view stream) -{ - for (std::size_t i = 0; i < num_allocations; i++) { - std::unique_lock lock(mtx); - allocations_ready.wait(lock, [&allocations] { return !allocations.empty(); }); - RMM_CUDA_TRY(cudaStreamWaitEvent(stream.value(), event)); - allocation alloc = allocations.front(); - allocations.pop_front(); - mr->deallocate(alloc.ptr, alloc.size, stream); - } - - // Work around for threads going away before cudaEvent has finished async processing - cudaEventSynchronize(event); -} -void test_allocate_free_different_threads(rmm::mr::device_memory_resource* mr, - rmm::cuda_stream_view streamA, - rmm::cuda_stream_view streamB) -{ - constexpr std::size_t num_allocations{100}; - - std::mutex mtx; - std::condition_variable allocations_ready; - std::list allocations; - cudaEvent_t event; - - RMM_CUDA_TRY(cudaEventCreate(&event)); - - std::thread producer(allocate_loop, - mr, - num_allocations, - std::ref(allocations), - std::ref(mtx), - std::ref(allocations_ready), - std::ref(event), - streamA); - - std::thread consumer(deallocate_loop, - mr, - num_allocations, - std::ref(allocations), - std::ref(mtx), - std::ref(allocations_ready), - std::ref(event), - streamB); - - producer.join(); - consumer.join(); - - RMM_CUDA_TRY(cudaEventDestroy(event)); -} - -TEST_P(mr_test_mt, AllocFreeDifferentThreadsDefaultStream) -{ - test_allocate_free_different_threads( - this->mr.get(), rmm::cuda_stream_default, rmm::cuda_stream_default); -} - -TEST_P(mr_test_mt, AllocFreeDifferentThreadsPerThreadDefaultStream) -{ - test_allocate_free_different_threads( - this->mr.get(), rmm::cuda_stream_per_thread, rmm::cuda_stream_per_thread); -} - -TEST_P(mr_test_mt, AllocFreeDifferentThreadsSameStream) -{ - test_allocate_free_different_threads(this->mr.get(), this->stream, this->stream); -} - -TEST_P(mr_test_mt, AllocFreeDifferentThreadsDifferentStream) -{ - rmm::cuda_stream streamB; - test_allocate_free_different_threads(this->mr.get(), this->stream, streamB); - streamB.synchronize(); -} - -} // namespace -} // namespace rmm::test diff --git a/tests/mr/device/mr_ref_multithreaded_tests.cpp b/tests/mr/device/mr_ref_multithreaded_tests.cpp index 48d642a32..352a9fa16 100644 --- a/tests/mr/device/mr_ref_multithreaded_tests.cpp +++ b/tests/mr/device/mr_ref_multithreaded_tests.cpp @@ -67,6 +67,76 @@ void spawn(Task task, Arguments&&... args) spawn_n(4, task, std::forward(args)...); } +TEST(DefaultTest, UseCurrentDeviceResource_mt) { spawn(test_get_current_device_resource); } + +TEST(DefaultTest, CurrentDeviceResourceIsCUDA_mt) +{ + spawn([]() { + EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); + EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); + }); +} + +TEST(DefaultTest, GetCurrentDeviceResource_mt) +{ + spawn([]() { + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource(); + EXPECT_NE(nullptr, mr); + EXPECT_TRUE(mr->is_equal(rmm::mr::cuda_memory_resource{})); + }); +} + +TEST_P(mr_ref_test_mt, SetCurrentDeviceResource_mt) +{ + // single thread changes default resource, then multiple threads use it + + rmm::mr::device_memory_resource* old = rmm::mr::set_current_device_resource(this->mr.get()); + EXPECT_NE(nullptr, old); + + spawn([mr = this->mr.get()]() { + EXPECT_EQ(mr, rmm::mr::get_current_device_resource()); + test_get_current_device_resource(); // test allocating with the new default resource + }); + + // setting default resource w/ nullptr should reset to initial + rmm::mr::set_current_device_resource(nullptr); + EXPECT_TRUE(old->is_equal(*rmm::mr::get_current_device_resource())); +} + +TEST_P(mr_ref_test_mt, SetCurrentDeviceResourcePerThread_mt) +{ + int num_devices{}; + RMM_CUDA_TRY(cudaGetDeviceCount(&num_devices)); + + std::vector threads; + threads.reserve(num_devices); + for (int i = 0; i < num_devices; ++i) { + threads.emplace_back(std::thread{[mr = this->mr.get()](auto dev_id) { + RMM_CUDA_TRY(cudaSetDevice(dev_id)); + rmm::mr::device_memory_resource* old = + rmm::mr::set_current_device_resource(mr); + EXPECT_NE(nullptr, old); + // initial resource for this device should be CUDA mr + EXPECT_TRUE(old->is_equal(rmm::mr::cuda_memory_resource{})); + // get_current_device_resource should equal the resource we + // just set + EXPECT_EQ(mr, rmm::mr::get_current_device_resource()); + // Setting current dev resource to nullptr should reset to + // cuda MR and return the MR we previously set + old = rmm::mr::set_current_device_resource(nullptr); + EXPECT_NE(nullptr, old); + EXPECT_EQ(old, mr); + EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal( + rmm::mr::cuda_memory_resource{})); + }, + i}); + } + + for (auto& thread : threads) { + thread.join(); + } +} + TEST_P(mr_ref_test_mt, Allocate) { spawn(test_various_allocations, this->ref); } TEST_P(mr_ref_test_mt, AllocateDefaultStream) diff --git a/tests/mr/device/mr_ref_test.hpp b/tests/mr/device/mr_ref_test.hpp index f999e08f4..0beea8656 100644 --- a/tests/mr/device/mr_ref_test.hpp +++ b/tests/mr/device/mr_ref_test.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,17 @@ struct allocation { }; // Various test functions, shared between single-threaded and multithreaded tests. + +inline void test_get_current_device_resource() +{ + EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); + void* ptr = rmm::mr::get_current_device_resource()->allocate(1_MiB); + EXPECT_NE(nullptr, ptr); + EXPECT_TRUE(is_properly_aligned(ptr)); + EXPECT_TRUE(is_device_accessible_memory(ptr)); + rmm::mr::get_current_device_resource()->deallocate(ptr, 1_MiB); +} + inline void test_allocate(resource_ref ref, std::size_t bytes) { try { @@ -357,6 +369,8 @@ struct mr_ref_allocation_test : public mr_ref_test {}; /// MR factory functions inline auto make_cuda() { return std::make_shared(); } +inline auto make_host_pinned() { return std::make_shared(); } + inline auto make_cuda_async() { if (rmm::detail::async_alloc::is_supported()) { @@ -373,6 +387,12 @@ inline auto make_pool() make_cuda(), rmm::percent_of_free_device_memory(50)); } +inline auto make_host_pinned_pool() +{ + return rmm::mr::make_owning_wrapper( + make_host_pinned(), 2_GiB, 8_GiB); +} + inline auto make_arena() { return rmm::mr::make_owning_wrapper(make_cuda()); diff --git a/tests/mr/device/mr_ref_tests.cpp b/tests/mr/device/mr_ref_tests.cpp index a9a94696a..c7c37d4cc 100644 --- a/tests/mr/device/mr_ref_tests.cpp +++ b/tests/mr/device/mr_ref_tests.cpp @@ -33,6 +33,7 @@ INSTANTIATE_TEST_SUITE_P(ResourceTests, #endif mr_factory{"Managed", &make_managed}, mr_factory{"Pool", &make_pool}, + mr_factory{"HostPinnedPool", &make_host_pinned_pool}, mr_factory{"Arena", &make_arena}, mr_factory{"Binning", &make_binning}, mr_factory{"Fixed_Size", &make_fixed_size}), @@ -47,9 +48,45 @@ INSTANTIATE_TEST_SUITE_P(ResourceAllocationTests, #endif mr_factory{"Managed", &make_managed}, mr_factory{"Pool", &make_pool}, + mr_factory{"HostPinnedPool", &make_host_pinned_pool}, mr_factory{"Arena", &make_arena}, mr_factory{"Binning", &make_binning}), [](auto const& info) { return info.param.name; }); + +TEST(DefaultTest, CurrentDeviceResourceIsCUDA) +{ + EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); + EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); +} + +TEST(DefaultTest, UseCurrentDeviceResource) { test_get_current_device_resource(); } + +TEST(DefaultTest, GetCurrentDeviceResource) +{ + auto* mr = rmm::mr::get_current_device_resource(); + EXPECT_NE(nullptr, mr); + EXPECT_TRUE(mr->is_equal(rmm::mr::cuda_memory_resource{})); +} + +TEST_P(mr_ref_test, SetCurrentDeviceResource) +{ + rmm::mr::device_memory_resource* old{}; + old = rmm::mr::set_current_device_resource(this->mr.get()); + EXPECT_NE(nullptr, old); + + // old mr should equal a cuda mr + EXPECT_TRUE(old->is_equal(rmm::mr::cuda_memory_resource{})); + + // current dev resource should equal this resource + EXPECT_TRUE(this->mr->is_equal(*rmm::mr::get_current_device_resource())); + + test_get_current_device_resource(); + + // setting to `nullptr` should reset to initial cuda resource + rmm::mr::set_current_device_resource(nullptr); + EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); +} + TEST_P(mr_ref_test, SelfEquality) { EXPECT_TRUE(this->ref == this->ref); } // Simple reproducer for https://github.com/rapidsai/rmm/issues/861 diff --git a/tests/mr/device/mr_test.hpp b/tests/mr/device/mr_test.hpp deleted file mode 100644 index 3808ec6f3..000000000 --- a/tests/mr/device/mr_test.hpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2019-2024, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../../byte_literals.hpp" -#include "test_utils.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace rmm::test { - -enum size_in_bytes : size_t {}; - -constexpr auto default_num_allocations{100}; -constexpr size_in_bytes default_max_size{5_MiB}; - -struct allocation { - void* ptr{nullptr}; - std::size_t size{0}; - allocation(void* ptr, std::size_t size) : ptr{ptr}, size{size} {} - allocation() = default; -}; - -// Various test functions, shared between single-threaded and multithreaded tests. - -inline void test_get_current_device_resource() -{ - EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); - void* ptr = rmm::mr::get_current_device_resource()->allocate(1_MiB); - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_properly_aligned(ptr)); - EXPECT_TRUE(is_device_accessible_memory(ptr)); - rmm::mr::get_current_device_resource()->deallocate(ptr, 1_MiB); -} - -inline void test_allocate(rmm::mr::device_memory_resource* mr, - std::size_t bytes, - cuda_stream_view stream = {}) -{ - void* ptr = mr->allocate(bytes); - if (not stream.is_default()) { stream.synchronize(); } - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_properly_aligned(ptr)); - EXPECT_TRUE(is_device_accessible_memory(ptr)); - mr->deallocate(ptr, bytes); - if (not stream.is_default()) { stream.synchronize(); } -} - -// Simple reproducer for https://github.com/rapidsai/rmm/issues/861 -inline void concurrent_allocations_are_different(rmm::mr::device_memory_resource* mr, - cuda_stream_view stream) -{ - const auto size{8_B}; - void* ptr1 = mr->allocate(size, stream); - void* ptr2 = mr->allocate(size, stream); - - EXPECT_NE(ptr1, ptr2); - - mr->deallocate(ptr1, size, stream); - mr->deallocate(ptr2, size, stream); -} - -inline void test_various_allocations(rmm::mr::device_memory_resource* mr, cuda_stream_view stream) -{ - // test allocating zero bytes on non-default stream - { - void* ptr = mr->allocate(0, stream); - stream.synchronize(); - EXPECT_NO_THROW(mr->deallocate(ptr, 0, stream)); - stream.synchronize(); - } - - test_allocate(mr, 4_B, stream); - test_allocate(mr, 1_KiB, stream); - test_allocate(mr, 1_MiB, stream); - test_allocate(mr, 1_GiB, stream); - - // should fail to allocate too much - { - void* ptr{nullptr}; - EXPECT_THROW(ptr = mr->allocate(1_PiB, stream), rmm::out_of_memory); - EXPECT_EQ(nullptr, ptr); - - // test e.what(); - try { - ptr = mr->allocate(1_PiB, stream); - } catch (rmm::out_of_memory const& e) { - EXPECT_NE(std::string{e.what()}.find("out_of_memory"), std::string::npos); - } - } -} - -inline void test_random_allocations(rmm::mr::device_memory_resource* mr, - std::size_t num_allocations = default_num_allocations, - size_in_bytes max_size = default_max_size, - cuda_stream_view stream = {}) -{ - std::vector allocations(num_allocations); - - std::default_random_engine generator; - std::uniform_int_distribution distribution(1, max_size); - - // num_allocations allocations from [0,max_size) - std::for_each(allocations.begin(), - allocations.end(), - [&generator, &distribution, stream, mr](allocation& alloc) { - alloc.size = distribution(generator); - EXPECT_NO_THROW(alloc.ptr = mr->allocate(alloc.size, stream)); - if (not stream.is_default()) { stream.synchronize(); } - EXPECT_NE(nullptr, alloc.ptr); - EXPECT_TRUE(is_properly_aligned(alloc.ptr)); - }); - - std::for_each(allocations.begin(), allocations.end(), [stream, mr](allocation& alloc) { - EXPECT_NO_THROW(mr->deallocate(alloc.ptr, alloc.size, stream)); - if (not stream.is_default()) { stream.synchronize(); } - }); -} - -inline void test_mixed_random_allocation_free(rmm::mr::device_memory_resource* mr, - size_in_bytes max_size = default_max_size, - cuda_stream_view stream = {}) -{ - std::default_random_engine generator; - constexpr std::size_t num_allocations{100}; - - std::uniform_int_distribution size_distribution(1, max_size); - - constexpr int allocation_probability{53}; // percent - constexpr int max_probability{99}; - std::uniform_int_distribution op_distribution(0, max_probability); - std::uniform_int_distribution index_distribution(0, num_allocations - 1); - - std::size_t active_allocations{0}; - std::size_t allocation_count{0}; - - std::vector allocations; - - for (std::size_t i = 0; i < num_allocations * 2; ++i) { - bool do_alloc = true; - if (active_allocations > 0) { - int chance = op_distribution(generator); - do_alloc = (chance < allocation_probability) && (allocation_count < num_allocations); - } - - if (do_alloc) { - std::size_t size = size_distribution(generator); - active_allocations++; - allocation_count++; - EXPECT_NO_THROW(allocations.emplace_back(mr->allocate(size, stream), size)); - auto new_allocation = allocations.back(); - EXPECT_NE(nullptr, new_allocation.ptr); - EXPECT_TRUE(is_properly_aligned(new_allocation.ptr)); - } else { - auto const index = static_cast(index_distribution(generator) % active_allocations); - active_allocations--; - allocation to_free = allocations[index]; - allocations.erase(std::next(allocations.begin(), index)); - EXPECT_NO_THROW(mr->deallocate(to_free.ptr, to_free.size, stream)); - } - } - - EXPECT_EQ(active_allocations, 0); - EXPECT_EQ(allocations.size(), active_allocations); -} - -using MRFactoryFunc = std::function()>; - -/// Encapsulates a `device_memory_resource` factory function and associated name -struct mr_factory { - mr_factory(std::string name, MRFactoryFunc factory) - : name{std::move(name)}, factory{std::move(factory)} - { - } - - std::string name; ///< Name to associate with tests that use this factory - MRFactoryFunc factory; ///< Factory function that returns shared_ptr to `device_memory_resource` - ///< instance to use in test -}; - -/// Test fixture class value-parameterized on different `mr_factory`s -struct mr_test : public ::testing::TestWithParam { - void SetUp() override - { - auto factory = GetParam().factory; - mr = factory(); - if (mr == nullptr) { - GTEST_SKIP() << "Skipping tests since the memory resource is not supported with this CUDA " - << "driver/runtime version"; - } - } - - std::shared_ptr mr; ///< Pointer to resource to use in tests - rmm::cuda_stream stream{}; -}; - -struct mr_allocation_test : public mr_test {}; - -/// MR factory functions -inline auto make_cuda() { return std::make_shared(); } - -inline auto make_host_pinned() { return std::make_shared(); } - -inline auto make_cuda_async() -{ - if (rmm::detail::async_alloc::is_supported()) { - return std::make_shared(); - } - return std::shared_ptr{nullptr}; -} - -inline auto make_managed() { return std::make_shared(); } - -inline auto make_pool() -{ - return rmm::mr::make_owning_wrapper( - make_cuda(), rmm::percent_of_free_device_memory(50)); -} - -inline auto make_host_pinned_pool() -{ - return rmm::mr::make_owning_wrapper( - make_host_pinned(), 2_GiB, 8_GiB); -} - -inline auto make_arena() -{ - return rmm::mr::make_owning_wrapper(make_cuda()); -} - -inline auto make_fixed_size() -{ - return rmm::mr::make_owning_wrapper(make_cuda()); -} - -inline auto make_binning() -{ - auto pool = make_pool(); - // Add a binning_memory_resource with fixed-size bins of sizes 256, 512, 1024, 2048 and 4096KiB - // Larger allocations will use the pool resource - auto const bin_range_start{18}; - auto const bin_range_end{22}; - - auto mr = rmm::mr::make_owning_wrapper( - pool, bin_range_start, bin_range_end); - return mr; -} - -} // namespace rmm::test diff --git a/tests/mr/device/mr_tests.cpp b/tests/mr/device/mr_tests.cpp deleted file mode 100644 index 5e496d2ee..000000000 --- a/tests/mr/device/mr_tests.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mr_test.hpp" - -#include - -#include - -namespace rmm::test { -namespace { - -INSTANTIATE_TEST_SUITE_P(ResourceTests, - mr_test, - ::testing::Values(mr_factory{"CUDA", &make_cuda}, -#ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT - mr_factory{"CUDA_Async", &make_cuda_async}, -#endif - mr_factory{"Managed", &make_managed}, - mr_factory{"Pool", &make_pool}, - mr_factory{"HostPinnedPool", &make_host_pinned_pool}, - mr_factory{"Arena", &make_arena}, - mr_factory{"Binning", &make_binning}, - mr_factory{"Fixed_Size", &make_fixed_size}), - [](auto const& info) { return info.param.name; }); - -// Leave out fixed-size MR here because it can't handle the dynamic allocation sizes -INSTANTIATE_TEST_SUITE_P(ResourceAllocationTests, - mr_allocation_test, - ::testing::Values(mr_factory{"CUDA", &make_cuda}, -#ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT - mr_factory{"CUDA_Async", &make_cuda_async}, -#endif - mr_factory{"Managed", &make_managed}, - mr_factory{"Pool", &make_pool}, - mr_factory{"HostPinnedPool", &make_host_pinned_pool}, - mr_factory{"Arena", &make_arena}, - mr_factory{"Binning", &make_binning}), - [](auto const& info) { return info.param.name; }); - -TEST(DefaultTest, CurrentDeviceResourceIsCUDA) -{ - EXPECT_NE(nullptr, rmm::mr::get_current_device_resource()); - EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); -} - -TEST(DefaultTest, UseCurrentDeviceResource) { test_get_current_device_resource(); } - -TEST(DefaultTest, GetCurrentDeviceResource) -{ - auto* mr = rmm::mr::get_current_device_resource(); - EXPECT_NE(nullptr, mr); - EXPECT_TRUE(mr->is_equal(rmm::mr::cuda_memory_resource{})); -} - -TEST_P(mr_test, SetCurrentDeviceResource) -{ - rmm::mr::device_memory_resource* old{}; - old = rmm::mr::set_current_device_resource(this->mr.get()); - EXPECT_NE(nullptr, old); - - // old mr should equal a cuda mr - EXPECT_TRUE(old->is_equal(rmm::mr::cuda_memory_resource{})); - - // current dev resource should equal this resource - EXPECT_TRUE(this->mr->is_equal(*rmm::mr::get_current_device_resource())); - - test_get_current_device_resource(); - - // setting to `nullptr` should reset to initial cuda resource - rmm::mr::set_current_device_resource(nullptr); - EXPECT_TRUE(rmm::mr::get_current_device_resource()->is_equal(rmm::mr::cuda_memory_resource{})); -} - -TEST_P(mr_test, SelfEquality) { EXPECT_TRUE(this->mr->is_equal(*this->mr)); } - -// Simple reproducer for https://github.com/rapidsai/rmm/issues/861 -TEST_P(mr_test, AllocationsAreDifferentDefaultStream) -{ - concurrent_allocations_are_different(this->mr.get(), cuda_stream_view{}); -} - -TEST_P(mr_test, AllocationsAreDifferent) -{ - concurrent_allocations_are_different(this->mr.get(), this->stream); -} - -TEST_P(mr_allocation_test, AllocateDefaultStream) -{ - test_various_allocations(this->mr.get(), cuda_stream_view{}); -} - -TEST_P(mr_allocation_test, AllocateOnStream) -{ - test_various_allocations(this->mr.get(), this->stream); -} - -TEST_P(mr_allocation_test, RandomAllocations) { test_random_allocations(this->mr.get()); } - -TEST_P(mr_allocation_test, RandomAllocationsStream) -{ - test_random_allocations(this->mr.get(), default_num_allocations, default_max_size, this->stream); -} - -TEST_P(mr_allocation_test, MixedRandomAllocationFree) -{ - test_mixed_random_allocation_free(this->mr.get(), default_max_size, cuda_stream_view{}); -} - -TEST_P(mr_allocation_test, MixedRandomAllocationFreeStream) -{ - test_mixed_random_allocation_free(this->mr.get(), default_max_size, this->stream); -} - -} // namespace -} // namespace rmm::test diff --git a/tests/mr/device/thrust_allocator_tests.cu b/tests/mr/device/thrust_allocator_tests.cu index 038f4b664..cabfe9661 100644 --- a/tests/mr/device/thrust_allocator_tests.cu +++ b/tests/mr/device/thrust_allocator_tests.cu @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "mr_test.hpp" +#include "mr_ref_test.hpp" #include #include @@ -32,7 +32,7 @@ template class rmm::mr::thrust_allocator; namespace rmm::test { namespace { -struct allocator_test : public mr_test {}; +struct allocator_test : public mr_ref_test {}; TEST_P(allocator_test, first) { diff --git a/tests/mr/host/mr_tests.cpp b/tests/mr/host/mr_tests.cpp deleted file mode 100644 index e0078c920..000000000 --- a/tests/mr/host/mr_tests.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2019-2024, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../byte_literals.hpp" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include -#include - -namespace rmm::test { -namespace { -inline bool is_aligned(void* ptr, std::size_t alignment = alignof(std::max_align_t)) -{ - return rmm::is_pointer_aligned(ptr, alignment); -} - -// Returns true if a pointer points to a device memory or managed memory allocation. -inline bool is_device_memory(void* ptr) -{ - cudaPointerAttributes attributes{}; - if (cudaSuccess != cudaPointerGetAttributes(&attributes, ptr)) { return false; } - return (attributes.type == cudaMemoryTypeDevice) or (attributes.type == cudaMemoryTypeManaged); -} - -/** - * @brief Returns if a pointer `p` points to pinned host memory. - */ -inline bool is_pinned_memory(void* ptr) -{ - cudaPointerAttributes attributes{}; - if (cudaSuccess != cudaPointerGetAttributes(&attributes, ptr)) { return false; } - return attributes.type == cudaMemoryTypeHost; -} - -constexpr std::size_t size_word{4_B}; -constexpr std::size_t size_kb{1_KiB}; -constexpr std::size_t size_mb{1_MiB}; -constexpr std::size_t size_gb{1_GiB}; -constexpr std::size_t size_pb{1_PiB}; - -struct allocation { - void* ptr{nullptr}; - std::size_t size{0}; - allocation(void* ptr, std::size_t size) : ptr{ptr}, size{size} {} - allocation() = default; -}; -} // namespace - -template -struct MRTest : public ::testing::Test { - std::unique_ptr mr; - - MRTest() : mr{new MemoryResourceType} {} -}; - -using resources = ::testing::Types; -static_assert(cuda::mr::resource_with); -static_assert(cuda::mr::resource_with); - -TYPED_TEST_CASE(MRTest, resources); - -TYPED_TEST(MRTest, SelfEquality) { EXPECT_TRUE(this->mr->is_equal(*this->mr)); } - -TYPED_TEST(MRTest, AllocateZeroBytes) -{ - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(0)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, 0)); -} - -TYPED_TEST(MRTest, AllocateWord) -{ - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(size_word)); - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_aligned(ptr)); - EXPECT_FALSE(is_device_memory(ptr)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, size_word)); -} - -TYPED_TEST(MRTest, AllocateKB) -{ - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(size_kb)); - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_aligned(ptr)); - EXPECT_FALSE(is_device_memory(ptr)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, size_kb)); -} - -TYPED_TEST(MRTest, AllocateMB) -{ - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(size_mb)); - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_aligned(ptr)); - EXPECT_FALSE(is_device_memory(ptr)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, size_mb)); -} - -TYPED_TEST(MRTest, AllocateGB) -{ - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(size_gb)); - EXPECT_NE(nullptr, ptr); - EXPECT_TRUE(is_aligned(ptr)); - EXPECT_FALSE(is_device_memory(ptr)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, size_gb)); -} - -TYPED_TEST(MRTest, AllocateTooMuch) -{ - void* ptr{nullptr}; - EXPECT_THROW(ptr = this->mr->allocate(size_pb), std::bad_alloc); - EXPECT_EQ(nullptr, ptr); -} - -TYPED_TEST(MRTest, RandomAllocations) -{ - constexpr std::size_t num_allocations{100}; - std::vector allocations(num_allocations); - - constexpr std::size_t MAX_ALLOCATION_SIZE{5 * size_mb}; - - std::default_random_engine generator; - std::uniform_int_distribution distribution(1, MAX_ALLOCATION_SIZE); - - // 100 allocations from [0,5MB) - std::for_each( - allocations.begin(), allocations.end(), [&generator, &distribution, this](allocation& alloc) { - alloc.size = distribution(generator); - EXPECT_NO_THROW(alloc.ptr = this->mr->allocate(alloc.size)); - EXPECT_NE(nullptr, alloc.ptr); - EXPECT_TRUE(is_aligned(alloc.ptr)); - }); - - std::for_each(allocations.begin(), allocations.end(), [this](allocation& alloc) { - EXPECT_NO_THROW(this->mr->deallocate(alloc.ptr, alloc.size)); - }); -} - -TYPED_TEST(MRTest, MixedRandomAllocationFree) -{ - std::default_random_engine generator; - - constexpr std::size_t MAX_ALLOCATION_SIZE{10 * size_mb}; - std::uniform_int_distribution size_distribution(1, MAX_ALLOCATION_SIZE); - - // How often a free will occur. For example, if `1`, then every allocation - // will immediately be free'd. Or, if 4, on average, a free will occur after - // every 4th allocation - constexpr std::size_t FREE_FREQUENCY{4}; - std::uniform_int_distribution free_distribution(1, FREE_FREQUENCY); - - std::deque allocations; - - constexpr std::size_t num_allocations{100}; - for (std::size_t i = 0; i < num_allocations; ++i) { - std::size_t allocation_size = size_distribution(generator); - EXPECT_NO_THROW(allocations.emplace_back(this->mr->allocate(allocation_size), allocation_size)); - auto new_allocation = allocations.back(); - EXPECT_NE(nullptr, new_allocation.ptr); - EXPECT_TRUE(is_aligned(new_allocation.ptr)); - - bool const free_front{free_distribution(generator) == free_distribution.max()}; - - if (free_front) { - auto front = allocations.front(); - EXPECT_NO_THROW(this->mr->deallocate(front.ptr, front.size)); - allocations.pop_front(); - } - } - // free any remaining allocations - for (auto alloc : allocations) { - EXPECT_NO_THROW(this->mr->deallocate(alloc.ptr, alloc.size)); - allocations.pop_front(); - } -} - -static constexpr std::size_t MinTestedAlignment{16}; -static constexpr std::size_t MaxTestedAlignment{4096}; -static constexpr std::size_t TestedAlignmentMultiplier{2}; -static constexpr std::size_t NUM_TRIALS{100}; - -TYPED_TEST(MRTest, AlignmentTest) -{ - std::default_random_engine generator(0); - constexpr std::size_t MAX_ALLOCATION_SIZE{10 * size_mb}; - std::uniform_int_distribution size_distribution(1, MAX_ALLOCATION_SIZE); - - for (std::size_t num_trials = 0; num_trials < NUM_TRIALS; ++num_trials) { - for (std::size_t alignment = MinTestedAlignment; alignment <= MaxTestedAlignment; - alignment *= TestedAlignmentMultiplier) { - auto allocation_size = size_distribution(generator); - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = this->mr->allocate(allocation_size, alignment)); - EXPECT_TRUE(is_aligned(ptr, alignment)); - EXPECT_NO_THROW(this->mr->deallocate(ptr, allocation_size, alignment)); - } - } -} - -TYPED_TEST(MRTest, UnsupportedAlignmentTest) -{ - std::default_random_engine generator(0); - constexpr std::size_t MAX_ALLOCATION_SIZE{10 * size_mb}; - std::uniform_int_distribution size_distribution(1, MAX_ALLOCATION_SIZE); - - for (std::size_t num_trials = 0; num_trials < NUM_TRIALS; ++num_trials) { - for (std::size_t alignment = MinTestedAlignment; alignment <= MaxTestedAlignment; - alignment *= TestedAlignmentMultiplier) { - auto allocation_size = size_distribution(generator); - void* ptr{nullptr}; - // An unsupported alignment (like an odd number) should result in an - // alignment of `alignof(std::max_align_t)` - auto const bad_alignment = alignment + 1; - EXPECT_NO_THROW(ptr = this->mr->allocate(allocation_size, bad_alignment)); - EXPECT_TRUE(is_aligned(ptr, alignof(std::max_align_t))); - EXPECT_NO_THROW(this->mr->deallocate(ptr, allocation_size, bad_alignment)); - } - } -} - -TEST(PinnedResource, isPinned) -{ - rmm::mr::pinned_memory_resource mr; - void* ptr{nullptr}; - EXPECT_NO_THROW(ptr = mr.allocate(100)); - EXPECT_TRUE(is_pinned_memory(ptr)); - EXPECT_NO_THROW(mr.deallocate(ptr, 100)); -} -} // namespace rmm::test