group_blas_subgroup_common.hh
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4
6
7namespace batchlas::device {
8
9namespace detail::subgroup {
10
11inline constexpr int kMaxSupportedSubgroupSize = 32;
12inline constexpr int kMaxSubgroupsPerWorkGroup = 8;
13inline constexpr int kMaxMatrixRowsPerSubgroup = 8;
14inline constexpr int kRegisterMatrixTileM = 128;
15inline constexpr int kRegisterMatrixTileN = 64;
16inline constexpr int kRegisterMatrixTileK = 32;
17inline constexpr int kRegisterMatrixThreadTileRows = 4;
18inline constexpr int kRegisterMatrixThreadTileCols = 8;
26inline constexpr int kRegisterMatrixLhsStages = 1;
27inline constexpr int kRegisterMatrixRhsStages = 1;
28inline constexpr int kComplexRank2kTileM = 64;
29inline constexpr int kComplexRank2kTileN = 64;
30inline constexpr int kComplexRank2kTileK = 32;
31inline constexpr int kComplexRank2kInKernelTileK = 24;
32inline constexpr int kComplexRank2kThreadTileRows = 4;
33inline constexpr int kComplexRank2kThreadTileCols = 4;
42inline constexpr int kOptimizedGemmTileM = 128;
43inline constexpr int kOptimizedGemmTileN = 32;
44inline constexpr int kOptimizedGemmTileK = 32;
45inline constexpr int kOptimizedGemmThreadTileRows = 4;
46inline constexpr int kOptimizedGemmThreadTileCols = 4;
47inline constexpr int kOptimizedGemmVecA = 4;
48inline constexpr int kOptimizedGemmVecB = 4;
49inline constexpr int kOptimizedGemmUnrollK = 2;
50inline constexpr int kOptimizedGemmStages = 2;
58inline constexpr std::size_t kSubgroupWorkspaceBudgetBytes =
59 static_cast<std::size_t>(::batchlas::device_limits::subgroup_workspace_budget_bytes());
60
61template <std::size_t FixedElements, std::size_t ElementsPerSubgroup, typename T>
62inline constexpr int subgroup_limit_for_workspace_v = []() {
63 constexpr std::size_t budget_elements = kSubgroupWorkspaceBudgetBytes / sizeof(T);
64 if constexpr (budget_elements <= FixedElements) {
65 return 0;
66 } else {
67 constexpr std::size_t remaining_elements = budget_elements - FixedElements;
68 constexpr std::size_t subgroup_limit = ElementsPerSubgroup == 0
69 ? static_cast<std::size_t>(kMaxSubgroupsPerWorkGroup)
70 : (remaining_elements / ElementsPerSubgroup);
71 return static_cast<int>(std::min<std::size_t>(static_cast<std::size_t>(kMaxSubgroupsPerWorkGroup), subgroup_limit));
72 }
73}();
74
75template <typename T>
79 T>;
80
81template <typename T>
85 T>;
86
87template <typename T>
91 T>;
92
93template <int Count, typename Fn, std::size_t... I>
94inline constexpr void static_for_impl(Fn&& fn, std::index_sequence<I...>) {
95 (fn(std::integral_constant<int, static_cast<int>(I)>{}), ...);
96}
97
98template <int Count, typename Fn>
99inline constexpr void static_for(Fn&& fn) {
100 static_for_impl<Count>(std::forward<Fn>(fn), std::make_index_sequence<Count>{});
101}
102
103template <typename T>
105 std::array<T, kRegisterMatrixThreadTileRows * kRegisterMatrixThreadTileCols> values{};
106
107 template <int Row, int Col>
108 inline constexpr T& get() {
109 return std::get<Row * kRegisterMatrixThreadTileCols + Col>(values);
110 }
111
112 template <int Row, int Col>
113 inline constexpr const T& get() const {
114 return std::get<Row * kRegisterMatrixThreadTileCols + Col>(values);
115 }
116};
117
118template <typename T>
120 std::array<T, kComplexRank2kThreadTileRows * kComplexRank2kThreadTileCols> values{};
121
122 template <int Row, int Col>
123 inline constexpr T& get() {
124 return std::get<Row * kComplexRank2kThreadTileCols + Col>(values);
125 }
126
127 template <int Row, int Col>
128 inline constexpr const T& get() const {
129 return std::get<Row * kComplexRank2kThreadTileCols + Col>(values);
130 }
131};
132
133template <typename T>
135 // clang-format off
136 // Extents clamp to 1 (no zero-length arrays); a budget of 0 sub-groups means never launch for T.
137 // evidence: docs/design/device-group-blas.md#device-group-blas-register-and-local-memory-caveats
141 // clang-format on
142};
143
144template <typename T>
150
151template <typename T>
157
158template <typename T>
163
164template <typename T>
171
172template <typename T>
174
175template <typename T>
177
178template <typename T>
181
182template <typename T>
184
185template <typename T>
187
188template <typename T>
189inline constexpr bool supports_packet4_v = std::is_same_v<T, float>;
190
191template <typename T, int Width>
192inline constexpr bool supports_packet_v = false;
193
194template <typename T>
196
197template <typename T, int Width>
198inline constexpr bool supports_aligned_packet_loads(const T* ptr, int ld, int stride) {
199 if constexpr (!supports_packet_v<T, Width>) {
200 static_cast<void>(ptr);
201 static_cast<void>(ld);
202 static_cast<void>(stride);
203 return false;
204 } else {
205 const auto address = reinterpret_cast<std::uintptr_t>(ptr);
206 return (address % alignof(sycl::vec<T, Width>) == 0) && (ld % Width == 0) && (stride % Width == 0);
207 }
208}
209
210// Non-packet types (complex) only instantiate dead fast paths; sycl::vec<std::complex> is not a
211// valid type outside DPC++, so they get a plain array.
212template <typename T, int Width>
213struct alignas(Width * sizeof(T)) PacketArray {
214 T v[Width];
215 constexpr const T& operator[](int i) const { return v[i]; }
216};
217
218template <typename T, int Width>
219using Packet = std::conditional_t<supports_packet_v<T, Width>, sycl::vec<T, Width>, PacketArray<T, Width>>;
220
221template <typename T, int Width>
222inline constexpr Packet<T, Width> packet_load_aligned(const T* ptr, int offset) {
223 return *reinterpret_cast<const Packet<T, Width>*>(ptr + offset);
224}
225
226inline constexpr int register_matrix_lhs_stage(int tile_idx) {
227 return tile_idx % kRegisterMatrixLhsStages;
228}
229
230inline constexpr int register_matrix_rhs_stage(int tile_idx) {
231 return tile_idx % kRegisterMatrixRhsStages;
232}
233
234template <typename Item>
235inline constexpr int matrix_tile_group_row(const Item&) {
236 return 0;
237}
238
239template <typename Item>
240inline constexpr int matrix_tile_group_col(const Item&) {
241 return 0;
242}
243
244template <typename Item>
245inline constexpr int matrix_tile_group_row_stride(const Item&) {
246 return 1;
247}
248
249template <typename Item>
250inline constexpr int matrix_tile_group_col_stride(const Item&) {
251 return 1;
252}
253
254inline constexpr int matrix_tile_group_row(const sycl::nd_item<3>& item) {
255 return static_cast<int>(item.get_group(1));
256}
257
258inline constexpr int matrix_tile_group_col(const sycl::nd_item<3>& item) {
259 return static_cast<int>(item.get_group(2));
260}
261
262inline constexpr int matrix_tile_group_row_stride(const sycl::nd_item<3>& item) {
263 return std::max(1, static_cast<int>(item.get_group_range(1)));
264}
265
266inline constexpr int matrix_tile_group_col_stride(const sycl::nd_item<3>& item) {
267 return std::max(1, static_cast<int>(item.get_group_range(2)));
268}
269
270template <typename Item>
271inline constexpr int subgroup_size(const Item& item) {
272 return static_cast<int>(item.get_sub_group().get_local_range().size());
273}
274
275inline constexpr int subgroup_size(const DeviceBlasLaunchInfo& launch) {
276 return std::max(1, launch.subgroup_size);
277}
278
279template <typename Item>
280inline constexpr int subgroup_local_id(const Item& item) {
281 return static_cast<int>(item.get_sub_group().get_local_linear_id());
282}
283
284template <typename Item>
285inline constexpr int subgroup_group_id(const Item& item) {
286 return detail::item_local_linear_id(item) / subgroup_size(item);
287}
288
289template <typename Item>
290inline constexpr int subgroup_count(const Item& item) {
291 const int sg_size = subgroup_size(item);
292 return std::max(1, detail::group_local_linear_range(item) / std::max(1, sg_size));
293}
294
295inline constexpr int subgroup_count(const DeviceBlasLaunchInfo& launch) {
296 const int sg_size = subgroup_size(launch);
297 return std::max(1, detail::group_local_linear_range(launch) / std::max(1, sg_size));
298}
299
300template <typename Exec>
301inline constexpr bool is_nd_item_3d_launch(const Exec&) {
302 return std::is_same_v<std::remove_cvref_t<Exec>, sycl::nd_item<3>>;
303}
304
305inline constexpr bool is_nd_item_3d_launch(const DeviceBlasLaunchInfo& launch) {
306 return launch.kind == DeviceBlasLaunchKind::NdItem3D;
307}
308
309inline constexpr int rows_per_subgroup(int extent, int total_sg, int max_rows) {
310 if (extent >= total_sg * max_rows) {
311 return max_rows;
312 }
313 if (max_rows >= 4 && extent >= total_sg * 2) {
314 return 2;
315 }
316 return 1;
317}
318
319inline constexpr int matrix_tile_k(int contract_extent, int sg_size, Side side) {
320 if (contract_extent >= 256 && sg_size == 32) {
321 return side == Side::Left ? 32 : 16;
322 }
323 if (contract_extent >= 128) {
324 return 16;
325 }
326 return 8;
327}
328
329inline constexpr int matrix_rows_per_subgroup(int row_extent,
330 int total_sg,
331 int sg_size,
332 int contract_extent,
333 Side side) {
334 if (side == Side::Left && sg_size == 32 && contract_extent >= 128 && row_extent >= total_sg * 8) {
335 return 8;
336 }
337 return rows_per_subgroup(row_extent, total_sg, 4);
338}
339
340inline constexpr bool subgroup_policy_matches(DeviceBlasPolicy policy, int actual_size) {
341 switch (policy) {
343 return false;
345 return actual_size == 16;
347 return actual_size == 32;
349 return actual_size == 16 || actual_size == 32;
350 }
351 return false;
352}
353
354template <typename T, typename Item>
355inline constexpr bool can_use_matrix_fast_path(const Item& item,
356 int row_extent,
357 int col_extent,
358 int contract_extent,
359 DeviceBlasPolicy policy) {
360 if constexpr (!std::is_same_v<T, float>) {
361 return false;
362 }
363
364 const int sg_size = subgroup_size(item);
365 if (!subgroup_policy_matches(policy, sg_size)) {
366 return false;
367 }
368 if (sg_size > kMaxSupportedSubgroupSize) {
369 return false;
370 }
371
372 return row_extent >= 8 && col_extent >= sg_size && contract_extent >= sg_size;
373}
374
375template <typename T, typename Item>
376inline constexpr bool can_use_matrix_register_fast_path(const Item& item,
377 int row_extent,
378 int col_extent,
379 int contract_extent,
380 DeviceBlasPolicy policy) {
382 return false;
383 }
384 if constexpr (!std::is_same_v<T, float>) {
385 return false;
386 }
387
388 if (policy == DeviceBlasPolicy::Generic) {
389 return false;
390 }
392 return false;
393 }
394
395 return row_extent >= kRegisterMatrixThreadTileRows &&
396 col_extent >= kRegisterMatrixThreadTileCols &&
397 contract_extent >= kRegisterMatrixTileK;
398}
399
400template <typename T, typename Item>
401inline constexpr bool can_use_matrix_aligned_nn_large_fast_path(const Item& item,
402 int row_extent,
403 int col_extent,
404 int contract_extent,
405 Transpose trans_a,
406 Transpose trans_b,
407 DeviceBlasPolicy policy);
408
409template <typename T, typename Item>
410inline constexpr bool can_use_matrix_aligned_nn_large_fast_path(const Item& item,
412 const MatrixMatrixOperand<T>& operand,
413 GeneralMatrixTransform transform,
414 DeviceBlasPolicy policy) {
415 if constexpr (!std::is_same_v<T, float>) {
416 return false;
417 }
418
419 if (policy == DeviceBlasPolicy::Generic) {
420 return false;
421 }
422 if (transform.trans_a != Transpose::NoTrans || transform.trans_b != Transpose::NoTrans) {
423 return false;
424 }
426 return false;
427 }
428
429 const int row_extent = detail::output_size(a, transform.trans_a);
430 const int col_extent = detail::input_size(operand.b, transform.trans_b);
431 const int contract_extent = detail::input_size(a, transform.trans_a);
432 if (row_extent < kOptimizedGemmTileM || col_extent < kOptimizedGemmTileN || contract_extent < kOptimizedGemmTileK) {
433 return false;
434 }
435 if ((row_extent % kOptimizedGemmTileM) != 0 ||
436 (col_extent % kRegisterMatrixTileN) != 0 ||
437 (contract_extent % kOptimizedGemmTileK) != 0) {
438 return false;
439 }
440
442 supports_aligned_packet_loads<T, kOptimizedGemmVecB>(operand.b.data(), operand.b.ld(), operand.b.stride());
443}
444
445template <typename T, typename Item>
446inline constexpr bool can_use_matrix_aligned_nn_large_fast_path(const Item& item,
447 int row_extent,
448 int col_extent,
449 int contract_extent,
450 Transpose trans_a,
451 Transpose trans_b,
452 DeviceBlasPolicy policy) {
453 if constexpr (!std::is_same_v<T, float>) {
454 return false;
455 }
456
457 if (policy == DeviceBlasPolicy::Generic) {
458 return false;
459 }
460 if (trans_a != Transpose::NoTrans || trans_b != Transpose::NoTrans) {
461 return false;
462 }
464 return false;
465 }
466
467 if (row_extent < kOptimizedGemmTileM || col_extent < kOptimizedGemmTileN || contract_extent < kOptimizedGemmTileK) {
468 return false;
469 }
470 if ((row_extent % kOptimizedGemmTileM) != 0 ||
471 (col_extent % kRegisterMatrixTileN) != 0 ||
472 (contract_extent % kOptimizedGemmTileK) != 0) {
473 return false;
474 }
475
476 return true;
477}
478
479template <typename T, typename Item>
480inline constexpr bool can_use_complex_rank2k_tiled_fast_path(const Item& item,
481 int extent,
482 int contract_extent,
483 DeviceBlasPolicy policy);
484
485template <typename T, typename Item>
486inline constexpr bool can_use_complex_rank2k_tiled_fast_path(const Item& item,
488 const MatrixMatrixOperand<T>& operand,
489 SymmetricRank2kTransform transform,
490 DeviceBlasPolicy policy) {
491 if constexpr (!std::is_same_v<T, std::complex<float>>) {
492 return false;
493 }
494
495 if (!is_nd_item_3d_launch(item)) {
496 return false;
497 }
498
499 if (policy != DeviceBlasPolicy::Auto) {
500 return false;
501 }
503 return false;
504 }
505
506 const int sg_size = subgroup_size(item);
507 const int sg_count = subgroup_count(item);
509 return false;
510 }
511
512 const int extent = detail::output_size(a, transform.trans);
513 const int contract_extent = detail::input_size(a, transform.trans);
514 return extent >= kComplexRank2kTileN &&
515 contract_extent >= 16 &&
516 operand.c.rows() == extent &&
517 operand.c.cols() == extent;
518}
519
520template <typename T, typename Item>
521inline constexpr bool can_use_complex_rank2k_tiled_fast_path(const Item& item,
522 int extent,
523 int contract_extent,
524 DeviceBlasPolicy policy) {
525 if constexpr (!std::is_same_v<T, std::complex<float>>) {
526 return false;
527 }
528
529 if (!is_nd_item_3d_launch(item)) {
530 return false;
531 }
532
533 if (policy != DeviceBlasPolicy::Auto) {
534 return false;
535 }
537 return false;
538 }
539
540 const int sg_size = subgroup_size(item);
541 const int sg_count = subgroup_count(item);
543 return false;
544 }
545
546 return extent >= kComplexRank2kTileN &&
547 contract_extent >= 16 &&
548 extent > 0;
549}
550
551template <typename T, typename Item>
552inline constexpr bool can_use_complex_rankk_tiled_fast_path(const Item& item,
553 int extent,
554 int contract_extent,
555 DeviceBlasPolicy policy);
556
557template <typename T, typename Item>
558inline constexpr bool can_use_complex_rankk_tiled_fast_path(const Item& item,
560 const RankKOperand<T>& operand,
561 SymmetricRankKTransform transform,
562 DeviceBlasPolicy policy) {
563 if constexpr (!std::is_same_v<T, std::complex<float>>) {
564 return false;
565 }
566
567 if (!is_nd_item_3d_launch(item)) {
568 return false;
569 }
570
571 if (policy != DeviceBlasPolicy::Auto) {
572 return false;
573 }
575 return false;
576 }
577
578 const int sg_size = subgroup_size(item);
579 const int sg_count = subgroup_count(item);
581 return false;
582 }
583
584 const int extent = detail::output_size(a, transform.trans);
585 const int contract_extent = detail::input_size(a, transform.trans);
586 return extent >= kComplexRank2kTileN &&
587 contract_extent >= 16 &&
588 operand.c.rows() == extent &&
589 operand.c.cols() == extent;
590}
591
592template <typename T, typename Item>
593inline constexpr bool can_use_complex_rankk_tiled_fast_path(const Item& item,
594 int extent,
595 int contract_extent,
596 DeviceBlasPolicy policy) {
597 if constexpr (!std::is_same_v<T, std::complex<float>>) {
598 return false;
599 }
600
601 if (!is_nd_item_3d_launch(item)) {
602 return false;
603 }
604
605 if (policy != DeviceBlasPolicy::Auto) {
606 return false;
607 }
609 return false;
610 }
611
612 const int sg_size = subgroup_size(item);
613 const int sg_count = subgroup_count(item);
615 return false;
616 }
617
618 return extent >= kComplexRank2kTileN &&
619 contract_extent >= 16 &&
620 extent > 0;
621}
622
623template <typename T, typename Item>
624inline constexpr bool can_use_rank2k_register_fast_path(const Item& item,
625 int extent,
626 int contract_extent,
627 DeviceBlasPolicy policy);
628
629template <typename T, typename Item>
630inline constexpr bool can_use_rank2k_register_fast_path(const Item& item,
632 const MatrixMatrixOperand<T>& operand,
633 SymmetricRank2kTransform transform,
634 DeviceBlasPolicy policy) {
635 if constexpr (!std::is_same_v<T, float>) {
636 return false;
637 }
638
639 if (!is_nd_item_3d_launch(item)) {
640 return false;
641 }
642
643 if (policy != DeviceBlasPolicy::Auto) {
644 return false;
645 }
647 return false;
648 }
649
650 const int sg_size = subgroup_size(item);
651 const int sg_count = subgroup_count(item);
652 if (sg_size != kMaxSupportedSubgroupSize || sg_count > kMaxSubgroupsPerWorkGroup) {
653 return false;
654 }
655
656 const int extent = detail::output_size(a, transform.trans);
657 const int contract_extent = detail::input_size(a, transform.trans);
658 return extent >= kRegisterMatrixTileN &&
659 contract_extent >= 16 &&
660 operand.c.rows() == extent &&
661 operand.c.cols() == extent;
662}
663
664template <typename T, typename Item>
665inline constexpr bool can_use_rank2k_register_fast_path(const Item& item,
666 int extent,
667 int contract_extent,
668 DeviceBlasPolicy policy) {
669 if constexpr (!std::is_same_v<T, float>) {
670 return false;
671 }
672
673 if (!is_nd_item_3d_launch(item)) {
674 return false;
675 }
676
677 if (policy != DeviceBlasPolicy::Auto) {
678 return false;
679 }
681 return false;
682 }
683
684 const int sg_size = subgroup_size(item);
685 const int sg_count = subgroup_count(item);
686 if (sg_size != kMaxSupportedSubgroupSize || sg_count > kMaxSubgroupsPerWorkGroup) {
687 return false;
688 }
689
690 return extent >= kRegisterMatrixTileN &&
691 contract_extent >= 16 &&
692 extent > 0;
693}
694
695template <typename T, typename Item>
696inline constexpr bool can_use_rankk_register_fast_path(const Item& item,
697 int extent,
698 int contract_extent,
699 DeviceBlasPolicy policy);
700
701template <typename T, typename Item>
702inline constexpr bool can_use_rankk_register_fast_path(const Item& item,
704 const RankKOperand<T>& operand,
705 SymmetricRankKTransform transform,
706 DeviceBlasPolicy policy) {
707 if constexpr (!std::is_same_v<T, float>) {
708 return false;
709 }
710
711 if (!is_nd_item_3d_launch(item)) {
712 return false;
713 }
714
715 if (policy != DeviceBlasPolicy::Auto) {
716 return false;
717 }
719 return false;
720 }
721
722 const int sg_size = subgroup_size(item);
723 const int sg_count = subgroup_count(item);
724 if (sg_size != kMaxSupportedSubgroupSize || sg_count > kMaxSubgroupsPerWorkGroup) {
725 return false;
726 }
727
728 const int extent = detail::output_size(a, transform.trans);
729 const int contract_extent = detail::input_size(a, transform.trans);
730 return extent >= kRegisterMatrixTileN &&
731 contract_extent >= 16 &&
732 operand.c.rows() == extent &&
733 operand.c.cols() == extent;
734}
735
736template <typename T, typename Item>
737inline constexpr bool can_use_rankk_register_fast_path(const Item& item,
738 int extent,
739 int contract_extent,
740 DeviceBlasPolicy policy) {
741 if constexpr (!std::is_same_v<T, float>) {
742 return false;
743 }
744
745 if (!is_nd_item_3d_launch(item)) {
746 return false;
747 }
748
749 if (policy != DeviceBlasPolicy::Auto) {
750 return false;
751 }
753 return false;
754 }
755
756 const int sg_size = subgroup_size(item);
757 const int sg_count = subgroup_count(item);
758 if (sg_size != kMaxSupportedSubgroupSize || sg_count > kMaxSubgroupsPerWorkGroup) {
759 return false;
760 }
761
762 return extent >= kRegisterMatrixTileN &&
763 contract_extent >= 16 &&
764 extent > 0;
765}
766
767inline constexpr int triangular_begin(int index, int extent, TriangularTransform transform, Side side) {
768 if (side == Side::Left) {
769 if (transform.trans == Transpose::NoTrans) {
770 return transform.uplo == Uplo::Lower ? 0 : index;
771 }
772 return transform.uplo == Uplo::Lower ? index : 0;
773 }
774
775 if (transform.trans == Transpose::NoTrans) {
776 return transform.uplo == Uplo::Lower ? index : 0;
777 }
778 return transform.uplo == Uplo::Lower ? 0 : index;
779}
780
781inline constexpr int triangular_end(int index, int extent, TriangularTransform transform, Side side) {
782 if (side == Side::Left) {
783 if (transform.trans == Transpose::NoTrans) {
784 return transform.uplo == Uplo::Lower ? index + 1 : extent;
785 }
786 return transform.uplo == Uplo::Lower ? extent : index + 1;
787 }
788
789 if (transform.trans == Transpose::NoTrans) {
790 return transform.uplo == Uplo::Lower ? extent : index + 1;
791 }
792 return transform.uplo == Uplo::Lower ? index + 1 : extent;
793}
794
795inline constexpr int triangular_tile_begin(int row_base,
796 int row_extent,
797 int col_base,
798 int col_extent,
799 int contract_extent,
800 TriangularTransform transform) {
801 if (transform.side == Side::Left) {
802 const int first_row = std::min(row_extent - 1, row_base);
803 const int last_row = std::min(row_extent - 1, row_base + kRegisterMatrixTileM - 1);
804 if (first_row < 0 || last_row < 0) {
805 return 0;
806 }
807 if (transform.trans == Transpose::NoTrans) {
808 return transform.uplo == Uplo::Lower ? 0 : first_row;
809 }
810 return transform.uplo == Uplo::Lower ? first_row : 0;
811 }
812
813 const int first_col = std::min(col_extent - 1, col_base);
814 const int last_col = std::min(col_extent - 1, col_base + kRegisterMatrixTileN - 1);
815 if (first_col < 0 || last_col < 0) {
816 return 0;
817 }
818 if (transform.trans == Transpose::NoTrans) {
819 return transform.uplo == Uplo::Lower ? first_col : 0;
820 }
821 return transform.uplo == Uplo::Lower ? 0 : first_col;
822}
823
824inline constexpr int triangular_tile_end(int row_base,
825 int row_extent,
826 int col_base,
827 int col_extent,
828 int contract_extent,
829 TriangularTransform transform) {
830 if (transform.side == Side::Left) {
831 const int last_row = std::min(row_extent - 1, row_base + kRegisterMatrixTileM - 1);
832 if (last_row < 0) {
833 return 0;
834 }
835 if (transform.trans == Transpose::NoTrans) {
836 return transform.uplo == Uplo::Lower ? std::min(contract_extent, last_row + 1) : contract_extent;
837 }
838 return transform.uplo == Uplo::Lower ? contract_extent : std::min(contract_extent, last_row + 1);
839 }
840
841 const int last_col = std::min(col_extent - 1, col_base + kRegisterMatrixTileN - 1);
842 if (last_col < 0) {
843 return 0;
844 }
845 if (transform.trans == Transpose::NoTrans) {
846 return transform.uplo == Uplo::Lower ? contract_extent : std::min(contract_extent, last_col + 1);
847 }
848 return transform.uplo == Uplo::Lower ? std::min(contract_extent, last_col + 1) : contract_extent;
849}
850
851template <typename Item, typename T, typename LhsLoader, typename RhsLoader>
852inline constexpr void load_register_matrix_stage(const Item& item,
854 int linear_tid,
855 int subgroup_id,
856 int lhs_stage,
857 int rhs_stage,
858 bool load_lhs,
859 bool load_rhs,
860 int k_base,
861 int tile_extent,
862 LhsLoader&& lhs_loader,
863 RhsLoader&& rhs_loader) {
864 const int subgroup_lane = subgroup_local_id(item);
865 T* lhs_stage_ptr = workspace->lhs[subgroup_id][lhs_stage];
866 T* rhs_stage_ptr = workspace->rhs[rhs_stage];
867
868 if (load_lhs) {
869 for (int index = subgroup_lane; index < kRegisterMatrixSubgroupTileM * kRegisterMatrixTileK; index += kMaxSupportedSubgroupSize) {
870 const int subgroup_tile_r = index / kRegisterMatrixTileK;
871 const int tile_k = index % kRegisterMatrixTileK;
872 const int tile_r = subgroup_id * kRegisterMatrixSubgroupTileM + subgroup_tile_r;
873 lhs_stage_ptr[subgroup_tile_r * kRegisterMatrixTileAStride + tile_k] =
874 tile_k < tile_extent ? lhs_loader(tile_r, k_base + tile_k) : T(0);
875 }
876 }
877 if (load_rhs) {
878 for (int index = linear_tid; index < kRegisterMatrixTileK * kRegisterMatrixTileN; index += kRegisterMatrixThreadsPerGroup) {
879 const int tile_k = index / kRegisterMatrixTileN;
880 const int tile_c = index % kRegisterMatrixTileN;
881 rhs_stage_ptr[tile_k * kRegisterMatrixTileBStride + tile_c] =
882 tile_k < tile_extent ? rhs_loader(tile_c, k_base + tile_k) : T(0);
883 }
884 }
885}
886
887template <typename T>
889 std::array<T, kRegisterMatrixThreadTileRows> lhs{};
890 std::array<T, kRegisterMatrixThreadTileCols> rhs{};
891
892 template <int Row>
893 inline constexpr T& lhs_value() {
894 return std::get<Row>(lhs);
895 }
896
897 template <int Row>
898 inline constexpr const T& lhs_value() const {
899 return std::get<Row>(lhs);
900 }
901
902 template <int Col>
903 inline constexpr T& rhs_value() {
904 return std::get<Col>(rhs);
905 }
906
907 template <int Col>
908 inline constexpr const T& rhs_value() const {
909 return std::get<Col>(rhs);
910 }
911};
912
913template <typename Item, typename T>
914inline constexpr void accumulate_register_matrix_stage(const Item& item,
915 const RegisterMatrixWorkspace<T>* workspace,
916 int subgroup_id,
917 int lhs_stage,
918 int rhs_stage,
919 int local_row,
920 int local_col,
921 int tile_extent,
923 const auto sg = item.get_sub_group();
924 const int sg_lane = subgroup_local_id(item);
925 const int sg_row = sg_lane / kRegisterMatrixLocalCols;
926 const int sg_col = sg_lane % kRegisterMatrixLocalCols;
927 const int lhs_row_base = (local_row % kRegisterMatrixSubgroupRows) * kRegisterMatrixThreadTileRows;
928 const int lhs_source_lane = sg_row * kRegisterMatrixLocalCols;
929 const int rhs_source_lane_base = sg_col;
930 const T* lhs_stage_ptr = workspace->lhs[subgroup_id][lhs_stage];
931 const T* rhs_stage_ptr = workspace->rhs[rhs_stage];
932 const int rhs_col_base = local_col * kRegisterMatrixThreadTileCols;
933
934 for (int kk = 0; kk < tile_extent; ++kk) {
937 constexpr int i = row_idx;
938 const T lhs_lane = sg_col == 0 ? lhs_stage_ptr[(lhs_row_base + i) * kRegisterMatrixTileAStride + kk] : T(0);
939 fragments.template lhs_value<i>() = batchlas::portable::select_from_group(sg, lhs_lane, static_cast<uint32_t>(lhs_source_lane));
940 });
941
943 constexpr int j = col_idx;
944 const T rhs_lane = sg_row == 0 ? rhs_stage_ptr[kk * kRegisterMatrixTileBStride + rhs_col_base + j] : T(0);
945 fragments.template rhs_value<j>() =
946 batchlas::portable::select_from_group(sg, rhs_lane, static_cast<uint32_t>(rhs_source_lane_base));
947 });
948
950 constexpr int i = row_idx;
951 const T lhs_value = fragments.template lhs_value<i>();
953 constexpr int j = col_idx;
954 accum.template get<i, j>() += lhs_value * fragments.template rhs_value<j>();
955 });
956 });
957 }
958}
959
960template <typename T>
962 int row_base,
963 int col_base,
964 int row_extent,
965 int col_extent,
966 const RegisterMatrixAccumTile<T>& accum) {
968 constexpr int i = row_idx;
969 const int row = row_base + i;
970 if (row >= row_extent) {
971 return;
972 }
974 constexpr int j = col_idx;
975 const int col = col_base + j;
976 if (col >= col_extent) {
977 return;
978 }
979 detail::write_matrix_output(operand, row, col, accum.template get<i, j>());
980 });
981 });
982}
983
984template <typename T, typename Transform>
986 Transform transform,
987 int row_base,
988 int col_base,
989 int extent,
990 const RegisterMatrixAccumTile<T>& accum) {
992 constexpr int i = row_idx;
993 const int row = row_base + i;
994 if (row >= extent) {
995 return;
996 }
998 constexpr int j = col_idx;
999 const int col = col_base + j;
1000 if (col >= extent || !detail::triangular_storage_contains(transform.uplo, row, col)) {
1001 return;
1002 }
1003 T value = operand.beta * operand.c(row, col) + accum.template get<i, j>();
1004 if constexpr (ComplexScalar<T>) {
1005 if (transform.hermitian && row == col) {
1006 value = T(value.real(), typename T::value_type(0));
1007 }
1008 }
1009 operand.c(row, col) = value;
1010 });
1011 });
1012}
1013
1014template <typename T, typename Transform>
1016 Transform transform,
1017 int row_base,
1018 int col_base,
1019 int extent,
1020 const ComplexRank2kAccumTile<T>& accum) {
1022 constexpr int i = row_idx;
1023 const int row = row_base + i;
1024 if (row >= extent) {
1025 return;
1026 }
1028 constexpr int j = col_idx;
1029 const int col = col_base + j;
1030 if (col >= extent || !detail::triangular_storage_contains(transform.uplo, row, col)) {
1031 return;
1032 }
1033 T value = operand.beta * operand.c(row, col) + accum.template get<i, j>();
1034 if constexpr (ComplexScalar<T>) {
1035 if (transform.hermitian && row == col) {
1036 value = T(value.real(), typename T::value_type(0));
1037 }
1038 }
1039 operand.c(row, col) = value;
1040 });
1041 });
1042}
1043
1044template <typename Item, typename T, typename LhsLoader, typename RhsLoader>
1045inline constexpr void accumulate_rank2k_register_tiled_pass(const Item& item,
1046 RegisterMatrixWorkspace<T>* workspace,
1047 int linear_tid,
1048 int subgroup_id,
1049 int contract_extent,
1050 LhsLoader&& lhs_loader,
1051 RhsLoader&& rhs_loader,
1053 const int tile_count = (contract_extent + kRegisterMatrixTileK - 1) / kRegisterMatrixTileK;
1054 if (tile_count <= 0) {
1055 return;
1056 }
1057
1059 workspace,
1060 linear_tid,
1061 subgroup_id,
1062 0,
1063 0,
1064 true,
1065 true,
1066 0,
1067 std::min(kRegisterMatrixTileK, contract_extent),
1068 lhs_loader,
1069 rhs_loader);
1070 sycl::group_barrier(item.get_group());
1071
1072 for (int tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
1073 const int current_k_base = tile_idx * kRegisterMatrixTileK;
1074 const int current_tile_extent = std::min(kRegisterMatrixTileK, contract_extent - current_k_base);
1075 const int next_tile_idx = tile_idx + 1;
1076 const int current_lhs_stage = register_matrix_lhs_stage(tile_idx);
1077 const int current_rhs_stage = register_matrix_rhs_stage(tile_idx);
1078
1079 if (next_tile_idx < tile_count) {
1080 const int next_k_base = next_tile_idx * kRegisterMatrixTileK;
1081 const int next_tile_extent = std::min(kRegisterMatrixTileK, contract_extent - next_k_base);
1082 if constexpr (kRegisterMatrixRhsStages > 1) {
1084 workspace,
1085 linear_tid,
1086 subgroup_id,
1087 current_lhs_stage,
1088 register_matrix_rhs_stage(next_tile_idx),
1089 false,
1091 next_k_base,
1092 next_tile_extent,
1093 lhs_loader,
1094 rhs_loader);
1095 }
1096 }
1097
1099 workspace,
1100 subgroup_id,
1101 current_lhs_stage,
1102 current_rhs_stage,
1103 linear_tid / kRegisterMatrixLocalCols,
1104 linear_tid % kRegisterMatrixLocalCols,
1105 current_tile_extent,
1106 accum);
1107
1108 if (next_tile_idx < tile_count) {
1109 const int next_k_base = next_tile_idx * kRegisterMatrixTileK;
1110 const int next_tile_extent = std::min(kRegisterMatrixTileK, contract_extent - next_k_base);
1111 if constexpr (kRegisterMatrixRhsStages == 1) {
1112 sycl::group_barrier(item.get_group());
1113 } else {
1114 sycl::group_barrier(item.get_sub_group());
1115 }
1117 workspace,
1118 linear_tid,
1119 subgroup_id,
1120 0,
1121 kRegisterMatrixRhsStages == 1 ? 0 : register_matrix_rhs_stage(next_tile_idx),
1122 true,
1124 next_k_base,
1125 next_tile_extent,
1126 lhs_loader,
1127 rhs_loader);
1128 sycl::group_barrier(item.get_group());
1129 }
1130 }
1131
1132 sycl::group_barrier(item.get_group());
1133}
1134
1135template <int TileK, int TileAStride, typename Workspace, typename Item, typename T, typename LhsLoader, typename RhsLoader>
1136inline constexpr void accumulate_complex_rank2k_tiled_pass_impl(const Item& item,
1137 Workspace* workspace,
1138 int linear_tid,
1139 int subgroup_id,
1140 int local_row,
1141 int local_col,
1142 int contract_extent,
1143 LhsLoader&& lhs_loader,
1144 RhsLoader&& rhs_loader,
1146 const auto sg = item.get_sub_group();
1147 const int sg_lane = subgroup_local_id(item);
1148 const int sg_row = sg_lane / kComplexRank2kLocalCols;
1149 const int sg_col = sg_lane % kComplexRank2kLocalCols;
1150 const int lhs_row_base = (local_row % kComplexRank2kSubgroupRows) * kComplexRank2kThreadTileRows;
1151 const int lhs_source_lane = sg_row * kComplexRank2kLocalCols;
1152 const int rhs_source_lane_base = sg_col;
1153 const int rhs_col_base = local_col * kComplexRank2kThreadTileCols;
1154 T* lhs_tile = workspace->lhs[subgroup_id];
1155 T* rhs_tile = workspace->rhs;
1156
1157 for (int k_base = 0; k_base < contract_extent; k_base += TileK) {
1158 const int tile_extent = std::min(TileK, contract_extent - k_base);
1159
1160 for (int index = sg_lane; index < kComplexRank2kSubgroupTileM * TileK; index += kMaxSupportedSubgroupSize) {
1161 const int subgroup_tile_r = index / TileK;
1162 const int tile_k = index % TileK;
1163 const int tile_r = subgroup_id * kComplexRank2kSubgroupTileM + subgroup_tile_r;
1164 lhs_tile[subgroup_tile_r * TileAStride + tile_k] =
1165 tile_k < tile_extent ? lhs_loader(tile_r, k_base + tile_k) : T(0);
1166 }
1167 for (int index = linear_tid; index < TileK * kComplexRank2kTileN; index += kComplexRank2kThreadsPerGroup) {
1168 const int tile_k = index / kComplexRank2kTileN;
1169 const int tile_c = index % kComplexRank2kTileN;
1170 rhs_tile[tile_k * kComplexRank2kTileBStride + tile_c] =
1171 tile_k < tile_extent ? rhs_loader(tile_c, k_base + tile_k) : T(0);
1172 }
1173 sycl::group_barrier(item.get_group());
1174
1175 for (int kk = 0; kk < tile_extent; ++kk) {
1176 std::array<T, kComplexRank2kThreadTileRows> lhs_frag{};
1177 std::array<T, kComplexRank2kThreadTileCols> rhs_frag{};
1178
1180 constexpr int i = row_idx;
1181 const T lhs_lane = sg_col == 0 ? lhs_tile[(lhs_row_base + i) * TileAStride + kk] : T(0);
1182 lhs_frag[static_cast<std::size_t>(i)] =
1183 batchlas::portable::select_from_group(sg, lhs_lane, static_cast<uint32_t>(lhs_source_lane));
1184 });
1185
1187 constexpr int j = col_idx;
1188 const T rhs_lane = sg_row == 0 ? rhs_tile[kk * kComplexRank2kTileBStride + rhs_col_base + j] : T(0);
1189 rhs_frag[static_cast<std::size_t>(j)] =
1190 batchlas::portable::select_from_group(sg, rhs_lane, static_cast<uint32_t>(rhs_source_lane_base));
1191 });
1192
1194 constexpr int i = row_idx;
1195 const T lhs_value = lhs_frag[static_cast<std::size_t>(i)];
1197 constexpr int j = col_idx;
1198 accum.template get<i, j>() += lhs_value * rhs_frag[static_cast<std::size_t>(j)];
1199 });
1200 });
1201 }
1202
1203 sycl::group_barrier(item.get_group());
1204 }
1205}
1206
1207} // namespace detail::subgroup
1208
1209} // namespace batchlas::device
A std::complex scalar (std::complex<float>, std::complex<double>).
Definition enums.hh:61
DeviceBlasPolicy
Which implementation family a device BLAS call may use.
@ 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
@ NdItem3D
the call receives a sycl::nd_item<3> whose dimensions 1 and 2 index output tiles
Transpose
The operator op() applied to an operand: op(A) = A, A^T or A^H.
Definition enums.hh:171
Side
Side on which a special operand multiplies: op(A) * B or B * op(A).
Definition enums.hh:319
@ NoTrans
op(A) = A.
Definition enums.hh:172
@ Lower
The lower triangle (i >= j).
Definition enums.hh:289
@ Left
The special operand is on the left.
Definition enums.hh:320
T select_from_group(Group g, T x, Id id)
sycl::select_from_group for any trivially copyable T. Collective.
constexpr bool can_use_rankk_register_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr bool can_use_matrix_fast_path(const Item &item, int row_extent, int col_extent, int contract_extent, DeviceBlasPolicy policy)
constexpr int triangular_begin(int index, int extent, TriangularTransform transform, Side side)
constexpr bool can_use_rank2k_register_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr int triangular_end(int index, int extent, TriangularTransform transform, Side side)
constexpr void static_for_impl(Fn &&fn, std::index_sequence< I... >)
constexpr int subgroup_group_id(const Item &item)
constexpr int matrix_tile_group_row(const Item &)
constexpr void accumulate_rank2k_register_tiled_pass(const Item &item, RegisterMatrixWorkspace< T > *workspace, int linear_tid, int subgroup_id, int contract_extent, LhsLoader &&lhs_loader, RhsLoader &&rhs_loader, RegisterMatrixAccumTile< T > &accum)
constexpr bool subgroup_policy_matches(DeviceBlasPolicy policy, int actual_size)
constexpr bool supports_aligned_packet_loads(const T *ptr, int ld, int stride)
constexpr int subgroup_size(const Item &item)
constexpr int matrix_tile_group_col(const Item &)
constexpr int matrix_rows_per_subgroup(int row_extent, int total_sg, int sg_size, int contract_extent, Side side)
constexpr void write_register_matrix_tile(MatrixMatrixOperand< T > operand, int row_base, int col_base, int row_extent, int col_extent, const RegisterMatrixAccumTile< T > &accum)
constexpr void load_register_matrix_stage(const Item &item, RegisterMatrixWorkspace< T > *workspace, int linear_tid, int subgroup_id, int lhs_stage, int rhs_stage, bool load_lhs, bool load_rhs, int k_base, int tile_extent, LhsLoader &&lhs_loader, RhsLoader &&rhs_loader)
constexpr int matrix_tile_k(int contract_extent, int sg_size, Side side)
constexpr int matrix_tile_group_row_stride(const Item &)
constexpr bool can_use_matrix_aligned_nn_large_fast_path(const Item &item, int row_extent, int col_extent, int contract_extent, Transpose trans_a, Transpose trans_b, DeviceBlasPolicy policy)
constexpr int matrix_tile_group_col_stride(const Item &)
std::conditional_t< supports_packet_v< T, Width >, sycl::vec< T, Width >, PacketArray< T, Width > > Packet
constexpr int register_matrix_rhs_stage(int tile_idx)
constexpr int register_matrix_lhs_stage(int tile_idx)
constexpr bool can_use_matrix_register_fast_path(const Item &item, int row_extent, int col_extent, int contract_extent, DeviceBlasPolicy policy)
constexpr bool can_use_complex_rank2k_tiled_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr int rows_per_subgroup(int extent, int total_sg, int max_rows)
constexpr void accumulate_complex_rank2k_tiled_pass_impl(const Item &item, Workspace *workspace, int linear_tid, int subgroup_id, int local_row, int local_col, int contract_extent, LhsLoader &&lhs_loader, RhsLoader &&rhs_loader, ComplexRank2kAccumTile< T > &accum)
constexpr int subgroup_local_id(const Item &item)
constexpr int triangular_tile_end(int row_base, int row_extent, int col_base, int col_extent, int contract_extent, TriangularTransform transform)
constexpr Packet< T, Width > packet_load_aligned(const T *ptr, int offset)
constexpr void write_rank2k_register_tile(MatrixMatrixOperand< T > operand, Transform transform, int row_base, int col_base, int extent, const RegisterMatrixAccumTile< T > &accum)
constexpr bool can_use_complex_rankk_tiled_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr void accumulate_register_matrix_stage(const Item &item, const RegisterMatrixWorkspace< T > *workspace, int subgroup_id, int lhs_stage, int rhs_stage, int local_row, int local_col, int tile_extent, RegisterMatrixAccumTile< T > &accum)
constexpr int triangular_tile_begin(int row_base, int row_extent, int col_base, int col_extent, int contract_extent, TriangularTransform transform)
constexpr bool is_nd_item_3d_launch(const Exec &)
constexpr int subgroup_count(const Item &item)
constexpr void write_complex_rank2k_tile(MatrixMatrixOperand< T > operand, Transform transform, int row_base, int col_base, int extent, const ComplexRank2kAccumTile< T > &accum)
constexpr int output_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr void write_matrix_output(const MatrixMatrixOperand< T > &operand, int row, int col, const T &value)
constexpr int input_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr int group_local_linear_range(const Group &group)
constexpr int item_local_linear_id(const Item &item)
constexpr bool triangular_storage_contains(int row, int col)
Trivially copyable view of a MatrixView for use inside a SYCL kernel.
Definition matrix.hh:145
auto data() const
Base pointer of item 0.
Definition matrix.hh:217
auto rows() const
Row count (the capacity on a heterogeneous batch).
Definition matrix.hh:219
auto ld() const
Dense leading dimension, in elements.
Definition matrix.hh:227
auto cols() const
Column count (the capacity on a heterogeneous batch).
Definition matrix.hh:223
auto stride() const
Dense batch stride, in elements.
Definition matrix.hh:228
Host-side description of a launch, for the *_workspace_elements queries.
DeviceBlasLaunchKind kind
executor the call receives
int subgroup_size
sub-group size of the kernel; 1 for DeviceBlasLaunchKind::Group
Runtime transpose modes of a general matrix-matrix product.
Operands of a matrix-matrix operation: .
KernelMatrixView< T, MatrixFormat::Dense > c
output matrix; read (scaled by beta) and overwritten
T beta
scale of the input C; 0 still reads C
KernelMatrixView< T, MatrixFormat::Dense > b
second input matrix
Operands of a rank-k update: .
KernelMatrixView< T, MatrixFormat::Dense > c
square output; only the uplo triangle is touched
Mode of a symmetric or Hermitian rank-2k update (syr2k, her2k).
Mode of a symmetric or Hermitian rank-k update (syrk, herk).
Transpose trans
NoTrans: ; otherwise .
Mode of a triangular product (trmv, trmm).
Uplo uplo
which triangle of A is stored
Side side
A multiplies from the left or the right
std::array< T, kComplexRank2kThreadTileRows *kComplexRank2kThreadTileCols > values
T rhs[kComplexRank2kInKernelTileK *kComplexRank2kTileBStride]
T lhs[kMaxSubgroups][kComplexRank2kSubgroupTileM *kComplexRank2kInKernelTileAStride]
T lhs[kMaxSubgroups][kComplexRank2kSubgroupTileM *kComplexRank2kTileAStride]
T rhs[kComplexRank2kTileK *kComplexRank2kTileBStride]
T rhs[kOptimizedGemmStages *kOptimizedGemmStageBSize]
T lhs[kOptimizedGemmStages *kOptimizedGemmStageASize]
std::array< T, kRegisterMatrixThreadTileRows *kRegisterMatrixThreadTileCols > values
T rhs[kRegisterMatrixRhsStages][kRegisterMatrixTileK *kRegisterMatrixTileBStride]
T lhs[kMaxSubgroups][kRegisterMatrixLhsStages][kRegisterMatrixSubgroupTileM *kRegisterMatrixTileAStride]