group_blas_gemv.hh
Go to the documentation of this file.
1#pragma once
2
4
5namespace batchlas::device {
6
7namespace detail::generic {
8
9template <typename Group>
10inline constexpr auto gemv_work_group(const Group& group) {
11 if constexpr (detail::NdItemLike<Group>) {
12 return group.get_group();
13 } else {
14 return group;
15 }
16}
17
18template <typename T>
20 static constexpr int kMaxTile = 32;
23};
24
25template <typename T>
26inline constexpr int gemv_transpose_tile_index(int row, int col) {
27 return row * (GemvTransposeWorkspace<T>::kMaxTile + 1) + col;
28}
29
30template <typename Group>
31inline constexpr int gemv_tiled_tile_size(const Group& group, DeviceBlasPolicy policy) {
32 const int local_size = detail::group_local_linear_range(group);
33 if (policy == DeviceBlasPolicy::Generic) {
34 return 0;
35 }
36 if (policy == DeviceBlasPolicy::Subgroup32) {
37 return local_size >= 32 ? 32 : 0;
38 }
39 if (policy == DeviceBlasPolicy::Subgroup16) {
40 return local_size >= 16 ? 16 : 0;
41 }
42 if (local_size >= 32) {
43 return 32;
44 }
45 if (local_size >= 16) {
46 return 16;
47 }
48 return 0;
49}
50
51template <typename Tag, typename Group, typename T>
52inline constexpr bool can_use_tiled_gemv(const Group& group,
54 DeviceBlasPolicy policy) {
55 if constexpr (Tag::trans != Transpose::NoTrans) {
56 (void)group;
57 (void)a;
58 (void)policy;
59 return false;
60 } else {
61 const int tile = gemv_tiled_tile_size(group, policy);
62 return tile > 0 && a.rows() >= tile && a.cols() >= tile;
63 }
64}
65
66template <typename Tag, typename Group>
67inline constexpr bool can_use_tiled_gemv(const Group& group,
68 int rows,
69 int cols,
70 DeviceBlasPolicy policy) {
71 if constexpr (Tag::trans != Transpose::NoTrans) {
72 (void)group;
73 (void)rows;
74 (void)cols;
75 (void)policy;
76 return false;
77 } else {
78 const int tile = gemv_tiled_tile_size(group, policy);
79 return tile > 0 && rows >= tile && cols >= tile;
80 }
81}
82
83template <typename Group, typename T>
84inline void gemv_tiled(const Group& group,
87 int tile,
88 T* workspace_ptr) {
89 auto work_group = gemv_work_group(group);
90 auto* workspace = detail::workspace_ptr_cast<GemvTransposeWorkspace<T>>(workspace_ptr);
91 T* matrix_tile = workspace->matrix;
92 T* x_tile = workspace->x;
93 const int local_id = detail::group_local_linear_id(group);
94 const int local_size = detail::group_local_linear_range(group);
95 const int row_extent = a.rows();
96 const int col_extent = a.cols();
97
98 for (int row_base = 0; row_base < row_extent; row_base += tile) {
99 const int row_tile_extent = std::min(tile, row_extent - row_base);
100
101 for (int row = local_id; row < row_tile_extent; row += local_size) {
102 operand.y(row_base + row) *= operand.beta;
103 }
104
105 for (int col_base = 0; col_base < col_extent; col_base += tile) {
106 const int col_tile_extent = std::min(tile, col_extent - col_base);
107
108 for (int col = local_id; col < col_tile_extent; col += local_size) {
109 x_tile[col] = operand.x(col_base + col);
110 }
111 for (int linear_index = local_id; linear_index < row_tile_extent * col_tile_extent; linear_index += local_size) {
112 const int row = linear_index % row_tile_extent;
113 const int col = linear_index / row_tile_extent;
114 matrix_tile[gemv_transpose_tile_index<T>(row, col)] = a(row_base + row, col_base + col);
115 }
116 sycl::group_barrier(work_group);
117
118 for (int row = local_id; row < row_tile_extent; row += local_size) {
119 T partial{};
120 for (int col = 0; col < col_tile_extent; ++col) {
121 partial += matrix_tile[gemv_transpose_tile_index<T>(row, col)] * x_tile[col];
122 }
123 operand.y(row_base + row) += operand.alpha * partial;
124 }
125 sycl::group_barrier(work_group);
126 }
127 }
128}
129
130template <typename Tag, typename Group, typename T>
131inline constexpr void gemv(const Group& group,
133 MatrixVectorOperand<T> operand) {
134 const int local_id = detail::group_local_linear_id(group);
135 const int local_size = detail::group_local_linear_range(group);
136 const int inner_extent = detail::input_size<Tag::trans>(a);
137 const int outer_extent = detail::output_size<Tag::trans>(a);
138
139 if (inner_extent < local_size) {
140 for (int output_index = local_id; output_index < outer_extent; output_index += local_size) {
141 T partial{};
142 for (int input_index = 0; input_index < inner_extent; ++input_index) {
143 partial += detail::matrix_entry<Tag::trans>(a, output_index, input_index) * operand.x(input_index);
144 }
145 operand.y(output_index) = operand.alpha * partial + operand.beta * operand.y(output_index);
146 }
147 return;
148 }
149
150 for (int output_index = 0; output_index < outer_extent; ++output_index) {
151 T partial{};
152 for (int input_index = local_id; input_index < inner_extent; input_index += local_size) {
153 partial += detail::matrix_entry<Tag::trans>(a, output_index, input_index) * operand.x(input_index);
154 }
155 partial = detail::reduce_sum_group(group, partial);
156 if (detail::group_is_leader(group)) {
157 operand.y(output_index) = operand.alpha * partial + operand.beta * operand.y(output_index);
158 }
159 }
160}
161
162} // namespace detail::generic
163
164namespace detail {
165
166template <typename Tag, DeviceBlasPolicy Policy, typename T>
167inline constexpr std::size_t gemv_workspace_elements(const DeviceBlasLaunchInfo& launch,
168 int rows,
169 int cols) {
170 assert(rows >= 0 && cols >= 0 && "device::gemv workspace query expects non-negative matrix extents");
171 if (generic::can_use_tiled_gemv<Tag>(launch, rows, cols, Policy)) {
173 }
174 return 0;
175}
176
177template <typename Tag, DeviceBlasPolicy Policy, typename Group, typename T>
178inline void dispatch_gemv(const Group& group,
181 T* workspace = nullptr) {
182 validate_operand(a, operand, Tag::trans);
183 if (workspace != nullptr && generic::can_use_tiled_gemv<Tag>(group, a, Policy)) {
184 generic::gemv_tiled(group, a, operand, generic::gemv_tiled_tile_size(group, Policy), workspace);
185 return;
186 }
187 generic::gemv<Tag>(group, a, operand);
188}
189
190} // namespace detail
191
192/// @addtogroup api_device_blas
193/// @{
194
195/// @brief General matrix-vector product \f$ y := \alpha\,\mathrm{op}(A)\,x + \beta\,y \f$.
196///
197/// With a workspace, `NoTrans`, a non-`Generic` policy, an executor of at least
198/// 16 work-items and both extents at least the tile (16 or 32), `A` is staged
199/// through local memory tile by tile; otherwise each output element is a
200/// group reduction (or one work-item's loop when the inner extent is smaller
201/// than the group).
202/// @tparam TransV op() applied to `A`
203/// @tparam Group `sycl::group`, `sycl::sub_group` or an `nd_item` (unwrapped to its work-group); every work-item must call
204/// @param group executor
205/// @param a single m x n matrix
206/// @param operand `x` (length of op(A)'s columns), `y` (length of op(A)'s rows), `alpha`, `beta`
207/// @param workspace local memory of gemv_workspace_elements() elements, or `nullptr`
208/// @pre `y` holds finite values even when `beta == 0`; `y` does not alias `x` or `A`.
209/// @note Issue a group barrier before other work-items read `y`.
210/// @see @ref design_device_group_blas
211template <Transpose TransV = Transpose::NoTrans, typename Group, typename T>
212inline constexpr void gemv(const Group& group,
215 T* workspace = nullptr) {
217}
218
219/// @brief As gemv(), with an explicit DeviceBlasPolicy.
220template <DeviceBlasPolicy Policy,
222 typename Group,
223 typename T>
224inline constexpr void gemv(const Group& group,
227 T* workspace = nullptr) {
229}
230
231/// @brief Local memory, in elements of `T`, that gemv() can use; 0 means pass `nullptr`.
232/// @param launch description of the launch the call runs in
233/// @param rows rows of `A`
234/// @param cols columns of `A`
235/// @return element count, or 0 when no staged path applies
236/// @ingroup api_device_blas_lowlevel
237template <typename T, Transpose TransV = Transpose::NoTrans>
238inline constexpr std::size_t gemv_workspace_elements(const DeviceBlasLaunchInfo& launch,
239 int rows,
240 int cols) {
242}
243
244/// @brief Local memory, in elements of `T`, that gemv() can use under `Policy`; 0 means pass `nullptr`.
245/// @param launch description of the launch the call runs in
246/// @param rows rows of `A`
247/// @param cols columns of `A`
248/// @return element count, or 0 when no staged path applies
249/// @ingroup api_device_blas_lowlevel
250template <typename T, DeviceBlasPolicy Policy, Transpose TransV = Transpose::NoTrans>
251inline constexpr std::size_t gemv_workspace_elements(const DeviceBlasLaunchInfo& launch,
252 int rows,
253 int cols) {
255}
256
257/// @brief As gemv(), with the operands passed separately instead of in an operand struct.
258template <Transpose TransV = Transpose::NoTrans, typename Group, typename T>
259inline constexpr void gemv(const Group& group,
261 const VectorView<T>& x,
262 const VectorView<T>& y,
263 T alpha = T(1),
264 T beta = T(0),
265 T* workspace = nullptr) {
266 gemv<TransV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
267}
268
269/// @brief As gemv(), with an explicit DeviceBlasPolicy and the operands passed separately.
270template <DeviceBlasPolicy Policy,
272 typename Group,
273 typename T>
274inline constexpr void gemv(const Group& group,
276 const VectorView<T>& x,
277 const VectorView<T>& y,
278 T alpha = T(1),
279 T beta = T(0),
280 T* workspace = nullptr) {
281 gemv<Policy, TransV>(group, a, make_matvec_operand(x, y, alpha, beta), workspace);
282}
283
284/// @}
285
286} // 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 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 gemv_workspace_elements(const DeviceBlasLaunchInfo &launch, int rows, int cols)
Local memory, in elements of T, that gemv() can use; 0 means pass nullptr.
DeviceBlasPolicy
Which implementation family a device BLAS call may use.
constexpr void gemv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace=nullptr)
General matrix-vector product .
@ 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
Transpose
The operator op() applied to an operand: op(A) = A, A^T or A^H.
Definition enums.hh:171
@ NoTrans
op(A) = A.
Definition enums.hh:172
constexpr void gemv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand)
constexpr auto gemv_work_group(const Group &group)
constexpr int gemv_transpose_tile_index(int row, int col)
constexpr bool can_use_tiled_gemv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, DeviceBlasPolicy policy)
void gemv_tiled(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, int tile, T *workspace_ptr)
constexpr int gemv_tiled_tile_size(const Group &group, DeviceBlasPolicy policy)
constexpr std::size_t gemv_workspace_elements(const DeviceBlasLaunchInfo &launch, int rows, int cols)
constexpr int output_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr int group_local_linear_id(const Group &group)
constexpr void validate_operand(const KernelMatrixView< T, MatrixFormat::Dense > &a, const MatrixVectorOperand< T > &operand, Transpose trans)
constexpr int input_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr int group_local_linear_range(const Group &group)
constexpr Workspace * workspace_ptr_cast(T *workspace)
void dispatch_gemv(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixVectorOperand< T > operand, T *workspace=nullptr)
constexpr T matrix_entry(const KernelMatrixView< T, MatrixFormat::Dense > &a, int row, int col, Transpose trans)
constexpr T reduce_sum_group(const Group &group, const T &value)
constexpr std::size_t workspace_elements_v
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