Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
level1.hpp
Go to the documentation of this file.
1#pragma once
2
14#include <uni20/core/types.hpp>
15
16#include <cuComplex.h>
17#include <cublas_v2.h>
18
19#include <concepts>
20#include <type_traits>
21
22namespace uni20::cublas
23{
24
26template <class Scalar>
27concept CublasLevelOneScalar = 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
33template <class Scalar> struct ProviderComplex;
34
35template <> struct ProviderComplex<uni20::cfloat>
36{
37 using type = cuComplex;
38};
39
40template <> struct ProviderComplex<uni20::cdouble>
41{
42 using type = cuDoubleComplex;
43};
44
45template <class Scalar> using provider_complex_t = typename ProviderComplex<Scalar>::type;
46
47template <class Scalar> consteval void require_complex_provider_abi()
48{
49 using provider_type = provider_complex_t<Scalar>;
50 static_assert(sizeof(Scalar) == sizeof(provider_type));
51 static_assert(std::is_trivially_copyable_v<Scalar>);
52 static_assert(std::is_trivially_copyable_v<provider_type>);
53}
54
55} // namespace detail
56
61template <CublasLevelOneScalar Scalar>
62[[nodiscard]] auto dotc(ExecutionLease& execution, int size, Scalar const* lhs, Scalar const* rhs) -> Scalar
63{
64 CHECK(execution);
65 CHECK(size >= 0, size);
66 if (size == 0) return Scalar{};
67 CHECK(lhs != nullptr && rhs != nullptr);
68
69 cuda::ScopedDevice guard(execution.stream().device());
70 cublasStatus_t status = CUBLAS_STATUS_INTERNAL_ERROR;
71 if constexpr (std::same_as<Scalar, float>)
72 {
73 Scalar result{};
74 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasSdot, size);
75 status = cublasSdot(execution.handle().native_handle(), size, lhs, 1, rhs, 1, &result);
76 check(status, "cublasSdot", execution.stream().device());
77 execution.stream().synchronize();
78 return result;
79 }
80 else if constexpr (std::same_as<Scalar, double>)
81 {
82 Scalar result{};
83 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasDdot, size);
84 status = cublasDdot(execution.handle().native_handle(), size, lhs, 1, rhs, 1, &result);
85 check(status, "cublasDdot", execution.stream().device());
86 execution.stream().synchronize();
87 return result;
88 }
89 else if constexpr (std::same_as<Scalar, uni20::cfloat>)
90 {
91 detail::require_complex_provider_abi<Scalar>();
92 cuComplex result{};
93 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasCdotc, size);
94 status = cublasCdotc(execution.handle().native_handle(), size, reinterpret_cast<cuComplex const*>(lhs), 1,
95 reinterpret_cast<cuComplex const*>(rhs), 1, &result);
96 check(status, "cublasCdotc", execution.stream().device());
97 execution.stream().synchronize();
98 return Scalar{result.x, result.y};
99 }
100 else
101 {
102 static_assert(std::same_as<Scalar, uni20::cdouble>);
103 detail::require_complex_provider_abi<Scalar>();
104 cuDoubleComplex result{};
105 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasZdotc, size);
106 status = cublasZdotc(execution.handle().native_handle(), size, reinterpret_cast<cuDoubleComplex const*>(lhs), 1,
107 reinterpret_cast<cuDoubleComplex const*>(rhs), 1, &result);
108 check(status, "cublasZdotc", execution.stream().device());
109 execution.stream().synchronize();
110 return Scalar{result.x, result.y};
111 }
112}
113
116template <CublasLevelOneScalar Scalar>
117[[nodiscard]] auto nrm2(ExecutionLease& execution, int size, Scalar const* input) -> uni20::make_real_t<Scalar>
118{
119 CHECK(execution);
120 CHECK(size >= 0, size);
121 using result_type = uni20::make_real_t<Scalar>;
122 if (size == 0) return result_type{};
123 CHECK(input != nullptr);
124
125 cuda::ScopedDevice guard(execution.stream().device());
126 result_type result{};
127 cublasStatus_t status = CUBLAS_STATUS_INTERNAL_ERROR;
128 if constexpr (std::same_as<Scalar, float>)
129 {
130 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasSnrm2, size);
131 status = cublasSnrm2(execution.handle().native_handle(), size, input, 1, &result);
132 check(status, "cublasSnrm2", execution.stream().device());
133 }
134 else if constexpr (std::same_as<Scalar, double>)
135 {
136 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasDnrm2, size);
137 status = cublasDnrm2(execution.handle().native_handle(), size, input, 1, &result);
138 check(status, "cublasDnrm2", execution.stream().device());
139 }
140 else if constexpr (std::same_as<Scalar, uni20::cfloat>)
141 {
142 detail::require_complex_provider_abi<Scalar>();
143 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasScnrm2, size);
144 status =
145 cublasScnrm2(execution.handle().native_handle(), size, reinterpret_cast<cuComplex const*>(input), 1, &result);
146 check(status, "cublasScnrm2", execution.stream().device());
147 }
148 else
149 {
150 static_assert(std::same_as<Scalar, uni20::cdouble>);
151 detail::require_complex_provider_abi<Scalar>();
152 UNI20_EXTERNAL_API_CALL(CUBLAS, cublasDznrm2, size);
153 status = cublasDznrm2(execution.handle().native_handle(), size, reinterpret_cast<cuDoubleComplex const*>(input), 1,
154 &result);
155 check(status, "cublasDznrm2", execution.stream().device());
156 }
157 execution.stream().synchronize();
158 return result;
159}
160
161} // namespace uni20::cublas
Move-only dynamic pairing of one cuBLAS handle slot and one idle stream.
Definition cublas/execution.hpp:56
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
Scalar types supported by the cuBLAS level-one wrappers.
Definition level1.hpp:27
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
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Definition cublas_error.cpp:11
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
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 async/async.hpp:26
complex< double > cdouble
Definition types.hpp:76
complex< float > cfloat
Definition types.hpp:75
#define CHECK(cond,...)
Definition trace.hpp:299