cuda_async_memory_resource.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021-2022, NVIDIA CORPORATION.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <rmm/cuda_device.hpp>
19 #include <rmm/cuda_stream_view.hpp>
20 #include <rmm/detail/cuda_util.hpp>
21 #include <rmm/detail/dynamic_load_runtime.hpp>
22 #include <rmm/detail/error.hpp>
25 
26 #include <rmm/detail/thrust_namespace.h>
27 #include <thrust/optional.h>
28 
29 #include <cuda_runtime_api.h>
30 
31 #include <cstddef>
32 #include <limits>
33 
34 #if CUDART_VERSION >= 11020 // 11.2 introduced cudaMallocAsync
35 #ifndef RMM_DISABLE_CUDA_MALLOC_ASYNC
36 #define RMM_CUDA_MALLOC_ASYNC_SUPPORT
37 #endif
38 #endif
39 
40 namespace rmm::mr {
52  public:
64  none = 0x0,
65  posix_file_descriptor = 0x1,
67  win32 = 0x2,
68  win32_kmt = 0x4
69  };
70 
88  // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
89  cuda_async_memory_resource(thrust::optional<std::size_t> initial_pool_size = {},
90  thrust::optional<std::size_t> release_threshold = {},
91  thrust::optional<allocation_handle_type> export_handle_type = {})
92  {
93 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
94  // Check if cudaMallocAsync Memory pool supported
95  RMM_EXPECTS(rmm::detail::async_alloc::is_supported(),
96  "cudaMallocAsync not supported with this CUDA driver/runtime version");
97 
98  // Construct explicit pool
99  cudaMemPoolProps pool_props{};
100  pool_props.allocType = cudaMemAllocationTypePinned;
101  pool_props.handleTypes = static_cast<cudaMemAllocationHandleType>(
102  export_handle_type.value_or(allocation_handle_type::none));
103  RMM_EXPECTS(rmm::detail::async_alloc::is_export_handle_type_supported(pool_props.handleTypes),
104  "Requested IPC memory handle type not supported");
105  pool_props.location.type = cudaMemLocationTypeDevice;
106  pool_props.location.id = rmm::get_current_cuda_device().value();
107  cudaMemPool_t cuda_pool_handle{};
108  RMM_CUDA_TRY(rmm::detail::async_alloc::cudaMemPoolCreate(&cuda_pool_handle, &pool_props));
109  pool_ = cuda_async_view_memory_resource{cuda_pool_handle};
110 
111  // CUDA drivers before 11.5 have known incompatibilities with the async allocator.
112  // We'll disable `cudaMemPoolReuseAllowOpportunistic` if cuda driver < 11.5.
113  // See https://github.com/NVIDIA/spark-rapids/issues/4710.
114  int driver_version{};
115  RMM_CUDA_TRY(cudaDriverGetVersion(&driver_version));
116  constexpr auto min_async_version{11050};
117  if (driver_version < min_async_version) {
118  int disabled{0};
119  RMM_CUDA_TRY(rmm::detail::async_alloc::cudaMemPoolSetAttribute(
120  pool_handle(), cudaMemPoolReuseAllowOpportunistic, &disabled));
121  }
122 
123  auto const [free, total] = rmm::detail::available_device_memory();
124 
125  // Need an l-value to take address to pass to cudaMemPoolSetAttribute
126  uint64_t threshold = release_threshold.value_or(total);
127  RMM_CUDA_TRY(rmm::detail::async_alloc::cudaMemPoolSetAttribute(
128  pool_handle(), cudaMemPoolAttrReleaseThreshold, &threshold));
129 
130  // Allocate and immediately deallocate the initial_pool_size to prime the pool with the
131  // specified size
132  auto const pool_size = initial_pool_size.value_or(free / 2);
133  auto* ptr = do_allocate(pool_size, cuda_stream_default);
134  do_deallocate(ptr, pool_size, cuda_stream_default);
135 #else
136  RMM_FAIL(
137  "cudaMallocAsync not supported by the version of the CUDA Toolkit used for this build");
138 #endif
139  }
140 
141 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
146  [[nodiscard]] cudaMemPool_t pool_handle() const noexcept { return pool_.pool_handle(); }
147 #endif
148 
149  ~cuda_async_memory_resource() override
150  {
151 #if defined(RMM_CUDA_MALLOC_ASYNC_SUPPORT)
152  RMM_ASSERT_CUDA_SUCCESS(rmm::detail::async_alloc::cudaMemPoolDestroy(pool_handle()));
153 #endif
154  }
157  cuda_async_memory_resource& operator=(cuda_async_memory_resource const&) = delete;
159 
166  [[nodiscard]] bool supports_streams() const noexcept override { return true; }
167 
173  [[nodiscard]] bool supports_get_mem_info() const noexcept override { return false; }
174 
175  private:
176 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
178 #endif
179 
189  void* do_allocate(std::size_t bytes, rmm::cuda_stream_view stream) override
190  {
191  void* ptr{nullptr};
192 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
193  ptr = pool_.allocate(bytes, stream);
194 #else
195  (void)bytes;
196  (void)stream;
197 #endif
198  return ptr;
199  }
200 
209  void do_deallocate(void* ptr, std::size_t bytes, rmm::cuda_stream_view stream) override
210  {
211 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
212  pool_.deallocate(ptr, bytes, stream);
213 #else
214  (void)ptr;
215  (void)bytes;
216  (void)stream;
217 #endif
218  }
219 
227  [[nodiscard]] bool do_is_equal(device_memory_resource const& other) const noexcept override
228  {
229  auto const* async_mr = dynamic_cast<cuda_async_memory_resource const*>(&other);
230 #ifdef RMM_CUDA_MALLOC_ASYNC_SUPPORT
231  return (async_mr != nullptr) && (this->pool_handle() == async_mr->pool_handle());
232 #else
233  return async_mr != nullptr;
234 #endif
235  }
236 
244  [[nodiscard]] std::pair<std::size_t, std::size_t> do_get_mem_info(
245  rmm::cuda_stream_view) const override
246  {
247  return std::make_pair(0, 0);
248  }
249 };
250  // end of group
252 } // namespace rmm::mr
Strongly-typed non-owning wrapper for CUDA streams with default constructor.
Definition: cuda_stream_view.hpp:41
device_memory_resource derived class that uses cudaMallocAsync/cudaFreeAsync for allocation/deallocat...
Definition: cuda_async_memory_resource.hpp:51
allocation_handle_type
Flags for specifying memory allocation handle types.
Definition: cuda_async_memory_resource.hpp:63
@ win32_kmt
Allows a Win32 KMT handle to be used for exporting. (D3DKMT_HANDLE)
@ win32
Allows a Win32 NT handle to be used for exporting. (HANDLE)
bool supports_streams() const noexcept override
Query whether the resource supports use of non-null CUDA streams for allocation/deallocation....
Definition: cuda_async_memory_resource.hpp:166
bool supports_get_mem_info() const noexcept override
Query whether the resource supports the get_mem_info API.
Definition: cuda_async_memory_resource.hpp:173
cuda_async_memory_resource(thrust::optional< std::size_t > initial_pool_size={}, thrust::optional< std::size_t > release_threshold={}, thrust::optional< allocation_handle_type > export_handle_type={})
Constructs a cuda_async_memory_resource with the optionally specified initial pool size and release t...
Definition: cuda_async_memory_resource.hpp:89
device_memory_resource derived class that uses cudaMallocAsync/cudaFreeAsync for allocation/deallocat...
Definition: cuda_async_view_memory_resource.hpp:48
Base class for all libcudf device memory allocation.
Definition: device_memory_resource.hpp:89
void * allocate(std::size_t bytes, cuda_stream_view stream=cuda_stream_view{})
Allocates memory of size at least bytes.
Definition: device_memory_resource.hpp:116
cuda_device_id get_current_cuda_device()
Returns a cuda_device_id for the current device.
Definition: cuda_device.hpp:86
constexpr value_type value() const noexcept
The wrapped integer value.
Definition: cuda_device.hpp:44