group_blas_symv.hh
Go to the documentation of this file.
1#pragma once
2
4
5namespace batchlas::device {
6
7namespace detail::generic {
8
9template <typename Tag, typename T>
10inline constexpr T symmetric_mirror(const T& value) {
11 if constexpr (Tag::hermitian) {
12 return detail::conj(value);
13 }
14 return value;
15}
16
17template <typename T>
19 static constexpr int kTile = 16;
20 T matrix[kTile * (kTile + 1)];
23};
24
25template <typename T>
26inline constexpr int symv_transpose_tile_index(int row, int col) {
27 return row * (SymvTransposeWorkspace<T>::kTile + 1) + col;
28}
29
30template <typename Group>
31inline constexpr bool symv_tiled_policy_matches(const Group& group, DeviceBlasPolicy policy) {
32 const int local_size = detail::group_local_linear_range(group);
33 if (policy == DeviceBlasPolicy::Generic) {
34 return false;
35 }
36 if (policy == DeviceBlasPolicy::Subgroup16) {
37 return local_size >= 16;
38 }
39 if (policy == DeviceBlasPolicy::Subgroup32) {
40 return local_size >= 32;
41 }
42 return local_size >= 16;
43}
44
45template <typename Tag, typename Group, typename T>
46inline constexpr bool can_use_tiled_symv(const Group& group,
48 DeviceBlasPolicy policy) {
49 return symv_tiled_policy_matches(group, policy) &&
52}
53
54template <typename T, typename Group>
55inline constexpr bool can_use_tiled_symv(const Group& group,
56 int extent,
57 DeviceBlasPolicy policy) {
58 return symv_tiled_policy_matches(group, policy) &&
60}
61
62template <typename Tag, typename Group, typename T>
63inline void symv_tiled(const Group& group,
66 T* workspace_ptr) {
67 constexpr int Tile = SymvTransposeWorkspace<T>::kTile;
68 auto* workspace = detail::workspace_ptr_cast<SymvTransposeWorkspace<T>>(workspace_ptr);
69 T* matrix_tile = workspace->matrix;
70 T* x_col_tile = workspace->x_col;
71 T* x_row_tile = workspace->x_row;
72 const int local_id = detail::group_local_linear_id(group);
73 const int local_size = detail::group_local_linear_range(group);
74 const int extent = a.rows();
75
76 for (int output_index = local_id; output_index < extent; output_index += local_size) {
77 operand.y(output_index) *= operand.beta;
78 }
79 sycl::group_barrier(group);
80
81 for (int col_base = 0; col_base < extent; col_base += Tile) {
82 const int col_extent = std::min(Tile, extent - col_base);
83
84 for (int col = local_id; col < col_extent; col += local_size) {
85 x_col_tile[col] = operand.x(col_base + col);
86 }
87 sycl::group_barrier(group);
88
89 for (int linear_index = local_id; linear_index < col_extent * col_extent; linear_index += local_size) {
90 const int row = linear_index % col_extent;
91 const int col = linear_index / col_extent;
92 T value{};
93 if constexpr (Tag::uplo == Uplo::Lower) {
94 if (row >= col) {
95 value = a(col_base + row, col_base + col);
96 } else {
97 value = symmetric_mirror<Tag>(a(col_base + col, col_base + row));
98 }
99 } else {
100 if (row <= col) {
101 value = a(col_base + row, col_base + col);
102 } else {
103 value = symmetric_mirror<Tag>(a(col_base + col, col_base + row));
104 }
105 }
106 matrix_tile[symv_transpose_tile_index<T>(row, col)] = value;
107 }
108 sycl::group_barrier(group);
109
110 for (int row = local_id; row < col_extent; row += local_size) {
111 T sum{};
112 for (int col = 0; col < col_extent; ++col) {
113 sum += matrix_tile[symv_transpose_tile_index<T>(row, col)] * x_col_tile[col];
114 }
115 operand.y(col_base + row) += operand.alpha * sum;
116 }
117 sycl::group_barrier(group);
118
119 if constexpr (Tag::uplo == Uplo::Lower) {
120 for (int row_base = col_base + Tile; row_base < extent; row_base += Tile) {
121 const int row_extent = std::min(Tile, extent - row_base);
122 for (int row = local_id; row < row_extent; row += local_size) {
123 x_row_tile[row] = operand.x(row_base + row);
124 }
125 for (int linear_index = local_id; linear_index < row_extent * col_extent; linear_index += local_size) {
126 const int row = linear_index % row_extent;
127 const int col = linear_index / row_extent;
128 matrix_tile[symv_transpose_tile_index<T>(row, col)] = a(row_base + row, col_base + col);
129 }
130 sycl::group_barrier(group);
131
132 for (int row = local_id; row < row_extent; row += local_size) {
133 T sum{};
134 for (int col = 0; col < col_extent; ++col) {
135 sum += matrix_tile[symv_transpose_tile_index<T>(row, col)] * x_col_tile[col];
136 }
137 operand.y(row_base + row) += operand.alpha * sum;
138 }
139 for (int col = local_id; col < col_extent; col += local_size) {
140 T sum{};
141 for (int row = 0; row < row_extent; ++row) {
142 sum += symmetric_mirror<Tag>(matrix_tile[symv_transpose_tile_index<T>(row, col)]) * x_row_tile[row];
143 }
144 operand.y(col_base + col) += operand.alpha * sum;
145 }
146 sycl::group_barrier(group);
147 }
148 } else {
149 for (int row_base = 0; row_base < col_base; row_base += Tile) {
150 const int row_extent = std::min(Tile, col_base - row_base);
151 for (int row = local_id; row < row_extent; row += local_size) {
152 x_row_tile[row] = operand.x(row_base + row);
153 }
154 for (int linear_index = local_id; linear_index < row_extent * col_extent; linear_index += local_size) {
155 const int row = linear_index % row_extent;
156 const int col = linear_index / row_extent;
157 matrix_tile[symv_transpose_tile_index<T>(row, col)] = a(row_base + row, col_base + col);
158 }
159 sycl::group_barrier(group);
160
161 for (int row = local_id; row < row_extent; row += local_size) {
162 T sum{};
163 for (int col = 0; col < col_extent; ++col) {
164 sum += matrix_tile[symv_transpose_tile_index<T>(row, col)] * x_col_tile[col];
165 }
166 operand.y(row_base + row) += operand.alpha * sum;
167 }
168 for (int col = local_id; col < col_extent; col += local_size) {
169 T sum{};
170 for (int row = 0; row < row_extent; ++row) {
171 sum += symmetric_mirror<Tag>(matrix_tile[symv_transpose_tile_index<T>(row, col)]) * x_row_tile[row];
172 }
173 operand.y(col_base + col) += operand.alpha * sum;
174 }
175 sycl::group_barrier(group);
176 }
177 }
178 }
179}
180
181template <typename Tag, typename Group, typename T>
182inline constexpr void symv(const Group& group,
184 MatrixVectorOperand<T> operand) {
185 const int local_id = detail::group_local_linear_id(group);
186 const int local_size = detail::group_local_linear_range(group);
187 const int extent = a.rows();
188
189 for (int output_index = local_id; output_index < extent; output_index += local_size) {
190 operand.y(output_index) *= operand.beta;
191 }
192 sycl::group_barrier(group);
193
194 for (int col = 0; col < extent; ++col) {
195 const T x_col = operand.x(col);
196 T mirrored_partial{};
197
198 if constexpr (Tag::uplo == Uplo::Lower) {
199 for (int row = col + 1 + local_id; row < extent; row += local_size) {
200 const T a_row_col = a(row, col);
201 operand.y(row) += operand.alpha * a_row_col * x_col;
202 mirrored_partial += symmetric_mirror<Tag>(a_row_col) * operand.x(row);
203 }
204 } else {
205 for (int row = local_id; row < col; row += local_size) {
206 const T a_row_col = a(row, col);
207 operand.y(row) += operand.alpha * a_row_col * x_col;
208 mirrored_partial += symmetric_mirror<Tag>(a_row_col) * operand.x(row);
209 }
210 }
211
212 mirrored_partial = detail::reduce_sum_group(group, mirrored_partial);
213 if (detail::group_is_leader(group)) {
214 operand.y(col) += operand.alpha * (a(col, col) * x_col + mirrored_partial);
215 }
216 sycl::group_barrier(group);
217 }
218}
219
220} // namespace detail::generic
221
222namespace detail {
223
224template <typename Tag, DeviceBlasPolicy Policy, typename T>
225inline constexpr std::size_t symv_workspace_elements(const DeviceBlasLaunchInfo& launch,
226 int extent) {
227 assert(extent >= 0 && "device::symv workspace query expects a non-negative matrix extent");
228 if (generic::can_use_tiled_symv<T>(launch, extent, Policy) && extent >= 4) {
230 }
231 return 0;
232}
233
234template <typename Tag, DeviceBlasPolicy Policy, typename Group, typename T>
235inline void dispatch_symv(const Group& group,
238 T* workspace = nullptr) {
240 operand,
241 SymmetricTransform{.side = Tag::side, .uplo = Tag::uplo, .hermitian = Tag::hermitian});
242 if (workspace != nullptr && generic::can_use_tiled_symv<Tag>(group, a, Policy) && a.cols() >= 4) {
243 generic::symv_tiled<Tag>(group, a, operand, workspace);
244 return;
245 }
246 generic::symv<Tag>(group, a, operand);
247}
248
249} // namespace detail
250
251/// @addtogroup api_device_blas
252/// @{
253
254/// @brief Symmetric matrix-vector product \f$ y := \alpha\,A\,x + \beta\,y \f$, reading only the `UploV` triangle of `A`.
255///
256/// With a workspace, a non-`Generic` policy, at least 16 work-items and
257/// n >= 16, 16 x 16 blocks of `A` are staged through local memory and each
258/// off-diagonal block is used for both its own and its mirrored contribution.
259/// @tparam UploV stored triangle of `A`
260/// @tparam Group `sycl::group` or `sycl::sub_group` (the tiled path barriers on it); every work-item must call
261/// @param group executor
262/// @param a single n x n matrix
263/// @param operand `x`, `y` (each of length at least n), `alpha`, `beta`
264/// @param workspace local memory of symv_workspace_elements() elements, or `nullptr`
265/// @pre `y` holds finite values even when `beta == 0`; `y` does not alias `x`.
266/// @see hemv(), @ref design_device_group_blas
267template <Uplo UploV = Uplo::Upper, typename Group, typename T>
268inline constexpr void symv(const Group& group,
271 T* workspace = nullptr) {
273}
274
275/// @brief As symv(), with an explicit DeviceBlasPolicy.
276template <DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper, typename Group, typename T>
277inline constexpr void symv(const Group& group,
280 T* workspace = nullptr) {
282}
283
284/// @brief Local memory, in elements of `T`, that symv() can use; 0 means pass `nullptr`.
285/// @param launch description of the launch the call runs in
286/// @param extent order of the matrix
287/// @return element count, or 0 when no staged path applies
288/// @ingroup api_device_blas_lowlevel
289template <typename T, Uplo UploV = Uplo::Upper>
290inline constexpr std::size_t symv_workspace_elements(const DeviceBlasLaunchInfo& launch,
291 int extent) {
293}
294
295/// @brief Local memory, in elements of `T`, that symv() can use under `Policy`; 0 means pass `nullptr`.
296/// @param launch description of the launch the call runs in
297/// @param extent order of the matrix
298/// @return element count, or 0 when no staged path applies
299/// @ingroup api_device_blas_lowlevel
300template <typename T, DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper>
301inline constexpr std::size_t symv_workspace_elements(const DeviceBlasLaunchInfo& launch,
302 int extent) {
304}
305
306/// @brief As symv(), with the operands passed separately instead of in an operand struct.
307template <Uplo UploV = Uplo::Upper, typename Group, typename T>
308inline constexpr void symv(const Group& group,
310 const VectorView<T>& x,
311 const VectorView<T>& y,
312 T alpha = T(1),
313 T beta = T(0),
314 T* workspace = nullptr) {
315 symv<UploV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
316}
317
318/// @brief As symv(), with an explicit DeviceBlasPolicy and the operands passed separately.
319template <DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper, typename Group, typename T>
320inline constexpr void symv(const Group& group,
322 const VectorView<T>& x,
323 const VectorView<T>& y,
324 T alpha = T(1),
325 T beta = T(0),
326 T* workspace = nullptr) {
327 symv<Policy, UploV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
328}
329
330/// @brief Hermitian matrix-vector product \f$ y := \alpha\,A\,x + \beta\,y \f$; symv() with conjugated mirroring.
331///
332/// For real `T` this is exactly symv(). The diagonal is read as stored; its
333/// imaginary part is not forced to zero.
334/// @param group executor (`sycl::group` or `sycl::sub_group`)
335/// @param a single n x n matrix, `UploV` triangle read
336/// @param operand `x`, `y`, `alpha`, `beta`
337/// @param workspace local memory of hemv_workspace_elements() elements, or `nullptr`
338template <Uplo UploV = Uplo::Upper, typename Group, typename T>
339inline constexpr void hemv(const Group& group,
342 T* workspace = nullptr) {
344}
345
346/// @brief As hemv(), with an explicit DeviceBlasPolicy.
347template <DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper, typename Group, typename T>
348inline constexpr void hemv(const Group& group,
351 T* workspace = nullptr) {
353}
354
355/// @brief Local memory, in elements of `T`, that hemv() can use; 0 means pass `nullptr`.
356/// @param launch description of the launch the call runs in
357/// @param extent order of the matrix
358/// @return element count, or 0 when no staged path applies
359/// @ingroup api_device_blas_lowlevel
360template <typename T, Uplo UploV = Uplo::Upper>
365
366/// @brief Local memory, in elements of `T`, that hemv() can use under `Policy`; 0 means pass `nullptr`.
367/// @param launch description of the launch the call runs in
368/// @param extent order of the matrix
369/// @return element count, or 0 when no staged path applies
370/// @ingroup api_device_blas_lowlevel
371template <typename T, DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper>
372inline constexpr std::size_t hemv_workspace_elements(const DeviceBlasLaunchInfo& launch,
373 int extent) {
375}
376
377/// @brief As hemv(), with the operands passed separately instead of in an operand struct.
378template <Uplo UploV = Uplo::Upper, typename Group, typename T>
379inline constexpr void hemv(const Group& group,
381 const VectorView<T>& x,
382 const VectorView<T>& y,
383 T alpha = T(1),
384 T beta = T(0),
385 T* workspace = nullptr) {
386 hemv<UploV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
387}
388
389/// @brief As hemv(), with an explicit DeviceBlasPolicy and the operands passed separately.
390template <DeviceBlasPolicy Policy, Uplo UploV = Uplo::Upper, typename Group, typename T>
391inline constexpr void hemv(const Group& group,
393 const VectorView<T>& x,
394 const VectorView<T>& y,
395 T alpha = T(1),
396 T beta = T(0),
397 T* workspace = nullptr) {
398 hemv<Policy, UploV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
399}
400
401/// @}
402
403} // namespace batchlas::device
Non-owning view of a batch of strided vectors: the vector argument type of the entry points.
Definition matrix.hh:1754
Policy
Synchronous or asynchronous execution policy.
constexpr std::size_t symv_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent)
Local memory, in elements of T, that symv() can use; 0 means pass nullptr.
constexpr MatrixVectorOperand< T > make_matvec_operand(const VectorView< T > &x, const VectorView< T > &y, T alpha=T(1), T beta=T(0))
Builds a MatrixVectorOperand.
constexpr std::size_t hemv_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent)
Local memory, in elements of T, that hemv() can use; 0 means pass nullptr.
DeviceBlasPolicy
Which implementation family a device BLAS call may use.
constexpr void symv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace=nullptr)
Symmetric matrix-vector product , reading only the UploV triangle of A.
constexpr void hemv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace=nullptr)
Hermitian matrix-vector product ; symv() with conjugated mirroring.
@ Subgroup16
fast paths only when the actual sub-group size is 16
@ Auto
any eligible path; the sub-group fast paths accept sub-group size 16 or 32
@ Subgroup32
fast paths only when the actual sub-group size is 32
@ Generic
the portable group-reduction path only; never stages through local memory
@ Lower
The lower triangle (i >= j).
Definition enums.hh:289
constexpr void symv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand)
void symv_tiled(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace_ptr)
constexpr bool symv_tiled_policy_matches(const Group &group, DeviceBlasPolicy policy)
constexpr T symmetric_mirror(const T &value)
constexpr bool can_use_tiled_symv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, DeviceBlasPolicy policy)
constexpr int symv_transpose_tile_index(int row, int col)
constexpr std::size_t symv_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent)
constexpr int group_local_linear_id(const Group &group)
constexpr void validate_symmetric_operand(const KernelMatrixView< T, MatrixFormat::Dense > &a, const MatrixVectorOperand< T > &operand, SymmetricTransform transform)
constexpr int group_local_linear_range(const Group &group)
constexpr Workspace * workspace_ptr_cast(T *workspace)
constexpr T reduce_sum_group(const Group &group, const T &value)
constexpr T conj(const T &value)
constexpr std::size_t workspace_elements_v
void dispatch_symv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace=nullptr)
constexpr bool group_is_leader(const Group &group)
Trivially copyable view of a MatrixView for use inside a SYCL kernel.
Definition matrix.hh:145
auto rows() const
Row count (the capacity on a heterogeneous batch).
Definition matrix.hh:219
auto cols() const
Column count (the capacity on a heterogeneous batch).
Definition matrix.hh:223
Host-side description of a launch, for the *_workspace_elements queries.
Operands of a matrix-vector operation: .
T beta
scale of the input y; 0 still reads y
VectorView< T > y
output vector; read (scaled by beta) and overwritten
Mode of a symmetric or Hermitian product (symv, hemv, symm).