22namespace uni20::linalg::detail::cublas_backend
25template <uni20::cublas::CublasScalar Scalar>
struct GemmPlan
27 blas::BlasWritableMatrix<Scalar, uni20::cuda::CudaBufferView<Scalar>> output{};
28 blas::BlasReadableMatrix<Scalar, uni20::cuda::CudaBufferView<Scalar const>> lhs{};
29 blas::BlasReadableMatrix<Scalar, uni20::cuda::CudaBufferView<Scalar const>> rhs{};
32 bool has_work =
false;
37 return output.data.buffer().resources();
41template <uni20::cublas::CublasScalar Scalar>
struct GemmPreparation
44 GemmPlan<Scalar> plan{};
47template <
class Accessor,
class Scalar>
struct IsCudaAccessorFor : std::false_type
50template <
class ElementType,
class Scalar>
51struct IsCudaAccessorFor<
uni20::cuda::CudaPointerAccessor<ElementType>, Scalar>
52 : std::bool_constant<std::same_as<std::remove_cv_t<ElementType>, Scalar>>
55template <
class Accessor,
class Scalar>
56struct IsCudaAccessorFor<
uni20::conjugated_accessor<Accessor>, Scalar> : IsCudaAccessorFor<Accessor, Scalar>
59template <
class Accessor,
class Scalar>
60inline constexpr bool cuda_accessor_for = IsCudaAccessorFor<std::remove_cvref_t<Accessor>, Scalar>
::value;
62template <
class Handle,
class Scalar>
struct IsCudaBufferViewFor : std::false_type
65template <
class ElementType,
class Scalar>
66struct IsCudaBufferViewFor<
uni20::cuda::CudaBufferView<ElementType>, Scalar>
67 : std::bool_constant<std::same_as<std::remove_cv_t<ElementType>, Scalar>>
70template <
class Handle,
class Scalar>
71inline constexpr bool is_cuda_buffer_view_for = IsCudaBufferViewFor<std::remove_cvref_t<Handle>, Scalar>
::value;
73template <
class Mdspan,
class Scalar>
74concept readable_cuda_mdspan_for =
76 std::same_as<std::remove_cv_t<typename std::remove_cvref_t<Mdspan>::element_type>, Scalar> &&
77 cuda_accessor_for<typename std::remove_cvref_t<Mdspan>::accessor_type, Scalar> &&
78 is_cuda_buffer_view_for<blas::detail::span_data_t<Mdspan>, Scalar>;
80template <
class Mdspan,
class Scalar>
81concept writable_cuda_mdspan_for =
83 std::same_as<typename std::remove_cvref_t<Mdspan>::element_type, Scalar> &&
85 is_cuda_buffer_view_for<blas::detail::span_data_t<Mdspan>, Scalar>;
87template <
class Scalar,
class OutputMdspan,
class LhsMdspan,
class RhsMdspan>
90 readable_cuda_mdspan_for<std::remove_cvref_t<LhsMdspan>, Scalar> &&
91 readable_cuda_mdspan_for<std::remove_cvref_t<RhsMdspan>, Scalar>;
94template <
class Scalar,
class OutputMdspan,
class LhsMdspan,
class RhsMdspan>
consteval bool accepts_gemm_types()
96 return GemmMdspans<Scalar, OutputMdspan, LhsMdspan, RhsMdspan>;
99template <
class Scalar,
class Handle> std::size_t required_elements(blas::BlasWritableMatrix<Scalar, Handle> matrix)
101 CHECK(matrix.rows >= 0 && matrix.cols >= 0 && matrix.leading_dimension > 0, matrix.rows, matrix.cols,
102 matrix.leading_dimension);
103 if (matrix.rows == 0 || matrix.cols == 0)
return 0;
105 auto const rows =
static_cast<std::size_t
>(matrix.rows);
106 auto const cols =
static_cast<std::size_t
>(matrix.cols);
107 auto const leading_dimension =
static_cast<std::size_t
>(matrix.leading_dimension);
108 CHECK(cols - 1 <= (std::numeric_limits<std::size_t>::max() - rows) / leading_dimension, rows, cols,
110 return (cols - 1) * leading_dimension +
rows;
113template <
class Scalar,
class Handle> std::size_t required_elements(blas::BlasReadableMatrix<Scalar, Handle> matrix)
115 return required_elements(blas::BlasWritableMatrix<Scalar, Handle>{
116 .data = matrix.data, .rows = matrix.rows, .cols = matrix.cols, .leading_dimension = matrix.leading_dimension});
119template <
class View,
class Matrix>
void require_view_covers_matrix(View
const& view, Matrix
const& matrix)
121 auto const offset = view.element_offset();
122 auto const size = view.buffer().size();
123 auto const required = required_elements(matrix);
124 CHECK(offset <= size && required <= size - offset, offset, required, size);
127template <
class Po
inter> Pointer offset_pointer(Pointer pointer, std::size_t offset)
129 if (offset == 0)
return pointer;
130 CHECK(pointer !=
nullptr, offset);
131 return pointer + offset;
134template <
class Scalar,
class Handle>
135auto with_data(blas::BlasWritableMatrix<Scalar, Handle> matrix, Scalar* data) -> blas::BlasWritableMatrix<Scalar>
137 return {.data = data, .rows = matrix.rows, .cols = matrix.cols, .leading_dimension = matrix.leading_dimension};
140template <
class Scalar,
class Handle>
141auto with_data(blas::BlasReadableMatrix<Scalar, Handle> matrix, Scalar
const* data) -> blas::BlasReadableMatrix<Scalar>
143 return {.data = data,
146 .leading_dimension = matrix.leading_dimension,
147 .transform = matrix.transform};
150template <uni20::cublas::CublasScalar Scalar,
class Handle>
151auto readonly_matrix(blas::BlasReadableMatrix<Scalar, Handle> matrix)
152 -> blas::BlasReadableMatrix<Scalar, uni20::cuda::CudaBufferView<Scalar const>>
157 .leading_dimension = matrix.leading_dimension,
158 .transform = matrix.transform};
161template <uni20::cublas::CublasScalar Scalar,
class LhsHandle,
class RhsHandle>
162GemmPreparation<Scalar>
164 blas::BlasReadableMatrix<Scalar, LhsHandle> lhs, blas::BlasReadableMatrix<Scalar, RhsHandle> rhs)
166 if (!uni20::linalg::cublas::detail::provider_transforms_are_supported(lhs, rhs))
171 blas_int
const inner = uni20::linalg::cublas::detail::require_gemm_shape(output, lhs, rhs);
172 if (output.rows == 0 || output.cols == 0)
177 auto& output_buffer = output.data.buffer();
178 auto const& lhs_buffer = lhs.data.buffer();
179 auto const& rhs_buffer = rhs.data.buffer();
180 CHECK(std::addressof(output_buffer) != std::addressof(lhs_buffer) &&
181 std::addressof(output_buffer) != std::addressof(rhs_buffer),
182 "cuBLAS GEMM output must not share a CUDA buffer with an input");
184 int const device = output_buffer.device().ordinal();
185 if (lhs_buffer.device().ordinal() != device || rhs_buffer.device().ordinal() != device)
189 require_view_covers_matrix(output.data, output);
190 require_view_covers_matrix(lhs.data, lhs);
191 require_view_covers_matrix(rhs.data, rhs);
194 .plan = {.output = output,
195 .lhs = readonly_matrix(lhs),
196 .rhs = readonly_matrix(rhs),
202template <uni20::cublas::CublasScalar Scalar,
class OutputMdspan,
class LhsMdspan,
class RhsMdspan>
203 requires GemmMdspans<Scalar, OutputMdspan, LhsMdspan, RhsMdspan>
204GemmPreparation<Scalar> prepare_gemm(OutputMdspan&& output, LhsMdspan&& lhs, RhsMdspan&& rhs)
210 if (output.extent(0) == 0 || output.extent(1) == 0)
213 .plan = {.device = blas::detail::span_data(output).buffer().device().ordinal()}};
219 if (!output_stage || !lhs_stage || !rhs_stage)
223 CHECK(!output_stage->needs_conjugation);
226 if (output_stage->unit_stride_axis == 0)
236 return prepare_staged_gemm(output_matrix, lhs_matrix, rhs_matrix);
239template <uni20::cublas::CublasScalar Scalar>
242 CHECK(plan.has_work);
245 auto& output_buffer = plan.output.data.buffer();
246 auto const& lhs_buffer = plan.lhs.data.buffer();
247 auto const& rhs_buffer = plan.rhs.data.buffer();
248 auto output_access = output_buffer.write_synchronized_with(execution.
stream());
249 auto lhs_access = lhs_buffer.read_synchronized_with(execution.
stream());
250 auto rhs_access = rhs_buffer.read_synchronized_with(execution.
stream());
252 auto const raw_output =
253 with_data(plan.output, offset_pointer(output_access.data(), plan.output.data.element_offset()));
254 auto const raw_lhs = with_data(plan.lhs, offset_pointer(lhs_access.data(), plan.lhs.data.element_offset()));
255 auto const raw_rhs = with_data(plan.rhs, offset_pointer(rhs_access.data(), plan.rhs.data.element_offset()));
259 completion.release(output_access);
260 completion.release(lhs_access);
261 completion.release(rhs_access);
264template <uni20::cublas::CublasScalar Scalar,
class OutputMdspan,
class LhsMdspan,
class RhsMdspan>
265 requires GemmMdspans<Scalar, OutputMdspan, LhsMdspan, RhsMdspan>
266KernelAttempt try_gemm(OutputMdspan&& output, Scalar alpha, LhsMdspan&& lhs, RhsMdspan&& rhs, Scalar
beta)
268 auto preparation = prepare_gemm<Scalar>(std::forward<OutputMdspan>(output), std::forward<LhsMdspan>(lhs),
269 std::forward<RhsMdspan>(rhs));
272 return preparation.attempt;
276 execute_gemm(execution, preparation.plan, alpha,
beta);
Typed CUDA device buffers and scoped stream access guards.
Move-only dynamic pairing of one cuBLAS handle slot and one idle stream.
Definition cublas/execution.hpp:56
cuda::Stream const & stream() const noexcept
Access the dynamically selected CUDA stream.
Definition cublas/execution.hpp:77
HandleSlot & handle() const
Access the exclusively leased cuBLAS handle slot.
Definition cublas/execution.hpp:74
ExecutionLease acquire()
Block until a handle and then an idle stream are available.
Definition cublas/execution.cpp:203
int device() const noexcept
Return the device on which this handle was created.
Definition cublas/execution.hpp:38
One operation-tail completion shared by multiple CUDA buffer accesses.
Definition buffer.hpp:1160
Non-owning opaque view into one logical CUDA buffer.
Definition cuda_storage.hpp:33
Mutable stream and provider resources associated with one CUDA device.
Definition runtime.hpp:222
Mutable strided mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:524
A strided mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:516
Scalar types supported by the cuBLAS GEMM wrappers.
Definition backend/cublas/gemm.hpp:27
CUDA Tensor storage with deferred device-memory data descriptors.
locale_t value
Definition data_table.cpp:49
Allocation-free runtime results for kernel backend attempts.
Checked provider-ready matrix wrappers over cuBLAS GEMM.
Lower mdspan matrix views to provider-ready BLAS operands.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
int rows(std::FILE *stream)
Return the terminal row count associated with a C stream.
Definition terminal.cpp:482
ExecutionPool & execution_pool(cuda::DeviceResources &resources)
Return the lazily constructed cuBLAS execution pool for one CUDA device.
Definition cublas/execution.cpp:215
constexpr auto blas_writable_matrix(MdspanMatrixStage< Scalar, Handle > const &stage) -> BlasWritableMatrix< Scalar, Handle >
Return the provider-ready writable operand for a staged mdspan matrix.
Definition mdspan_matrix.hpp:225
auto try_mdspan_matrix_stage(Mdspan const &span) -> std::optional< MdspanMatrixStage< std::remove_cv_t< typename Mdspan::element_type >, detail::span_data_t< Mdspan > > >
Build a matrix staging descriptor when direct provider lowering is possible.
Definition mdspan_matrix.hpp:202
constexpr auto blas_readable_matrix(MdspanMatrixStage< Scalar, Handle > const &stage) -> BlasReadableMatrix< Scalar, Handle >
Return the provider-ready readable operand for a staged mdspan matrix.
Definition mdspan_matrix.hpp:238
constexpr MatrixTransform compose(MatrixTransform outer, MatrixTransform inner)
Compose two matrix transforms as outer(inner(A)).
Definition blas_matrix.hpp:58
void gemm(uni20::cublas::ExecutionLease &execution, blas::BlasWritableMatrix< Scalar, OutputHandle > output, Scalar alpha, blas::BlasReadableMatrix< Scalar, LhsHandle > lhs, blas::BlasReadableMatrix< Scalar, RhsHandle > rhs, Scalar beta)
Run provider-ready column-major GEMM through cuBLAS.
Definition linalg/cublas/gemm.hpp:58
constexpr bool kernel_attempt_succeeded(KernelAttempt attempt) noexcept
Return whether a backend performed the operation.
Definition kernel_attempt.hpp:40
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
Definition async/async.hpp:26
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
Accessor for a CUDA mdspan whose device pointer has been leased.
Definition cuda_accessor.hpp:80
#define CHECK(cond,...)
Definition trace.hpp:299
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309