Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
partitioned_buffer_linear.hpp
Go to the documentation of this file.
1#pragma once
2
10#include <uni20/config.hpp>
15
16#if UNI20_BACKEND_CUBLAS
19#endif
20
21#include <array>
22#include <concepts>
23#include <cstddef>
24#include <cstdint>
25#include <limits>
26#include <type_traits>
27
28namespace uni20::linalg
29{
30namespace partitioned_buffer_detail
31{
32
33template <std::size_t OperandCount, std::size_t MaximumRank>
34[[nodiscard]] auto make_contiguous_plan(std::size_t size)
35 -> detail::cuda_reference::LoweredStridedElementwisePlan<OperandCount, MaximumRank>
36{
37 using namespace detail::cuda_reference;
38 LoweredStridedElementwisePlan<OperandCount, MaximumRank> result;
39 if (size <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
40 {
41 result.index_kind = ElementwiseIndexKind::index_32;
42 result.plan_32.element_count = static_cast<std::uint32_t>(size);
43 result.plan_32.compact_rank = size == 0 ? 0 : 1;
44 if (size != 0)
45 {
46 result.plan_32.extents[0] = static_cast<std::uint32_t>(size);
47 result.plan_32.strides[0].fill(1);
48 }
49 }
50 else
51 {
52 result.index_kind = ElementwiseIndexKind::index_64;
53 result.plan_64.element_count = static_cast<std::uint64_t>(size);
54 result.plan_64.compact_rank = size == 0 ? 0 : 1;
55 if (size != 0)
56 {
57 result.plan_64.extents[0] = static_cast<std::uint64_t>(size);
58 result.plan_64.strides[0].fill(1);
59 }
60 }
61 return result;
62}
63
64template <class... Accesses> void publish(cuda::Stream const& stream, Accesses&... accesses)
65{
66 cuda::AccessCompletion completion(stream);
67 (completion.release(accesses), ...);
68}
69
70} // namespace partitioned_buffer_detail
71
73template <class Scalar>
74inline constexpr bool cuda_partitioned_fill_scalar = detail::cuda_reference::supports_elementwise_arithmetic<Scalar>;
75
77template <class Scalar, class Factor>
78inline constexpr bool cuda_partitioned_scale_scalars =
79 detail::cuda_reference::supports_elementwise_scale<Scalar, std::remove_cvref_t<Factor>>;
80
82template <class Scalar>
83 requires cuda_partitioned_fill_scalar<Scalar>
85{
86 if (output.size() == 0) return;
87 using namespace detail::cuda_reference;
88 auto stream = output.resources().streams().acquire();
89 auto access = output.write_synchronized_with(stream);
90 auto const plan =
91 partitioned_buffer_detail::make_contiguous_plan<1, elementwise_arithmetic_maximum_rank>(output.size());
92 cuda::ScopedDevice guard(output.device().ordinal());
93 plan.visit([&](auto const& concrete) {
94 enqueue_elementwise_fill(access.data(), Scalar{}, concrete, stream.native_handle(), stream.device());
95 });
97}
98
100template <class Scalar, class Factor>
101 requires cuda_partitioned_scale_scalars<Scalar, Factor>
103{
104 if (output.size() == 0) return;
105 using namespace detail::cuda_reference;
106 auto stream = output.resources().streams().acquire();
107 auto access = output.write_synchronized_with(stream);
108 auto const plan =
109 partitioned_buffer_detail::make_contiguous_plan<1, elementwise_arithmetic_maximum_rank>(output.size());
110 cuda::ScopedDevice guard(output.device().ordinal());
111 plan.visit([&](auto const& concrete) {
112 enqueue_elementwise_inplace_scale(access.data(), factor, concrete, stream.native_handle(), stream.device());
113 });
115}
116
118template <class Scalar, class Factor>
119 requires cuda_partitioned_scale_scalars<Scalar, Factor>
122{
123 CHECK_EQUAL(output.size(), input.size());
124 if (output.size() == 0) return;
125 CHECK_EQUAL(output.device().ordinal(), input.device().ordinal());
126 using namespace detail::cuda_reference;
127 auto stream = output.resources().streams().acquire();
128 auto output_access = output.write_synchronized_with(stream);
129 auto input_access = input.read_synchronized_with(stream);
130 auto const plan = partitioned_buffer_detail::make_contiguous_plan<2, elementwise_scale_maximum_rank>(output.size());
131 cuda::ScopedDevice guard(output.device().ordinal());
132 plan.visit([&](auto const& concrete) {
133 enqueue_elementwise_scale(output_access.data(), input_access.data(), factor, concrete, stream.native_handle(),
134 stream.device());
135 });
136 partitioned_buffer_detail::publish(stream, output_access, input_access);
137}
138
140template <class Scalar, class Factor>
141 requires cuda_partitioned_scale_scalars<Scalar, Factor>
144{
145 CHECK_EQUAL(output.size(), input.size());
146 if (output.size() == 0) return;
147 CHECK_EQUAL(output.device().ordinal(), input.device().ordinal());
148 using namespace detail::cuda_reference;
149 auto stream = output.resources().streams().acquire();
150 auto output_access = output.write_synchronized_with(stream);
151 auto input_access = input.read_synchronized_with(stream);
152 auto const plan =
153 partitioned_buffer_detail::make_contiguous_plan<2, elementwise_arithmetic_maximum_rank>(output.size());
154 cuda::ScopedDevice guard(output.device().ordinal());
155 plan.visit([&](auto const& concrete) {
156 enqueue_elementwise_add_scaled(output_access.data(), input_access.data(), factor, concrete, stream.native_handle(),
157 stream.device());
158 });
159 partitioned_buffer_detail::publish(stream, output_access, input_access);
160}
161
162#if UNI20_BACKEND_CUBLAS
163
165[[nodiscard]] constexpr auto cuda_partitioned_reduction_size_supported(std::size_t size) noexcept -> bool
166{
167 return size <= static_cast<std::size_t>(std::numeric_limits<int>::max());
168}
169
171template <uni20::cublas::CublasLevelOneScalar Scalar>
172[[nodiscard]] auto cuda_partitioned_inner_product_host(cuda::PartitionedCudaBuffer<Scalar> const& lhs,
173 cuda::PartitionedCudaBuffer<Scalar> const& rhs) -> Scalar
174{
175 CHECK_EQUAL(lhs.size(), rhs.size());
176 CHECK(cuda_partitioned_reduction_size_supported(lhs.size()), lhs.size());
177 if (lhs.size() == 0) return Scalar{};
178 CHECK_EQUAL(lhs.device().ordinal(), rhs.device().ordinal());
179 auto execution = uni20::cublas::execution_pool(lhs.resources()).acquire();
180 auto lhs_access = lhs.read_synchronized_with(execution.stream());
181 auto rhs_access = rhs.read_synchronized_with(execution.stream());
182 auto result = uni20::cublas::dotc(execution, static_cast<int>(lhs.size()), lhs_access.data(), rhs_access.data());
183 lhs_access.release_after_synchronization();
184 rhs_access.release_after_synchronization();
185 return result;
186}
187
189template <uni20::cublas::CublasLevelOneScalar Scalar>
190[[nodiscard]] auto cuda_partitioned_norm_host(cuda::PartitionedCudaBuffer<Scalar> const& input) -> make_real_t<Scalar>
191{
192 CHECK(cuda_partitioned_reduction_size_supported(input.size()), input.size());
193 if (input.size() == 0) return make_real_t<Scalar>{};
194 auto execution = uni20::cublas::execution_pool(input.resources()).acquire();
195 auto access = input.read_synchronized_with(execution.stream());
196 auto result = uni20::cublas::nrm2(execution, static_cast<int>(input.size()), access.data());
197 access.release_after_synchronization();
198 return result;
199}
200
201#endif
202
203} // namespace uni20::linalg
Typed CUDA device buffers and scoped stream access guards.
ExecutionLease acquire()
Block until a handle and then an idle stream are available.
Definition cublas/execution.cpp:203
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
StreamPool & streams() noexcept
Return the device's actually-idle stream pool.
Definition runtime.hpp:244
constexpr int ordinal() const noexcept
Return this device's CUDA runtime ordinal.
Definition device.hpp:123
One CUDA allocation partitioned into independent logical buffers.
Definition buffer.hpp:709
auto read_synchronized_with(Stream const &stream) const -> PartitionedReadAccess< T >
Acquire read access to the complete contiguous allocation.
Definition buffer.hpp:1195
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
DeviceResources & resources() const
Return the device resources shared by every child buffer.
Definition buffer.hpp:748
auto write_synchronized_with(Stream const &stream) -> PartitionedWriteAccess< T >
Acquire write access to the complete contiguous allocation.
Definition buffer.hpp:1202
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
Stream acquire()
Block until one actually idle stream can be leased.
Definition runtime.cpp:594
Reference-counted lease of one CUDA stream-pool slot.
Definition runtime.hpp:65
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
cuBLAS handle slots and dynamically paired stream execution leases.
Compiled CUDA entry points for named stateless arithmetic transforms.
Typed launch boundary for CUDA accessor-respecting element copies.
CUDA lowering for compact affine elementwise iteration plans.
Compiled CUDA entry points for elementwise scaling.
Checked cuBLAS dot-product and Euclidean-norm wrappers.
auto nrm2(ExecutionLease &execution, int size, Scalar const *input) -> uni20::make_real_t< Scalar >
Compute a Euclidean norm and return a host real scalar.
Definition level1.hpp:117
auto dotc(ExecutionLease &execution, int size, Scalar const *lhs, Scalar const *rhs) -> Scalar
Compute the conjugate-linear dot product and return a host scalar.
Definition level1.hpp:62
ExecutionPool & execution_pool(cuda::DeviceResources &resources)
Return the lazily constructed cuBLAS execution pool for one CUDA device.
Definition cublas/execution.cpp:215
void publish(cuda::Stream const &stream, Accesses &... accesses)
Definition partitioned_buffer_linear.hpp:64
auto make_contiguous_plan(std::size_t size) -> detail::cuda_reference::LoweredStridedElementwisePlan< OperandCount, MaximumRank >
Definition partitioned_buffer_linear.hpp:34
Definition linalg/async.hpp:28
void cuda_partitioned_axpy(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor, cuda::PartitionedCudaBuffer< Scalar > const &input)
Add a scaled partitioned CUDA allocation to another in place.
Definition partitioned_buffer_linear.hpp:142
void cuda_partitioned_set_zero(cuda::PartitionedCudaBuffer< Scalar > &output)
Set a complete partitioned CUDA allocation to numerical zero.
Definition partitioned_buffer_linear.hpp:84
void cuda_partitioned_scale(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor)
Scale a complete partitioned CUDA allocation in place.
Definition partitioned_buffer_linear.hpp:102
void cuda_partitioned_assign_scale(cuda::PartitionedCudaBuffer< Scalar > &output, Factor factor, cuda::PartitionedCudaBuffer< Scalar > const &input)
Overwrite one partitioned CUDA allocation with a scaled peer allocation.
Definition partitioned_buffer_linear.hpp:120
constexpr bool cuda_partitioned_fill_scalar
Whether allocation-wide CUDA fill operations support Scalar.
Definition partitioned_buffer_linear.hpp:74
constexpr bool cuda_partitioned_scale_scalars
Whether allocation-wide CUDA scale operations support this scalar pair.
Definition partitioned_buffer_linear.hpp:78
#define CHECK(cond,...)
Definition trace.hpp:299
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309