28template <
typename T = std::
byte>
29 requires std::is_trivially_copyable_v<T>
31template <
typename T>
class ReadAccess;
32template <
typename T>
class WriteAccess;
33template <
typename T>
class BlockingReadAccess;
34template <
typename T>
class BlockingWriteAccess;
35template <
typename T>
class OwningReadAccess;
36template <
typename T>
class PartitionedReadAccess;
37template <
typename T>
class PartitionedWriteAccess;
38class AccessCompletion;
50[[noreturn]]
inline void cuda_cleanup_failure(cudaError_t status,
char const* operation,
int device)
52 PANIC(
"CUDA cleanup operation failed", operation, device, cudaGetErrorName(status), cudaGetErrorString(status));
55inline void check_cuda_cleanup(cudaError_t status,
char const* operation,
int device)
noexcept
57 if (status != cudaSuccess)
59 cuda_cleanup_failure(status, operation, device);
63class BufferCleanupDeviceGuard {
65 explicit BufferCleanupDeviceGuard(
int device)
noexcept
67 check_cuda_cleanup(cudaGetDevice(&previous_device_),
"cudaGetDevice", device);
68 restore_ = previous_device_ != device;
71 check_cuda_cleanup(cudaSetDevice(device),
"cudaSetDevice", device);
75 BufferCleanupDeviceGuard(BufferCleanupDeviceGuard
const&) =
delete;
76 BufferCleanupDeviceGuard& operator=(BufferCleanupDeviceGuard
const&) =
delete;
78 ~BufferCleanupDeviceGuard()
82 check_cuda_cleanup(cudaSetDevice(previous_device_),
"cudaSetDevice restore", previous_device_);
87 int previous_device_ = -1;
88 bool restore_ =
false;
91inline void synchronize_after_failed_publication(Stream
const& stream,
char const* operation)
noexcept
99 PANIC(
"CUDA buffer completion publication failed and stream synchronization also failed", operation,
104inline void synchronize_after_failed_publication(cudaStream_t stream,
int device,
char const* operation)
noexcept
106 BufferCleanupDeviceGuard guard(device);
107 cudaError_t
const status = cudaStreamSynchronize(stream);
108 if (status != cudaSuccess)
110 PANIC(
"CUDA buffer completion publication failed and stream synchronization also failed", operation, device,
111 cudaGetErrorName(status), cudaGetErrorString(status));
115template <
typename T>
class CudaAllocation {
117 CudaAllocation(DeviceResources& resources, std::size_t size) : resources_(&resources), size_(
size)
119 CHECK(size <= std::numeric_limits<std::size_t>::max() /
sizeof(T), size,
sizeof(T));
120 std::size_t
const bytes =
size *
sizeof(T);
123 if (resources.device().capabilities().memory_pools_supported)
124 this->allocate_stream_ordered(
bytes);
126 this->allocate_blocking(
bytes);
128 resources_->register_allocation();
132 CudaAllocation(CudaAllocation
const&) =
delete;
133 CudaAllocation& operator=(CudaAllocation
const&) =
delete;
134 ~CudaAllocation() { this->reset(); }
136 [[nodiscard]] T* data() noexcept {
return data_; }
137 [[nodiscard]] T
const* data() const noexcept {
return data_; }
138 [[nodiscard]] std::size_t
size() const noexcept {
return size_; }
139 [[nodiscard]] DeviceResources& resources()
const
141 CHECK(resources_ !=
nullptr);
144 [[nodiscard]] Completion
const& initial_completion() const noexcept {
return initial_completion_; }
145 void require_blocking_deallocation() noexcept { force_blocking_deallocation_.store(
true); }
146 void defer_release_after(Completion
const& writer, std::span<Completion const> readers)
noexcept
148 if (writer) resources_->defer_allocation_release_after(std::span(&writer, 1));
149 if (!readers.empty()) resources_->defer_allocation_release_after(readers);
153 void allocate_blocking(std::size_t
bytes)
155 void* raw_data =
nullptr;
156 ScopedDevice guard(resources_->device().ordinal());
157 check(cudaMalloc(&raw_data,
bytes),
"cudaMalloc", resources_->device().ordinal());
158 data_ =
static_cast<T*
>(raw_data);
161 void allocate_stream_ordered(std::size_t
bytes)
163 auto stream = resources_->streams().acquire();
164 void* raw_data =
nullptr;
165 ScopedDevice guard(resources_->device().ordinal());
166 check(cudaMallocAsync(&raw_data,
bytes,
stream.native_handle()),
"cudaMallocAsync",
167 resources_->device().ordinal());
168 data_ =
static_cast<T*
>(raw_data);
172 initial_completion_ =
stream.record_completion();
176 synchronize_after_failed_publication(stream,
"publish CUDA async allocation completion");
177 check_cuda_cleanup(cudaFreeAsync(data_,
stream.native_handle()),
178 "cudaFreeAsync after cudaMallocAsync publication failure", resources_->device().ordinal());
179 synchronize_after_failed_publication(stream,
"free CUDA async allocation after publication failure");
186 void reset() noexcept
188 if (resources_ ==
nullptr)
return;
189 if (initial_completion_) resources_->defer_allocation_release_after(std::span(&initial_completion_, 1));
191 if (data_ !=
nullptr)
193 resources_->release_allocation(data_, force_blocking_deallocation_.load());
196 auto* resources = std::exchange(resources_,
nullptr);
199 initial_completion_ = {};
200 if (registered_) resources->unregister_allocation();
204 DeviceResources* resources_ =
nullptr;
206 std::size_t size_ = 0;
207 Completion initial_completion_;
208 std::atomic_bool force_blocking_deallocation_ =
false;
209 bool registered_ =
false;
221 requires std::is_trivially_copyable_v<T>
224 static_assert(std::is_object_v<T>,
"CUDA buffers require an object element type");
225 static_assert(!std::is_const_v<T>,
"CUDA buffers own mutable storage; constness belongs on read guards");
239 writer_completion_(allocation_->initial_completion())
252 this->move_from(other);
260 [[nodiscard]] std::size_t
size() const noexcept {
return size_; }
263 [[nodiscard]] std::size_t
size_bytes() const noexcept {
return size_ *
sizeof(T); }
266 [[nodiscard]]
bool empty() const noexcept {
return size_ == 0; }
271 CHECK(allocation_ !=
nullptr);
272 return allocation_->resources();
284 return allocation_ !=
nullptr && allocation_ == other.allocation_;
291 [[nodiscard]]
auto partition(std::span<CudaBufferRange const> ranges) && -> std::vector<CudaBuffer>
293 CHECK(allocation_ !=
nullptr,
"cannot partition a moved-from CUDA buffer");
294 std::size_t previous_end = 0;
295 for (
auto const& range : ranges)
297 CHECK(range.offset >= previous_end, range.offset, previous_end);
298 CHECK(range.offset <= size_ && range.size <= size_ - range.offset, range.offset, range.size, size_);
299 previous_end = range.offset + range.size;
302 std::lock_guard lock(state_mutex_);
303 CHECK(live_read_accesses_ == 0 && !live_write_access_,
304 "cannot partition a CUDA buffer while access guards are live", this->data(), live_read_accesses_,
306 std::vector<CudaBuffer> result;
307 result.reserve(ranges.size());
308 for (
auto const& range : ranges)
310 result.push_back(
CudaBuffer(allocation_, allocation_offset_ + range.offset, range.size, writer_completion_,
311 reader_completions_));
314 allocation_offset_ = 0;
316 writer_completion_ = {};
317 reader_completions_.clear();
325 std::vector<Completion> reader_completions;
327 std::lock_guard lock(state_mutex_);
328 CHECK(live_read_accesses_ == 0 && !live_write_access_,
329 "cannot synchronize a CUDA buffer while access guards are live", this->data(), live_read_accesses_,
331 writer_completion = writer_completion_;
332 reader_completions = reader_completions_;
334 if (writer_completion)
338 for (
Completion const& reader_completion : reader_completions)
340 reader_completion.synchronize();
367 Completion writer_completion, std::vector<Completion> reader_completions)
369 writer_completion_(
std::move(writer_completion)), reader_completions_(
std::move(reader_completions))
372 [[nodiscard]] T* data() noexcept
374 return allocation_ ==
nullptr || allocation_->data() ==
nullptr ? nullptr
375 : allocation_->data() + allocation_offset_;
377 [[nodiscard]] T
const* data() const noexcept
379 return allocation_ ==
nullptr || allocation_->data() ==
nullptr ? nullptr
380 : allocation_->data() + allocation_offset_;
383 void acquire_read_access(Stream
const& stream)
const
385 CHECK(stream,
"cannot synchronize CUDA buffer access with an empty stream", this->data());
386 Completion writer_completion;
388 std::lock_guard lock(state_mutex_);
389 CHECK(!live_write_access_,
"cannot acquire CUDA read access while a write access is live", this->data());
390 writer_completion = writer_completion_;
391 ++live_read_accesses_;
396 if (writer_completion)
398 stream.wait_on(writer_completion);
403 std::lock_guard lock(state_mutex_);
404 CHECK(live_read_accesses_ > 0);
405 --live_read_accesses_;
410 void acquire_write_access(Stream
const& stream)
const
412 CHECK(stream,
"cannot synchronize CUDA buffer access with an empty stream", this->data());
413 Completion writer_completion;
414 std::vector<Completion> reader_completions;
416 std::lock_guard lock(state_mutex_);
417 CHECK(!live_write_access_ && live_read_accesses_ == 0,
418 "cannot acquire CUDA write access while another access is live", this->data(), live_read_accesses_,
420 writer_completion = writer_completion_;
421 std::erase_if(reader_completions_, [](Completion
const& completion) {
return completion.ready(); });
422 reader_completions = reader_completions_;
423 live_write_access_ =
true;
428 if (writer_completion)
430 stream.wait_on(writer_completion);
432 for (Completion
const& reader_completion : reader_completions)
434 stream.wait_on(reader_completion);
439 std::lock_guard lock(state_mutex_);
440 CHECK(live_write_access_);
441 live_write_access_ =
false;
446 void acquire_blocking_read_access()
const
448 Completion writer_completion;
450 std::lock_guard lock(state_mutex_);
451 CHECK(!live_write_access_,
"cannot acquire blocking CUDA read access while a write access is live",
453 writer_completion = writer_completion_;
454 ++live_read_accesses_;
459 if (writer_completion)
461 writer_completion.synchronize();
466 this->release_blocking_read_access();
471 void acquire_blocking_write_access()
const
473 Completion writer_completion;
474 std::vector<Completion> reader_completions;
476 std::lock_guard lock(state_mutex_);
477 CHECK(!live_write_access_ && live_read_accesses_ == 0,
478 "cannot acquire blocking CUDA write access while another access is live", this->data(),
479 live_read_accesses_, live_write_access_);
480 writer_completion = writer_completion_;
481 reader_completions = reader_completions_;
482 live_write_access_ =
true;
487 if (writer_completion)
489 writer_completion.synchronize();
491 for (Completion
const& reader_completion : reader_completions)
493 reader_completion.synchronize();
498 std::lock_guard lock(state_mutex_);
499 CHECK(live_write_access_);
500 live_write_access_ =
false;
505 void release_blocking_read_access() const noexcept
507 std::lock_guard lock(state_mutex_);
508 CHECK(live_read_accesses_ > 0);
509 --live_read_accesses_;
512 void release_blocking_write_access(Completion completion = {})
const noexcept
514 std::lock_guard lock(state_mutex_);
515 CHECK(live_write_access_);
516 writer_completion_ = std::move(completion);
517 reader_completions_.clear();
518 live_write_access_ =
false;
521 void release_read_access(Stream
const& stream)
const noexcept
525 Completion completion =
stream.record_completion();
526 this->release_read_access(std::move(completion));
531 detail::synchronize_after_failed_publication(stream,
"publish CUDA buffer reader completion");
533 this->release_read_access(Completion{});
536 void release_write_access(Stream
const& stream)
const noexcept
540 Completion completion =
stream.record_completion();
541 this->release_write_access(std::move(completion));
546 detail::synchronize_after_failed_publication(stream,
"publish CUDA buffer writer completion");
549 this->release_write_access(Completion{});
552 void release_read_access(Completion completion)
const noexcept
554 std::lock_guard lock(state_mutex_);
555 CHECK(live_read_accesses_ > 0);
558 if (reader_completions_.size() == reader_completions_.capacity())
560 std::erase_if(reader_completions_,
561 [](Completion
const& reader_completion) {
return reader_completion.ready(); });
563 reader_completions_.push_back(std::move(completion));
565 --live_read_accesses_;
568 void release_write_access(Completion completion)
const noexcept
570 std::lock_guard lock(state_mutex_);
571 CHECK(live_write_access_);
572 writer_completion_ = std::move(completion);
573 reader_completions_.clear();
574 live_write_access_ =
false;
577 void prepare_owning_read_access(Stream
const& stream)
const
579 CHECK(stream,
"cannot synchronize owning CUDA buffer access with an empty stream", this->data());
580 Completion writer_completion;
582 std::lock_guard lock(state_mutex_);
583 CHECK(live_read_accesses_ == 0 && !live_write_access_,
584 "cannot take owning CUDA buffer access while another access is live", this->data(), live_read_accesses_,
586 writer_completion = writer_completion_;
588 if (writer_completion)
590 stream.wait_on(writer_completion);
594 void prepare_owning_blocking_read_access()
const
596 Completion writer_completion;
598 std::lock_guard lock(state_mutex_);
599 CHECK(live_read_accesses_ == 0 && !live_write_access_,
600 "cannot take owning CUDA buffer access while another access is live", this->data(), live_read_accesses_,
602 writer_completion = writer_completion_;
604 if (writer_completion)
610 void publish_owning_read_completion(Stream
const& stream)
const noexcept
614 Completion completion =
stream.record_completion();
615 std::lock_guard lock(state_mutex_);
616 if (reader_completions_.size() == reader_completions_.capacity())
618 std::erase_if(reader_completions_,
619 [](Completion
const& reader_completion) {
return reader_completion.ready(); });
621 reader_completions_.push_back(std::move(completion));
626 detail::synchronize_after_failed_publication(stream,
"publish owning CUDA buffer reader completion");
630 void reset() noexcept { this->reset_impl(); }
632 void reset_owned() noexcept
634 if (allocation_ !=
nullptr) allocation_->require_blocking_deallocation();
638 void reset_impl() noexcept
640 if (allocation_ ==
nullptr)
645 Completion writer_completion;
646 std::vector<Completion> reader_completions;
648 std::lock_guard lock(state_mutex_);
649 CHECK(live_read_accesses_ == 0 && !live_write_access_,
650 "cannot destroy or reset a CUDA buffer while access guards are live", this->data(), live_read_accesses_,
652 writer_completion = std::move(writer_completion_);
653 reader_completions = std::move(reader_completions_);
655 allocation_->defer_release_after(writer_completion, reader_completions);
658 allocation_offset_ = 0;
660 writer_completion_ = {};
661 reader_completions_.clear();
664 void move_from(CudaBuffer& other)
noexcept
666 if (other.allocation_ ==
nullptr)
671 std::lock_guard lock(other.state_mutex_);
672 CHECK(other.live_read_accesses_ == 0 && !other.live_write_access_,
673 "cannot move a CUDA buffer while access guards are live", other.data(), other.live_read_accesses_,
674 other.live_write_access_);
675 allocation_ = std::move(other.allocation_);
676 allocation_offset_ = std::exchange(other.allocation_offset_, 0);
678 writer_completion_ = std::move(other.writer_completion_);
679 reader_completions_ = std::move(other.reader_completions_);
680 live_read_accesses_ = 0;
681 live_write_access_ =
false;
685 std::shared_ptr<detail::CudaAllocation<T>> allocation_;
686 std::size_t allocation_offset_ = 0;
687 std::size_t size_ = 0;
688 mutable std::mutex state_mutex_;
689 mutable Completion writer_completion_;
690 mutable std::vector<Completion> reader_completions_;
691 mutable std::size_t live_read_accesses_ = 0;
692 mutable bool live_write_access_ =
false;
708 requires std::is_trivially_copyable_v<T>
721 : resources_(std::exchange(other.resources_,
nullptr)), size_(std::exchange(other.size_, 0)),
722 buffers_(std::move(other.buffers_))
728 buffers_ = std::move(other.buffers_);
729 resources_ = std::exchange(other.resources_,
nullptr);
730 size_ = std::exchange(other.size_, 0);
736 [[nodiscard]] std::size_t
size() const noexcept {
return size_; }
739 [[nodiscard]] std::size_t
buffer_count() const noexcept {
return buffers_.size(); }
750 CHECK(resources_ !=
nullptr);
768 void require_complete_partition()
const
770 std::size_t next_offset = 0;
771 for (
auto const&
buffer : buffers_)
774 next_offset +=
buffer.size();
779 static auto make_buffers(DeviceResources&
resources, std::size_t
size,
780 std::span<CudaBufferRange const> ranges) -> std::vector<CudaBuffer<T>>
783 return std::move(allocation).partition(ranges);
786 DeviceResources* resources_ =
nullptr;
787 std::size_t size_ = 0;
788 std::vector<CudaBuffer<T>> buffers_;
805 : storage_(std::exchange(other.storage_,
nullptr)), stream_(std::move(other.stream_))
812 storage_ = std::exchange(other.storage_,
nullptr);
813 stream_ = std::move(other.stream_);
820 [[nodiscard]] T
const*
data() const noexcept {
return storage_ ==
nullptr ? nullptr : storage_->data(); }
825 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released CUDA read access");
830 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
833 [[nodiscard]] std::size_t
size_bytes() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size_bytes(); }
838 if (storage_ ==
nullptr)
842 storage_->release_read_access(stream_);
850 if (storage_ ==
nullptr)
return;
852 storage_->release_read_access(std::move(completion));
860 if (storage_ ==
nullptr)
return;
869 storage.acquire_read_access(stream_);
890 : storage_(std::exchange(other.storage_,
nullptr)), stream_(std::move(other.stream_))
897 storage_ = std::exchange(other.storage_,
nullptr);
898 stream_ = std::move(other.stream_);
905 [[nodiscard]] T*
data() const noexcept {
return storage_ ==
nullptr ? nullptr : storage_->data(); }
910 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released CUDA write access");
917 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released CUDA write access");
922 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
925 [[nodiscard]] std::size_t
size_bytes() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size_bytes(); }
930 if (storage_ ==
nullptr)
934 storage_->release_write_access(stream_);
942 if (storage_ ==
nullptr)
return;
944 storage_->release_write_access(std::move(completion));
952 if (storage_ ==
nullptr)
return;
961 storage.acquire_write_access(stream_);
982 : storage_(std::exchange(other.storage_,
nullptr)), accesses_(std::move(other.accesses_)),
983 stream_(std::move(other.stream_))
990 storage_ = std::exchange(other.storage_,
nullptr);
991 accesses_ = std::move(other.accesses_);
992 stream_ = std::move(other.stream_);
999 [[nodiscard]] T
const*
data() const noexcept {
return accesses_.empty() ? nullptr : accesses_.front().data(); }
1002 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
1007 if (storage_ ==
nullptr)
return;
1015 detail::synchronize_after_failed_publication(stream_,
"publish partitioned CUDA reader completion");
1023 if (storage_ ==
nullptr)
return;
1025 for (
auto& access : accesses_)
1026 access.release_with_completion(completion);
1033 if (storage_ ==
nullptr)
return;
1034 for (
auto& access : accesses_)
1035 access.release_after_synchronization();
1041 : storage_(&storage), stream_(stream)
1043 storage.require_complete_partition();
1044 accesses_.reserve(storage.buffers_.size());
1045 for (
auto const& buffer : storage.buffers_)
1046 accesses_.push_back(buffer.read_synchronized_with(stream_));
1049 void clear() noexcept
1057 std::vector<ReadAccess<T>> accesses_;
1074 : storage_(std::exchange(other.storage_,
nullptr)), accesses_(std::move(other.accesses_)),
1075 stream_(std::move(other.stream_))
1082 storage_ = std::exchange(other.storage_,
nullptr);
1083 accesses_ = std::move(other.accesses_);
1084 stream_ = std::move(other.stream_);
1091 [[nodiscard]] T*
data() const noexcept {
return accesses_.empty() ? nullptr : accesses_.front().data(); }
1094 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
1099 if (storage_ ==
nullptr)
return;
1107 detail::synchronize_after_failed_publication(stream_,
"publish partitioned CUDA writer completion");
1115 if (storage_ ==
nullptr)
return;
1117 for (
auto& access : accesses_)
1118 access.release_with_completion(completion);
1125 if (storage_ ==
nullptr)
return;
1126 for (
auto& access : accesses_)
1127 access.release_after_synchronization();
1133 : storage_(&storage), stream_(stream)
1135 storage.require_complete_partition();
1136 accesses_.reserve(storage.buffers_.size());
1137 for (
auto& buffer : storage.buffers_)
1138 accesses_.push_back(buffer.write_synchronized_with(stream_));
1141 void clear() noexcept
1149 std::vector<WriteAccess<T>> accesses_;
1167 completion_ = stream.record_completion();
1171 detail::synchronize_after_failed_publication(stream,
"publish shared CUDA buffer access completion");
1181 template <class Access>
void release(Access& access) const noexcept
1184 access.release_with_completion(completion_);
1186 access.release_after_synchronization();
1193template <
typename T>
1194 requires std::is_trivially_copyable_v<T>
1200template <
typename T>
1201 requires std::is_trivially_copyable_v<T>
1223 storage_ = std::exchange(other.storage_,
nullptr);
1230 [[nodiscard]] T
const*
data() const noexcept {
return storage_ ==
nullptr ? nullptr : storage_->data(); }
1235 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released blocking CUDA read access");
1240 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
1245 if (storage_ ==
nullptr)
return;
1246 storage_->release_blocking_read_access();
1253 storage.acquire_blocking_read_access();
1278 storage_ = std::exchange(other.storage_,
nullptr);
1285 [[nodiscard]] T*
data() const noexcept {
return storage_ ==
nullptr ? nullptr : storage_->data(); }
1290 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released blocking CUDA write access");
1297 CHECK(storage_ !=
nullptr,
"cannot inspect storage through a released blocking CUDA write access");
1302 [[nodiscard]] std::size_t
size() const noexcept {
return storage_ ==
nullptr ? 0 : storage_->size(); }
1307 if (storage_ ==
nullptr)
return;
1308 storage_->release_blocking_write_access();
1317 if (storage_ ==
nullptr)
return;
1319 storage_->release_blocking_write_access(std::move(completion));
1326 storage.acquire_blocking_write_access();
1349 : storage_(std::move(other.storage_)), stream_(std::move(other.stream_)),
1350 stream_ordered_(std::exchange(other.stream_ordered_,
false)), active_(std::exchange(other.active_,
false))
1358 [[nodiscard]] T
const*
data() const noexcept {
return active_ ? storage_.data() :
nullptr; }
1363 if (!active_)
return;
1364 bool const blocking_deallocation = !stream_ordered_;
1365 if (stream_ordered_)
1367 storage_.publish_owning_read_completion(stream_);
1370 stream_ordered_ =
false;
1372 if (blocking_deallocation)
1373 storage_.reset_owned();
1381 storage_.allocation_->require_blocking_deallocation();
1382 storage_.prepare_owning_blocking_read_access();
1385 OwningReadAccess(
CudaBuffer<T>&& storage, Stream
const& stream)
1386 : storage_(
std::move(storage)), stream_(stream), stream_ordered_(true), active_(true)
1388 storage_.prepare_owning_read_access(stream_);
1393 bool stream_ordered_ =
false;
1394 bool active_ =
false;
1399template <
typename T>
1400 requires std::is_trivially_copyable_v<T>
1406template <
typename T>
1407 requires std::is_trivially_copyable_v<T>
One operation-tail completion shared by multiple CUDA buffer accesses.
Definition buffer.hpp:1160
void release(Access &access) const noexcept
Release one participating access through the shared completion.
Definition buffer.hpp:1181
AccessCompletion(AccessCompletion const &)=delete
AccessCompletion(AccessCompletion &&) noexcept=default
AccessCompletion & operator=(AccessCompletion const &)=delete
AccessCompletion(Stream const &stream) noexcept
Record the current stream tail, synchronizing only if publication fails.
Definition buffer.hpp:1163
Scoped read-only CUDA pointer access after a host-side synchronization.
Definition buffer.hpp:1211
T element_type
Definition buffer.hpp:1213
~BlockingReadAccess()
Definition buffer.hpp:1227
BlockingReadAccess(BlockingReadAccess const &)=delete
CudaBuffer< T > const & storage() const
Return the CUDA buffer retained by this access object.
Definition buffer.hpp:1233
T const * data() const noexcept
Return the typed device pointer for a blocking runtime operation.
Definition buffer.hpp:1230
void release() noexcept
End the scoped host-synchronized read access.
Definition buffer.hpp:1243
BlockingReadAccess(BlockingReadAccess &&other) noexcept
Definition buffer.hpp:1217
std::size_t size() const noexcept
Return the number of typed elements in the allocation.
Definition buffer.hpp:1240
BlockingReadAccess & operator=(BlockingReadAccess const &)=delete
BlockingReadAccess & operator=(BlockingReadAccess &&other) noexcept
Definition buffer.hpp:1218
Scoped read/write CUDA pointer access after a host-side synchronization.
Definition buffer.hpp:1266
std::size_t size() const noexcept
Return the number of typed elements in the allocation.
Definition buffer.hpp:1302
BlockingWriteAccess(BlockingWriteAccess &&other) noexcept
Definition buffer.hpp:1272
BlockingWriteAccess(BlockingWriteAccess const &)=delete
T * data() const noexcept
Return the typed device pointer for a blocking runtime operation.
Definition buffer.hpp:1285
CudaBuffer< T > const & storage() const
Return the read-only CUDA buffer retained by this access object.
Definition buffer.hpp:1295
CudaBuffer< T > & storage()
Return the mutable CUDA buffer retained by this access object.
Definition buffer.hpp:1288
BlockingWriteAccess & operator=(BlockingWriteAccess const &)=delete
~BlockingWriteAccess()
Definition buffer.hpp:1282
void release_with_completion(Completion completion) noexcept
Publish externally submitted device completion and end the scoped access.
Definition buffer.hpp:1315
BlockingWriteAccess & operator=(BlockingWriteAccess &&other) noexcept
Definition buffer.hpp:1273
T element_type
Definition buffer.hpp:1268
void release() noexcept
End the scoped host-synchronized write access.
Definition buffer.hpp:1305
Shared completion token for one submitted CUDA operation.
Definition runtime.hpp:104
void synchronize() const
Wait on the host for the operation to complete.
Definition runtime.cpp:327
Move-only logical CUDA buffer and completion state.
Definition buffer.hpp:222
WriteAccess< T > write_synchronized_with(Stream const &stream)
Acquire scoped read/write access synchronized with stream.
Definition buffer.hpp:351
CudaBuffer(CudaBuffer const &)=delete
void synchronize() const
Wait for every submitted operation currently involving this buffer.
Definition buffer.hpp:322
bool empty() const noexcept
Return whether this is a zero-sized or moved-from allocation.
Definition buffer.hpp:266
CudaBuffer(CudaBuffer &&other) noexcept
Definition buffer.hpp:245
BlockingReadAccess< T > blocking_read_access() const
Acquire read-only access after host-waiting for prior CUDA work.
Definition buffer.hpp:354
~CudaBuffer()
Definition buffer.hpp:257
std::size_t allocation_offset() const noexcept
Return this logical buffer's offset in its physical allocation.
Definition buffer.hpp:279
std::size_t size_bytes() const noexcept
Return the allocation size in bytes.
Definition buffer.hpp:263
CudaBuffer & operator=(CudaBuffer &&other) noexcept
Definition buffer.hpp:247
CudaBuffer & operator=(CudaBuffer const &)=delete
CudaBuffer(std::size_t size)
Allocate size elements on the installed runtime's default CUDA device.
Definition buffer.hpp:234
ReadAccess< T > read_synchronized_with(Stream const &stream) const
Acquire scoped read-only access synchronized with stream.
Definition buffer.hpp:345
OwningReadAccess< T > into_blocking_read_access() &&
Move this buffer into an owning host-synchronized read access.
Definition buffer.hpp:1401
bool shares_allocation_with(CudaBuffer const &other) const noexcept
Return whether another logical buffer shares this physical allocation.
Definition buffer.hpp:282
OwningReadAccess< T > into_read_synchronized_with(Stream const &stream) &&
Move this buffer into an owning stream-ordered read access.
Definition buffer.hpp:1408
Device device() const
Return the allocation's CUDA device.
Definition buffer.hpp:276
CudaBuffer(DeviceResources &resources, std::size_t size)
Allocate size elements from one CUDA device's resources.
Definition buffer.hpp:237
auto partition(std::span< CudaBufferRange const > ranges) &&-> std::vector< CudaBuffer >
Consume this buffer into statically disjoint logical child buffers.
Definition buffer.hpp:291
std::size_t size() const noexcept
Return the number of typed elements in the allocation.
Definition buffer.hpp:260
DeviceResources & resources() const
Return the device resources borrowed by this buffer.
Definition buffer.hpp:269
T element_type
Definition buffer.hpp:227
BlockingWriteAccess< T > blocking_write_access()
Acquire read/write access after host-waiting for prior CUDA work.
Definition buffer.hpp:357
Mutable stream and provider resources associated with one CUDA device.
Definition runtime.hpp:222
Device device() const noexcept
Return the device served by this resource set.
Definition runtime.hpp:241
Validated CUDA device identity with cached immutable capabilities.
Definition device.hpp:105
Read access that owns a moved CUDA buffer.
Definition buffer.hpp:1341
OwningReadAccess(OwningReadAccess &&other) noexcept
Definition buffer.hpp:1348
T element_type
Definition buffer.hpp:1343
T const * data() const noexcept
Return the typed device pointer owned by this access state.
Definition buffer.hpp:1358
~OwningReadAccess()
Definition buffer.hpp:1355
friend class CudaBuffer< T >
Definition buffer.hpp:1396
OwningReadAccess & operator=(OwningReadAccess const &)=delete
void release() noexcept
End access and destroy the owned allocation.
Definition buffer.hpp:1361
OwningReadAccess(OwningReadAccess const &)=delete
OwningReadAccess & operator=(OwningReadAccess &&)=delete
One CUDA allocation partitioned into independent logical buffers.
Definition buffer.hpp:709
PartitionedCudaBuffer & operator=(PartitionedCudaBuffer &&other) noexcept
Definition buffer.hpp:724
std::size_t buffer_count() const noexcept
Return the number of logical child buffers.
Definition buffer.hpp:739
auto read_synchronized_with(Stream const &stream) const -> PartitionedReadAccess< T >
Acquire read access to the complete contiguous allocation.
Definition buffer.hpp:1195
PartitionedCudaBuffer(DeviceResources &resources, std::size_t size, std::span< CudaBufferRange const > ranges)
Definition buffer.hpp:714
PartitionedCudaBuffer(PartitionedCudaBuffer &&other) noexcept
Definition buffer.hpp:720
Device device() const
Return the CUDA device containing the physical allocation.
Definition buffer.hpp:755
std::size_t size() const noexcept
Return the total physical allocation size in elements.
Definition buffer.hpp:736
PartitionedCudaBuffer & operator=(PartitionedCudaBuffer const &)=delete
DeviceResources & resources() const
Return the device resources shared by every child buffer.
Definition buffer.hpp:748
T element_type
Definition buffer.hpp:711
PartitionedCudaBuffer(PartitionedCudaBuffer const &)=delete
CudaBuffer< T > & buffer(std::size_t ordinal)
Return one mutable logical child buffer.
Definition buffer.hpp:742
auto write_synchronized_with(Stream const &stream) -> PartitionedWriteAccess< T >
Acquire write access to the complete contiguous allocation.
Definition buffer.hpp:1202
CudaBuffer< T > const & buffer(std::size_t ordinal) const
Return one read-only logical child buffer.
Definition buffer.hpp:745
Scoped read access to every child of one partitioned CUDA allocation.
Definition buffer.hpp:975
void release() noexcept
Record and publish one shared reader completion.
Definition buffer.hpp:1005
PartitionedReadAccess & operator=(PartitionedReadAccess const &)=delete
PartitionedReadAccess(PartitionedReadAccess const &)=delete
std::size_t size() const noexcept
Return the physical allocation size in elements.
Definition buffer.hpp:1002
T const * data() const noexcept
Return the first element of the complete physical allocation.
Definition buffer.hpp:999
T element_type
Definition buffer.hpp:977
void release_after_synchronization() noexcept
End access after the caller has synchronized the operation stream.
Definition buffer.hpp:1031
PartitionedReadAccess & operator=(PartitionedReadAccess &&other) noexcept
Definition buffer.hpp:985
PartitionedReadAccess(PartitionedReadAccess &&other) noexcept
Definition buffer.hpp:981
~PartitionedReadAccess()
Definition buffer.hpp:996
void release_with_completion(Completion completion) noexcept
Publish a caller-recorded completion to every child ledger.
Definition buffer.hpp:1021
friend class PartitionedCudaBuffer< T >
Definition buffer.hpp:1060
Scoped write access to every child of one partitioned CUDA allocation.
Definition buffer.hpp:1067
~PartitionedWriteAccess()
Definition buffer.hpp:1088
PartitionedWriteAccess & operator=(PartitionedWriteAccess const &)=delete
PartitionedWriteAccess(PartitionedWriteAccess const &)=delete
void release_after_synchronization() noexcept
End access after the caller has synchronized the operation stream.
Definition buffer.hpp:1123
PartitionedWriteAccess & operator=(PartitionedWriteAccess &&other) noexcept
Definition buffer.hpp:1077
T element_type
Definition buffer.hpp:1069
PartitionedWriteAccess(PartitionedWriteAccess &&other) noexcept
Definition buffer.hpp:1073
std::size_t size() const noexcept
Return the physical allocation size in elements.
Definition buffer.hpp:1094
void release_with_completion(Completion completion) noexcept
Publish a caller-recorded completion to every child ledger.
Definition buffer.hpp:1113
void release() noexcept
Record and publish one shared writer completion.
Definition buffer.hpp:1097
T * data() const noexcept
Return the first element of the complete physical allocation.
Definition buffer.hpp:1091
friend class PartitionedCudaBuffer< T >
Definition buffer.hpp:1152
Scoped read-only access to a CUDA buffer on one stream.
Definition buffer.hpp:798
T const * data() const noexcept
Return the typed device pointer for read-only CUDA work.
Definition buffer.hpp:820
T element_type
Definition buffer.hpp:800
std::size_t size() const noexcept
Return the number of typed elements in the view.
Definition buffer.hpp:830
friend class CudaBuffer< T >
Definition buffer.hpp:876
void release() noexcept
Publish this read completion and end the scoped access.
Definition buffer.hpp:836
void release_with_completion(Completion completion) noexcept
Publish a caller-recorded completion and end scoped access.
Definition buffer.hpp:848
ReadAccess(ReadAccess &&other) noexcept
Definition buffer.hpp:804
std::size_t size_bytes() const noexcept
Return the number of bytes in the view.
Definition buffer.hpp:833
ReadAccess & operator=(ReadAccess const &)=delete
void release_after_synchronization() noexcept
End access after the caller has synchronized the operation stream.
Definition buffer.hpp:858
ReadAccess & operator=(ReadAccess &&other) noexcept
Definition buffer.hpp:807
~ReadAccess()
Definition buffer.hpp:817
CudaBuffer< T > const & storage() const
Return the CUDA buffer retained by this access object.
Definition buffer.hpp:823
ReadAccess(ReadAccess const &)=delete
Reference-counted lease of one CUDA stream-pool slot.
Definition runtime.hpp:65
Completion record_completion() const
Record and return an immutable completion for the current stream tail.
Definition runtime.cpp:298
Scoped read/write access to a CUDA buffer on one stream.
Definition buffer.hpp:883
T element_type
Definition buffer.hpp:885
CudaBuffer< T > & storage()
Return the mutable CUDA buffer retained by this access object.
Definition buffer.hpp:908
CudaBuffer< T > const & storage() const
Return the read-only CUDA buffer retained by this access object.
Definition buffer.hpp:915
WriteAccess & operator=(WriteAccess &&other) noexcept
Definition buffer.hpp:892
std::size_t size() const noexcept
Return the number of typed elements in the view.
Definition buffer.hpp:922
friend class CudaBuffer< T >
Definition buffer.hpp:968
void release_with_completion(Completion completion) noexcept
Publish a caller-recorded completion and end scoped access.
Definition buffer.hpp:940
WriteAccess(WriteAccess const &)=delete
void release_after_synchronization() noexcept
End access after the caller has synchronized the operation stream.
Definition buffer.hpp:950
void release() noexcept
Publish this write completion and end the scoped access.
Definition buffer.hpp:928
WriteAccess(WriteAccess &&other) noexcept
Definition buffer.hpp:889
std::size_t size_bytes() const noexcept
Return the number of bytes in the view.
Definition buffer.hpp:925
~WriteAccess()
Definition buffer.hpp:902
WriteAccess & operator=(WriteAccess const &)=delete
T * data() const noexcept
Return the typed device pointer for mutating CUDA work.
Definition buffer.hpp:905
Structured CUDA runtime failures and checked-call helpers.
CUDA device discovery and immutable hardware capability snapshots.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
void check(cublasStatus_t status, std::string_view operation, int device, std::source_location where)
Raise unless a cuBLAS call returned CUBLAS_STATUS_SUCCESS.
Definition cublas_error.cpp:52
Definition cublas/execution.hpp:18
DeviceResources & device_resources()
Return canonical resources for the installed runtime's default device.
Definition runtime.cpp:820
stream
Selects the ordinary display stream for a message.
Definition display.hpp:25
void status(presentation::semantic_glyph glyph, checked_format_string< Args... > format, Args &&... args)
Emit a semantic status message through the active display sink.
Definition display.hpp:388
std::size_t bytes
Definition presentation.cpp:18
CUDA device, stream, resource, and scoped runtime primitives.
One logical range used to partition a CUDA allocation.
Definition buffer.hpp:42
std::size_t offset
Definition buffer.hpp:43
std::size_t size
Definition buffer.hpp:44
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309