Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
linalg/backends/cublas/reductions.hpp
Go to the documentation of this file.
1#pragma once
2
16
17#include <concepts>
18#include <cstddef>
19#include <limits>
20#include <type_traits>
21
22namespace uni20::linalg
23{
24namespace detail::cublas_backend
25{
26
27template <class Output, class Scalar>
28concept HostScalarOutput = (!uni20::MdspecLike<Output>) && std::same_as<std::remove_cvref_t<Output>, Scalar> &&
29 requires(Output& output, Scalar value) { output = value; };
30
31template <class Mdspec, class Scalar>
32concept RawReadableCudaReductionMdspec =
35 std::same_as<typename std::remove_cvref_t<Mdspec>::element_type, Scalar const> &&
36 std::same_as<typename std::remove_cvref_t<Mdspec>::accessor_type, uni20::cuda::CudaPointerAccessor<Scalar const>>;
37
38template <class Mdspec> [[nodiscard]] std::size_t reduction_element_count(Mdspec const& span)
39{
40 if (!span.mapping().is_unique() || !span.mapping().is_exhaustive()) return 0;
41 auto const required = span.mapping().required_span_size();
42 CHECK(std::cmp_greater_equal(required, 0), required);
43 return static_cast<std::size_t>(required);
44}
45
46template <class Mdspec> void require_reduction_descriptor_range(Mdspec const& span, std::size_t size)
47{
48 auto const& descriptor = span.data_descriptor();
49 auto const offset = descriptor.element_offset();
50 auto const buffer_size = descriptor.buffer().size();
51 CHECK(offset <= buffer_size && size <= buffer_size - offset, offset, size, buffer_size);
52}
53
54template <class Scalar, class LhsMdspec, class RhsMdspec>
55KernelAttempt try_inner_product(Scalar& output, LhsMdspec const& lhs, RhsMdspec const& rhs)
56{
57 static_assert(RawReadableCudaReductionMdspec<LhsMdspec, Scalar>);
58 static_assert(RawReadableCudaReductionMdspec<RhsMdspec, Scalar>);
59 static_assert(std::remove_cvref_t<LhsMdspec>::rank() == std::remove_cvref_t<RhsMdspec>::rank());
60 for (std::size_t axis = 0; axis < std::remove_cvref_t<LhsMdspec>::rank(); ++axis)
61 CHECK_EQUAL(lhs.extent(axis), rhs.extent(axis));
62
63 if (!lhs.mapping().is_unique() || !lhs.mapping().is_exhaustive() || !rhs.mapping().is_unique() ||
64 !rhs.mapping().is_exhaustive())
66 for (std::size_t axis = 0; axis < std::remove_cvref_t<LhsMdspec>::rank(); ++axis)
67 {
68 if (lhs.extent(axis) > 1 && lhs.stride(axis) != rhs.stride(axis)) return KernelAttempt::unsupported_layout;
69 }
70
71 auto const size = reduction_element_count(lhs);
72 CHECK_EQUAL(size, reduction_element_count(rhs));
73 if (size > static_cast<std::size_t>(std::numeric_limits<int>::max())) return KernelAttempt::unsupported_instance;
74 if (size == 0)
75 {
76 output = Scalar{};
78 }
79
80 auto const& lhs_descriptor = lhs.data_descriptor();
81 auto const& rhs_descriptor = rhs.data_descriptor();
82 if (lhs_descriptor.buffer().device() != rhs_descriptor.buffer().device()) return KernelAttempt::incompatible_devices;
83 require_reduction_descriptor_range(lhs, size);
84 require_reduction_descriptor_range(rhs, size);
85
86 auto execution = uni20::cublas::execution_pool(lhs_descriptor.buffer().resources()).acquire();
87 auto lhs_access = lhs_descriptor.buffer().read_synchronized_with(execution.stream());
88 auto rhs_access = rhs_descriptor.buffer().read_synchronized_with(execution.stream());
89 output = uni20::cublas::dotc(execution, static_cast<int>(size), lhs_access.data() + lhs_descriptor.element_offset(),
90 rhs_access.data() + rhs_descriptor.element_offset());
91 lhs_access.release_after_synchronization();
92 rhs_access.release_after_synchronization();
94}
95
96template <class Scalar, class InputMdspec>
97KernelAttempt try_norm(uni20::make_real_t<Scalar>& output, InputMdspec const& input)
98{
99 static_assert(RawReadableCudaReductionMdspec<InputMdspec, Scalar>);
100 if (!input.mapping().is_unique() || !input.mapping().is_exhaustive()) return KernelAttempt::unsupported_layout;
101
102 auto const size = reduction_element_count(input);
103 if (size > static_cast<std::size_t>(std::numeric_limits<int>::max())) return KernelAttempt::unsupported_instance;
104 if (size == 0)
105 {
108 }
109
110 auto const& descriptor = input.data_descriptor();
111 require_reduction_descriptor_range(input, size);
112 auto execution = uni20::cublas::execution_pool(descriptor.buffer().resources()).acquire();
113 auto access = descriptor.buffer().read_synchronized_with(execution.stream());
114 output = uni20::cublas::nrm2(execution, static_cast<int>(size), access.data() + descriptor.element_offset());
115 access.release_after_synchronization();
117}
118
119} // namespace detail::cublas_backend
120
122template <class Output, uni20::MdspecLike LhsMdspec, uni20::MdspecLike RhsMdspec>
123consteval auto kernel_accepts_types(CublasBackend const&, inner_product_op const&, Output&, LhsMdspec&, RhsMdspec&)
124{
125 using lhs_type = std::remove_cvref_t<LhsMdspec>;
126 using rhs_type = std::remove_cvref_t<RhsMdspec>;
127 if constexpr (lhs_type::rank() == rhs_type::rank() &&
128 std::same_as<typename lhs_type::value_type, typename rhs_type::value_type> &&
129 detail::cublas_backend::HostScalarOutput<Output, typename lhs_type::value_type> &&
130 detail::cublas_backend::RawReadableCudaReductionMdspec<LhsMdspec, typename lhs_type::value_type> &&
131 detail::cublas_backend::RawReadableCudaReductionMdspec<RhsMdspec, typename lhs_type::value_type>)
132 return kernel_types_maybe;
133 else
134 return kernel_types_no;
135}
136
138template <class Output, uni20::MdspecLike LhsMdspec, uni20::MdspecLike RhsMdspec>
139 requires detail::cublas_backend::HostScalarOutput<Output, typename std::remove_cvref_t<LhsMdspec>::value_type> &&
140 detail::cublas_backend::RawReadableCudaReductionMdspec<
141 LhsMdspec, typename std::remove_cvref_t<LhsMdspec>::value_type> &&
142 detail::cublas_backend::RawReadableCudaReductionMdspec<RhsMdspec,
143 typename std::remove_cvref_t<LhsMdspec>::value_type>
144KernelAttempt try_kernel(CublasBackend, inner_product_op const&, Output& output, LhsMdspec& lhs, RhsMdspec& rhs)
145{
146 return detail::cublas_backend::try_inner_product(output, lhs, rhs);
147}
148
150template <class Output, uni20::MdspecLike InputMdspec>
151consteval auto kernel_accepts_types(CublasBackend const&, norm_op const&, Output&, InputMdspec&)
152{
153 using scalar_type = typename std::remove_cvref_t<InputMdspec>::value_type;
154 if constexpr (detail::cublas_backend::HostScalarOutput<Output, uni20::make_real_t<scalar_type>> &&
155 detail::cublas_backend::RawReadableCudaReductionMdspec<InputMdspec, scalar_type>)
156 return kernel_types_maybe;
157 else
158 return kernel_types_no;
159}
160
162template <class Output, uni20::MdspecLike InputMdspec>
163 requires detail::cublas_backend::HostScalarOutput<
165 detail::cublas_backend::RawReadableCudaReductionMdspec<InputMdspec,
166 typename std::remove_cvref_t<InputMdspec>::value_type>
167KernelAttempt try_kernel(CublasBackend, norm_op const&, Output& output, InputMdspec& input)
168{
169 using scalar_type = typename std::remove_cvref_t<InputMdspec>::value_type;
170 return detail::cublas_backend::try_norm<scalar_type>(output, input);
171}
172
173} // 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
Mdspan metadata whose data handle is immediate or available through a data descriptor.
Definition mdspan/concepts.hpp:304
An mdspec-like type whose mapping exposes multidimensional strides.
Definition mdspan/concepts.hpp:427
Scalar types supported by the cuBLAS level-one wrappers.
Definition level1.hpp:27
CUDA-accessible mdspec backed by a CudaBufferView descriptor.
Definition cuda_storage.hpp:118
cuBLAS handle slots and dynamically paired stream execution leases.
CUDA-device-callable accessors for leased Tensor storage.
CUDA Tensor storage with deferred device-memory data descriptors.
locale_t value
Definition data_table.cpp:49
Operation-tag backend dispatch over tensor-view operands.
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Checked cuBLAS dot-product and Euclidean-norm wrappers.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
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
Definition linalg/async.hpp:28
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr KernelAcceptance< KernelTypeAcceptance::no > kernel_types_no
Report that a backend rejects the argument types.
Definition dispatch_error.hpp:34
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
constexpr KernelAcceptance< KernelTypeAcceptance::maybe > kernel_types_maybe
Report that runtime operand values determine backend acceptance.
Definition dispatch_error.hpp:37
Backend-independent operation values used with normalized kernel operands.
std::size_t span
Definition presentation.cpp:2017
Accessor for a CUDA mdspan whose device pointer has been leased.
Definition cuda_accessor.hpp:80
Backend value for cuBLAS dense linalg kernels.
Definition backend_selector.hpp:34
Conjugate-linear-left tensor inner-product operation tag.
Definition operation_tags.hpp:105
Euclidean tensor norm operation tag.
Definition operation_tags.hpp:111
Trait to extract the scalar type from a type T.
Definition scalar_traits.hpp:93
#define CHECK(cond,...)
Definition trace.hpp:299
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309