OpenVDB 13.1.0
Loading...
Searching...
No Matches
DeviceMesh.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 \file DeviceMesh.h
6
7 \brief nanovdb::cuda::DeviceMesh encapsulates device IDs, CUDA streams,
8 and NCCL communicators in order to facilitate multi-GPU applications.
9*/
10
11#ifndef NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
12#define NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
13
14#include <vector>
15#include <algorithm>
16#include <thread>
17
19#ifdef NANOVDB_USE_NCCL
20#include <nccl.h>
21#endif
22
23namespace nanovdb {
24
25namespace cuda {
26
27namespace detail {
28
29/// @brief RAII class that caches/restores the current device at construction/destruction
31 public:
32 DeviceGuard() { cudaGetDevice(&deviceId); }
33 ~DeviceGuard() { cudaSetDevice(deviceId); }
34
35 /// @{
36 /// @brief DeviceGuard is not copyable nor movable
37 DeviceGuard(const DeviceGuard&) = delete;
39 DeviceGuard(DeviceGuard&& other) = delete;
40 DeviceGuard& operator=(DeviceGuard&& other) = delete;
41 /// @}
42 private:
43 int deviceId = -1;
44};
45
46}
47
48/// @brief POD struct representing a device id and a stream on that device
50{
51 int id = -1;
52 cudaStream_t stream = cudaStream_t(0);
53};
54
55/// @brief This class wraps a vector of per-device IDs and CUDA streams and holds inter-device connectivity information and NCCL comms.
57{
58 using DeviceVecT = std::vector<DeviceNode>;
59 using const_iterator = DeviceVecT::const_iterator;
60 using size_type = DeviceVecT::size_type;
61public:
62 /// @brief Constructs a device mesh for all devices on the host and initializes a CUDA stream (and NCCL communicator) for each device
63 DeviceMesh();
64 /// @brief Destroys the per-device CUDA stream and finalizes and destroys the per-device NCCL communicators
66 /// @brief Disallow copy-construction
67 DeviceMesh(const DeviceMesh&) = delete;
68 /// @brief Move constructor. Underlying CUDA streams and NCCL communicators are not reinitialized.
69 /// @param other DeviceMesh instance that will be moved into this DeviceMesh.
70 DeviceMesh(DeviceMesh&&) noexcept;
71 /// @brief Disallow copy-assignment
72 DeviceMesh& operator=(const DeviceMesh&) = delete;
73 /// @brief Move assignment. Underlying CUDA streams and NCCL communicators are not reinitialized.
74 /// @param other DeviceMesh instance that will be moved into this DeviceMesh.
75 DeviceMesh& operator=(DeviceMesh&&) noexcept;
76
77 /// @brief Returns the number of devices
78 size_type deviceCount() const { return mDeviceNodes.size(); };
79
80 /// @brief Returns a const reference to the DeviceNode for a particular device
81 const DeviceNode& operator [](int deviceId) const { return mDeviceNodes[deviceId]; }
82
83 /// @brief Returns an iterator to the first device node
84 const_iterator begin() const { return mDeviceNodes.begin(); };
85 /// @brief Returns an iterator past the last device node
86 const_iterator end() const { return mDeviceNodes.end(); };
87
88#ifdef NANOVDB_USE_NCCL
89 /// @brief Returns the NCCL communicator for a particular device
90 ncclComm_t comm(int deviceId) const { return mComms[deviceId]; }
91#endif
92
93 /// @brief Returns whether or not peer to peer access is supported between two devices.
94 bool canAccessPeer(int deviceId, int peerId) const { return mConnectivity[deviceId][peerId] & (1 << cudaDevP2PAttrAccessSupported); }
95
96private:
97 /// @brief Returns whether or not a device supports managed memory
98 bool hasManagedMemory(int deviceId) const {
99 int managedMemoryValue = 0;
100 cudaDeviceGetAttribute(&managedMemoryValue, cudaDevAttrManagedMemory, deviceId);
101 return static_cast<bool>(managedMemoryValue);
102 }
103
104 DeviceVecT mDeviceNodes;
105
106#ifdef NANOVDB_USE_NCCL
107 std::vector<ncclComm_t> mComms;
108#endif
109 std::vector<std::vector<uint32_t>> mConnectivity;
110};
111
113{
114 detail::DeviceGuard deviceGuard;
115
116 int deviceCount = -1;
117 cudaGetDeviceCount(&deviceCount);
118
119 for (int deviceId = 0; deviceId < deviceCount; ++deviceId) {
120 NANOVDB_ASSERT(hasManagedMemory(deviceId));
121 cudaSetDevice(deviceId);
122 cudaStream_t stream;
123 cudaStreamCreate(&stream);
124 mDeviceNodes.push_back({deviceId, stream});
125 }
126
127#ifdef NANOVDB_USE_NCCL
128 mComms.resize(deviceCount);
129 ncclCommInitAll(mComms.data(), deviceCount, nullptr);
130#endif
131
132 mConnectivity.resize(deviceCount);
133 for (const auto& deviceNode : mDeviceNodes) {
134 mConnectivity[deviceNode.id].resize(deviceCount);
135 for (const auto& peerNode : mDeviceNodes) {
136 mConnectivity[deviceNode.id][peerNode.id] = 0;
137 if (deviceNode.id != peerNode.id) {
138 int peerAccessSupportedValue = 0;
139 cudaDeviceGetP2PAttribute(&peerAccessSupportedValue, cudaDevP2PAttrAccessSupported, deviceNode.id, peerNode.id);
140 if (peerAccessSupportedValue) {
141 mConnectivity[deviceNode.id][peerNode.id] |= (1 << cudaDevP2PAttrAccessSupported);
142 }
143 }
144 }
145 }
146}
147
148inline DeviceMesh::DeviceMesh(DeviceMesh&& other) noexcept
149 : mDeviceNodes(std::move(other.mDeviceNodes)),
150#ifdef NANOVDB_USE_NCCL
151 mComms(std::move(other.mComms)),
152#endif
153 mConnectivity(std::move(other.mConnectivity))
154{
155}
156
158{
159 detail::DeviceGuard deviceGuard;
160
161#ifdef NANOVDB_USE_NCCL
162 std::for_each(mComms.begin(), mComms.end(), [](ncclComm_t comm) {
163 ncclCommFinalize(comm);
164 ncclCommDestroy(comm);
165 });
166#endif
167
168 std::for_each(mDeviceNodes.begin(), mDeviceNodes.end(), [](DeviceNode& deviceNode) {
169 cudaSetDevice(deviceNode.id);
170 cudaStreamDestroy(deviceNode.stream);
171 });
172}
173
175{
176 mDeviceNodes = std::move(other.mDeviceNodes);
177#ifdef NANOVDB_USE_NCCL
178 mComms = std::move(other.mComms);
179#endif
180 mConnectivity = std::move(other.mConnectivity);
181 return *this;
182}
183
184namespace detail {
185
187{
188 void* entryPoint = nullptr;
189#if CUDART_VERSION >= 12500 // cudaGetDriverEntryPointByVersion was added in CUDA 12.5 with cudaGetDriverEntryPoint being potentially deprecated
190 cudaDriverEntryPointQueryResult queryResult = cudaDriverEntryPointSymbolNotFound;
191 cudaCheck(cudaGetDriverEntryPointByVersion("cuMemGetAllocationGranularity", &entryPoint, 12000, cudaEnableDefault, &queryResult));
192 NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
193#elif CUDART_VERSION >= 12000 // queryResult argument was added in CUDA 12
194 cudaDriverEntryPointQueryResult queryResult = cudaDriverEntryPointSymbolNotFound;
195 cudaCheck(cudaGetDriverEntryPoint("cuMemGetAllocationGranularity", &entryPoint, cudaEnableDefault, &queryResult));
196 NANOVDB_ASSERT(queryResult == cudaDriverEntryPointSuccess);
197#else
198 cudaCheck(cudaGetDriverEntryPoint("cuMemGetAllocationGranularity", &entryPoint, cudaEnableDefault));
199#endif
200 return entryPoint;
201}// queryAllocationGranularityEntryPoint
202
203}
204
205/// @brief Returns the minimum page size (in bytes) across all devices on the system
206inline size_t minDevicePageSize(const DeviceMesh& mesh)
207{
208 using FuncT = CUresult(size_t*, const CUmemAllocationProp*, CUmemAllocationGranularity_flags);
209 static FuncT* functPtr = reinterpret_cast<FuncT*>(detail::queryAllocationGranularityEntryPoint());
210
211 NANOVDB_ASSERT(functPtr);
212 CUmemAllocationProp prop = {};
213 prop.type = CU_MEM_ALLOCATION_TYPE_PINNED;
214 prop.location.type = CU_MEM_LOCATION_TYPE_DEVICE;
215 size_t minGranularity = 0;
216 for (const auto& node : mesh) {
217 prop.location.id = node.id;
218 size_t granularity = 0;
219 (*functPtr)(&granularity, &prop, CU_MEM_ALLOC_GRANULARITY_MINIMUM);
220 if (minGranularity < granularity) minGranularity = granularity;
221 }
222 return minGranularity;
223}// minDevicePageSize
224
225} // namespace cuda
226
227} // namespace nanovdb
228
229#endif // end of NANOVDB_CUDA_DEVICEMESH_H_HAS_BEEN_INCLUDED
This class wraps a vector of per-device IDs and CUDA streams and holds inter-device connectivity info...
Definition DeviceMesh.h:57
size_type deviceCount() const
Returns the number of devices.
Definition DeviceMesh.h:78
~DeviceMesh()
Destroys the per-device CUDA stream and finalizes and destroys the per-device NCCL communicators.
Definition DeviceMesh.h:157
const_iterator begin() const
Returns an iterator to the first device node.
Definition DeviceMesh.h:84
DeviceMesh & operator=(const DeviceMesh &)=delete
Disallow copy-assignment.
const DeviceNode & operator[](int deviceId) const
Returns a const reference to the DeviceNode for a particular device.
Definition DeviceMesh.h:81
DeviceMesh(const DeviceMesh &)=delete
Disallow copy-construction.
const_iterator end() const
Returns an iterator past the last device node.
Definition DeviceMesh.h:86
bool canAccessPeer(int deviceId, int peerId) const
Returns whether or not peer to peer access is supported between two devices.
Definition DeviceMesh.h:94
DeviceMesh()
Constructs a device mesh for all devices on the host and initializes a CUDA stream (and NCCL communic...
Definition DeviceMesh.h:112
RAII class that caches/restores the current device at construction/destruction.
Definition DeviceMesh.h:30
DeviceGuard()
Definition DeviceMesh.h:32
DeviceGuard(DeviceGuard &&other)=delete
DeviceGuard is not copyable nor movable.
DeviceGuard & operator=(const DeviceGuard &)=delete
DeviceGuard is not copyable nor movable.
DeviceGuard & operator=(DeviceGuard &&other)=delete
DeviceGuard is not copyable nor movable.
~DeviceGuard()
Definition DeviceMesh.h:33
DeviceGuard(const DeviceGuard &)=delete
DeviceGuard is not copyable nor movable.
Definition VoxToNanoVDB.h:15
Definition GridHandle.h:37
void * queryAllocationGranularityEntryPoint()
Definition DeviceMesh.h:186
Definition GridHandle.h:37
size_t minDevicePageSize(const DeviceMesh &mesh)
Returns the minimum page size (in bytes) across all devices on the system.
Definition DeviceMesh.h:206
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:31
#define NANOVDB_ASSERT(x)
Definition Util.h:53
Cuda specific utility functions.
#define cudaCheck(ans)
Definition Util.h:49
POD struct representing a device id and a stream on that device.
Definition DeviceMesh.h:50
cudaStream_t stream
Definition DeviceMesh.h:52