Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
runtime.hpp
Go to the documentation of this file.
1#pragma once
2
11
12#include <cuda_runtime_api.h>
13
14#include <atomic>
15#include <cstddef>
16#include <memory>
17#include <mutex>
18#include <optional>
19#include <span>
20#include <type_traits>
21#include <typeindex>
22#include <unordered_map>
23#include <utility>
24#include <vector>
25
26namespace uni20::cuda
27{
28
29class Completion;
30class DeviceResources;
31class StreamPool;
32template <typename T>
33 requires std::is_trivially_copyable_v<T>
34class CudaBuffer;
35
36namespace detail
37{
38class AllocationReclaimer;
39class StreamWaiter;
40template <typename T> class CudaAllocation;
41} // namespace detail
42
45 public:
47 explicit ScopedDevice(int device);
48 ScopedDevice(ScopedDevice const&) = delete;
51
53 [[nodiscard]] int device() const noexcept { return device_; }
54
55 private:
56 int device_;
57 int previous_device_;
58 bool restore_;
59};
60
65class Stream {
66 public:
67 class State;
68
69 Stream() = default;
70 Stream(Stream const&) noexcept = default;
71 Stream& operator=(Stream const&) noexcept = default;
72 Stream(Stream&& other) noexcept = default;
73 Stream& operator=(Stream&& other) noexcept = default;
74 ~Stream() = default;
75
77 [[nodiscard]] explicit operator bool() const noexcept { return static_cast<bool>(state_); }
78
80 [[nodiscard]] int device() const noexcept;
81
83 [[nodiscard]] cudaStream_t native_handle() const noexcept;
84
86 void synchronize() const;
87
89 [[nodiscard]] Completion record_completion() const;
90
93 void wait_on(Completion const& completion) const;
94
95 private:
96 explicit Stream(std::shared_ptr<State> state) noexcept;
97
98 std::shared_ptr<State> state_;
99
100 friend class StreamPool;
101};
102
105 public:
106 Completion() = default;
107
111 [[nodiscard]] static Completion record(int device, cudaStream_t stream);
112
114 [[nodiscard]] explicit operator bool() const noexcept { return static_cast<bool>(state_); }
115
117 [[nodiscard]] int device() const noexcept;
118
120 [[nodiscard]] bool ready() const;
121
123 void synchronize() const;
124
125 private:
126 class State;
127
128 explicit Completion(std::shared_ptr<State> state);
129
130 std::shared_ptr<State> state_;
131
132 friend class Stream;
133 friend class Stream::State;
134 friend class detail::AllocationReclaimer;
135};
136
137namespace detail
138{
139
141class StreamWaiter {
142 public:
143 using notify_type = void (*)(StreamWaiter&, Stream) noexcept;
144
145 explicit StreamWaiter(notify_type notify) noexcept : notify_(notify) { CHECK(notify_ != nullptr); }
146 StreamWaiter(StreamWaiter const&) = delete;
147 StreamWaiter& operator=(StreamWaiter const&) = delete;
148
149 private:
150 void notify(Stream stream) noexcept { notify_(*this, std::move(stream)); }
151
152 notify_type notify_;
153 StreamWaiter* next_ = nullptr;
154 bool queued_ = false;
155
156 friend class ::uni20::cuda::StreamPool;
157};
158
159} // namespace detail
160
164 public:
166 struct Config
167 {
169 std::size_t stream_count;
170 unsigned int stream_flags = cudaStreamNonBlocking;
171 };
172
174 explicit StreamPool(Config config);
175 StreamPool(StreamPool const&) = delete;
176 StreamPool& operator=(StreamPool const&) = delete;
178
180 [[nodiscard]] std::optional<Stream> try_acquire();
181
185 [[nodiscard]] Stream acquire();
186
188 [[nodiscard]] std::optional<Stream> acquire_or_enqueue(detail::StreamWaiter& waiter) noexcept;
189
191 [[nodiscard]] int device() const noexcept;
192
194 [[nodiscard]] std::size_t size() const noexcept;
195
197 [[nodiscard]] std::size_t idle_stream_count() const noexcept;
198
200 [[nodiscard]] std::size_t leased_stream_count() const noexcept;
201
203 [[nodiscard]] std::size_t pending_stream_count() const noexcept;
204
206 void synchronize();
207
208 private:
209 class Impl;
210 std::unique_ptr<Impl> impl_;
211
212 friend class Stream::State;
213};
214
223 public:
225 struct Config
226 {
228 std::size_t stream_count;
229 unsigned int stream_flags = cudaStreamNonBlocking;
230 };
231
233 explicit DeviceResources(Config config);
238 ~DeviceResources() noexcept;
239
241 [[nodiscard]] Device device() const noexcept { return device_; }
242
244 [[nodiscard]] StreamPool& streams() noexcept { return streams_; }
245 [[nodiscard]] StreamPool const& streams() const noexcept { return streams_; }
246
252 template <class Resource, class... Args> [[nodiscard]] Resource& provider_resource(Args&&... args)
253 {
254 std::lock_guard lock(provider_resources_mutex_);
255 auto const key = std::type_index(typeid(Resource));
256 auto found = provider_resources_.find(key);
257 if (found != provider_resources_.end())
258 {
259 return static_cast<ProviderResourceModel<Resource>&>(*found->second).value;
260 }
261
262 auto resource = std::make_unique<ProviderResourceModel<Resource>>(std::forward<Args>(args)...);
263 Resource& result = resource->value;
264 provider_resources_.emplace(key, std::move(resource));
265 return result;
266 }
267
269 [[nodiscard]] std::size_t live_allocation_count() const noexcept
270 {
271 return live_allocation_count_.load(std::memory_order_acquire);
272 }
273
274 private:
275 struct ProviderResourceBase
276 {
277 virtual ~ProviderResourceBase() = default;
278 };
279
280 template <class Resource> struct ProviderResourceModel final : ProviderResourceBase
281 {
282 template <class... Args> explicit ProviderResourceModel(Args&&... args) : value(std::forward<Args>(args)...) {}
283
284 Resource value;
285 };
286
287 void register_allocation() noexcept { live_allocation_count_.fetch_add(1, std::memory_order_relaxed); }
288 void unregister_allocation() noexcept;
289 void defer_allocation_release_after(std::span<Completion const> completions) noexcept;
290 void release_allocation(void* allocation, bool blocking) noexcept;
291 void destroy_provider_resources() noexcept;
292
293 Device device_;
294 StreamPool streams_;
295 std::unique_ptr<detail::AllocationReclaimer> allocation_reclaimer_;
296 std::mutex provider_resources_mutex_;
297 std::unordered_map<std::type_index, std::unique_ptr<ProviderResourceBase>> provider_resources_;
298 std::atomic<std::size_t> live_allocation_count_ = 0;
299
300 template <typename T>
301 requires std::is_trivially_copyable_v<T>
302 friend class CudaBuffer;
303 template <typename> friend class detail::CudaAllocation;
304};
305
308{
310 std::vector<int> device_ordinals{};
312 std::optional<int> default_device{};
314 std::size_t streams_per_device = 8;
316 unsigned int stream_flags = cudaStreamNonBlocking;
317};
318
324class Runtime {
325 public:
326 Runtime(Runtime const&) = delete;
327 Runtime& operator=(Runtime const&) = delete;
328 Runtime(Runtime&&) = delete;
330 ~Runtime() noexcept;
331
333 [[nodiscard]] bool has_device(int device) const noexcept;
334
336 [[nodiscard]] DeviceResources& device_resources(int device) const;
337
339 [[nodiscard]] DeviceResources& default_device_resources() const;
340
342 [[nodiscard]] std::optional<int> default_device() const noexcept { return default_device_; }
343
344 private:
345 struct construction_key
346 {};
347
348 explicit Runtime(RuntimeConfig config, construction_key);
349
350 std::vector<std::unique_ptr<DeviceResources>> device_resources_;
351 std::optional<int> default_device_;
352
353 friend Runtime initialize(RuntimeConfig);
354};
355
359[[nodiscard]] Runtime initialize(RuntimeConfig config = {});
360
362[[nodiscard]] bool is_initialized() noexcept;
363
365[[nodiscard]] Runtime& runtime();
366
368[[nodiscard]] DeviceResources& device_resources(int device);
369
371[[nodiscard]] DeviceResources& device_resources();
372
373} // namespace uni20::cuda
Definition runtime.cpp:89
Shared completion token for one submitted CUDA operation.
Definition runtime.hpp:104
Move-only logical CUDA buffer and completion state.
Definition buffer.hpp:222
Mutable stream and provider resources associated with one CUDA device.
Definition runtime.hpp:222
std::size_t live_allocation_count() const noexcept
Return the number of live CUDA allocations borrowing these resources.
Definition runtime.hpp:269
DeviceResources & operator=(DeviceResources const &)=delete
DeviceResources(DeviceResources const &)=delete
StreamPool const & streams() const noexcept
Definition runtime.hpp:245
DeviceResources(DeviceResources &&)=delete
Resource & provider_resource(Args &&... args)
Return the resource-owned instance of one provider resource type.
Definition runtime.hpp:252
DeviceResources & operator=(DeviceResources &&)=delete
StreamPool & streams() noexcept
Return the device's actually-idle stream pool.
Definition runtime.hpp:244
Validated CUDA device identity with cached immutable capabilities.
Definition device.hpp:105
Scoped owner of the process-wide CUDA resource installation.
Definition runtime.hpp:324
Runtime(Runtime &&)=delete
Runtime & operator=(Runtime const &)=delete
Runtime & operator=(Runtime &&)=delete
Runtime(Runtime const &)=delete
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
ScopedDevice & operator=(ScopedDevice const &)=delete
int device() const noexcept
Return the selected device ordinal.
Definition runtime.hpp:53
~ScopedDevice()
Definition runtime.cpp:81
ScopedDevice(ScopedDevice const &)=delete
Definition runtime.cpp:333
Device-local pool whose streams become available only after their queued work completes.
Definition runtime.hpp:163
StreamPool(StreamPool const &)=delete
StreamPool & operator=(StreamPool const &)=delete
Definition runtime.cpp:260
Reference-counted lease of one CUDA stream-pool slot.
Definition runtime.hpp:65
void synchronize() const
Wait on all work currently queued in this stream.
Definition runtime.cpp:292
cudaStream_t native_handle() const noexcept
Return the CUDA runtime stream handle.
Definition runtime.cpp:286
Completion record_completion() const
Record and return an immutable completion for the current stream tail.
Definition runtime.cpp:298
Stream(Stream const &) noexcept=default
int device() const noexcept
Return the device that owns this stream.
Definition runtime.cpp:280
void wait_on(Completion const &completion) const
Make this stream wait for completion before executing subsequent work.
Definition runtime.cpp:304
Stream(Stream &&other) noexcept=default
friend class StreamPool
Definition runtime.hpp:100
Stream & operator=(Stream const &) noexcept=default
Stream & operator=(Stream &&other) noexcept=default
locale_t value
Definition data_table.cpp:49
CUDA device discovery and immutable hardware capability snapshots.
Definition qnum.hpp:403
Definition cublas/execution.hpp:18
Runtime & runtime()
Return the installed runtime or fail when CUDA was not initialized.
Definition runtime.cpp:811
Runtime initialize(RuntimeConfig config)
Install and return a scoped process-wide CUDA runtime owner.
Definition runtime.cpp:807
bool is_initialized() noexcept
Return whether a process-wide CUDA runtime is currently installed.
Definition runtime.cpp:809
DeviceResources & device_resources()
Return canonical resources for the installed runtime's default device.
Definition runtime.cpp:820
std::size_t span
Definition presentation.cpp:2017
Configuration for one device's mutable CUDA resources.
Definition runtime.hpp:226
Device device
Definition runtime.hpp:227
std::size_t stream_count
Definition runtime.hpp:228
Configuration for one scoped process-wide CUDA runtime installation.
Definition runtime.hpp:308
Configuration for a device-local idle-stream pool.
Definition runtime.hpp:167
std::size_t stream_count
Definition runtime.hpp:169
int device
Definition runtime.hpp:168
#define CHECK(cond,...)
Definition trace.hpp:299