Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
backend/cublas/gemm.hpp
Go to the documentation of this file.
1#pragma once
2
13#include <uni20/core/types.hpp>
14
15#include <cuComplex.h>
16#include <cublas_v2.h>
17
18#include <algorithm>
19#include <concepts>
20#include <type_traits>
21
22namespace uni20::cublas
23{
24
26template <class Scalar>
27concept CublasScalar = std::same_as<Scalar, float> || std::same_as<Scalar, double> ||
28 std::same_as<Scalar, uni20::cfloat> || std::same_as<Scalar, uni20::cdouble>;
29
30namespace detail
31{
32
33inline cublasOperation_t operation(char transform)
34{
35 switch (transform)
36 {
37 case 'N':
38 case 'n':
39 return CUBLAS_OP_N;
40 case 'T':
41 case 't':
42 return CUBLAS_OP_T;
43 case 'C':
44 case 'c':
45 return CUBLAS_OP_C;
46 }
47 PANIC("invalid cuBLAS matrix transform", transform);
48}
49
50template <CublasScalar Scalar>
51void gemm_call(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int rows, int cols, int inner,
52 Scalar const& alpha, Scalar const* lhs, int lhs_leading_dimension, Scalar const* rhs,
53 int rhs_leading_dimension, Scalar const& beta, Scalar* output, int output_leading_dimension, int device)
54{
55 cublasStatus_t status = CUBLAS_STATUS_INTERNAL_ERROR;
56 if constexpr (std::same_as<Scalar, float>)
57 {
58 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasSgemm, rows, cols, inner);
59 status = cublasSgemm(handle, transa, transb, rows, cols, inner, &alpha, lhs, lhs_leading_dimension, rhs,
60 rhs_leading_dimension, &beta, output, output_leading_dimension);
61 }
62 else if constexpr (std::same_as<Scalar, double>)
63 {
64 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasDgemm, rows, cols, inner);
65 status = cublasDgemm(handle, transa, transb, rows, cols, inner, &alpha, lhs, lhs_leading_dimension, rhs,
66 rhs_leading_dimension, &beta, output, output_leading_dimension);
67 }
68 else if constexpr (std::same_as<Scalar, uni20::cfloat>)
69 {
70 static_assert(sizeof(Scalar) == sizeof(cuComplex));
71 cuComplex const provider_alpha = make_cuComplex(alpha.real(), alpha.imag());
72 cuComplex const provider_beta = make_cuComplex(beta.real(), beta.imag());
73 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasCgemm, rows, cols, inner);
74 status =
75 cublasCgemm(handle, transa, transb, rows, cols, inner, &provider_alpha, reinterpret_cast<cuComplex const*>(lhs),
76 lhs_leading_dimension, reinterpret_cast<cuComplex const*>(rhs), rhs_leading_dimension,
77 &provider_beta, reinterpret_cast<cuComplex*>(output), output_leading_dimension);
78 }
79 else
80 {
81 static_assert(std::same_as<Scalar, uni20::cdouble>);
82 static_assert(sizeof(Scalar) == sizeof(cuDoubleComplex));
83 cuDoubleComplex const provider_alpha = make_cuDoubleComplex(alpha.real(), alpha.imag());
84 cuDoubleComplex const provider_beta = make_cuDoubleComplex(beta.real(), beta.imag());
85 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasZgemm, rows, cols, inner);
86 status = cublasZgemm(handle, transa, transb, rows, cols, inner, &provider_alpha,
87 reinterpret_cast<cuDoubleComplex const*>(lhs), lhs_leading_dimension,
88 reinterpret_cast<cuDoubleComplex const*>(rhs), rhs_leading_dimension, &provider_beta,
89 reinterpret_cast<cuDoubleComplex*>(output), output_leading_dimension);
90 }
91 check(status, "cublasGemm", device);
92}
93
94} // namespace detail
95
97template <CublasScalar Scalar>
98void gemm(ExecutionLease& execution, char lhs_transform, char rhs_transform, int rows, int cols, int inner,
99 Scalar alpha, Scalar const* lhs, int lhs_leading_dimension, Scalar const* rhs, int rhs_leading_dimension,
100 Scalar beta, Scalar* output, int output_leading_dimension)
101{
102 CHECK(execution);
103 CHECK(rows >= 0 && cols >= 0 && inner >= 0, rows, cols, inner);
104 auto const transa = detail::operation(lhs_transform);
105 auto const transb = detail::operation(rhs_transform);
106 if (rows == 0 || cols == 0) return;
107
108 int const minimum_lhs_leading_dimension = std::max(1, transa == CUBLAS_OP_N ? rows : inner);
109 int const minimum_rhs_leading_dimension = std::max(1, transb == CUBLAS_OP_N ? inner : cols);
110 int const minimum_output_leading_dimension = std::max(1, rows);
111 CHECK(lhs_leading_dimension >= minimum_lhs_leading_dimension, lhs_leading_dimension, minimum_lhs_leading_dimension);
112 CHECK(rhs_leading_dimension >= minimum_rhs_leading_dimension, rhs_leading_dimension, minimum_rhs_leading_dimension);
113 CHECK(output_leading_dimension >= minimum_output_leading_dimension, output_leading_dimension,
114 minimum_output_leading_dimension);
115 cuda::ScopedDevice guard(execution.stream().device());
116 detail::gemm_call(execution.handle().native_handle(), transa, transb, rows, cols, inner, alpha, lhs,
117 lhs_leading_dimension, rhs, rhs_leading_dimension, beta, output, output_leading_dimension,
118 execution.stream().device());
119}
120
121} // namespace uni20::cublas
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
cublasHandle_t native_handle() const noexcept
Return the raw handle for narrow provider-wrapper use.
Definition cublas/execution.hpp:41
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
int device() const noexcept
Return the device that owns this stream.
Definition runtime.cpp:280
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
Scalar types supported by the cuBLAS GEMM wrappers.
Definition backend/cublas/gemm.hpp:27
cuda::ResourceLease< HandleSlot > handle
Definition cublas/execution.cpp:33
cuBLAS handle slots and dynamically paired stream execution leases.
Structured cuBLAS failures and checked-call helpers.
#define UNI20_EXTERNAL_API_CALL(module, func,...)
Emits a trace log entry for an outgoing backend API call.
Definition backend.hpp:90
Definition cublas_error.cpp:11
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
void gemm(ExecutionLease &execution, char lhs_transform, char rhs_transform, int rows, int cols, int inner, Scalar alpha, Scalar const *lhs, int lhs_leading_dimension, Scalar const *rhs, int rhs_leading_dimension, Scalar beta, Scalar *output, int output_leading_dimension)
Enqueue column-major GEMM through an exclusive handle/stream lease.
Definition backend/cublas/gemm.hpp:98
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382