Kernels
danieldk HF Staff commited on
Commit
d7f7ab1
·
verified ·
1 Parent(s): 65bb2f7

Build uploaded using `kernels` (batch 4/10).

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/kernel/sm100_mla_tile_scheduler.hpp +160 -0
  2. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_bwd_reference.hpp +410 -0
  3. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_fwd_gen_reference.hpp +185 -0
  4. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_fwd_reference.hpp +195 -0
  5. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_mla_reference.hpp +201 -0
  6. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/reference_abs_error.hpp +282 -0
  7. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/86_blackwell_mixed_dtype_gemm/mixed_dtype_helper.cuh +269 -0
  8. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/87_blackwell_geforce_gemm_blockwise/utils.h +83 -0
  9. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_bwd_tma_warpspecialized.hpp +863 -0
  10. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_load.hpp +140 -0
  11. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_softmax.hpp +305 -0
  12. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_tma.hpp +526 -0
  13. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_tma_warpspecialized.hpp +560 -0
  14. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_common.hpp +245 -0
  15. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_epilogue.hpp +156 -0
  16. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_epilogue_bwd.hpp +157 -0
  17. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_fusion.hpp +283 -0
  18. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/device/device_universal.hpp +278 -0
  19. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/device/fmha_device_bwd.hpp +299 -0
  20. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_builder.hpp +158 -0
  21. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_bwd_convert.hpp +143 -0
  22. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_bwd_sum_OdO.hpp +134 -0
  23. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_tma.hpp +226 -0
  24. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_tma_warpspecialized.hpp +422 -0
  25. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_options.hpp +83 -0
  26. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_tile_scheduler.hpp +204 -0
  27. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/fmha_bwd_reference.hpp +357 -0
  28. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/fmha_reference.hpp +156 -0
  29. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/reference_abs_error.hpp +129 -0
  30. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/dist_gemm_helpers.h +164 -0
  31. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/gather_tensor.hpp +215 -0
  32. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/helper.h +108 -0
  33. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/cute/tutorial/blackwell/example_utils.hpp +105 -0
  34. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/call_bypass_dlpack.py +166 -0
  35. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/call_from_jit.py +259 -0
  36. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/dynamic_smem_size.py +114 -0
  37. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/elementwise_add.py +403 -0
  38. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/elementwise_apply.py +393 -0
  39. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/flash_attention_v2.py +1334 -0
  40. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/sgemm.py +866 -0
  41. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/smem_allocator.py +200 -0
  42. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/tensorop_gemm.py +1012 -0
  43. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_blockscaled_gemm_persistent.py +2466 -0
  44. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm.py +1890 -0
  45. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm_persistent.py +2193 -0
  46. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm_software_pipeline.py +1831 -0
  47. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/fmha.py +0 -0
  48. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/grouped_gemm.py +2445 -0
  49. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py +0 -0
  50. build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd_reference.py +397 -0
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/kernel/sm100_mla_tile_scheduler.hpp ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/fast_math.h"
36
+ #include "cutlass/kernel_hardware_info.h"
37
+
38
+ namespace cutlass::fmha::kernel {
39
+
40
+ ////////////////////////////////////////////////////////////////////////////////
41
+
42
+ struct Sm100MlaIndividualTileScheduler {
43
+
44
+ struct Params {
45
+ dim3 grid;
46
+ };
47
+
48
+ bool valid_ = true;
49
+
50
+ CUTLASS_DEVICE
51
+ Sm100MlaIndividualTileScheduler(Params const&) {}
52
+
53
+ template<class ProblemShape, class ClusterShape>
54
+ static Params to_underlying_arguments(
55
+ ProblemShape const& problem_shape, KernelHardwareInfo hw_info,
56
+ ClusterShape const& cluster_shape, int const& split_kv) {
57
+ using namespace cute;
58
+ dim3 grid(get<0>(cluster_shape), get<3>(problem_shape) /* Batch */, split_kv /*Maximum Split KV*/);
59
+ return Params{ grid };
60
+ }
61
+
62
+ static dim3 get_grid_shape(Params const& params) {
63
+ return params.grid;
64
+ }
65
+
66
+ CUTLASS_DEVICE
67
+ bool is_valid() {
68
+ return valid_;
69
+ }
70
+
71
+ CUTLASS_DEVICE
72
+ auto get_block_coord() {
73
+ using namespace cute;
74
+ return make_coord(blockIdx.x, _0{}, blockIdx.y, blockIdx.z);
75
+ }
76
+
77
+ CUTLASS_DEVICE
78
+ Sm100MlaIndividualTileScheduler& operator++() {
79
+ valid_ = false;
80
+ return *this;
81
+ }
82
+ };
83
+
84
+ ////////////////////////////////////////////////////////////////////////////////
85
+
86
+ struct Sm100MlaPersistentTileScheduler {
87
+
88
+ struct Params {
89
+ int num_blocks;
90
+ FastDivmod divmod_m_block;
91
+ FastDivmod divmod_b;
92
+ FastDivmod divmod_split_kv;
93
+ KernelHardwareInfo hw_info;
94
+ };
95
+
96
+ int block_idx = 0;
97
+ Params params;
98
+
99
+ CUTLASS_DEVICE
100
+ Sm100MlaPersistentTileScheduler(Params const& params) : block_idx(blockIdx.x), params(params) {}
101
+
102
+ template<class ProblemShape, class ClusterShape>
103
+ static Params to_underlying_arguments(
104
+ ProblemShape const& problem_shape, KernelHardwareInfo hw_info,
105
+ ClusterShape const& cluster_shape, int const& split_kv) {
106
+ using namespace cute;
107
+ // Get SM count if needed, otherwise use user supplied SM count
108
+ int sm_count = hw_info.sm_count;
109
+ if (sm_count <= 1 || sm_count % size<0>(cluster_shape) != 0) {
110
+ CUTLASS_TRACE_HOST(" WARNING: Arguments do not include a valid SM count.\n"
111
+ " For optimal performance, populate the arguments KernelHardwareInfo struct with the SM count.");
112
+ sm_count = KernelHardwareInfo::query_device_multiprocessor_count(hw_info.device_id);
113
+ }
114
+
115
+ CUTLASS_TRACE_HOST("to_underlying_arguments(): Setting persistent grid SM count to " << sm_count);
116
+ hw_info.sm_count = sm_count;
117
+
118
+ int num_m_blocks = size<0>(cluster_shape);
119
+ int num_blocks = num_m_blocks * get<3>(problem_shape) /* Batch */;
120
+ num_blocks *= split_kv; /* Maximum Split KV*/
121
+
122
+ return Params {
123
+ num_blocks,
124
+ { num_m_blocks}, { get<3>(problem_shape) }, {split_kv},
125
+ hw_info
126
+ };
127
+ }
128
+
129
+ static dim3 get_grid_shape(Params const& params) {
130
+ dim3 grid(std::min(params.num_blocks, params.hw_info.sm_count), 1, 1);
131
+ return grid;
132
+ }
133
+
134
+ CUTLASS_DEVICE
135
+ bool is_valid() {
136
+ return block_idx < params.num_blocks;
137
+ }
138
+
139
+ CUTLASS_DEVICE
140
+ auto get_block_coord() {
141
+ using namespace cute;
142
+ int block_decode = block_idx;
143
+ int m_block, bidb, n_split_kv;
144
+ params.divmod_m_block(block_decode, m_block, block_decode);
145
+ params.divmod_split_kv(block_decode, n_split_kv, block_decode);
146
+ params.divmod_b(block_decode, bidb, block_decode);
147
+ return make_coord(m_block, _0{}, bidb, n_split_kv);
148
+ }
149
+
150
+ CUTLASS_DEVICE
151
+ Sm100MlaPersistentTileScheduler& operator++() {
152
+ block_idx += gridDim.x;
153
+ return *this;
154
+ }
155
+ };
156
+
157
+ ////////////////////////////////////////////////////////////////////////////////
158
+
159
+ } // namespace cutlass::fmha::kernel
160
+
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_bwd_reference.hpp ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2025 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+
33
+ #pragma once
34
+
35
+ #include "cute/tensor.hpp"
36
+ #include "collective/fmha_fusion.hpp"
37
+
38
+ using namespace cutlass::fmha::collective;
39
+ /////////////////////////////////////////////////////////////////////////////////////////////////
40
+
41
+ template<
42
+ class ProblemShape,
43
+ class TensorQ, class TensorK, class TensorV,
44
+ class TensorO, class TensorLSE, class TensorDO,
45
+ class TensorDQ, /* class TensorDK, class TensorDV, */
46
+ class Fusion
47
+ >
48
+ void __global__ fmha_bwd_reference_dQ_kernel(
49
+ ProblemShape problem_shape_in,
50
+ TensorQ mQ_in, TensorK mK_in, TensorV mV_in,
51
+ TensorO mO_in, TensorLSE mLSE_in, TensorDO mDO_in,
52
+ TensorDQ mDQ_in, /* TensorDK mDK, TensorDV mDV, */
53
+ Fusion fusion) {
54
+
55
+ using namespace cute;
56
+ using namespace cutlass::fmha::collective;
57
+
58
+ using Element = typename TensorO::value_type;
59
+ using ElementAcc = typename TensorLSE::value_type;
60
+
61
+ extern __shared__ char mS_mem[];
62
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
63
+
64
+ ElementAcc softmax_scale = 1.0f / sqrtf(size<2>(problem_shape_in));
65
+
66
+ for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
67
+ auto [problem_shape, offset] = apply_variable_length_offset(
68
+ problem_shape_in,
69
+ make_coord(_0{}, _0{}, _0{}, _0{},idx2crd(idx_L, get<4>(problem_shape_in)))
70
+ );
71
+ // problem_shape = problem_shape_in;
72
+ // offset = repeat_like(problem_shape_in, _0{});
73
+ auto mQ = domain_offset(select<0,2,4>(offset), mQ_in);
74
+ auto mK = domain_offset(select<1,2,4>(offset), mK_in);
75
+ auto mV = domain_offset(select<1,3,4>(offset), mV_in);
76
+ auto mO = domain_offset(select<0,3,4>(offset), mO_in);
77
+ auto mLSE = domain_offset(select<0,4>(offset), mLSE_in);
78
+ auto mDO = domain_offset(select<0,3,4>(offset), mDO_in);
79
+ auto mDQ = domain_offset(select<0,2,4>(offset), mDQ_in);
80
+ for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape); idx_Q += gridDim.x) {
81
+ for (int idx_K = threadIdx.x; idx_K < size<1>(problem_shape); idx_K += blockDim.x) {
82
+ ElementAcc acc_qk = 0;
83
+ ElementAcc acc_dov = 0;
84
+ ElementAcc acc_doo = 0;
85
+ for (int idx_D0 = 0; idx_D0 < size<2>(problem_shape); idx_D0++) {
86
+ acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
87
+ // acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
88
+ // acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
89
+ } // for idx_D0
90
+
91
+ for (int idx_D1 = 0; idx_D1 < size<3>(problem_shape); idx_D1++) {
92
+ acc_dov += mDO(idx_Q, idx_D1, idx_L) * mV(idx_K, idx_D1, idx_L);
93
+ acc_doo += mDO(idx_Q, idx_D1, idx_L) * mO(idx_Q, idx_D1, idx_L);
94
+ }
95
+
96
+ auto id = make_identity_tensor(make_shape(1, 1));
97
+ auto frag = make_tensor<ElementAcc>(Shape<_1, _1>{});
98
+ frag(0) = acc_qk;
99
+ fusion.apply_mask(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
100
+ acc_qk = frag(0);
101
+
102
+ mS[idx_K] = static_cast<Element>(expf(softmax_scale * acc_qk - mLSE(idx_Q, idx_L)) * softmax_scale * (acc_dov - acc_doo));
103
+ } // for idx_K
104
+
105
+ __syncthreads();
106
+
107
+ for (int idx_D = threadIdx.x; idx_D < size<2>(problem_shape); idx_D += blockDim.x) {
108
+ ElementAcc acc = 0;
109
+ for (int idx_K = 0; idx_K < size<1>(problem_shape); idx_K++) {
110
+ ElementAcc rK = mK(idx_K, idx_D, idx_L);
111
+ ElementAcc rDS = mS[idx_K];
112
+ acc += rDS * rK;
113
+ }
114
+ mDQ(idx_Q, idx_D, idx_L) = static_cast<typename TensorDQ::value_type>(acc);
115
+ } // for idx_D
116
+ }
117
+ }
118
+ }
119
+
120
+ /////////////////////////////////////////////////////////////////////////////////////////////////
121
+
122
+ template<
123
+ class ProblemShape,
124
+ class TensorQ, class TensorK, class TensorV,
125
+ class TensorO, class TensorLSE, class TensorDO,
126
+ /* class TensorDQ, */ class TensorDK, /* class TensorDV, */
127
+ class Fusion
128
+ >
129
+ void __global__ fmha_bwd_reference_dK_kernel(
130
+ ProblemShape problem_shape_in,
131
+ TensorQ mQ_in, TensorK mK_in, TensorV mV_in,
132
+ TensorO mO_in, TensorLSE mLSE_in, TensorDO mDO_in,
133
+ /* TensorDQ mDQ_in, */ TensorDK mDK_in, /* TensorDV mDV_in, */
134
+ Fusion fusion) {
135
+
136
+ using namespace cute;
137
+ using namespace cutlass::fmha::collective;
138
+
139
+ using Element = typename TensorO::value_type;
140
+ using ElementAcc = typename TensorLSE::value_type;
141
+
142
+ extern __shared__ char mS_mem[];
143
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
144
+
145
+ ElementAcc softmax_scale = 1.0f / sqrtf(size<2>(problem_shape_in));
146
+
147
+ auto [H, B] = get<4>(problem_shape_in);
148
+ auto [H_R, H_K] = H;
149
+
150
+ for (int idx_HB = blockIdx.y; idx_HB < H_K * B; idx_HB += gridDim.y) {
151
+ auto [idx_H_K, idx_B] = idx2crd(idx_HB, make_shape(H_K, B));
152
+ auto [problem_shape, offset] = apply_variable_length_offset(
153
+ problem_shape_in,
154
+ make_coord(_0{}, _0{}, _0{}, _0{}, make_coord(make_coord(_0{}, idx_H_K), idx_B))
155
+ );
156
+ auto [Q, K, D, D_VO, HB] = problem_shape;
157
+ auto [offset_Q, offset_K, offset_D, offset_D_VO, offset_HB] = offset;
158
+
159
+ auto mQ = domain_offset(make_coord(offset_Q, offset_D, offset_HB), mQ_in);
160
+ auto mK = domain_offset(make_coord(offset_K, offset_D, offset_HB), mK_in);
161
+ auto mV = domain_offset(make_coord(offset_K, offset_D_VO, offset_HB), mV_in);
162
+ auto mO = domain_offset(make_coord(offset_Q, offset_D_VO, offset_HB), mO_in);
163
+ auto mLSE = domain_offset(make_coord(offset_Q, offset_HB), mLSE_in);
164
+ auto mDO = domain_offset(make_coord(offset_Q, offset_D_VO, offset_HB), mDO_in);
165
+ auto mDK = domain_offset(make_coord(offset_K, offset_D, offset_HB), mDK_in);
166
+
167
+ for (int idx_K = blockIdx.x; idx_K < K; idx_K += gridDim.x) {
168
+ ElementAcc acc_dk = 0;
169
+ for (int idx_H_R = 0; idx_H_R < H_R; idx_H_R++) {
170
+ auto coord_HB = make_coord(make_coord(idx_H_R, idx_H_K), idx_B);
171
+ for (int idx_Q = threadIdx.x; idx_Q < Q; idx_Q += blockDim.x) {
172
+ ElementAcc acc_qk = 0;
173
+ ElementAcc acc_dov = 0;
174
+ ElementAcc acc_doo = 0;
175
+ for (int idx_D0 = 0; idx_D0 < D; idx_D0++) {
176
+ ElementAcc rQ = mQ(idx_Q, idx_D0, coord_HB);
177
+ ElementAcc rK = mK(idx_K, idx_D0, coord_HB);
178
+ acc_qk += rQ * rK;
179
+ } // for idx_D0
180
+
181
+ for (int idx_D1 = 0; idx_D1 < D_VO; idx_D1++) {
182
+ ElementAcc rDO = mDO(idx_Q, idx_D1, coord_HB);
183
+ ElementAcc rV = mV(idx_K, idx_D1, coord_HB);
184
+ ElementAcc rO = mO(idx_Q, idx_D1, coord_HB);
185
+ acc_dov += rDO * rV;
186
+ acc_doo += rDO * rO ;
187
+ }
188
+ auto id = make_identity_tensor(make_shape(1, 1));
189
+ auto frag = make_tensor<ElementAcc>(Shape<_1, _1>{});
190
+ frag(0) = acc_qk;
191
+ fusion.apply_mask(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
192
+ acc_qk = frag(0);
193
+
194
+ mS[idx_Q] = static_cast<Element>(expf(softmax_scale * acc_qk - mLSE(idx_Q, coord_HB)) * softmax_scale * (acc_dov - acc_doo));
195
+ } // for idx_Q
196
+
197
+ __syncthreads();
198
+
199
+ int idx_D = threadIdx.x;
200
+ if (idx_D < D) {
201
+ for (int idx_Q = 0; idx_Q < Q; idx_Q++) {
202
+ ElementAcc rQ = mQ(idx_Q, idx_D, coord_HB);
203
+ ElementAcc rDS = mS[idx_Q];
204
+ acc_dk += rDS * rQ;
205
+ }
206
+ }
207
+ __syncthreads();
208
+ } // for idx_H_R
209
+
210
+ int idx_D = threadIdx.x;
211
+ if (idx_D < D) {
212
+ auto coord_HB = make_coord(make_coord(0, idx_H_K), idx_B);
213
+ mDK(idx_K, idx_D, coord_HB) = static_cast<typename TensorDK::value_type>(acc_dk);
214
+ }
215
+ } // for idx_K
216
+ } // for idx_HB
217
+ }
218
+
219
+ /////////////////////////////////////////////////////////////////////////////////////////////////
220
+
221
+ template<
222
+ class ProblemShape,
223
+ class TensorQ, class TensorK, class TensorV,
224
+ class TensorO, class TensorLSE, class TensorDO,
225
+ /* class TensorDQ, class TensorDK, */ class TensorDV,
226
+ class Fusion
227
+ >
228
+ void __global__ fmha_bwd_reference_dV_kernel(
229
+ ProblemShape problem_shape_in,
230
+ TensorQ mQ_in, TensorK mK_in, TensorV mV_in,
231
+ TensorO mO_in, TensorLSE mLSE_in, TensorDO mDO_in,
232
+ /* TensorDQ mDQ_in, TensorDK mDK_in, */ TensorDV mDV_in,
233
+ Fusion fusion) {
234
+
235
+ using namespace cute;
236
+ using namespace cutlass::fmha::collective;
237
+
238
+ using Element = typename TensorO::value_type;
239
+ using ElementAcc = typename TensorLSE::value_type;
240
+
241
+ extern __shared__ char mS_mem[];
242
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
243
+
244
+ ElementAcc softmax_scale = 1.0f / sqrtf(size<2>(problem_shape_in));
245
+
246
+ auto [H, B] = get<4>(problem_shape_in);
247
+ auto [H_R, H_K] = H;
248
+
249
+ for (int idx_HB = blockIdx.y; idx_HB < H_K * B; idx_HB += gridDim.y) {
250
+ auto [idx_H_K, idx_B] = idx2crd(idx_HB, make_shape(H_K, B));
251
+ auto [problem_shape, offset] = apply_variable_length_offset(
252
+ problem_shape_in,
253
+ make_coord(_0{}, _0{}, _0{}, _0{}, make_coord(make_coord(_0{}, idx_H_K), idx_B))
254
+ );
255
+ auto [Q, K, D, D_VO, HB] = problem_shape;
256
+ auto [offset_Q, offset_K, offset_D, offset_D_VO, offset_HB] = offset;
257
+
258
+ auto mQ = domain_offset(make_coord(offset_Q, offset_D, offset_HB), mQ_in);
259
+ auto mK = domain_offset(make_coord(offset_K, offset_D, offset_HB), mK_in);
260
+ auto mV = domain_offset(make_coord(offset_K, offset_D_VO, offset_HB), mV_in);
261
+ auto mO = domain_offset(make_coord(offset_Q, offset_D_VO, offset_HB), mO_in);
262
+ auto mLSE = domain_offset(make_coord(offset_Q, offset_HB), mLSE_in);
263
+ auto mDO = domain_offset(make_coord(offset_Q, offset_D_VO, offset_HB), mDO_in);
264
+ auto mDV = domain_offset(make_coord(offset_K, offset_D_VO, offset_HB), mDV_in);
265
+
266
+ for (int idx_K = blockIdx.x; idx_K < K; idx_K += gridDim.x) {
267
+ ElementAcc acc_dv = 0;
268
+ for (int idx_H_R = 0; idx_H_R < H_R; idx_H_R++) {
269
+ auto coord_HB = make_coord(make_coord(idx_H_R, idx_H_K), idx_B);
270
+ for (int idx_Q = threadIdx.x; idx_Q < Q; idx_Q += blockDim.x) {
271
+ ElementAcc acc_qk = 0;
272
+
273
+ for (int idx_D0 = 0; idx_D0 < D; idx_D0++) {
274
+ ElementAcc rQ = mQ(idx_Q, idx_D0, coord_HB);
275
+ ElementAcc rK = mK(idx_K, idx_D0, coord_HB);
276
+ acc_qk += rQ * rK;
277
+ } // for idx_D0
278
+
279
+ auto id = make_identity_tensor(make_shape(1, 1));
280
+ auto frag = make_tensor<ElementAcc>(Shape<_1, _1>{});
281
+ frag(0) = acc_qk;
282
+ fusion.apply_mask(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
283
+ acc_qk = frag(0);
284
+
285
+ mS[idx_Q] = static_cast<Element>(expf(softmax_scale * acc_qk - mLSE(idx_Q, coord_HB)));
286
+ } // for idx_Q
287
+
288
+ __syncthreads();
289
+
290
+ int idx_D_VO = threadIdx.x;
291
+ if (idx_D_VO < D_VO) {
292
+ for (int idx_Q = 0; idx_Q < Q; idx_Q++) {
293
+ ElementAcc rDO = mDO(idx_Q, idx_D_VO, coord_HB);
294
+ ElementAcc rP = mS[idx_Q];
295
+ acc_dv += rP * rDO;
296
+ }
297
+ } // for idx_D
298
+
299
+ __syncthreads();
300
+ } // for idx_H_R
301
+
302
+ int idx_D_VO = threadIdx.x;
303
+ if (idx_D_VO < D_VO) {
304
+ auto coord_HB = make_coord(make_coord(0, idx_H_K), idx_B);
305
+ mDV(idx_K, idx_D_VO, coord_HB) = static_cast<typename TensorDV::value_type>(acc_dv);
306
+ }
307
+ } // for idx_K
308
+ } // for idx_L
309
+ }
310
+
311
+ /////////////////////////////////////////////////////////////////////////////////////////////////
312
+
313
+ template<
314
+ class ProblemShape,
315
+ class TensorQ, class TensorK, class TensorV,
316
+ class TensorO, class TensorLSE, class TensorDO,
317
+ /**/ class TensorDQ, /** / class TensorDK, / ** / class TensorDV, / **/
318
+ class Fusion
319
+ >
320
+ void fmha_bwd_reference_dQ(
321
+ ProblemShape problem_shape,
322
+ TensorQ mQ, TensorK mK, TensorV mV,
323
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
324
+ /**/ TensorDQ mDQ, /** / TensorDK mDK, / ** / TensorDV mDV, / **/
325
+ Fusion fusion) {
326
+
327
+ using namespace cute;
328
+
329
+ dim3 grid(size<0>(mDQ), size<2>(mDQ), 1);
330
+ dim3 block(256);
331
+ int shared_mem = size<0>(mK) * sizeof(typename TensorDQ::value_type);
332
+ fmha_bwd_reference_dQ_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, fusion);
333
+ }
334
+
335
+ /////////////////////////////////////////////////////////////////////////////////////////////////
336
+
337
+ template<
338
+ class ProblemShape,
339
+ class TensorQ, class TensorK, class TensorV,
340
+ class TensorO, class TensorLSE, class TensorDO,
341
+ /** / class TensorDQ, / **/ class TensorDK, /** / class TensorDV, / **/
342
+ class Fusion
343
+ >
344
+ void fmha_bwd_reference_dK(
345
+ ProblemShape problem_shape,
346
+ TensorQ mQ, TensorK mK, TensorV mV,
347
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
348
+ /** / TensorDQ mDQ, / **/ TensorDK mDK, /** / TensorDV mDV, / **/
349
+ Fusion fusion) {
350
+
351
+ using namespace cute;
352
+
353
+ auto [K, D, HB] = mDK.shape();
354
+ auto [H, B] = HB;
355
+ auto [H_R, H_K] = H;
356
+ dim3 grid(K, H_K * B, 1);
357
+ dim3 block(std::max(D, 256));
358
+ int shared_mem = size<0>(mDO) * sizeof(typename TensorDK::value_type);
359
+ fmha_bwd_reference_dK_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDK, fusion);
360
+ }
361
+
362
+ /////////////////////////////////////////////////////////////////////////////////////////////////
363
+
364
+ template<
365
+ class ProblemShape,
366
+ class TensorQ, class TensorK, class TensorV,
367
+ class TensorO, class TensorLSE, class TensorDO,
368
+ /** / class TensorDQ, / ** / class TensorDK, / **/ class TensorDV, /**/
369
+ class Fusion
370
+ >
371
+ void fmha_bwd_reference_dV(
372
+ ProblemShape problem_shape,
373
+ TensorQ mQ, TensorK mK, TensorV mV,
374
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
375
+ /** / TensorDQ mDQ, / ** / TensorDK mDK, / **/ TensorDV mDV, /**/
376
+ Fusion fusion) {
377
+
378
+ using namespace cute;
379
+
380
+ auto [K, D_VO, HB] = mDV.shape();
381
+ auto [H, B] = HB;
382
+ auto [H_R, H_K] = H;
383
+ dim3 grid(K, H_K * B, 1);
384
+ dim3 block(std::max(D_VO, 256));
385
+ int shared_mem = size<0>(mDO) * sizeof(typename TensorDV::value_type);
386
+ fmha_bwd_reference_dV_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDV, fusion);
387
+ }
388
+
389
+ /////////////////////////////////////////////////////////////////////////////////////////////////
390
+
391
+ template<
392
+ class ProblemShape,
393
+ class TensorQ, class TensorK, class TensorV,
394
+ class TensorO, class TensorLSE, class TensorDO,
395
+ class TensorDQ, class TensorDK, class TensorDV,
396
+ class Fusion
397
+ >
398
+ void fmha_bwd_reference(
399
+ ProblemShape problem_shape,
400
+ TensorQ mQ, TensorK mK, TensorV mV,
401
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
402
+ TensorDQ mDQ, TensorDK mDK, TensorDV mDV,
403
+ Fusion fusion) {
404
+
405
+ fmha_bwd_reference_dQ(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, fusion);
406
+ fmha_bwd_reference_dK(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDK, fusion);
407
+ fmha_bwd_reference_dV(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDV, fusion);
408
+ }
409
+
410
+ /////////////////////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_fwd_gen_reference.hpp ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include <vector>
34
+ #include "cute/tensor.hpp"
35
+
36
+ /////////////////////////////////////////////////////////////////////////////////////////////////
37
+
38
+ template<
39
+ class ElementAcc,
40
+ class ProblemShape,
41
+ class TensorQ,
42
+ class TensorNewK,
43
+ class TensorNewV,
44
+ class TensorCacheK,
45
+ class TensorCacheV,
46
+ class TensorO
47
+ >
48
+ void __global__ fmha_fwd_gen_reference_kernel(
49
+ ProblemShape problem_shape,
50
+ const int* seqlen_kv, const int* cache_batch_idx,
51
+ TensorQ mQ, TensorNewK mNewK, TensorNewV mNewV,
52
+ TensorCacheK mCacheK, TensorCacheV mCacheV, TensorO mO) {
53
+
54
+ using namespace cute;
55
+ extern __shared__ char mS_mem[];
56
+ ElementAcc* mS = reinterpret_cast<ElementAcc*>(mS_mem);
57
+
58
+ float scale = 1.0f / std::sqrt(float(get<2>(problem_shape)));
59
+
60
+ if (mNewK.data() != nullptr) {
61
+ // 1. copy in new_k to cache
62
+ for (int idx_h = blockIdx.x; idx_h < size<3,0,1>(problem_shape); idx_h += gridDim.x) {
63
+ for (int idx_b = blockIdx.z; idx_b < size<3,1>(problem_shape); idx_b += gridDim.z) {
64
+ int idx_b_kv = cache_batch_idx != nullptr ? cache_batch_idx[idx_b] : idx_b;
65
+ for (int idx_d = threadIdx.x; idx_d < size<2>(problem_shape); idx_d += blockDim.x) {
66
+ mCacheK(seqlen_kv[idx_b], idx_d, make_coord(make_coord(_0{}, idx_h), idx_b_kv)) =
67
+ mNewK(_0{}, idx_d, make_coord(make_coord(_0{}, idx_h), idx_b));
68
+ mCacheV(seqlen_kv[idx_b], idx_d, make_coord(make_coord(_0{}, idx_h), idx_b_kv)) =
69
+ mNewV(_0{}, idx_d, make_coord(make_coord(_0{}, idx_h), idx_b));
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ // 2. compute attention
76
+ for (int idx_h_kv = blockIdx.x; idx_h_kv < size<3,0,1>(problem_shape); idx_h_kv += gridDim.x) {
77
+ for (int idx_h_qo = blockIdx.y; idx_h_qo < size<3,0,0>(problem_shape); idx_h_qo += gridDim.y) {
78
+ int idx_h = idx_h_qo + size<3,0,0>(problem_shape) * idx_h_kv;
79
+ for (int idx_b = blockIdx.z; idx_b < size<3,1>(problem_shape); idx_b += gridDim.z) {
80
+ int idx_b_kv = cache_batch_idx != nullptr ? cache_batch_idx[idx_b] : idx_b;
81
+ const int kDim = 128;
82
+ ElementAcc reg_o[kDim] = {0};
83
+ ElementAcc row_max = -INFINITY;
84
+ ElementAcc row_sum = 0;
85
+ auto iteration = [&](auto const& tK, auto const& tV) {
86
+ ElementAcc reg_s = 0;
87
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
88
+ ElementAcc eQ = mQ(_0{}, idx_d, make_coord(idx_h, idx_b));
89
+ ElementAcc eK = tK(idx_d);
90
+ reg_s += eQ * eK;
91
+ }
92
+
93
+ ElementAcc old_row_max = row_max;
94
+ row_max = std::max(row_max, reg_s);
95
+
96
+ ElementAcc adjustment = std::exp(scale * (old_row_max - row_max));
97
+ row_sum *= adjustment;
98
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
99
+ reg_o[idx_d] *= adjustment;
100
+ }
101
+
102
+ ElementAcc reg_p = std::exp(scale * (reg_s - row_max));
103
+ row_sum += reg_p;
104
+
105
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
106
+ ElementAcc eV = tV(idx_d);
107
+ reg_o[idx_d] += reg_p * eV;
108
+ }
109
+ };
110
+
111
+ for (int idx_s = threadIdx.x; idx_s < seqlen_kv[idx_b]; idx_s += blockDim.x) {
112
+ iteration(mCacheK(idx_s, _, make_coord(idx_h, idx_b_kv)), mCacheV(idx_s, _, make_coord(idx_h, idx_b_kv)));
113
+ }
114
+
115
+ if (mNewK.data() != nullptr && threadIdx.x == 0) {
116
+ iteration(mNewK(_0{}, _, make_coord(idx_h, idx_b)), mNewV(_0{}, _, make_coord(idx_h, idx_b)));
117
+ }
118
+
119
+ mS[threadIdx.x] = row_max;
120
+ __syncthreads();
121
+ float old_row_max = row_max;
122
+ for (int i = 0; i < blockDim.x; i++) {
123
+ row_max = std::max(row_max, mS[i]);
124
+ }
125
+ __syncthreads();
126
+
127
+ ElementAcc adjustment = std::exp(scale * (old_row_max - row_max));
128
+ row_sum *= adjustment;
129
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
130
+ reg_o[idx_d] *= adjustment;
131
+ }
132
+ mS[threadIdx.x] = row_sum;
133
+ __syncthreads();
134
+
135
+ row_sum = 0;
136
+ for (int i = 0; i < blockDim.x; i++) {
137
+ row_sum += mS[i];
138
+ }
139
+ __syncthreads();
140
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
141
+ mS[idx_d] = 0;
142
+ }
143
+ __syncthreads();
144
+
145
+ for (int idx_d = 0; idx_d < kDim; idx_d++) {
146
+ reg_o[idx_d] /= row_sum;
147
+ atomicAdd(&mS[idx_d], reg_o[idx_d]);
148
+ }
149
+
150
+ __syncthreads();
151
+ for (int idx_d = threadIdx.x; idx_d < kDim; idx_d += blockDim.x) {
152
+ mO(_0{}, idx_d, make_coord(idx_h, idx_b)) = static_cast<typename TensorO::value_type>(mS[idx_d]);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ template<
160
+ class ElementAcc,
161
+ class ProblemShape,
162
+ class TensorQ,
163
+ class TensorNewK,
164
+ class TensorNewV,
165
+ class TensorCacheK,
166
+ class TensorCacheV,
167
+ class TensorO
168
+ >
169
+ void fmha_fwd_gen_reference(
170
+ ProblemShape problem_shape,
171
+ const int* seqlen_kv, const int* cache_batch_idx,
172
+ TensorQ mQ, TensorNewK mNewK, TensorNewV mNewV,
173
+ TensorCacheK mCacheK, TensorCacheV mCacheV, TensorO mO) {
174
+
175
+ using namespace cute;
176
+
177
+ dim3 grid(get<3,0,1>(problem_shape), get<3,0,0>(problem_shape), get<3,1>(problem_shape));
178
+ dim3 block(128);
179
+ int shared_mem = int(sizeof(ElementAcc)) * std::max<int>(128, block.x);
180
+ assert(get<2>(problem_shape) == 128);
181
+ fmha_fwd_gen_reference_kernel<ElementAcc><<<grid, block, shared_mem>>>(
182
+ problem_shape, seqlen_kv, cache_batch_idx,
183
+ mQ, mNewK, mNewV, mCacheK, mCacheV, mO
184
+ );
185
+ }
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_fwd_reference.hpp ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include "cute/tensor.hpp"
34
+ #include "collective/fmha_fusion.hpp"
35
+
36
+ /////////////////////////////////////////////////////////////////////////////////////////////////
37
+
38
+ template<
39
+ class ProblemShapeIn,
40
+ class TensorQ,
41
+ class TensorK,
42
+ class TensorV,
43
+ class TensorO,
44
+ class TensorLSE,
45
+ class Mask
46
+ >
47
+ void __global__ fmha_reference_kernel(
48
+ ProblemShapeIn problem_shape_in,
49
+ TensorQ mQ, TensorK mK, TensorV mV,
50
+ TensorO mO, TensorLSE mLSE,
51
+ Mask mask) {
52
+
53
+ using namespace cute;
54
+ using namespace cutlass::fmha::collective;
55
+
56
+ using Element = typename TensorO::value_type;
57
+ using ElementAccumulator = typename TensorLSE::value_type;
58
+
59
+ extern __shared__ char mS_mem[];
60
+ ElementAccumulator* mS = reinterpret_cast<ElementAccumulator*>(mS_mem);
61
+
62
+ ElementAccumulator softmax_scale = static_cast<ElementAccumulator>(1.0 / sqrt(1.0 * size<1>(mQ)));
63
+
64
+ auto id = make_identity_tensor(make_shape(1, 1));
65
+
66
+ for (int idx_L = blockIdx.y; idx_L < size<4>(problem_shape_in); idx_L += gridDim.y) {
67
+ for (int idx_Q = blockIdx.x; idx_Q < size<0>(problem_shape_in); idx_Q += gridDim.x) {
68
+
69
+ auto coord_L = idx2crd(idx_L, shape<4>(problem_shape_in));
70
+ auto get_coord_in = [&]() {
71
+ if constexpr (rank_v<decltype(get<2>(ProblemShapeIn{}))> == 2) {
72
+ return cute::make_tuple(idx_Q, _0{}, cute::make_tuple(_0{}, _0{}), cute::make_tuple(_0{}, _0{}), coord_L);
73
+ } else {
74
+ return cute::make_tuple(idx_Q, _0{}, _0{}, _0{}, coord_L);
75
+ }
76
+ };
77
+ auto coord_in = get_coord_in();
78
+ auto [problem_shape, coord] = apply_variable_length(problem_shape_in, coord_in, get<4,1>(coord_in));
79
+
80
+ int head_qk = 0;
81
+ int head_v = 0;
82
+ if constexpr (rank_v<decltype(get<2>(problem_shape))> == 2) {
83
+ // MLA case: head_qk 192, head_v = 128
84
+ head_qk = size<2, 0>(problem_shape) + size<2, 1>(problem_shape);
85
+ head_v = size<2, 0>(problem_shape);
86
+ } else {
87
+ head_qk = size<3>(problem_shape);
88
+ head_v = head_qk;
89
+ }
90
+
91
+ if (get<0,0>(coord) >= get<0>(problem_shape)) continue;
92
+
93
+ int offset_Q = 0;
94
+ if constexpr (rank<0>(decltype(coord){}) == 2) {
95
+ offset_Q = get<0,1>(coord);
96
+ }
97
+
98
+ int offset_K = 0;
99
+ if constexpr (rank<1>(decltype(coord){}) == 2) {
100
+ offset_K = get<1,1>(coord);
101
+ }
102
+
103
+ if (get<1>(problem_shape) == 0) {
104
+ for (int idx_D = threadIdx.x; idx_D < head_qk; idx_D += blockDim.x) {
105
+ mO(idx_Q + offset_Q, idx_D, idx_L) = Element(0);
106
+ }
107
+
108
+ if (threadIdx.x == 0 && mLSE.data() != nullptr) {
109
+ mLSE(idx_Q + offset_Q, idx_L) = -INFINITY;
110
+ }
111
+ continue;
112
+ }
113
+
114
+ for (int idx_K = threadIdx.x; idx_K < size<1>(problem_shape); idx_K += blockDim.x) {
115
+ ElementAccumulator acc = 0;
116
+ for (int idx_D = 0; idx_D < head_qk; idx_D++) {
117
+ ElementAccumulator eQ = mQ(idx_Q + offset_Q, idx_D, idx_L);
118
+ ElementAccumulator eK = mK(idx_K + offset_K, idx_D, idx_L);
119
+ acc += eQ * eK;
120
+ }
121
+ auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
122
+ frag(0) = acc;
123
+ mask.apply_mask(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
124
+ mS[idx_K] = frag(0);
125
+ }
126
+
127
+ __syncthreads();
128
+
129
+ ElementAccumulator maxS = -std::numeric_limits<ElementAccumulator>::infinity();
130
+ for (int idx_K = 0; idx_K < size<1>(problem_shape); idx_K++) {
131
+ maxS = std::max<ElementAccumulator>(maxS, mS[idx_K]);
132
+ }
133
+ if (maxS == -std::numeric_limits<ElementAccumulator>::infinity()) maxS = 0;
134
+
135
+ __syncthreads();
136
+
137
+ for (int idx_K = threadIdx.x; idx_K < size<1>(problem_shape); idx_K += blockDim.x) {
138
+ mS[idx_K] = expf(softmax_scale * (mS[idx_K] - maxS));
139
+ }
140
+
141
+ __syncthreads();
142
+
143
+ ElementAccumulator sum = 0;
144
+ for (int idx_K = 0; idx_K < size<1>(problem_shape); idx_K++) {
145
+ sum += mS[idx_K];
146
+ }
147
+
148
+ ElementAccumulator scale = 1.0f / sum;
149
+
150
+
151
+ for (int idx_D = threadIdx.x; idx_D < head_v; idx_D += blockDim.x) {
152
+ ElementAccumulator acc = 0;
153
+ for (int idx_K = 0; idx_K < size<1>(problem_shape); idx_K++) {
154
+ ElementAccumulator eV = mV(idx_K + offset_K, idx_D, idx_L);
155
+ ElementAccumulator eK = static_cast<Element>(mS[idx_K]);
156
+ acc += eK * eV;
157
+ }
158
+ mO(idx_Q + offset_Q, idx_D, idx_L) = static_cast<typename TensorO::value_type>(acc * scale);
159
+ }
160
+
161
+
162
+ if (threadIdx.x == 0 && mLSE.data() != nullptr) {
163
+ mLSE(idx_Q + offset_Q, idx_L) = log(sum) + softmax_scale * maxS;
164
+ }
165
+
166
+ }
167
+ }
168
+ }
169
+
170
+ /////////////////////////////////////////////////////////////////////////////////////////////////
171
+
172
+ template<
173
+ class ProblemShapeIn,
174
+ class TensorQ,
175
+ class TensorK,
176
+ class TensorV,
177
+ class TensorO,
178
+ class TensorLSE,
179
+ class Mask
180
+ >
181
+ void fmha_reference(
182
+ ProblemShapeIn problem_shape_in,
183
+ TensorQ mQ, TensorK mK, TensorV mV,
184
+ TensorO mO, TensorLSE mLSE,
185
+ Mask mask) {
186
+
187
+ using namespace cute;
188
+
189
+ dim3 grid(size<0>(mO), size<2>(mO), 1);
190
+ dim3 block(256);
191
+ int shared_mem = size<0>(mK) * int(sizeof(typename TensorLSE::value_type));
192
+ fmha_reference_kernel<<<grid, block, shared_mem>>>(problem_shape_in, mQ, mK, mV, mO, mLSE, mask);
193
+ }
194
+
195
+ /////////////////////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/fmha_mla_reference.hpp ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include "cute/tensor.hpp"
34
+
35
+ /////////////////////////////////////////////////////////////////////////////////////////////////
36
+
37
+ template<
38
+ class ProblemShape,
39
+ class TensorSeq,
40
+ class TensorPageTable,
41
+ class TensorQL,
42
+ class TensorQR,
43
+ class TensorCL,
44
+ class TensorKR,
45
+ class TensorO,
46
+ class TensorLSE,
47
+ class Scale
48
+ >
49
+ void __global__ fmha_mla_reference_kernel(
50
+ ProblemShape problem_shape,
51
+ TensorSeq mSeq, TensorPageTable mPT,
52
+ TensorQL mQL, TensorQR mQR,
53
+ TensorCL mCL, TensorKR mKR,
54
+ TensorO mO, TensorLSE mLSE,
55
+ Scale softmax_scale) {
56
+
57
+ using namespace cute;
58
+
59
+ auto [H, K, D, B] = problem_shape;
60
+ auto [D_latent, D_rope] = D;
61
+
62
+ using Element = typename TensorO::value_type;
63
+ using ElementAcc = typename TensorLSE::value_type;
64
+
65
+ extern __shared__ ElementAcc mS[];
66
+ // ElementAcc* mS = reinterpret_cast<ElementAcc*>(mS_mem);
67
+
68
+ for (int idx_B = blockIdx.y; idx_B < B; idx_B += gridDim.y) {
69
+ if (mSeq.data() != nullptr) {
70
+ K = mSeq(idx_B);
71
+ }
72
+
73
+ for (int idx_H = blockIdx.x; idx_H < H; idx_H += gridDim.x) {
74
+
75
+ for (int idx_K = threadIdx.x; idx_K < K; idx_K += blockDim.x) {
76
+ ElementAcc acc = 0;
77
+
78
+ for (int idx_D = 0; idx_D < D_latent; idx_D++) {
79
+ int page_idx_K = idx_K;
80
+ int page_idx_B = idx_B;
81
+ if (mPT.data() != nullptr) {
82
+ page_idx_B = mPT(idx_K / size<0>(mCL), idx_B);
83
+ page_idx_K = idx_K % size<0>(mCL);
84
+ }
85
+ ElementAcc eQ = mQL(idx_H, idx_D, idx_B);
86
+ ElementAcc eK = mCL(page_idx_K, idx_D, page_idx_B);
87
+ acc += eQ * eK;
88
+ }
89
+
90
+ for (int idx_D = 0; idx_D < D_rope; idx_D++) {
91
+ int page_idx_K = idx_K;
92
+ int page_idx_B = idx_B;
93
+ if (mPT.data() != nullptr) {
94
+ page_idx_B = mPT(idx_K / size<0>(mCL), idx_B);
95
+ page_idx_K = idx_K % size<0>(mCL);
96
+ }
97
+ ElementAcc eQ = mQR(idx_H, idx_D, idx_B);
98
+ ElementAcc eK = mKR(page_idx_K, idx_D, page_idx_B);
99
+ acc += eQ * eK;
100
+ }
101
+ mS[idx_K] = acc;
102
+ }
103
+
104
+ __syncthreads();
105
+
106
+ ElementAcc maxS = -std::numeric_limits<ElementAcc>::infinity();
107
+ for (int idx_K = 0; idx_K < K; idx_K++) {
108
+ maxS = std::max<ElementAcc>(maxS, mS[idx_K]);
109
+ }
110
+ if (maxS == -std::numeric_limits<ElementAcc>::infinity()) maxS = 0;
111
+
112
+ __syncthreads();
113
+
114
+ for (int idx_K = threadIdx.x; idx_K < K; idx_K += blockDim.x) {
115
+ mS[idx_K] = expf(softmax_scale * (mS[idx_K] - maxS));
116
+ }
117
+
118
+ __syncthreads();
119
+
120
+ ElementAcc sum = 0;
121
+ for (int idx_K = 0; idx_K < K; idx_K++) {
122
+ sum += mS[idx_K];
123
+ }
124
+
125
+ ElementAcc o_scale = 1.0f / sum;
126
+
127
+ for (int idx_D = threadIdx.x; idx_D < D_latent; idx_D += blockDim.x) {
128
+ ElementAcc acc = 0;
129
+ for (int idx_K = 0; idx_K < K; idx_K++) {
130
+ int page_idx_K = idx_K;
131
+ int page_idx_B = idx_B;
132
+ if (mPT.data() != nullptr) {
133
+ page_idx_B = mPT(idx_K / size<0>(mCL), idx_B);
134
+ page_idx_K = idx_K % size<0>(mCL);
135
+ }
136
+ ElementAcc eV = mCL(page_idx_K, idx_D, page_idx_B);
137
+ ElementAcc eK = static_cast<Element>(mS[idx_K]);
138
+ acc += eK * eV;
139
+ }
140
+ mO(idx_H, idx_D, idx_B) = static_cast<typename TensorO::value_type>(acc * o_scale);
141
+ }
142
+
143
+ if (threadIdx.x == 0) {
144
+ mLSE(idx_H, idx_B) = log(sum) + softmax_scale * maxS;
145
+ }
146
+
147
+ }
148
+ }
149
+ }
150
+
151
+ /////////////////////////////////////////////////////////////////////////////////////////////////
152
+
153
+ template<
154
+ class ProblemShape,
155
+ class TensorSeq,
156
+ class TensorPageTable,
157
+ class TensorQL,
158
+ class TensorQR,
159
+ class TensorCL,
160
+ class TensorKR,
161
+ class TensorO,
162
+ class TensorLSE,
163
+ class Scale
164
+ >
165
+ void fmha_mla_reference(
166
+ ProblemShape problem_shape,
167
+ TensorSeq mSeq, TensorPageTable mPT,
168
+ TensorQL mQL, TensorQR mQR,
169
+ TensorCL mCL, TensorKR mKR,
170
+ TensorO mO, TensorLSE mLSE,
171
+ Scale scale) {
172
+
173
+ using namespace cute;
174
+
175
+ auto [H, K, D, B] = problem_shape;
176
+ auto [D_latent, D_rope] = D;
177
+
178
+ dim3 grid(H, B, 1);
179
+ dim3 block(256);
180
+ int shared_mem = K * int(sizeof(typename TensorLSE::value_type)) + 16;
181
+ cudaError_t result;
182
+ if (shared_mem >= (48 << 10)) {
183
+ result = cudaFuncSetAttribute(
184
+ &fmha_mla_reference_kernel<ProblemShape, TensorSeq, TensorPageTable, TensorQL, TensorQR, TensorCL, TensorKR, TensorO, TensorLSE, Scale>,
185
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
186
+ shared_mem);
187
+ if (cudaSuccess != result) {
188
+ result = cudaGetLastError(); // to clear the error bit
189
+ throw std::runtime_error("couldn't perform smem optin");
190
+ }
191
+ }
192
+ fmha_mla_reference_kernel<<<grid, block, shared_mem>>>(
193
+ problem_shape, mSeq, mPT, mQL, mQR, mCL, mKR, mO, mLSE, scale);
194
+ cudaDeviceSynchronize();
195
+ result = cudaGetLastError();
196
+ if (cudaSuccess != result) {
197
+ throw std::runtime_error("couldn't execute reference");
198
+ }
199
+ }
200
+
201
+ /////////////////////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/77_blackwell_fmha/reference/reference_abs_error.hpp ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+
33
+
34
+ #pragma once
35
+
36
+ #include <math.h>
37
+
38
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
39
+
40
+ template<typename T>
41
+ struct DeviceAllocation {
42
+ T* ptr_ = nullptr;
43
+ size_t offset_ = 0;
44
+ size_t size_ = 0;
45
+
46
+ DeviceAllocation(DeviceAllocation const&) = delete;
47
+ DeviceAllocation& operator=(DeviceAllocation const&) = delete;
48
+
49
+ DeviceAllocation() = default;
50
+ DeviceAllocation(size_t size) { reset(size); }
51
+ ~DeviceAllocation() { reset(); }
52
+
53
+ void reset(size_t size, size_t offset=0) {
54
+ reset();
55
+ auto ret = cudaMalloc(&ptr_, sizeof(T) * (size + offset));
56
+ assert(ret == cudaSuccess);
57
+ size_ = size;
58
+ offset_ = offset;
59
+ }
60
+
61
+ T* get() {
62
+ return ptr_ + offset_;
63
+ }
64
+
65
+ const T* get() const {
66
+ return ptr_ + offset_;
67
+ }
68
+
69
+ void reset() {
70
+ if (ptr_ != nullptr) {
71
+ auto ret = cudaFree(ptr_);
72
+ assert(ret == cudaSuccess);
73
+ }
74
+ }
75
+
76
+ size_t size() const { return size_; }
77
+
78
+ size_t get_storage_size() const { return (size_ + offset_) * sizeof(T); }
79
+
80
+ void copy_from_host(const T* ptr, size_t sz) {
81
+ auto ret = cudaMemcpy(ptr_, ptr, sz * sizeof(T), cudaMemcpyDefault);
82
+ assert(ret == cudaSuccess);
83
+ }
84
+
85
+ void copy_from_device(const T* ptr, size_t sz) {
86
+ auto ret = cudaMemcpy(ptr_, ptr, sz * sizeof(T), cudaMemcpyDefault);
87
+ assert(ret == cudaSuccess);
88
+ }
89
+ };
90
+
91
+ template<typename Element>
92
+ __global__ void reference_abs_diff_kernel(
93
+ Element* data, Element* data_ref, size_t count,
94
+ double* max_diff, double* sum_diff,
95
+ bool print_diff ) {
96
+
97
+ double thread_max_diff = 0;
98
+ double thread_sum_diff = 0;
99
+
100
+ __shared__ double block_max_diff;
101
+ __shared__ double block_sum_diff;
102
+
103
+ for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < count; i += blockDim.x * gridDim.x) {
104
+ if (data[i] == data_ref[i]) {
105
+ continue;
106
+ }
107
+
108
+ double diff = fabs(data[i] - data_ref[i]);
109
+ if (print_diff) if (not isfinite(diff) || diff > 0.01f) printf("difference at %lld: %f ... %f vs %f\n", static_cast<long long int>(i), diff, (double)data[i], (double)data_ref[i]);
110
+ thread_max_diff = fmax(diff, thread_max_diff);
111
+ thread_sum_diff += diff;
112
+ }
113
+
114
+ for (int i = 0; i < blockDim.x; i++) {
115
+ if (i == threadIdx.x) {
116
+ if (i == 0) {
117
+ block_max_diff = thread_max_diff;
118
+ block_sum_diff = thread_sum_diff;
119
+ }
120
+ else {
121
+ block_max_diff = fmax(block_max_diff, thread_max_diff);
122
+ block_sum_diff += thread_sum_diff;
123
+ }
124
+ }
125
+ __syncthreads();
126
+ }
127
+
128
+ if (threadIdx.x == 0) {
129
+ atomicAdd(sum_diff, block_sum_diff);
130
+
131
+ for (;;) {
132
+ unsigned long long prev = *reinterpret_cast<unsigned long long*>(max_diff);
133
+ double prev_diff = reinterpret_cast<double const&>(prev);
134
+ double new_max_diff = fmax(block_max_diff, prev_diff);
135
+ unsigned long long found = atomicCAS(reinterpret_cast<unsigned long long*>(max_diff), prev, reinterpret_cast<unsigned long long const&>(new_max_diff));
136
+ if (found == prev) break;
137
+ }
138
+ }
139
+ }
140
+
141
+ template<typename Element>
142
+ void reference_abs_diff(
143
+ DeviceAllocation<Element> const& data,
144
+ DeviceAllocation<Element> const& data_ref,
145
+ double& max_diff, double& mean_diff) {
146
+
147
+ static bool kPrintDiff = getenv("REF_PRINT_DIFF") && atoi(getenv("REF_PRINT_DIFF")) == 1;
148
+
149
+ DeviceAllocation<double> result;
150
+ result.reset(2);
151
+ assert(data.size() == data_ref.size());
152
+
153
+ cudaError_t err = cudaMemset(result.get(), 0, result.size() * sizeof(double));
154
+ if (err != cudaSuccess) {
155
+ std::cerr << "Memset failed. Last CUDA error: "
156
+ << cudaGetErrorString(err) << std::endl;
157
+ max_diff = mean_diff = 1e20;
158
+ return;
159
+ }
160
+
161
+ dim3 block(256, 1, 1);
162
+ dim3 grid(1024, 1, 1);
163
+ reference_abs_diff_kernel<<<block, grid>>>(
164
+ data.get(), data_ref.get(), data.size(),
165
+ result.get(), result.get() + 1, kPrintDiff);
166
+
167
+ err = cudaDeviceSynchronize();
168
+ if (err != cudaSuccess) {
169
+ std::cerr << "Difference kernel failed. Last CUDA error: "
170
+ << cudaGetErrorString(err) << std::endl;
171
+ max_diff = mean_diff = 1e20;
172
+ return;
173
+ }
174
+
175
+ double result_host[2];
176
+ err = cudaMemcpy(result_host, result.get(), result.size() * sizeof(double), cudaMemcpyDefault);
177
+ if (err != cudaSuccess) {
178
+ std::cerr << "Copy failed. Last CUDA error: "
179
+ << cudaGetErrorString(err) << std::endl;
180
+ max_diff = mean_diff = 1e20;
181
+ return;
182
+ }
183
+
184
+ max_diff = result_host[0];
185
+ mean_diff = result_host[1] / static_cast<double>(data.size());
186
+ }
187
+
188
+ template<typename Element>
189
+ __global__ void reference_rel_diff_kernel(
190
+ Element* data, Element* data_ref, size_t count,
191
+ double* max_diff, double* sum_diff,
192
+ bool print_diff ) {
193
+
194
+ double thread_max_diff = 0;
195
+ double thread_sum_diff = 0;
196
+
197
+ __shared__ double block_max_diff;
198
+ __shared__ double block_sum_diff;
199
+
200
+ for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < count; i += blockDim.x * gridDim.x) {
201
+ if (data[i] == data_ref[i]) {
202
+ continue;
203
+ }
204
+ double diff = fabs(data[i] - data_ref[i]) / fabs(data_ref[i]);
205
+ if (print_diff) if (not isfinite(diff) || diff > 0.01f) printf("difference at %lld: %f ... %f vs %f\n", static_cast<long long int>(i), diff, (double)data[i], (double)data_ref[i]);
206
+ thread_max_diff = fmax(diff, thread_max_diff);
207
+ thread_sum_diff += diff;
208
+ }
209
+
210
+ for (int i = 0; i < blockDim.x; i++) {
211
+ if (i == threadIdx.x) {
212
+ if (i == 0) {
213
+ block_max_diff = thread_max_diff;
214
+ block_sum_diff = thread_sum_diff;
215
+ }
216
+ else {
217
+ block_max_diff = fmax(block_max_diff, thread_max_diff);
218
+ block_sum_diff += thread_sum_diff;
219
+ }
220
+ }
221
+ __syncthreads();
222
+ }
223
+
224
+ if (threadIdx.x == 0) {
225
+ atomicAdd(sum_diff, block_sum_diff);
226
+
227
+ for (;;) {
228
+ unsigned long long prev = *reinterpret_cast<unsigned long long*>(max_diff);
229
+ double prev_diff = reinterpret_cast<double const&>(prev);
230
+ double new_max_diff = fmax(block_max_diff, prev_diff);
231
+ unsigned long long found = atomicCAS(reinterpret_cast<unsigned long long*>(max_diff), prev, reinterpret_cast<unsigned long long const&>(new_max_diff));
232
+ if (found == prev) break;
233
+ }
234
+ }
235
+ }
236
+
237
+ template<typename Element>
238
+ void reference_rel_diff(
239
+ DeviceAllocation<Element> const& data,
240
+ DeviceAllocation<Element> const& data_ref,
241
+ double& max_diff, double& mean_diff) {
242
+
243
+ static bool kPrintDiff = getenv("REF_PRINT_DIFF") && atoi(getenv("REF_PRINT_DIFF")) == 1;
244
+
245
+ DeviceAllocation<double> result;
246
+ result.reset(2);
247
+ assert(data.size() == data_ref.size());
248
+
249
+ cudaError_t err = cudaMemset(result.get(), 0, result.size() * sizeof(double));
250
+ if (err != cudaSuccess) {
251
+ std::cerr << "Memset failed. Last CUDA error: "
252
+ << cudaGetErrorString(err) << std::endl;
253
+ max_diff = mean_diff = 1e20;
254
+ return;
255
+ }
256
+
257
+ dim3 block(256, 1, 1);
258
+ dim3 grid(1024, 1, 1);
259
+ reference_rel_diff_kernel<<<block, grid>>>(
260
+ data.get(), data_ref.get(), data.size(),
261
+ result.get(), result.get() + 1, kPrintDiff);
262
+
263
+ err = cudaDeviceSynchronize();
264
+ if (err != cudaSuccess) {
265
+ std::cerr << "Difference kernel failed. Last CUDA error: "
266
+ << cudaGetErrorString(err) << std::endl;
267
+ max_diff = mean_diff = 1e20;
268
+ return;
269
+ }
270
+
271
+ double result_host[2];
272
+ err = cudaMemcpy(result_host, result.get(), result.size() * sizeof(double), cudaMemcpyDefault);
273
+ if (err != cudaSuccess) {
274
+ std::cerr << "Copy failed. Last CUDA error: "
275
+ << cudaGetErrorString(err) << std::endl;
276
+ max_diff = mean_diff = 1e20;
277
+ return;
278
+ }
279
+
280
+ max_diff = result_host[0];
281
+ mean_diff = result_host[1] / static_cast<double>(data.size());
282
+ }
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/86_blackwell_mixed_dtype_gemm/mixed_dtype_helper.cuh ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/gemm/dispatch_policy.hpp"
36
+ #include "cutlass/epilogue/collective/default_epilogue.hpp"
37
+ #include "cutlass/epilogue/collective/collective_builder.hpp"
38
+ #include "cutlass/gemm/device/gemm_universal_adapter.h"
39
+ #include "cutlass/gemm/kernel/gemm_universal.hpp"
40
+ #include "cutlass/util/command_line.h"
41
+ #include "cutlass/util/reference/device/tensor_fill.h"
42
+ #include "cutlass/util/reference/device/tensor_compare.h"
43
+
44
+ #include "cute/tensor.hpp"
45
+
46
+ #include <cuda.h>
47
+ #include <numeric>
48
+ #include "helper.h"
49
+
50
+ enum MixedDtypeGemmMode {
51
+ ConvertOnly,
52
+ ScaleOnly,
53
+ ScaleWithZeroPoint
54
+ };
55
+
56
+ /// Command line options parsing
57
+ struct MixedDtypeOptions {
58
+
59
+ bool help = false;
60
+ bool verify = false;
61
+
62
+ float alpha = 1.0f;
63
+ float beta = 0.0f;
64
+ int iterations = 1000;
65
+ int warmup = 1000;
66
+ int mode = 1;
67
+ int m = 5120, n = 4096, k = 4096;
68
+ int l = 1;
69
+
70
+ // Parses the command line
71
+ void parse(int argc, char const **args) {
72
+ cutlass::CommandLine cmd(argc, args);
73
+
74
+ if (cmd.check_cmd_line_flag("help")) {
75
+ help = true;
76
+ return;
77
+ }
78
+
79
+ if (cmd.check_cmd_line_flag("verify")) {
80
+ verify = true;
81
+ }
82
+
83
+ cmd.get_cmd_line_argument("m", m);
84
+ cmd.get_cmd_line_argument("n", n);
85
+ cmd.get_cmd_line_argument("k", k);
86
+ cmd.get_cmd_line_argument("l", l);
87
+ cmd.get_cmd_line_argument("mode", mode);
88
+ cmd.get_cmd_line_argument("alpha", alpha, 1.f);
89
+ cmd.get_cmd_line_argument("beta", beta, 0.f);
90
+ cmd.get_cmd_line_argument("iterations", iterations);
91
+ cmd.get_cmd_line_argument("warmup", warmup);
92
+ }
93
+
94
+ /// Prints the usage statement.
95
+ std::ostream & print_usage(std::ostream &out) const {
96
+
97
+ out << "86_blackwell_mixed_dtype_gemm\n\n"
98
+ << " Blackwell Mixed Data Type GEMM using a Warp Specialized kernel.\n\n"
99
+ << "Options:\n\n"
100
+ << " --help If specified, displays this usage statement\n\n"
101
+ << " --m=<int> Sets the M extent of the GEMM\n"
102
+ << " --n=<int> Sets the N extent of the GEMM\n"
103
+ << " --k=<int> Sets the K extent of the GEMM\n"
104
+ << " --l=<int> The number of independent gemm problems with mnk shape\n"
105
+ << " --mode=<int> The mode to run the gemm. 0 does (A @ B), 1 means A @ (scale * B), 2 means A @ (scale * B + zero-point).\n"
106
+ << " --alpha=<f32> Epilogue scalar alpha\n"
107
+ << " --beta=<f32> Epilogue scalar beta\n\n"
108
+ << " --iterations=<int> Number of profiling iterations to perform.\n\n"
109
+ << " --warmup=<int> Number of warmup iterations to perform.\n\n"
110
+ << " --verify=<int> Run verification.\n\n";
111
+
112
+ out
113
+ << "\n\nExamples:\n\n"
114
+ << "$ " << "86_blackwell_mixed_dtype_gemm" << " --m=1024 --n=512 --k=1024 --l=10 --alpha=2 --mode=2 --beta=0.707 \n\n";
115
+
116
+ return out;
117
+ }
118
+
119
+ /// Compute performance in GFLOP/s
120
+ double gflops(double runtime_s) const
121
+ {
122
+ // Two flops per multiply-add
123
+ uint64_t flop = uint64_t(2) * m * n * k * l;
124
+ double gflop = double(flop) / double(1.0e9);
125
+ return gflop / runtime_s;
126
+ }
127
+ };
128
+
129
+ /// Result structure
130
+ struct MixedDtypeResult
131
+ {
132
+ double avg_runtime_ms = 0.0;
133
+ double gflops = 0.0;
134
+ cutlass::Status status = cutlass::Status::kSuccess;
135
+ cudaError_t error = cudaSuccess;
136
+ bool passed = false;
137
+
138
+ };
139
+
140
+ /// Profiling Loop
141
+ template <class Gemm>
142
+ void mixed_dtype_profiling(
143
+ Gemm& gemm,
144
+ MixedDtypeOptions const& options,
145
+ MixedDtypeResult& result) {
146
+
147
+ if (options.iterations <= 0) return;
148
+
149
+ cudaEvent_t start, stop;
150
+ cudaEventCreate(&start);
151
+ cudaEventCreate(&stop);
152
+
153
+ std::vector<float> runtimes;
154
+ runtimes.reserve(options.iterations);
155
+
156
+ for (int iter = 0; iter < options.warmup + options.iterations; ++iter) {
157
+ cudaEventRecord(start);
158
+ CUTLASS_CHECK(gemm.run());
159
+ cudaEventRecord(stop);
160
+ cudaEventSynchronize(stop);
161
+
162
+ if (iter >= options.warmup) {
163
+ float milliseconds = 0;
164
+ cudaEventElapsedTime(&milliseconds, start, stop);
165
+ runtimes.push_back(milliseconds);
166
+ }
167
+ }
168
+
169
+ cudaEventDestroy(start);
170
+ cudaEventDestroy(stop);
171
+
172
+ // Compute average setup and runtime and GFLOPs.
173
+ result.avg_runtime_ms = std::accumulate(runtimes.begin(), runtimes.end(), 0.0f) / runtimes.size();
174
+ result.gflops = options.gflops(result.avg_runtime_ms / 1000.0);
175
+
176
+ std::cout << " Problem Size: " << options.m << 'x' << options.n << 'x' << options.k << 'x' << options.l << std::endl;
177
+ std::cout << " Avg runtime: " << result.avg_runtime_ms << " ms" << std::endl;
178
+ std::cout << " GFLOPS: " << result.gflops << std::endl;
179
+
180
+ }
181
+
182
+ /// Helpers to initialize a block of device data
183
+ template <class Element>
184
+ bool initialize_tensor(
185
+ cutlass::DeviceAllocation<Element>& block,
186
+ uint64_t seed = 2023) {
187
+
188
+ double scope_max, scope_min;
189
+ int bits_input = cutlass::sizeof_bits<Element>::value;
190
+ int bits_output = cutlass::sizeof_bits<Element>::value;
191
+
192
+ if (bits_input == 1) {
193
+ scope_max = 2;
194
+ scope_min = 0;
195
+ }
196
+ else if (bits_input <= 8) {
197
+ scope_max = 2;
198
+ scope_min = -2;
199
+ }
200
+ else if (bits_output == 16) {
201
+ scope_max = 5;
202
+ scope_min = -5;
203
+ }
204
+ else {
205
+ scope_max = 8;
206
+ scope_min = -8;
207
+ }
208
+ cutlass::reference::device::BlockFillRandomUniform(
209
+ block.get(), block.size(), seed, Element(scope_max), Element(scope_min));
210
+
211
+ return true;
212
+ }
213
+
214
+ template <typename Element>
215
+ bool initialize_quant_tensor(
216
+ cutlass::DeviceAllocation<Element>& block,
217
+ uint64_t seed = 2023) {
218
+
219
+ float scope_min = float(cutlass::platform::numeric_limits<Element>::lowest());
220
+ float scope_max = float(cutlass::platform::numeric_limits<Element>::max());
221
+
222
+ cutlass::reference::device::BlockFillRandomUniform(
223
+ block.get(), block.size(), seed, Element(scope_max), Element(scope_min));
224
+
225
+ return true;
226
+ }
227
+
228
+ template <class QuantType, class Element>
229
+ bool initialize_scale(
230
+ cutlass::DeviceAllocation<Element>& block,
231
+ MixedDtypeOptions const& options,
232
+ uint64_t seed = 2023) {
233
+
234
+ if (options.mode == MixedDtypeGemmMode::ConvertOnly) {
235
+ // No scales, so just initialize with 1 so we can use the same kernel to dequantize the data.
236
+ std::vector<Element> stage(block.size(), Element(1.0f));
237
+ block.copy_from_host(stage.data());
238
+ }
239
+ else {
240
+ float elt_max_f = float(cutlass::platform::numeric_limits<QuantType>::max());
241
+ const float max_dequant_val = 4.f;
242
+ const float min_dequant_val = 0.5f;
243
+
244
+ float scope_max(max_dequant_val / elt_max_f);
245
+ float scope_min(min_dequant_val / elt_max_f);
246
+
247
+ cutlass::reference::device::BlockFillRandomUniform(
248
+ block.get(), block.size(), seed, Element(scope_max), Element(scope_min));
249
+ }
250
+ return true;
251
+ }
252
+
253
+ template <class Element>
254
+ bool initialize_zero(
255
+ cutlass::DeviceAllocation<Element>& block,
256
+ MixedDtypeOptions const& options,
257
+ uint64_t seed = 2023) {
258
+
259
+ if (options.mode == MixedDtypeGemmMode::ScaleWithZeroPoint) {
260
+ cutlass::reference::device::BlockFillRandomUniform(
261
+ block.get(), block.size(), seed, Element(2.0f), Element(-2.0f));
262
+ } else {
263
+ // No bias, so just initialize with 1 so we can use the same kernel to dequantize the data.
264
+ std::vector<Element> stage(block.size(), Element(0.0f));
265
+ block.copy_from_host(stage.data());
266
+ }
267
+ return true;
268
+ }
269
+
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/87_blackwell_geforce_gemm_blockwise/utils.h ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2025 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ /// Helper to initialize a block of device data
33
+ template <class Element, class Layout>
34
+ bool initialize_tensor(
35
+ cutlass::TensorView<Element, Layout> view,
36
+ cutlass::Distribution::Kind dist_kind,
37
+ uint64_t seed) {
38
+
39
+ if (dist_kind == cutlass::Distribution::Uniform) {
40
+
41
+ double scope_max, scope_min;
42
+ int bits_input = cutlass::sizeof_bits<Element>::value;
43
+
44
+ if (bits_input == 1) {
45
+ scope_max = 2;
46
+ scope_min = 0;
47
+ }
48
+ else if (bits_input <= 8) {
49
+ scope_max = 2;
50
+ scope_min = -2;
51
+ }
52
+ else if (bits_input == 16) {
53
+ scope_max = 5;
54
+ scope_min = -5;
55
+ }
56
+ else {
57
+ scope_max = 8;
58
+ scope_min = -8;
59
+ }
60
+
61
+ cutlass::reference::host::TensorFillRandomUniform(
62
+ view, seed, scope_max, scope_min, 0);
63
+ }
64
+ else if (dist_kind == cutlass::Distribution::AllZeros) {
65
+ cutlass::reference::host::TensorFill(view);
66
+ }
67
+ else if (dist_kind == cutlass::Distribution::Identity) {
68
+
69
+ cutlass::reference::host::TensorFillIdentity(view);
70
+ }
71
+ else if (dist_kind == cutlass::Distribution::Gaussian) {
72
+
73
+ cutlass::reference::host::TensorFillRandomGaussian(view, seed, 0, 0.5);
74
+ }
75
+ else if (dist_kind == cutlass::Distribution::Sequential) {
76
+ cutlass::reference::host::BlockFillSequential(view.data(), view.capacity());
77
+ }
78
+ else {
79
+ throw std::runtime_error("Not implementated.");
80
+ }
81
+
82
+ return true;
83
+ }
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_bwd_tma_warpspecialized.hpp ADDED
@@ -0,0 +1,863 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/gemm/collective/collective_builder.hpp"
36
+
37
+ #include "../collective/fmha_common.hpp"
38
+ #include "../collective/fmha_collective_load.hpp"
39
+ #include "../collective/fmha_collective_softmax.hpp"
40
+ #include "../kernel/fmha_options.hpp"
41
+
42
+ namespace cutlass::fmha::collective {
43
+
44
+ template<
45
+ typename Element_,
46
+ typename ElementAccumulator_,
47
+ typename TileShape_, // BlockQO, BlockKV, BlockHead
48
+ class Fusion,
49
+ class... Options
50
+ >
51
+ struct FmhaBwdMainloopTmaWarpSpecialized {
52
+
53
+ using Element = Element_;
54
+ using ElementAccumulator = ElementAccumulator_;
55
+ using TileShape = TileShape_;
56
+
57
+ static constexpr bool kIsPersistent = false;
58
+
59
+ static const int NumLoadWarpGroups = 1;
60
+ static constexpr int NumMmaWarpGroups = 2;
61
+ static constexpr int StageCountQ = 2 /*K, V*/ * NumMmaWarpGroups;
62
+ static constexpr int StageCount = 2 /*Q, dO*/ * 2 /* actual stages */;
63
+
64
+ static const int kOuterLoads = 2;
65
+ using StagesQ = cutlass::gemm::collective::StageCount<StageCountQ>;
66
+ using Stages = cutlass::gemm::collective::StageCount<StageCount>;
67
+ using ClusterShape = Shape<_1, _1, _1>;
68
+ static_assert(StagesQ::value >= 2);
69
+ static_assert(Stages::value >= 2 * NumMmaWarpGroups);
70
+
71
+ // 16B alignment lets us use TMA
72
+ static constexpr int Alignment = 16 / sizeof(Element);
73
+
74
+ using TileShapeNM = Shape< // (N,M,D)
75
+ decltype(tuple_element_t<1, TileShape>{} / Int<NumMmaWarpGroups>{}),
76
+ tuple_element_t<0, TileShape>,
77
+ tuple_element_t<2, TileShape>>;
78
+
79
+ using TileShapeND = decltype(select<0,2,1>(TileShapeNM{})); // (N,D,M)
80
+
81
+ using TileShapeMD = decltype(select<2,1,0>(TileShapeND{})); // (M,D,N)
82
+
83
+ using CollectiveMmaNM = typename cutlass::gemm::collective::CollectiveBuilder<
84
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
85
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
86
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
87
+ ElementAccumulator,
88
+ TileShapeNM, ClusterShape, Stages,
89
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
90
+
91
+ using CollectiveMmaND = typename cutlass::gemm::collective::CollectiveBuilder<
92
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
93
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment, // from register, doesn't matter
94
+ Element, cute::tuple<_1, int, cute::tuple<int, int>>, Alignment,
95
+ ElementAccumulator,
96
+ TileShapeND, ClusterShape, Stages,
97
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
98
+
99
+ using CollectiveMmaND_SS = typename cutlass::gemm::collective::CollectiveBuilder<
100
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
101
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment, // from register, doesn't matter
102
+ Element, cute::tuple<_1, int, cute::tuple<int, int>>, Alignment,
103
+ ElementAccumulator,
104
+ TileShapeND, ClusterShape, Stages,
105
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
106
+
107
+
108
+ using CollectiveMmaMD = typename cutlass::gemm::collective::CollectiveBuilder<
109
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
110
+ Element, cute::tuple<_1, int, cute::tuple<int, int>>, Alignment, // from smem, might matter (?)
111
+ Element, cute::tuple<_1, int, cute::tuple<int, int>>, Alignment,
112
+ ElementAccumulator,
113
+ TileShapeMD, ClusterShape, Stages,
114
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
115
+
116
+ using TiledMmaNM = typename CollectiveMmaNM::TiledMma;
117
+ using TiledMmaND_SS = typename CollectiveMmaND_SS::TiledMma;
118
+ using TiledMmaND_RS = decltype(convert_to_gmma_rs(typename CollectiveMmaND::TiledMma{}));
119
+ using TiledMmaND = TiledMmaND_RS;
120
+ using TiledMmaMD = typename CollectiveMmaMD::TiledMma;
121
+
122
+ using SmemLayoutQ = typename CollectiveMmaNM::SmemLayoutB;
123
+ using SmemLayoutK = typename CollectiveMmaNM::SmemLayoutA;
124
+ using SmemLayoutV = typename CollectiveMmaNM::SmemLayoutA;
125
+ using SmemLayoutDO = typename CollectiveMmaNM::SmemLayoutB;
126
+
127
+ //using SmemLayoutDQ = Layout<
128
+ // Shape<
129
+ // tuple_element_t<0, TileShapeMD>,
130
+ // Shape<_2, _4, decltype(tuple_element_t<1, TileShapeMD>{} / _8{})>,
131
+ // _2
132
+ // >,
133
+ // Stride<
134
+ // _4,
135
+ // Stride<decltype(tuple_element_t<0, TileShapeMD>{} * _4{}), _1, decltype(tuple_element_t<0, TileShapeMD>{} * _8{})>,
136
+ // decltype(tuple_element_t<0, TileShapeMD>{} * tuple_element_t<1, TileShapeMD>{})
137
+ // >>;
138
+
139
+ using SmemLayoutDQ_0 = Layout<
140
+ Shape<
141
+ tuple_element_t<0, TileShapeMD>,
142
+ tuple_element_t<1, TileShapeMD>,
143
+ _2
144
+ >,
145
+ Stride<
146
+ tuple_element_t<1, TileShapeMD>,
147
+ _1,
148
+ decltype(tuple_element_t<0, TileShapeMD>{} * tuple_element_t<1, TileShapeMD>{})
149
+ >>;
150
+
151
+ using SmemAtomDQ = decltype(cutlass::gemm::collective::detail::ss_smem_selector<
152
+ cute::GMMA::Major::K, ElementAccumulator, tuple_element_t<0, TileShapeMD>, tuple_element_t<1, TileShapeMD>>());
153
+ using SmemLayoutDQ_1 = decltype(tile_to_shape(SmemAtomDQ{}, make_shape(get<0>(TileShapeMD{}), get<1>(TileShapeMD{}), _2{}), Step<_2, _1, _3>{}));
154
+ using SmemLayoutDQ = SmemLayoutDQ_1;
155
+
156
+
157
+ using PipelineDQ = cutlass::PipelineAsync<2>;
158
+
159
+
160
+ using SmemLayoutDS_0 = decltype(unstageSmemLayout(typename CollectiveMmaMD::SmemLayoutA{}, Int<NumMmaWarpGroups>{}));
161
+
162
+ using SmemLayoutDS = decltype(tile_to_shape(GMMA::Layout_MN_INTER_Atom<Element>{}, make_shape(size<0>(SmemLayoutDS_0{}), size<1>(SmemLayoutDS_0{}), size<2>(SmemLayoutDS_0{})), Step<_1, _2, _3>{}));
163
+ using SmemLayoutKp = typename CollectiveMmaMD::SmemLayoutB;
164
+
165
+ using SmemLayoutQp = typename CollectiveMmaND::SmemLayoutB;
166
+ using SmemLayoutDOp = typename CollectiveMmaND::SmemLayoutB;
167
+
168
+ using SmemLayoutLSE = Layout<Shape<tuple_element_t<1, TileShapeNM>, Int<StageCount>>>;
169
+
170
+ using MainloopPipeline = cutlass::PipelineTmaAsync<Stages::value>;
171
+ using MainloopPipelineQ = cutlass::PipelineTmaAsync<StagesQ::value>;
172
+
173
+ using PipelineState = typename cutlass::PipelineState<MainloopPipeline::Stages>;
174
+ using PipelineStateQ = typename cutlass::PipelineState<MainloopPipelineQ::Stages>;
175
+
176
+ using TileShapePV = TileShapeND; // To work with the kernel level
177
+ using TiledMmaPV = TiledMmaND;
178
+
179
+ static constexpr int kInnerLoadBytes = size(SmemLayoutQ{}(_,_,_0{})) * sizeof(Element) + size(SmemLayoutLSE{}(_,_0{})) * sizeof(ElementAccumulator);
180
+ static constexpr int kOuterLoadBytes = size(SmemLayoutK{}(_,_,_0{})) * sizeof(Element);
181
+
182
+ struct SharedStorage {
183
+ // One for each consumer WG
184
+ union {
185
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutK>> smem_k;
186
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutKp>> smem_kp;
187
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutV>> smem_v;
188
+ };
189
+
190
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutDS>> smem_ds;
191
+
192
+ // Loaded by producer, consumed by both WGs
193
+ union {
194
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutQ>> smem_q;
195
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutDO>> smem_do;
196
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutQp>> smem_qp;
197
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutDOp>> smem_dop;
198
+ };
199
+
200
+ // Accumulated into by both consumers, potentially loaded, potentially written
201
+ cute::array_aligned<ElementAccumulator, cute::cosize_v<SmemLayoutDQ>> smem_dq;
202
+
203
+ union {
204
+ cute::array_aligned<ElementAccumulator, cute::cosize_v<SmemLayoutLSE>> smem_lse;
205
+ cute::array_aligned<ElementAccumulator, cute::cosize_v<SmemLayoutLSE>> smem_sumOdO;
206
+ };
207
+ };
208
+
209
+ struct Arguments {
210
+ const Element* ptr_Q;
211
+ cute::tuple<int, int, int, _1> dQ;
212
+ const Element* ptr_K;
213
+ cute::tuple<int, int, int, _1> dK;
214
+ const Element* ptr_V;
215
+ cute::tuple<int, int, int, _1> dV;
216
+
217
+ const Element* ptr_dO;
218
+ cute::tuple<int, int, int, _1> dDO;
219
+
220
+ const ElementAccumulator* ptr_LSE;
221
+ cute::tuple<int, int, _1> dLSE;
222
+ const ElementAccumulator* ptr_sum_OdO;
223
+ cute::tuple<int, int, _1> dSumOdO;
224
+
225
+ ElementAccumulator* ptr_dQ;
226
+ cute::tuple<int, int, int, _1> dDQ;
227
+ };
228
+
229
+ using TMA_Q = typename CollectiveMmaNM::Params::TMA_B;
230
+ using TMA_K = typename CollectiveMmaNM::Params::TMA_A;
231
+ using TMA_V = typename CollectiveMmaNM::Params::TMA_A;
232
+ using TMA_DO = typename CollectiveMmaNM::Params::TMA_B;
233
+
234
+ using TMA_LSE = decltype(make_tma_copy(SM90_TMA_LOAD{}, make_tensor((const ElementAccumulator*)nullptr, make_shape(1, 1, 1), make_stride(_1{}, 0, 0)), SmemLayoutLSE{}(_,_0{})));
235
+ using TMA_ODO = TMA_LSE;
236
+
237
+ using TMA_DQ = decltype(make_tma_copy(SM90_TMA_REDUCE_ADD{}, make_tensor((const ElementAccumulator*)nullptr, make_shape(1, 1, 1, 1), make_stride(0, _1{}, 0, 0)), SmemLayoutDQ{}(_,_,_0{})));
238
+
239
+ using LoadQ = CollectiveLoadTma<
240
+ LoadKind::kBwdM,
241
+ MainloopPipeline,
242
+ Element,
243
+ SmemLayoutQ,
244
+ TMA_Q
245
+ >;
246
+
247
+ using LoadK = CollectiveLoadTma<
248
+ LoadKind::kBwdN,
249
+ MainloopPipelineQ,
250
+ Element,
251
+ SmemLayoutK,
252
+ TMA_K
253
+ >;
254
+
255
+ using LoadV = CollectiveLoadTma<
256
+ LoadKind::kBwdN,
257
+ MainloopPipelineQ,
258
+ Element,
259
+ SmemLayoutV,
260
+ TMA_V
261
+ >;
262
+
263
+ using LoadDO = CollectiveLoadTma<
264
+ LoadKind::kBwdM,
265
+ MainloopPipeline,
266
+ Element,
267
+ SmemLayoutDO,
268
+ TMA_DO
269
+ >;
270
+
271
+ using LoadLSE = CollectiveLoadTma<
272
+ LoadKind::kBwdScalar,
273
+ MainloopPipeline,
274
+ ElementAccumulator,
275
+ SmemLayoutLSE,
276
+ TMA_LSE
277
+ >;
278
+
279
+ using LoadODO = CollectiveLoadTma<
280
+ LoadKind::kBwdScalar,
281
+ MainloopPipeline,
282
+ ElementAccumulator,
283
+ SmemLayoutLSE,
284
+ TMA_ODO
285
+ >;
286
+
287
+ struct Params {
288
+ TMA_Q tma_load_q;
289
+ TMA_K tma_load_k;
290
+ TMA_V tma_load_v;
291
+ TMA_DO tma_load_do;
292
+
293
+ TMA_LSE tma_load_lse;
294
+ TMA_ODO tma_load_odo;
295
+
296
+ TMA_DQ tma_red_dq;
297
+
298
+ float scale_softmax;
299
+ float scale_softmax_log2;
300
+ };
301
+
302
+ static_assert(size(TiledMmaNM{}) == size(TiledMmaND{}));
303
+ static_assert(size(TiledMmaNM{}) == size(TiledMmaMD{}));
304
+
305
+ template<class ProblemShape>
306
+ static bool can_implement(ProblemShape const& problem_size, Arguments const& args) {
307
+ return true
308
+ && (get<4>(problem_size) <= get<2>(TileShape{}))
309
+ && ((get<4>(problem_size) % Alignment) == 0)
310
+ && ((get<2>(problem_size) % Alignment) == 0)
311
+ ;
312
+ }
313
+
314
+ template<class ProblemShape>
315
+ static Params to_underlying_arguments(ProblemShape const& problem_size, Arguments const& args, void* workspace) {
316
+ auto problem_shape_nm = make_shape(get<3>(problem_size), get<2>(problem_size), get<4>(problem_size), make_shape(get<0>(problem_size), get<1>(problem_size)));
317
+
318
+ auto dK = make_stride(get<2>(args.dK), get<3>(args.dK), make_stride(get<0>(args.dK), get<1>(args.dK)));
319
+ auto dQ = make_stride(get<2>(args.dQ), get<3>(args.dQ), make_stride(get<0>(args.dQ), get<1>(args.dQ)));
320
+ auto params_nm_kq = CollectiveMmaNM::to_underlying_arguments(problem_shape_nm,
321
+ typename CollectiveMmaNM::Arguments {
322
+ args.ptr_K, dK,
323
+ args.ptr_Q, dQ,
324
+ }, /*workspace=*/ nullptr);
325
+
326
+ auto dV = make_stride(get<2>(args.dV), get<3>(args.dV), make_stride(get<0>(args.dV), get<1>(args.dV)));
327
+ auto dDO = make_stride(get<2>(args.dDO), get<3>(args.dDO), make_stride(get<0>(args.dDO), get<1>(args.dDO)));
328
+ auto params_nm_vdo = CollectiveMmaNM::to_underlying_arguments(problem_shape_nm,
329
+ typename CollectiveMmaNM::Arguments {
330
+ args.ptr_V, dV,
331
+ args.ptr_dO, dDO,
332
+ }, /*workspace=*/ nullptr);
333
+
334
+
335
+ TMA_LSE tma_load_lse = make_tma_copy(SM90_TMA_LOAD{}, make_tensor(args.ptr_LSE, select<2,0,1>(problem_size), select<2,0,1>(args.dLSE)), SmemLayoutLSE{}(_,_0{}));
336
+ TMA_ODO tma_load_odo = make_tma_copy(SM90_TMA_LOAD{}, make_tensor(args.ptr_sum_OdO, select<2,0,1>(problem_size), select<2,0,1>(args.dSumOdO)), SmemLayoutLSE{}(_,_0{}));
337
+
338
+ TMA_DQ tma_red_dq = make_tma_copy(SM90_TMA_REDUCE_ADD{}, make_tensor(args.ptr_dQ, select<2,4,0,1>(problem_size), select<2,3,0,1>(args.dDQ)), SmemLayoutDQ{}(_,_,_0{}));
339
+
340
+ return Params{
341
+ params_nm_kq.tma_load_b,
342
+ params_nm_kq.tma_load_a,
343
+ params_nm_vdo.tma_load_a,
344
+ params_nm_vdo.tma_load_b,
345
+ tma_load_lse, tma_load_odo,
346
+ tma_red_dq,
347
+ 1.0f / (float) std::sqrt(get<4>(problem_size)),
348
+ (float) (std::log2(std::exp(1.0)) / std::sqrt(get<4>(problem_size)))
349
+ };
350
+ }
351
+
352
+ template<class BlkCoord, class ProblemSize>
353
+ CUTLASS_DEVICE
354
+ auto
355
+ get_inner_tile_count(BlkCoord const& blk_coord, ProblemSize const& problem_size) {
356
+ return Fusion{}.get_trip_count(blk_coord, TileShape{}, problem_size);
357
+ }
358
+
359
+ CUTLASS_DEVICE
360
+ static void prefetch_tma_descriptors(Params const& params) {
361
+ cute::prefetch_tma_descriptor(params.tma_load_q.get_tma_descriptor());
362
+ cute::prefetch_tma_descriptor(params.tma_load_k.get_tma_descriptor());
363
+ cute::prefetch_tma_descriptor(params.tma_load_v.get_tma_descriptor());
364
+ cute::prefetch_tma_descriptor(params.tma_load_do.get_tma_descriptor());
365
+ cute::prefetch_tma_descriptor(params.tma_load_odo.get_tma_descriptor());
366
+ cute::prefetch_tma_descriptor(params.tma_load_lse.get_tma_descriptor());
367
+ }
368
+
369
+ template<bool kLoadOuter, class BlkCoord, class ProblemShape, class LoadWarpBarrier>
370
+ CUTLASS_DEVICE void
371
+ load_kv_maybe_q(
372
+ int block_rank_in_cluster,
373
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
374
+ MainloopPipeline& pipeline_inner, PipelineState& smem_pipe_write_inner,
375
+ MainloopPipelineQ& pipeline_outer, PipelineStateQ& smem_pipe_write_outer,
376
+ SharedStorage& storage,
377
+ LoadWarpBarrier& load_warp_barrier, bool do_barrier)
378
+ {
379
+ // Load pattern:
380
+ // K0 V0 K1 V1
381
+ // Q0 DO0 Q1 DO1 Q2 DO2 ...
382
+ // K0 Q0 V0 K1 DO0 V1 ...
383
+ int lane_predicate = cute::elect_one_sync();
384
+
385
+ int outer_tile_count = NumMmaWarpGroups;
386
+ int inner_tile_count = get_inner_tile_count(blk_coord, problem_size);
387
+
388
+ auto outer_tile_iter = cute::make_coord_iterator(outer_tile_count);
389
+ auto inner_tile_iter = cute::make_coord_iterator(inner_tile_count);
390
+
391
+ uint16_t mcast_mask_b = 0;
392
+
393
+ LoadQ load_q{params.tma_load_q, pipeline_inner, storage.smem_q};
394
+ auto load_state_q = load_q.init_state(block_rank_in_cluster, problem_size, TileShapeNM{}, blk_coord, inner_tile_count);
395
+
396
+ LoadDO load_do{params.tma_load_do, pipeline_inner, storage.smem_do};
397
+ auto load_state_do = load_do.init_state(block_rank_in_cluster, problem_size, TileShapeNM{}, blk_coord, inner_tile_count);
398
+
399
+ LoadK load_k{params.tma_load_k, pipeline_outer, storage.smem_k};
400
+ auto load_state_k = load_k.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
401
+
402
+ LoadV load_v{params.tma_load_v, pipeline_outer, storage.smem_v};
403
+ auto load_state_v = load_v.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
404
+
405
+ LoadLSE load_lse{params.tma_load_lse, pipeline_inner, storage.smem_lse};
406
+ auto load_state_lse = load_lse.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
407
+
408
+ LoadODO load_odo{params.tma_load_odo, pipeline_inner, storage.smem_sumOdO};
409
+ auto load_state_odo = load_odo.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
410
+
411
+ outer_tile_count *= 2; // K & V
412
+ inner_tile_count *= 4; // Q & dO & LSE & sumOdO
413
+
414
+ while (inner_tile_count > 0) {
415
+ if (Fusion{}.is_contributing(make_coord(*inner_tile_iter, get<1>(blk_coord)), TileShape{}, problem_size)) {
416
+ break;
417
+ }
418
+ inner_tile_count -= 4;
419
+ ++inner_tile_iter;
420
+ }
421
+
422
+ if constexpr (kLoadOuter) {
423
+ load_k.template step<false>(outer_tile_iter, load_state_k, smem_pipe_write_outer, lane_predicate, outer_tile_count);
424
+ }
425
+
426
+ load_q.template step<false,false,true>(inner_tile_iter, load_state_q, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
427
+ load_lse.template step<false,true,false>(inner_tile_iter, load_state_lse, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
428
+
429
+ if constexpr (! kLoadOuter) {
430
+ if (do_barrier) {
431
+ load_warp_barrier.arrive();
432
+ load_warp_barrier.wait(/*phase=*/ 0);
433
+ do_barrier = false;
434
+ }
435
+ }
436
+
437
+ if constexpr (kLoadOuter) {
438
+ load_v.template step<true>(outer_tile_iter, load_state_v, smem_pipe_write_outer, lane_predicate, outer_tile_count);
439
+ load_k.template step<false>(outer_tile_iter, load_state_k, smem_pipe_write_outer, lane_predicate, outer_tile_count);
440
+ }
441
+
442
+ load_do.template step<false,false,true>(inner_tile_iter, load_state_do, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
443
+ load_odo.template step<true,true,false>(inner_tile_iter, load_state_odo, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
444
+
445
+ if constexpr (kLoadOuter) {
446
+ load_v.template step<true>(outer_tile_iter, load_state_v, smem_pipe_write_outer, lane_predicate, outer_tile_count);
447
+ }
448
+
449
+ if constexpr (kLoadOuter) {
450
+ while (outer_tile_count > 0) {
451
+ load_k.template step<false>(outer_tile_iter, load_state_k, smem_pipe_write_outer, lane_predicate, outer_tile_count);
452
+ load_v.template step<true>(outer_tile_iter, load_state_v, smem_pipe_write_outer, lane_predicate, outer_tile_count);
453
+ }
454
+ }
455
+
456
+ CUTLASS_PRAGMA_NO_UNROLL
457
+ while (inner_tile_count > 0) {
458
+ while (inner_tile_count > 0) {
459
+ if (Fusion{}.is_contributing(make_coord(*inner_tile_iter, get<1>(blk_coord)), TileShape{}, problem_size)) {
460
+ break;
461
+ }
462
+ inner_tile_count -= 4;
463
+ ++inner_tile_iter;
464
+ }
465
+ load_q.template step<false,false,true>(inner_tile_iter, load_state_q, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
466
+ load_lse.template step<false,true,false>(inner_tile_iter, load_state_lse, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
467
+
468
+ load_do.template step<false,false,true>(inner_tile_iter, load_state_do, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
469
+ load_odo.template step<true,true,false>(inner_tile_iter, load_state_odo, smem_pipe_write_inner, lane_predicate, inner_tile_count, mcast_mask_b);
470
+ }
471
+ }
472
+
473
+ template<class BlkCoord, class ProblemShape, class LoadWarpBarrier>
474
+ CUTLASS_DEVICE void
475
+ load_maybe_q(
476
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
477
+ MainloopPipelineQ& pipeline_outer, PipelineStateQ& smem_pipe_write_outer,
478
+ SharedStorage& storage,
479
+ LoadWarpBarrier& load_warp_barrier, bool do_barrier)
480
+ {
481
+ // Load pattern:
482
+ // K0 V0 K1 V1
483
+ // Q0 DO0 Q1 DO1 Q2 DO2 ...
484
+ // K0 Q0 V0 K1 DO0 V1 ...
485
+ int lane_predicate = cute::elect_one_sync();
486
+
487
+ int outer_tile_count = NumMmaWarpGroups;
488
+
489
+ auto outer_tile_iter = cute::make_coord_iterator(outer_tile_count);
490
+
491
+ LoadK load_k{params.tma_load_k, pipeline_outer, storage.smem_k};
492
+ auto load_state_k = load_k.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
493
+
494
+ LoadV load_v{params.tma_load_v, pipeline_outer, storage.smem_v};
495
+ auto load_state_v = load_v.init_state(_0{}, problem_size, TileShapeNM{}, blk_coord, outer_tile_count);
496
+
497
+ outer_tile_count *= 2; // K & V
498
+
499
+ load_k.template step<false>(outer_tile_iter, load_state_k, smem_pipe_write_outer, lane_predicate, outer_tile_count);
500
+
501
+ if (do_barrier) {
502
+ load_warp_barrier.arrive();
503
+ load_warp_barrier.wait(/*phase=*/ 0);
504
+ do_barrier = false;
505
+ }
506
+
507
+ load_v.template step<true>(outer_tile_iter, load_state_v, smem_pipe_write_outer, lane_predicate, outer_tile_count);
508
+
509
+ while (outer_tile_count > 0) {
510
+ load_k.template step<false>(outer_tile_iter, load_state_k, smem_pipe_write_outer, lane_predicate, outer_tile_count);
511
+ load_v.template step<true>(outer_tile_iter, load_state_v, smem_pipe_write_outer, lane_predicate, outer_tile_count);
512
+ }
513
+ }
514
+
515
+ template<class BlkCoord, class ProblemShape, class MainloopPipelineReducer, class PipelineStateReducer>
516
+ CUTLASS_DEVICE void
517
+ reduce(
518
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
519
+ MainloopPipelineReducer& pipeline_reducer, PipelineStateReducer& smem_pipe_read_reducer,
520
+ SharedStorage& storage)
521
+ {
522
+ int lane_predicate = cute::elect_one_sync();
523
+
524
+ Tensor mDQ_full = params.tma_red_dq.get_tma_tensor(select<2,4,0,1>(problem_size));
525
+ Tensor gDQ_full = local_tile(mDQ_full, TileShapeMD{}, make_coord(_, _, _), Step<_1, _1, Underscore>{});
526
+ Tensor gDQ = gDQ_full(_, _, _, _0{}, get<2,0>(blk_coord), get<2,1>(blk_coord));
527
+ Tensor sDQ = make_tensor(make_smem_ptr(storage.smem_dq.data()), SmemLayoutDQ{});
528
+
529
+ auto block_tma = params.tma_red_dq.get_slice(_0{});
530
+
531
+ Tensor tDQsDQ = block_tma.partition_S(sDQ);
532
+ Tensor tDQgDQ = block_tma.partition_D(gDQ);
533
+
534
+ int inner_tile_count = get_inner_tile_count(blk_coord, problem_size);
535
+ int g_index = 0;
536
+
537
+ auto smem_pipe_release_reducer = smem_pipe_read_reducer;
538
+ bool first = true;
539
+ while (inner_tile_count > 0) {
540
+ while (inner_tile_count > 0) {
541
+ if (Fusion{}.is_contributing(make_coord(g_index, get<1>(blk_coord)), TileShape{}, problem_size)) {
542
+ break;
543
+ }
544
+ inner_tile_count -= 1;
545
+ ++g_index;
546
+ }
547
+ if (inner_tile_count == 0) break;
548
+
549
+ pipeline_reducer.consumer_wait(smem_pipe_read_reducer);
550
+ if (lane_predicate == 1) {
551
+ tma_store_wait<1>();
552
+ }
553
+ if (! first) {
554
+ pipeline_reducer.consumer_release(smem_pipe_release_reducer);
555
+ ++smem_pipe_release_reducer;
556
+ } else {
557
+ first = false;
558
+ }
559
+ if (lane_predicate == 1) {
560
+ copy(params.tma_red_dq, tDQsDQ(_,_,_,smem_pipe_read_reducer.index()), tDQgDQ(_,_,_,g_index));
561
+ tma_store_arrive();
562
+ }
563
+ ++smem_pipe_read_reducer;
564
+ --inner_tile_count;
565
+ ++g_index;
566
+ }
567
+ if (lane_predicate) {
568
+ tma_store_wait<0>();
569
+ }
570
+ pipeline_reducer.consumer_release(smem_pipe_release_reducer);
571
+ ++smem_pipe_release_reducer;
572
+ }
573
+
574
+ template<class BlkCoord, class ProblemShape, class MainloopPipelineReducer, class PipelineStateReducer, class MathWgOrderBarrier>
575
+ CUTLASS_DEVICE auto
576
+ compute(
577
+ BlkCoord const& blk_coord, BlkCoord const& wg_coord,
578
+ Params const& params, ProblemShape const& problem_size,
579
+ MainloopPipeline& pipeline_inner, PipelineState& smem_pipe_read_inner,
580
+ MainloopPipelineQ& pipeline_outer, PipelineStateQ& smem_pipe_read_outer,
581
+ MainloopPipelineReducer& pipeline_reducer, PipelineStateReducer& smem_pipe_write_reducer,
582
+ SharedStorage& storage,
583
+ MathWgOrderBarrier& math_wg_order_barrier)
584
+ {
585
+ TiledMmaND tiled_mma_nd;
586
+
587
+ Tensor acc_DV = partition_fragment_C(tiled_mma_nd, take<0,2>(TileShapeND{}));
588
+ clear(acc_DV);
589
+
590
+ Tensor acc_DK = partition_fragment_C(tiled_mma_nd, take<0,2>(TileShapeND{}));
591
+ clear(acc_DK);
592
+
593
+ int thread_idx = int(threadIdx.x) % cutlass::NumThreadsPerWarpGroup;
594
+
595
+ PipelineState smem_pipe_release_inner = smem_pipe_read_inner;
596
+
597
+ pipeline_outer.consumer_wait(smem_pipe_read_outer);
598
+ PipelineStateQ smem_pipe_read_k = smem_pipe_read_outer;
599
+ ++smem_pipe_read_outer;
600
+ pipeline_outer.consumer_wait(smem_pipe_read_outer);
601
+ PipelineStateQ smem_pipe_read_v = smem_pipe_read_outer;
602
+
603
+ int inner_tile_count = get_inner_tile_count(wg_coord, problem_size);
604
+
605
+ TiledMmaNM tiled_mma_nm;
606
+ Tensor sK = make_tensor(make_smem_ptr(storage.smem_k.data()), SmemLayoutK{});
607
+ Tensor sQ = make_tensor(make_smem_ptr(storage.smem_q.data()), SmemLayoutQ{});
608
+ auto thr_mma_nm = tiled_mma_nm.get_thread_slice(thread_idx);
609
+ Tensor tSsK = thr_mma_nm.partition_A(sK);
610
+ Tensor tSsQ = thr_mma_nm.partition_B(sQ);
611
+ Tensor tSrK = thr_mma_nm.make_fragment_A(tSsK);
612
+ Tensor tSrQ = thr_mma_nm.make_fragment_B(tSsQ);
613
+
614
+ Tensor sV = make_tensor(make_smem_ptr(storage.smem_v.data()), SmemLayoutV{});
615
+ Tensor sDO = make_tensor(make_smem_ptr(storage.smem_do.data()), SmemLayoutDO{});
616
+
617
+ Tensor tDPsV = thr_mma_nm.partition_A(sV);
618
+ Tensor tDPsDO = thr_mma_nm.partition_B(sDO);
619
+ Tensor tDPrV = thr_mma_nm.make_fragment_A(tDPsV);
620
+ Tensor tDPrDO = thr_mma_nm.make_fragment_B(tDPsDO);
621
+
622
+ auto thr_mma_nd = tiled_mma_nd.get_thread_slice(thread_idx);
623
+
624
+ Tensor sDOp = make_tensor(make_smem_ptr(storage.smem_dop.data()), SmemLayoutDOp{});
625
+ Tensor tDV_sDO = thr_mma_nd.partition_B(sDOp);
626
+ Tensor tDVrDO = thr_mma_nd.make_fragment_B(tDV_sDO);
627
+
628
+
629
+ Tensor sQp = make_tensor(make_smem_ptr(storage.smem_qp.data()), SmemLayoutQp{});
630
+ Tensor tDK_sQ = thr_mma_nd.partition_B(sQp);
631
+ Tensor tDKrQ = thr_mma_nd.make_fragment_B(tDK_sQ);
632
+
633
+
634
+ int wg_idx = __shfl_sync(0xffffffff, get<1>(wg_coord) % NumMmaWarpGroups, 0);
635
+
636
+ TiledMmaMD tiled_mma_md;
637
+ auto thr_mma_md = tiled_mma_md.get_thread_slice(thread_idx);
638
+ Tensor sDS = make_tensor(make_smem_ptr(storage.smem_ds.data()), SmemLayoutDS{});
639
+ Tensor tDQsDS = thr_mma_md.partition_A(sDS);
640
+ Tensor tDQrDS_full = thr_mma_md.make_fragment_A(tDQsDS);
641
+ Tensor tDQrDS = tDQrDS_full(_,_,_,_);
642
+ Tensor sKp = make_tensor(make_smem_ptr(storage.smem_kp.data()), SmemLayoutKp{});
643
+ Tensor tDQsK = thr_mma_md.partition_B(sKp);
644
+ Tensor tDQrK = thr_mma_md.make_fragment_B(tDQsK);
645
+
646
+ Tensor sLSE = make_tensor(make_smem_ptr(storage.smem_lse.data()), make_shape(get<0>(TileShapeNM{}), get<1>(TileShapeNM{}), Int<StageCount>{}), make_stride(_0{}, _1{}, get<1>(TileShapeNM{})));
647
+ Tensor tSsLSE = thr_mma_nm.partition_C(sLSE);
648
+
649
+ Tensor sODO = make_tensor(make_smem_ptr(storage.smem_sumOdO.data()), make_shape(get<0>(TileShapeNM{}), get<1>(TileShapeNM{}), Int<StageCount>{}), make_stride(_0{}, _1{}, get<1>(TileShapeNM{})));
650
+ Tensor tDPsODO = thr_mma_nm.partition_C(sODO);
651
+
652
+ Tensor cS = make_identity_tensor(take<0,2>(TileShapeNM{}));
653
+ Tensor tScS = thr_mma_nm.partition_C(cS);
654
+ int n_block = get<1>(wg_coord);
655
+ tScS.data() = tScS.data() + E<0>{} * n_block * get<0>(TileShapeNM{});
656
+
657
+
658
+ // Transpose
659
+ Tensor sDSp_full = sDS.compose(make_layout(make_shape(size<1>(sDS), size<0>(sDS), size<2>(sDS)), make_stride(size<0>(sDS), _1{}, size<1>(sDS) * size<0>(sDS))));
660
+ Tensor sDSp = sDSp_full(_,_,_);
661
+ Tensor tDPsDS = thr_mma_nm.partition_C(sDSp);
662
+
663
+ auto thr_mma_nd_ss = TiledMmaND_SS{}.get_thread_slice(thread_idx);
664
+ Tensor tDKsDSp = thr_mma_nd_ss.partition_A(sDSp);
665
+
666
+ Tensor tDKrDSp = thr_mma_nd_ss.make_fragment_A(tDKsDSp);
667
+
668
+ Tensor sDQ = make_tensor(make_smem_ptr(storage.smem_dq.data()), SmemLayoutDQ{});
669
+ auto tDQsDQ_full = thr_mma_md.partition_C(sDQ);
670
+
671
+
672
+ auto smem_pipe_read_k_other = smem_pipe_read_k;
673
+ smem_pipe_read_k_other.advance(2);
674
+
675
+ int k_index = 0;
676
+
677
+ while (inner_tile_count > 0) {
678
+ while (inner_tile_count > 0) {
679
+ if (Fusion{}.is_contributing(make_coord(k_index, get<1>(blk_coord)), TileShape{}, problem_size)) {
680
+ break;
681
+ }
682
+ inner_tile_count -= 1;
683
+ tScS.data() = tScS.data() + E<1>{} * get<1>(TileShapeNM{});
684
+ k_index += 1;
685
+ }
686
+ if (inner_tile_count == 0) break;
687
+
688
+ pipeline_inner.consumer_wait(smem_pipe_read_inner);
689
+ PipelineState smem_pipe_read_q = smem_pipe_read_inner;
690
+ ++smem_pipe_read_inner;
691
+ PipelineState smem_pipe_read_do = smem_pipe_read_inner;
692
+ ++smem_pipe_read_inner;
693
+
694
+ // GEMM KQ -> S
695
+ Tensor acc_S = partition_fragment_C(tiled_mma_nm, take<0,2>(TileShapeNM{}));
696
+
697
+ warpgroup_fence_operand(acc_S);
698
+ warpgroup_arrive();
699
+ gemm_zero_acc(tiled_mma_nm, tSrK(_,_,_,smem_pipe_read_k.index()), tSrQ(_,_,_,smem_pipe_read_q.index()), acc_S);
700
+ warpgroup_commit_batch();
701
+
702
+ pipeline_inner.consumer_wait(smem_pipe_read_do);
703
+
704
+ // GEMM VdO -> dP
705
+ Tensor acc_DP = partition_fragment_C(tiled_mma_nm, take<0,2>(TileShapeNM{}));
706
+
707
+ warpgroup_fence_operand(acc_DP);
708
+ warpgroup_arrive();
709
+ gemm_zero_acc(tiled_mma_nm, tDPrV(_,_,_,smem_pipe_read_v.index()), tDPrDO(_,_,_,smem_pipe_read_do.index()), acc_DP);
710
+ warpgroup_commit_batch();
711
+
712
+ Tensor reg_LSE = make_fragment_like<ElementAccumulator>(acc_S);
713
+ for (int i = 0; i < size(reg_LSE); i++) {
714
+ reg_LSE(i) = ((ElementAccumulator)std::log2(std::exp(1.0))) * tSsLSE(_,_,_,smem_pipe_read_q.index())(i);
715
+ }
716
+
717
+ Tensor reg_ODO = make_fragment_like<ElementAccumulator>(acc_S);
718
+ if constexpr (decltype(get<0>(TileShape{}) != _128{})::value) {
719
+ for (int i = 0; i < size(reg_ODO); i++) {
720
+ reg_ODO(i) = tDPsODO(_,_,_,smem_pipe_read_do.index())(i);
721
+ }
722
+ }
723
+
724
+ warpgroup_wait<1>();
725
+ warpgroup_fence_operand(acc_S);
726
+
727
+ math_wg_order_barrier.wait();
728
+ // Compute S -> P
729
+ Fusion{}.before_softmax(acc_S, tScS, problem_size);
730
+ auto acc_P = make_fragment_like<ElementAccumulator>(acc_S);
731
+ CUTLASS_PRAGMA_UNROLL
732
+ for (int i = 0; i < size(acc_P); i++) {
733
+ acc_P(i) = ::exp2f(params.scale_softmax_log2 * acc_S(i) - reg_LSE(i));
734
+ }
735
+ math_wg_order_barrier.arrive();
736
+
737
+ if constexpr (decltype(get<0>(TileShape{}) == _128{})::value) {
738
+ for (int i = 0; i < size(reg_ODO); i++) {
739
+ reg_ODO(i) = tDPsODO(_,_,_,smem_pipe_read_do.index())(i);
740
+ }
741
+ }
742
+
743
+ warpgroup_wait<0>();
744
+ warpgroup_fence_operand(acc_DP);
745
+
746
+ // Compute dP P -> dS
747
+ auto acc_DS = make_fragment_like<Element>(acc_DP);
748
+ CUTLASS_PRAGMA_UNROLL
749
+ for (int i = 0; i < size(acc_DS); i++) {
750
+ // We could move the scale out and into the respective epilogues (or a final scaling step)
751
+ acc_DS(i) = acc_P(i) * params.scale_softmax * (acc_DP(i) - reg_ODO(i));
752
+ }
753
+
754
+ // GEMM PdO -> dV
755
+ auto op_P = make_acc_into_op<Element>(acc_P, typename TiledMmaND::LayoutA_TV{});
756
+ warpgroup_fence_operand(acc_DV);
757
+ warpgroup_fence_operand(op_P);
758
+ warpgroup_arrive();
759
+ cute::gemm(tiled_mma_nd, op_P, tDVrDO(_,_,_,smem_pipe_read_do.index()), acc_DV);
760
+ warpgroup_commit_batch();
761
+
762
+ // Store dS to smem dS'
763
+ if (wg_idx == 0) math_wg_order_barrier.wait();
764
+
765
+ auto recast_bits = [](auto sz, auto t) {
766
+ return recast<uint_bit_t<decltype(sz)::value>>(t);
767
+ };
768
+ auto tDPsDS_v = recast_bits(Int<sizeof_bits_v<Element> * 2>{}, tDPsDS);
769
+ auto acc_DS_v = recast_bits(Int<sizeof_bits_v<Element> * 2>{}, acc_DS);
770
+
771
+ CUTLASS_PRAGMA_UNROLL
772
+ for (int i = 0; i < size(acc_DS_v); i++) {
773
+ tDPsDS_v(_,_,_,wg_idx)(i) = acc_DS_v(i);
774
+ }
775
+
776
+ cutlass::arch::fence_view_async_shared();
777
+ if (wg_idx == 0) math_wg_order_barrier.arrive();
778
+
779
+ // GEMM dS Q -> dK
780
+ if (wg_idx == 1) {
781
+
782
+ math_wg_order_barrier.wait();
783
+
784
+ // GEMM dS' K -> dQ
785
+ Tensor acc_DQ = partition_fragment_C(tiled_mma_md, take<0,2>(TileShapeMD{}));
786
+
787
+ warpgroup_fence_operand(acc_DQ);
788
+ warpgroup_arrive();
789
+ gemm_zero_acc(tiled_mma_md, tDQrDS(_,_,_,0), tDQrK(_,_,_,smem_pipe_read_k_other.index()), acc_DQ);
790
+ cute::gemm(tiled_mma_md, tDQrDS(_,_,_,1), tDQrK(_,_,_,smem_pipe_read_k.index()), acc_DQ);
791
+ warpgroup_commit_batch();
792
+
793
+ warpgroup_fence_operand(acc_DK);
794
+ warpgroup_arrive();
795
+ cute::gemm(TiledMmaND_SS{}, tDKrDSp(_,_,_,wg_idx), tDKrQ(_,_,_,smem_pipe_read_q.index()), acc_DK);
796
+ warpgroup_commit_batch();
797
+
798
+ warpgroup_wait<1>();
799
+ warpgroup_fence_operand(acc_DK);
800
+
801
+ warpgroup_wait<1>();
802
+ warpgroup_fence_operand(acc_DQ);
803
+
804
+ math_wg_order_barrier.arrive();
805
+
806
+ pipeline_reducer.producer_acquire(smem_pipe_write_reducer);
807
+ auto tDQsDQ = tDQsDQ_full(_,_,_,smem_pipe_write_reducer.index());
808
+
809
+ // Store dQ to smem dQ'
810
+ // Invoke TMA reduce on dQ'
811
+ using Vec = uint_bit_t<sizeof_bits_v<ElementAccumulator> * 2>;
812
+ auto tDQsDQ_v = recast<Vec>(tDQsDQ);
813
+ auto acc_DQ_v = recast<Vec>(acc_DQ);
814
+
815
+ CUTLASS_PRAGMA_UNROLL
816
+ for (int i = 0; i < size(acc_DQ_v); i++) {
817
+ tDQsDQ_v(i) = acc_DQ_v(i);
818
+ }
819
+
820
+ cutlass::arch::fence_view_async_shared();
821
+
822
+ pipeline_reducer.producer_commit(smem_pipe_write_reducer);
823
+ ++smem_pipe_write_reducer;
824
+ } else {
825
+
826
+ warpgroup_fence_operand(acc_DK);
827
+ warpgroup_arrive();
828
+ cute::gemm(TiledMmaND_SS{}, tDKrDSp(_,_,_,wg_idx), tDKrQ(_,_,_,smem_pipe_read_q.index()), acc_DK);
829
+ warpgroup_commit_batch();
830
+
831
+ warpgroup_wait<1>();
832
+ warpgroup_fence_operand(acc_DK);
833
+
834
+ pipeline_reducer.producer_acquire(smem_pipe_write_reducer);
835
+ pipeline_reducer.producer_commit(smem_pipe_write_reducer);
836
+ ++smem_pipe_write_reducer;
837
+ }
838
+
839
+ --inner_tile_count;
840
+
841
+ pipeline_inner.consumer_release(smem_pipe_release_inner);
842
+ ++smem_pipe_release_inner;
843
+ pipeline_inner.consumer_release(smem_pipe_release_inner);
844
+ ++smem_pipe_release_inner;
845
+
846
+ tScS.data() = tScS.data() + E<1>{} * get<1>(TileShapeNM{});
847
+ k_index += 1;
848
+ }
849
+
850
+ pipeline_outer.consumer_release(smem_pipe_read_k);
851
+ pipeline_outer.consumer_release(smem_pipe_read_outer);
852
+ pipeline_reducer.producer_tail(smem_pipe_write_reducer);
853
+ ++smem_pipe_read_outer;
854
+
855
+ warpgroup_wait<0>();
856
+ warpgroup_fence_operand(acc_DK);
857
+ warpgroup_fence_operand(acc_DV);
858
+
859
+ return make_tuple(acc_DK, acc_DV);
860
+ }
861
+ };
862
+
863
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_load.hpp ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cute/tensor.hpp"
36
+
37
+ namespace cutlass::fmha::collective {
38
+
39
+ enum class LoadKind {
40
+ kQ, kK, kV,
41
+ kBwdN, kBwdM, kBwdScalar
42
+ };
43
+
44
+ template<
45
+ LoadKind kKind,
46
+ class Pipeline,
47
+ class Element,
48
+ class SmemLayout,
49
+ class TMA
50
+ >
51
+ struct CollectiveLoadTma {
52
+
53
+ using Params = TMA;
54
+ using SharedStorage = cute::array_aligned<Element, cute::cosize_v<SmemLayout>>;
55
+ using PipelineState = typename cutlass::PipelineState<Pipeline::Stages>;
56
+
57
+ Params const& params;
58
+ Pipeline& pipeline;
59
+ SharedStorage& storage;
60
+
61
+ CUTLASS_DEVICE
62
+ CollectiveLoadTma(Params const& params, Pipeline& pipeline, SharedStorage& storage)
63
+ : params(params), pipeline(pipeline), storage(storage) {}
64
+
65
+ template<class ProblemSize, class TileShape, class BlockCoord>
66
+ CUTLASS_DEVICE auto init_g(ProblemSize const& problem_size, TileShape const& tile_shape,
67
+ BlockCoord const& blk_coord, int loop_count
68
+ ) {
69
+ using X = Underscore;
70
+ if constexpr (kKind == LoadKind::kK) {
71
+ Tensor mK_full = params.get_tma_tensor(make_shape(get<3>(problem_size), get<4>(problem_size), select<0,1>(problem_size)));
72
+ Tensor gK_full = local_tile(mK_full, tile_shape, make_coord(_, _, _), Step<X, _1, _1>{});
73
+ Tensor gK = gK_full(_, _, _, _0{}, get<2>(blk_coord));
74
+ return gK;
75
+ } else if constexpr (kKind == LoadKind::kQ) {
76
+ Tensor mQ_full = params.get_tma_tensor(make_shape(get<2>(problem_size), get<4>(problem_size), select<0,1>(problem_size)));
77
+ Tensor gQ_full = local_tile(mQ_full, tile_shape, make_coord(_, _, _), Step<_1, X, _1>{});
78
+ Tensor gQ = gQ_full(_, _, _, _0{}, get<2>(blk_coord));
79
+ return make_tensor(gQ.data() + loop_count * get<0>(blk_coord) * stride<2>(gQ), gQ.layout());
80
+ } else if constexpr (kKind == LoadKind::kV) {
81
+ Tensor mV_full = params.get_tma_tensor(make_shape(get<4>(problem_size), get<3>(problem_size), select<0,1>(problem_size)));
82
+ Tensor gV_full = local_tile(mV_full, tile_shape, make_coord(_, _, _), Step<X, _1, _1>{});
83
+ Tensor gV = gV_full(_, _, _0{}, _, get<2>(blk_coord));
84
+ return gV;
85
+ } else if constexpr (kKind == LoadKind::kBwdN) {
86
+ Tensor m_full = params.get_tma_tensor(make_shape(get<3>(problem_size), get<4>(problem_size), select<0,1>(problem_size)));
87
+ Tensor g_full = local_tile(m_full, tile_shape, make_coord(_, _, _), Step<_1, X, _1>{});
88
+ Tensor g = g_full(_, _, _, _0{}, get<2>(blk_coord));
89
+ return make_tensor(g.data() + loop_count * get<1>(blk_coord) * stride<2>(g), g.layout());
90
+ } else if constexpr (kKind == LoadKind::kBwdM) {
91
+ Tensor m_full = params.get_tma_tensor(make_shape(get<2>(problem_size), get<4>(problem_size), select<0,1>(problem_size)));
92
+ Tensor g_full = local_tile(m_full, tile_shape, make_coord(_, _, _), Step<X, _1, _1>{});
93
+ Tensor g = g_full(_, _, _, _0{}, get<2>(blk_coord));
94
+ return g;
95
+ } else if constexpr (kKind == LoadKind::kBwdScalar) {
96
+ Tensor m_full = params.get_tma_tensor(select<2,0,1>(problem_size));
97
+ Tensor g_full = local_tile(m_full, tile_shape, make_coord(_, _, _), Step<X, _1, X>{});
98
+ Tensor g = g_full(_, _, get<2,0>(blk_coord), get<2,1>(blk_coord));
99
+ return g;
100
+ }
101
+ }
102
+
103
+ template<class ClusterRank, class ProblemSize, class TileShape, class BlockCoord>
104
+ CUTLASS_DEVICE auto init_state(ClusterRank const& block_rank_in_cluster,
105
+ ProblemSize const& problem_size, TileShape const& tile_shape,
106
+ BlockCoord const& block_coord, int loop_count
107
+ ) {
108
+ Tensor g = init_g(problem_size, tile_shape, block_coord, loop_count);
109
+ Tensor s = make_tensor(make_smem_ptr(storage.data()), SmemLayout{});
110
+
111
+ auto block_tma = params.get_slice(block_rank_in_cluster);
112
+ Tensor ts = block_tma.partition_D(s);
113
+ Tensor tg = block_tma.partition_S(g);
114
+
115
+ return make_tuple(tg, ts);
116
+ }
117
+
118
+ template<bool kAdvanceIterator=true, bool kAdvancePipe=true, bool kAcquireBarrier=true, class TileIterator, class State>
119
+ CUTLASS_DEVICE void step(TileIterator& tile_iter, State const& state,
120
+ PipelineState& smem_pipe_write,
121
+ int lane_predicate, int& tile_count, uint16_t mcast_mask = 0
122
+ ) {
123
+ if ((lane_predicate == 1) && (tile_count > 0)) {
124
+ if constexpr (kAcquireBarrier) pipeline.producer_acquire(smem_pipe_write);
125
+ using BarrierType = typename Pipeline::ProducerBarrierType;
126
+ BarrierType* tma_barrier = pipeline.producer_get_barrier(smem_pipe_write);
127
+
128
+ if constexpr (kKind == LoadKind::kBwdScalar) {
129
+ copy(params.with(*tma_barrier, mcast_mask), get<0>(state)(_,_,*tile_iter), get<1>(state)(_,_,smem_pipe_write.index()));
130
+ } else {
131
+ copy(params.with(*tma_barrier, mcast_mask), get<0>(state)(_,_,_,*tile_iter), get<1>(state)(_,_,_,smem_pipe_write.index()));
132
+ }
133
+ if constexpr (kAdvancePipe) ++smem_pipe_write;
134
+ if constexpr (kAdvanceIterator) ++tile_iter;
135
+ }
136
+ --tile_count;
137
+ }
138
+ };
139
+
140
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_softmax.hpp ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cute/tensor.hpp"
36
+
37
+ #include "../collective/fmha_common.hpp"
38
+
39
+ namespace cutlass::fmha::collective {
40
+
41
+ template<
42
+ class ElementAccumulator,
43
+ class Fusion,
44
+ class Params
45
+ >
46
+ struct CollectiveSoftmax {
47
+ Params const& params;
48
+ CUTLASS_DEVICE CollectiveSoftmax(Params const& params) : params(params) {}
49
+
50
+ using SumType = float;
51
+ using MaxType = ElementAccumulator;
52
+
53
+ template<class AccPV, class TiledMmaPV>
54
+ CUTLASS_DEVICE auto init(AccPV const& acc_pv, TiledMmaPV const& tiled_mma_pv) {
55
+ Tensor s_max = make_fragment_like<MaxType>(size<0>(layout_acc_mn(tiled_mma_pv, acc_pv.layout())));
56
+ Tensor a_sum = make_fragment_like<SumType>(s_max);
57
+ return make_tuple(s_max, a_sum);
58
+ }
59
+
60
+ CUTLASS_DEVICE float overload_exp2(float f) {
61
+ return ::exp2f(f);
62
+ }
63
+
64
+ CUTLASS_DEVICE cutlass::half_t overload_exp2(cutlass::half_t f) {
65
+ auto a = f.raw();
66
+ decltype(a) d;
67
+ asm("ex2.approx.f16 %0, %1;" : "=h"(d) : "h"(a));
68
+ return cutlass::half_t::bitcast(d);
69
+ }
70
+
71
+
72
+ CUTLASS_DEVICE float overload_max(float a, float b) {
73
+ return ::max(a, b);
74
+ }
75
+
76
+ CUTLASS_DEVICE cutlass::half_t overload_max(cutlass::half_t a, cutlass::half_t b) {
77
+ return cutlass::half_t{__hmax_nan(a.to_half(), b.to_half())};
78
+ }
79
+
80
+ CUTLASS_DEVICE half overload_to_native(cutlass::half_t f) {
81
+ return f.to_half();
82
+ }
83
+
84
+ CUTLASS_DEVICE float overload_to_native(float f) {
85
+ return f;
86
+ }
87
+
88
+ template<class AccQK, class TiledMmaQK, class CountQK, class State, class ProblemShape>
89
+ CUTLASS_DEVICE auto step(AccQK& acc_qk, TiledMmaQK const& tiled_mma_qk, CountQK const& count_qk, State& state, ProblemShape const& problem_shape) {
90
+ Fusion{}.before_softmax(acc_qk, count_qk, problem_shape);
91
+ Tensor acc_qk_mn = make_tensor(acc_qk.data(), layout_acc_mn(tiled_mma_qk, acc_qk.layout()));
92
+ auto reduction_target_qk = reduction_target_n(tiled_mma_qk);
93
+ constexpr int red_rank = decltype(rank(reduction_target_qk))::value;
94
+
95
+ auto& s_max = get<0>(state);
96
+ auto& a_sum = get<1>(state);
97
+
98
+ // Linear reduction is faster for the first iteration
99
+ CUTLASS_PRAGMA_UNROLL
100
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
101
+ s_max(i) = acc_qk_mn(i, 0);
102
+ }
103
+ CUTLASS_PRAGMA_UNROLL
104
+ for (int j = 1; j < size<1>(acc_qk_mn); j++) {
105
+ CUTLASS_PRAGMA_UNROLL
106
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
107
+ s_max(i) = overload_max(s_max(i), acc_qk_mn(i, j));
108
+ }
109
+ }
110
+
111
+ for_each(make_seq<red_rank>{}, [&](auto r) {
112
+ CUTLASS_PRAGMA_UNROLL
113
+ for (int j = 1; j < shape<r>(reduction_target_qk); j *= 2) {
114
+ CUTLASS_PRAGMA_UNROLL
115
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
116
+ s_max(i) = overload_max(s_max(i), MaxType{__shfl_xor_sync(uint32_t(-1), overload_to_native(s_max(i)), stride<r>(reduction_target_qk) * j)});
117
+ }
118
+ }
119
+ });
120
+ CUTLASS_PRAGMA_UNROLL
121
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
122
+ MaxType local_max = s_max(i) == static_cast<MaxType>(-INFINITY) ? static_cast<MaxType>(0) : s_max(i);
123
+ MaxType scale = static_cast<MaxType>(params.scale_softmax_log2);
124
+ MaxType scale_max = scale * local_max;
125
+
126
+ CUTLASS_PRAGMA_UNROLL
127
+ for (int j = 0; j < size<1>(acc_qk_mn); j++) {
128
+ acc_qk_mn(i, j) = overload_exp2(scale * acc_qk_mn(i, j) - scale_max);
129
+ }
130
+ }
131
+ CUTLASS_PRAGMA_UNROLL
132
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
133
+ a_sum(i) = SumType{reduce(acc_qk_mn(i, _), cute::plus{})};
134
+ }
135
+ }
136
+
137
+ template<bool kUseFusion=true, class AccQK, class TiledMmaQK, class CountQK, class State, class AccPV, class TiledMmaPV, class ProblemShape>
138
+ CUTLASS_DEVICE auto step_interleave_begin(AccQK& acc_qk, TiledMmaQK const& tiled_mma_qk, CountQK const& count_qk, State& state, AccPV& acc_pv, TiledMmaPV const& tiled_mma_pv, ProblemShape const& problem_shape) {
139
+
140
+ if constexpr (kUseFusion) {
141
+ Fusion{}.before_softmax(acc_qk, count_qk, problem_shape);
142
+ }
143
+
144
+ Tensor acc_qk_mn = make_tensor(acc_qk.data(), layout_acc_mn(tiled_mma_qk, acc_qk.layout()));
145
+ Tensor acc_pv_mn = make_tensor(acc_pv.data(), layout_acc_mn(tiled_mma_pv, acc_pv.layout()));
146
+
147
+ static_assert(size<0>(acc_qk_mn) == size<0>(acc_pv_mn));
148
+ auto reduction_target_qk = reduction_target_n(tiled_mma_qk);
149
+ constexpr int red_rank = decltype(rank(reduction_target_qk))::value;
150
+
151
+ auto& s_max = get<0>(state);
152
+ auto& a_sum = get<1>(state);
153
+
154
+ Tensor s_max_prev = make_fragment_like(s_max);
155
+ CUTLASS_PRAGMA_UNROLL
156
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
157
+ s_max_prev(i) = s_max(i);
158
+ }
159
+ CUTLASS_PRAGMA_UNROLL
160
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
161
+ // Linear reduction is faster here, as well
162
+ CUTLASS_PRAGMA_UNROLL
163
+ for (int j = 0; j < size<1>(acc_qk_mn); j++) {
164
+ s_max(i) = overload_max(s_max(i), acc_qk_mn(i, j));
165
+ }
166
+ }
167
+ // reduce max
168
+ for_each(make_seq<red_rank>{}, [&](auto r) {
169
+ CUTLASS_PRAGMA_UNROLL
170
+ for (int j = 1; j < shape<r>(reduction_target_qk); j *= 2) {
171
+ CUTLASS_PRAGMA_UNROLL
172
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
173
+ s_max(i) = overload_max(s_max(i), __shfl_xor_sync(uint32_t(-1), s_max(i), stride<r>(reduction_target_qk) * j));
174
+ }
175
+ }
176
+ });
177
+ CUTLASS_PRAGMA_UNROLL
178
+ for (int i = 0; i < size<0>(acc_pv_mn); i++) {
179
+ float s_max_cur = s_max(i) == -INFINITY ? 0.0f : s_max(i);
180
+ float scale = ::exp2f((s_max_prev(i) - s_max_cur) * params.scale_softmax_log2);
181
+ a_sum(i) *= scale;
182
+ CUTLASS_PRAGMA_UNROLL
183
+ for (int j = 0; j < size<1>(acc_pv_mn); j++) {
184
+ acc_pv_mn(i, j) *= scale;
185
+ }
186
+ }
187
+ }
188
+
189
+ template<class AccQK_MN, class State>
190
+ CUTLASS_DEVICE auto step_interleave_step(AccQK_MN& acc_qk_mn, State& state) {
191
+
192
+ auto& s_max = get<0>(state);
193
+ auto& a_sum = get<1>(state);
194
+
195
+ CUTLASS_PRAGMA_UNROLL
196
+ for (int j = 0; j < size<0>(acc_qk_mn); j++) {
197
+ float local_max = s_max(j) == -INFINITY ? 0.f : s_max(j);
198
+ float scale_max = params.scale_softmax_log2 * local_max;
199
+
200
+ CUTLASS_PRAGMA_UNROLL
201
+ for (int k = 0; k < size<1>(acc_qk_mn); k++) {
202
+ acc_qk_mn(j, k) = ::exp2f(params.scale_softmax_log2 * acc_qk_mn(j, k) - scale_max);
203
+ a_sum(j) += acc_qk_mn(j, k);
204
+ }
205
+ }
206
+ }
207
+
208
+ template<bool kUseFusion=true, class AccQK, class TiledMmaQK, class CountQK, class State, class AccPV, class TiledMmaPV, class ProblemShape>
209
+ CUTLASS_DEVICE auto step(AccQK& acc_qk, TiledMmaQK const& tiled_mma_qk, CountQK const& count_qk, State& state, AccPV& acc_pv, TiledMmaPV const& tiled_mma_pv, ProblemShape const& problem_shape) {
210
+
211
+ if constexpr (kUseFusion) {
212
+ Fusion{}.before_softmax(acc_qk, count_qk, problem_shape);
213
+ }
214
+
215
+ Tensor acc_qk_mn = make_tensor(acc_qk.data(), layout_acc_mn(tiled_mma_qk, acc_qk.layout()));
216
+ Tensor acc_pv_mn = make_tensor(acc_pv.data(), layout_acc_mn(tiled_mma_pv, acc_pv.layout()));
217
+
218
+ static_assert(size<0>(acc_qk_mn) == size<0>(acc_pv_mn));
219
+ auto reduction_target_qk = reduction_target_n(tiled_mma_qk);
220
+ constexpr int red_rank = decltype(rank(reduction_target_qk))::value;
221
+
222
+ auto& s_max = get<0>(state);
223
+ auto& a_sum = get<1>(state);
224
+
225
+ Tensor s_max_prev = make_fragment_like(s_max);
226
+ CUTLASS_PRAGMA_UNROLL
227
+ for (int i = 0; i < size<0>(acc_qk_mn); i++) {
228
+ s_max_prev(i) = s_max(i);
229
+
230
+ // Linear reduction is faster here, as well
231
+ CUTLASS_PRAGMA_UNROLL
232
+ for (int j = 0; j < size<1>(acc_qk_mn); j++) {
233
+ s_max(i) = overload_max(s_max(i), acc_qk_mn(i, j));
234
+ }
235
+ // reduce max
236
+ for_each(make_seq<red_rank>{}, [&](auto r) {
237
+ CUTLASS_PRAGMA_UNROLL
238
+ for (int j = 1; j < shape<r>(reduction_target_qk); j *= 2) {
239
+ s_max(i) = overload_max(s_max(i), MaxType{__shfl_xor_sync(uint32_t(-1), overload_to_native(s_max(i)), stride<r>(reduction_target_qk) * j)});
240
+ }
241
+ });
242
+
243
+ MaxType local_max = s_max(i) == static_cast<MaxType>(-INFINITY) ? static_cast<MaxType>(0) : s_max(i);
244
+ MaxType scale = static_cast<MaxType>(params.scale_softmax_log2);
245
+ MaxType scale_max = scale * local_max;
246
+
247
+ CUTLASS_PRAGMA_UNROLL
248
+ for (int j = 0; j < size<1>(acc_qk_mn); j++) {
249
+ acc_qk_mn(i, j) = overload_exp2(scale * acc_qk_mn(i, j) - scale_max);
250
+ }
251
+
252
+ MaxType s_max_cur = s_max(i) == static_cast<MaxType>(-INFINITY) ? static_cast<MaxType>(0) : s_max(i);
253
+ SumType scale_pv = overload_exp2((s_max_prev(i) - s_max_cur) * scale);
254
+ a_sum(i) *= scale_pv;
255
+
256
+ using ElementPV = typename AccPV::value_type;
257
+ ElementPV scale_pv_ele = static_cast<ElementPV>(scale_pv);
258
+ CUTLASS_PRAGMA_UNROLL
259
+ for (int j = 0; j < size<1>(acc_pv_mn); j++) {
260
+ acc_pv_mn(i, j) *= scale_pv_ele;
261
+ }
262
+ a_sum(i) += SumType{reduce(acc_qk_mn(i, _), cute::plus{})};
263
+ }
264
+ }
265
+
266
+
267
+ template<class State, class AccPV, class TiledMmaPV>
268
+ CUTLASS_DEVICE auto tail(State& state, AccPV& acc_pv, TiledMmaPV const& tiled_mma_pv) {
269
+ auto& s_max = get<0>(state);
270
+ auto& a_sum = get<1>(state);
271
+
272
+ Tensor acc_pv_mn = make_tensor(acc_pv.data(), layout_acc_mn(tiled_mma_pv, acc_pv.layout()));
273
+
274
+ auto reduction_target = reduction_target_n(tiled_mma_pv);
275
+ constexpr int red_rank = decltype(rank(reduction_target))::value;
276
+ for_each(make_seq<red_rank>{}, [&](auto r) {
277
+ CUTLASS_PRAGMA_UNROLL
278
+ for (int j = 1; j < shape<r>(reduction_target); j *= 2) {
279
+ CUTLASS_PRAGMA_UNROLL
280
+ for (int i = 0; i < size<0>(acc_pv_mn); i++) {
281
+ a_sum(i) = a_sum(i) + __shfl_xor_sync(uint32_t(-1), a_sum(i), stride<r>(reduction_target) * j);
282
+ }
283
+ }
284
+ });
285
+
286
+ Tensor acc_mn = make_tensor(acc_pv.data(), layout_acc_mn(tiled_mma_pv, acc_pv.layout()));
287
+
288
+ Tensor lse = make_fragment_like(a_sum);
289
+ CUTLASS_PRAGMA_UNROLL
290
+ for (int i = 0; i < size<0>(acc_mn); i++) {
291
+ float sum = a_sum(i);
292
+ float inv_sum = (sum == 0.f || sum != sum) ? 1.f : __frcp_rn(sum);
293
+ lse(i) = (sum == 0.f || sum != sum) ? INFINITY : s_max(i) * params.scale_softmax + __logf(sum);
294
+ float scale = params.rp_dropout * inv_sum;
295
+ CUTLASS_PRAGMA_UNROLL
296
+ for (int j = 0; j < size<1>(acc_mn); j++) {
297
+ acc_mn(i, j) *= scale;
298
+ }
299
+ }
300
+
301
+ return lse;
302
+ }
303
+ };
304
+
305
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_tma.hpp ADDED
@@ -0,0 +1,526 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/gemm/collective/collective_builder.hpp"
36
+
37
+ #include "../collective/fmha_common.hpp"
38
+ #include "../collective/fmha_collective_load.hpp"
39
+ #include "../collective/fmha_collective_softmax.hpp"
40
+ #include "../kernel/fmha_options.hpp"
41
+
42
+ namespace cutlass::fmha::collective {
43
+
44
+ using namespace cute;
45
+ using cutlass::fmha::kernel::Tag;
46
+ using cutlass::fmha::kernel::find_option_t;
47
+
48
+ template<
49
+ typename Element_,
50
+ typename ElementAccumulator_,
51
+ typename TileShape_, // BlockQO, BlockKV, BlockHead
52
+ class Fusion,
53
+ class... Options
54
+ >
55
+ struct FmhaMainloopTma {
56
+
57
+ using Element = Element_;
58
+ using ElementAccumulator = ElementAccumulator_;
59
+ using TileShape = TileShape_;
60
+
61
+ // Options
62
+ using kClusterM = find_option_t<Tag::kClusterM, Int<1>, Options...>;
63
+ static constexpr int StageCount = find_option_t<Tag::kStagesKV, Int<4>, Options...>::value;
64
+ static constexpr int StageCountQ = find_option_t<Tag::kStagesQ, Int<1>, Options...>::value;
65
+
66
+ using StagesQ = cutlass::gemm::collective::StageCount<StageCountQ>;
67
+ using Stages = cutlass::gemm::collective::StageCount<StageCount>;
68
+ using ClusterShape = Shape<kClusterM, _1, _1>;
69
+
70
+ // 16B alignment lets us use TMA
71
+ static constexpr int Alignment = 16 / sizeof(Element);
72
+
73
+ using TileShapeQK = TileShape;
74
+ using TileShapePV = decltype(select<0,2,1>(TileShapeQK{}));
75
+
76
+ using LayoutQKV = cute::tuple<int, _1, cute::tuple<int, int>>;
77
+ using LayoutQ = LayoutQKV;
78
+ using LayoutK = LayoutQKV;
79
+ using LayoutV = LayoutQKV;
80
+
81
+ using CollectiveMmaQK = typename cutlass::gemm::collective::CollectiveBuilder<
82
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
83
+ Element, LayoutQ, Alignment,
84
+ Element, LayoutK, Alignment,
85
+ ElementAccumulator,
86
+ TileShapeQK, ClusterShape, Stages,
87
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
88
+
89
+ using CollectiveMmaPV = typename cutlass::gemm::collective::CollectiveBuilder<
90
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
91
+ // the stride for A does not matter since we do not load from smem at all
92
+ Element, LayoutK, Alignment,
93
+ Element, decltype(select<1,0,2>(LayoutV{})), Alignment,
94
+ ElementAccumulator,
95
+ TileShapePV, ClusterShape, Stages,
96
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
97
+
98
+ using TiledMmaQK = typename CollectiveMmaQK::TiledMma;
99
+ using TiledMmaPV = decltype(convert_to_gmma_rs(typename CollectiveMmaPV::TiledMma{}));
100
+
101
+ using SmemLayoutQ = decltype(unstageSmemLayout(typename CollectiveMmaQK::SmemLayoutA{}, Int<StagesQ::value>{}));
102
+ using SmemLayoutK = typename CollectiveMmaQK::SmemLayoutB;
103
+ using SmemLayoutV = typename CollectiveMmaPV::SmemLayoutB;
104
+
105
+ using MainloopPipeline = cutlass::PipelineTmaAsync<Stages::value>;
106
+ using MainloopPipelineQ = cutlass::PipelineTmaAsync<StagesQ::value>;
107
+
108
+ using PipelineState = typename cutlass::PipelineState<MainloopPipeline::Stages>;
109
+ using PipelineStateQ = typename cutlass::PipelineState<MainloopPipelineQ::Stages>;
110
+
111
+ using TileShapeOut = TileShapePV;
112
+ using TiledMmaOut = TiledMmaPV;
113
+ using ElementOut = ElementAccumulator;
114
+
115
+ struct SharedStorage {
116
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutQ>> smem_q;
117
+ union {
118
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutK>> smem_k;
119
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutV>> smem_v;
120
+ };
121
+ };
122
+
123
+ struct Arguments {
124
+ const Element* ptr_Q;
125
+ LayoutQ dQ;
126
+ const Element* ptr_K;
127
+ LayoutK dK;
128
+ const Element* ptr_V;
129
+ LayoutV dV;
130
+ };
131
+
132
+ using TMA_Q = typename CollectiveMmaQK::Params::TMA_A;
133
+ using TMA_K = typename CollectiveMmaQK::Params::TMA_B;
134
+ using TMA_V = typename CollectiveMmaPV::Params::TMA_B;
135
+
136
+ struct Params {
137
+ TMA_Q tma_load_q;
138
+ TMA_K tma_load_k;
139
+ TMA_V tma_load_v;
140
+
141
+ float scale_softmax;
142
+ float scale_softmax_log2;
143
+ float rp_dropout;
144
+ };
145
+
146
+ using LoadQ = cutlass::fmha::collective::CollectiveLoadTma<
147
+ cutlass::fmha::collective::LoadKind::kQ,
148
+ MainloopPipelineQ,
149
+ Element,
150
+ SmemLayoutQ,
151
+ TMA_Q
152
+ >;
153
+
154
+ using LoadK = cutlass::fmha::collective::CollectiveLoadTma<
155
+ cutlass::fmha::collective::LoadKind::kK,
156
+ MainloopPipeline,
157
+ Element,
158
+ SmemLayoutK,
159
+ TMA_K
160
+ >;
161
+
162
+ using LoadV = cutlass::fmha::collective::CollectiveLoadTma<
163
+ cutlass::fmha::collective::LoadKind::kV,
164
+ MainloopPipeline,
165
+ Element,
166
+ SmemLayoutV,
167
+ TMA_V
168
+ >;
169
+
170
+ static_assert(size(typename CollectiveMmaQK::TiledMma{}) == size(typename CollectiveMmaPV::TiledMma{}));
171
+
172
+ static const int MaxThreadsPerBlock = size(typename CollectiveMmaQK::TiledMma{});
173
+
174
+ template<class ProblemShape>
175
+ static bool can_implement(ProblemShape const& problem_size, Arguments const& args) {
176
+ return true
177
+ && (get<4>(problem_size) <= get<2>(TileShape{}))
178
+ && ((get<4>(problem_size) % Alignment) == 0)
179
+ && ((get<2>(problem_size) % Alignment) == 0)
180
+ ;
181
+ }
182
+
183
+ template<class ProblemShape>
184
+ static Params to_underlying_arguments(ProblemShape const& problem_size, Arguments const& args, void* workspace) {
185
+
186
+ auto problem_shape_qk = make_shape(get<2>(problem_size), get<3>(problem_size), get<4>(problem_size), make_shape(get<0>(problem_size), get<1>(problem_size)));
187
+ auto params_qk = CollectiveMmaQK::to_underlying_arguments(problem_shape_qk,
188
+ typename CollectiveMmaQK::Arguments {
189
+ args.ptr_Q, args.dQ,
190
+ args.ptr_K, args.dK,
191
+ }, /*workspace=*/ nullptr);
192
+
193
+ auto problem_shape_pv = select<0,2,1,3>(problem_shape_qk);
194
+ auto params_pv = CollectiveMmaPV::to_underlying_arguments(problem_shape_pv,
195
+ typename CollectiveMmaPV::Arguments {
196
+ args.ptr_K, args.dK, // never used, dummy
197
+ args.ptr_V, select<1,0,2>(args.dV),
198
+ }, /*workspace=*/ nullptr);
199
+
200
+ return Params{
201
+ params_qk.tma_load_a,
202
+ params_qk.tma_load_b,
203
+ params_pv.tma_load_b,
204
+ 1.0f / (float) std::sqrt(get<4>(problem_size)),
205
+ (float) (std::log2(std::exp(1.0)) / std::sqrt(get<4>(problem_size))),
206
+ 1.0f
207
+ };
208
+ }
209
+
210
+ CUTLASS_DEVICE
211
+ static void prefetch_tma_descriptors(Params const& params) {
212
+ cute::prefetch_tma_descriptor(params.tma_load_q.get_tma_descriptor());
213
+ cute::prefetch_tma_descriptor(params.tma_load_k.get_tma_descriptor());
214
+ cute::prefetch_tma_descriptor(params.tma_load_v.get_tma_descriptor());
215
+ }
216
+
217
+ template<class BlkCoord, class ProblemShape>
218
+ CUTLASS_DEVICE auto
219
+ compute(
220
+ int block_rank_in_cluster,
221
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
222
+ MainloopPipeline& pipeline, PipelineState& smem_pipe_read, PipelineState& smem_pipe_write,
223
+ MainloopPipelineQ& pipeline_q, PipelineStateQ& smem_pipe_read_q, PipelineStateQ& smem_pipe_write_q,
224
+ SharedStorage& storage)
225
+ {
226
+ int warp_idx = cutlass::canonical_warp_idx_sync();
227
+ int thread_idx = threadIdx.x;
228
+
229
+ PipelineState smem_pipe_release = smem_pipe_read;
230
+ [[maybe_unused]] PipelineStateQ smem_pipe_release_q = smem_pipe_read_q;
231
+
232
+
233
+ int fusion_tile_count = Fusion{}.get_trip_count(blk_coord, TileShape{}, problem_size);
234
+
235
+ LoadQ load_q{params.tma_load_q, pipeline_q, storage.smem_q};
236
+ auto load_state_q = load_q.init_state(_0{}, problem_size, TileShapeQK{}, blk_coord, 1);
237
+
238
+ LoadK load_k{params.tma_load_k, pipeline, storage.smem_k};
239
+ auto load_state_k = load_k.init_state(block_rank_in_cluster, problem_size, TileShapeQK{}, blk_coord, fusion_tile_count);
240
+
241
+ LoadV load_v{params.tma_load_v, pipeline, storage.smem_v};
242
+ auto load_state_v = load_v.init_state(block_rank_in_cluster, problem_size, TileShapePV{}, blk_coord, fusion_tile_count);
243
+
244
+ // Set predicate for the lowest lane_id in the warp
245
+ int lane_predicate = cute::elect_one_sync();
246
+
247
+ // Issue TmaLoads (Prologue fetches)
248
+ if (warp_idx == 0) {
249
+ auto q_tile_iter = cute::make_coord_iterator(1);
250
+ int q_tile_count = 1;
251
+ load_q.step(q_tile_iter, load_state_q, smem_pipe_write_q, lane_predicate, q_tile_count);
252
+ }
253
+
254
+ // Loop over K elems
255
+ auto k_tile_iter = cute::make_coord_iterator(fusion_tile_count);
256
+
257
+ int k_tile_count_tma = 2 * fusion_tile_count;
258
+
259
+ uint16_t mcast_mask_b = 0;
260
+
261
+ if (warp_idx == 0 && lane_predicate == 1) {
262
+ if constexpr (cute::is_same_v<typename CollectiveMmaQK::GmemTiledCopyB, SM90_TMA_LOAD_MULTICAST>) {
263
+ auto block_layout = Layout<ClusterShape>{}; // (m,n) -> block_id
264
+ for (int m = 0; m < size<0>(block_layout); ++m) {
265
+ mcast_mask_b |= (uint16_t(1) << block_layout(m,_0{},Int<0>{}));
266
+ }
267
+ }
268
+
269
+ CUTLASS_PRAGMA_UNROLL
270
+ for (int i = 0; i < StageCount; i++) {
271
+ if (i % 2 == 0) {
272
+ load_k.template step<false>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
273
+ } else {
274
+ load_v.template step<true>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
275
+ }
276
+ }
277
+ }
278
+
279
+ TiledMmaQK tiled_mma_qk;
280
+ auto thr_mma_qk = tiled_mma_qk.get_thread_slice(thread_idx);
281
+
282
+ // Mainloop setup QK
283
+ Tensor sQ = make_tensor(make_smem_ptr(storage.smem_q.data()), SmemLayoutQ{});
284
+ Tensor sK = make_tensor(make_smem_ptr(storage.smem_k.data()), SmemLayoutK{});
285
+
286
+ Tensor tSsQ = thr_mma_qk.partition_A(sQ); // (MMA,MMA_M,MMA_K,PIPE)
287
+ Tensor tSsK = thr_mma_qk.partition_B(sK); // (MMA,MMA_N,MMA_K,PIPE)
288
+ Tensor tSrQ = thr_mma_qk.make_fragment_A(tSsQ); // (MMA,MMA_N,MMA_K,PIPE)
289
+ Tensor tSrK = thr_mma_qk.make_fragment_B(tSsK); // (MMA,MMA_M,MMA_N,PIPE)
290
+
291
+ // Prepare: MMA PV
292
+ TiledMmaPV tiled_mma_pv;
293
+ auto thr_mma_pv = tiled_mma_pv.get_thread_slice(thread_idx);
294
+
295
+ // Mainloop setup PV
296
+ Tensor sV = make_tensor(make_smem_ptr(storage.smem_v.data()), SmemLayoutV{});
297
+
298
+ Tensor tOsV = thr_mma_pv.partition_B(sV); // (MMA,MMA_N,MMA_K,PIPE)
299
+ Tensor tOrV = thr_mma_pv.make_fragment_B(tOsV); // (MMA,MMA_M,MMA_N,PIPE)
300
+
301
+ int k_tile_count = Fusion{}.get_unmasked_trip_count(blk_coord, TileShape{}, problem_size);
302
+
303
+ pipeline_q.consumer_wait(smem_pipe_read_q);
304
+
305
+ // mapping into QK accumulator
306
+ Tensor cP = make_identity_tensor(take<0,2>(TileShapeQK{}));
307
+ Tensor tPcP = thr_mma_qk.partition_C(cP);
308
+ int m_block = get<0>(blk_coord);
309
+ tPcP.data() = tPcP.data() + E<0>{} * m_block * get<0>(TileShapeQK{});
310
+
311
+ // Allocate PV acc
312
+ Tensor acc_pv = partition_fragment_C(tiled_mma_pv, take<0, 2>(TileShapePV{}));
313
+
314
+ cutlass::fmha::collective::CollectiveSoftmax<ElementAccumulator, Fusion, decltype(params)> softmax{params};
315
+ auto softmax_state = softmax.init(acc_pv, tiled_mma_pv);
316
+
317
+ if (true)
318
+ {
319
+ --k_tile_count;
320
+ // Allocate QK acc
321
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
322
+
323
+ pipeline.consumer_wait(smem_pipe_read);
324
+
325
+ // MMA QK
326
+ warpgroup_fence_operand(acc_qk);
327
+ warpgroup_arrive();
328
+
329
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,_0{}), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
330
+ warpgroup_commit_batch();
331
+
332
+ ++smem_pipe_read;
333
+
334
+ // Wait for the pipeline MMAs to drain
335
+ warpgroup_wait<0>();
336
+ warpgroup_fence_operand(acc_qk);
337
+
338
+ softmax.step(acc_qk, tiled_mma_qk, tPcP, softmax_state, problem_size);
339
+
340
+ Tensor acc_qk_fixed = make_fragment_like<Element>(convert_c_layout_to_a_layout(acc_qk.layout(), shape<1>(typename decltype(tiled_mma_pv)::LayoutA_TV{})));
341
+
342
+ Tensor acc_qk_input = make_tensor(acc_qk_fixed.data(), acc_qk.layout());
343
+ CUTLASS_PRAGMA_UNROLL
344
+ for (int i = 0; i < size(acc_qk); i++) {
345
+ acc_qk_input(i) = static_cast<Element>(acc_qk(i));
346
+ }
347
+
348
+ pipeline.consumer_wait(smem_pipe_read);
349
+
350
+ // MMA PV
351
+ warpgroup_fence_operand(acc_pv);
352
+ warpgroup_fence_operand(acc_qk_fixed);
353
+ warpgroup_arrive();
354
+
355
+ gemm_zero_acc(tiled_mma_pv, acc_qk_fixed, tOrV(_,_,_,smem_pipe_read.index()), acc_pv);
356
+ warpgroup_commit_batch();
357
+
358
+ //
359
+ // Advance the pipe
360
+ //
361
+
362
+ // Advance consumer pipeline
363
+ ++smem_pipe_read;
364
+
365
+ pipeline.consumer_release(smem_pipe_release);
366
+ ++smem_pipe_release;
367
+
368
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
369
+ }
370
+
371
+ CUTLASS_PRAGMA_NO_UNROLL
372
+ for ( ; k_tile_count > 0; --k_tile_count)
373
+ {
374
+ // Allocate QK acc
375
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
376
+
377
+ pipeline.consumer_wait(smem_pipe_read);
378
+
379
+ // MMA QK
380
+ warpgroup_fence_operand(acc_qk);
381
+ warpgroup_arrive();
382
+
383
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,_0{}), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
384
+ warpgroup_commit_batch();
385
+
386
+ ++smem_pipe_read;
387
+
388
+ if (warp_idx == 0) {
389
+ load_k.template step<false>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
390
+ }
391
+
392
+ // Wait for the pipeline MMAs to drain
393
+ warpgroup_wait<0>();
394
+ warpgroup_fence_operand(acc_qk);
395
+ warpgroup_fence_operand(acc_pv);
396
+
397
+ softmax.template step_interleave_begin<false>(acc_qk, tiled_mma_qk, tPcP, softmax_state, acc_pv, tiled_mma_pv, problem_size);
398
+
399
+ pipeline.consumer_release(smem_pipe_release);
400
+
401
+ ++smem_pipe_release;
402
+
403
+ pipeline.consumer_wait(smem_pipe_read);
404
+
405
+ // MMA PV
406
+ auto layout_qk_input = convert_c_layout_to_a_layout(acc_qk.layout(), shape<1>(typename decltype(tiled_mma_pv)::LayoutA_TV{}));
407
+
408
+ Tensor acc_qk_input = make_tensor(acc_qk.data(), layout_qk_input);
409
+
410
+ static_assert(decltype(size<1>(layout_qk_input) == _1{})::value);
411
+ CUTLASS_PRAGMA_UNROLL
412
+ for (int i = 0; i < size<2>(tOrV); i++) {
413
+ Tensor acc_qk_element = make_fragment_like<Element>(layout_qk_input(_, _0{}, _0{}));
414
+ Tensor acc_qk_element_mk = tensor_op_mk_v(tiled_mma_pv, acc_qk_element);
415
+ Tensor acc_qk_input_mk = tensor_op_mk_v(tiled_mma_pv, acc_qk_input(_, _0{}, i));
416
+ softmax.step_interleave_step(acc_qk_input_mk, softmax_state);
417
+ CUTLASS_PRAGMA_UNROLL
418
+ for (int j = 0; j < size(acc_qk_element_mk); j++) {
419
+ acc_qk_element_mk(j) = static_cast<Element>(acc_qk_input_mk(j));
420
+ }
421
+ warpgroup_arrive();
422
+ CUTLASS_PRAGMA_UNROLL
423
+ for (int j = 0; j < size<1>(tOrV); j++) {
424
+ cute::gemm(tiled_mma_pv, acc_qk_element, tOrV(_,j,i,smem_pipe_read.index()), acc_pv(_,_0{},j));
425
+ }
426
+ }
427
+ warpgroup_commit_batch();
428
+
429
+ // Wait for the pipeline MMAs to drain
430
+ pipeline.consumer_release(smem_pipe_release);
431
+ ++smem_pipe_release;
432
+
433
+ ++smem_pipe_read;
434
+
435
+ if (warp_idx == 0) {
436
+ load_v.template step<true>(k_tile_iter, load_state_v, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
437
+ }
438
+
439
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
440
+ }
441
+
442
+ k_tile_count += Fusion{}.get_masked_trip_count(blk_coord, TileShape{}, problem_size);
443
+
444
+ CUTLASS_PRAGMA_NO_UNROLL
445
+ for ( ; k_tile_count > 0; --k_tile_count)
446
+ {
447
+ // Allocate QK acc
448
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
449
+
450
+ pipeline.consumer_wait(smem_pipe_read);
451
+
452
+ // MMA QK
453
+ warpgroup_fence_operand(acc_qk);
454
+ warpgroup_arrive();
455
+
456
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,_0{}), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
457
+ warpgroup_commit_batch();
458
+
459
+ ++smem_pipe_read;
460
+
461
+ if (warp_idx == 0) {
462
+ load_k.template step<false>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
463
+ }
464
+
465
+ // Wait for the pipeline MMAs to drain
466
+ warpgroup_wait<0>();
467
+ warpgroup_fence_operand(acc_qk);
468
+ warpgroup_fence_operand(acc_pv);
469
+
470
+ softmax.step_interleave_begin(acc_qk, tiled_mma_qk, tPcP, softmax_state, acc_pv, tiled_mma_pv, problem_size);
471
+
472
+ pipeline.consumer_release(smem_pipe_release);
473
+
474
+ ++smem_pipe_release;
475
+
476
+ pipeline.consumer_wait(smem_pipe_read);
477
+
478
+ // MMA PV
479
+ auto layout_qk_input = convert_c_layout_to_a_layout(acc_qk.layout(), shape<1>(typename decltype(tiled_mma_pv)::LayoutA_TV{}));
480
+
481
+ Tensor acc_qk_input = make_tensor(acc_qk.data(), layout_qk_input);
482
+
483
+ static_assert(decltype(size<1>(layout_qk_input) == _1{})::value);
484
+ CUTLASS_PRAGMA_UNROLL
485
+ for (int i = 0; i < size<2>(tOrV); i++) {
486
+ Tensor acc_qk_element = make_fragment_like<Element>(layout_qk_input(_, _0{}, _0{}));
487
+ Tensor acc_qk_element_mk = tensor_op_mk_v(tiled_mma_pv, acc_qk_element);
488
+ Tensor acc_qk_input_mk = tensor_op_mk_v(tiled_mma_pv, acc_qk_input(_, _0{}, i));
489
+ softmax.step_interleave_step(acc_qk_input_mk, softmax_state);
490
+ CUTLASS_PRAGMA_UNROLL
491
+ for (int j = 0; j < size(acc_qk_element_mk); j++) {
492
+ acc_qk_element_mk(j) = static_cast<Element>(acc_qk_input_mk(j));
493
+ }
494
+ warpgroup_arrive();
495
+ CUTLASS_PRAGMA_UNROLL
496
+ for (int j = 0; j < size<1>(tOrV); j++) {
497
+ cute::gemm(tiled_mma_pv, acc_qk_element, tOrV(_,j,i,smem_pipe_read.index()), acc_pv(_,_0{},j));
498
+ }
499
+ }
500
+ warpgroup_commit_batch();
501
+
502
+ // Wait for the pipeline MMAs to drain
503
+ pipeline.consumer_release(smem_pipe_release);
504
+ ++smem_pipe_release;
505
+
506
+ ++smem_pipe_read;
507
+
508
+ if (warp_idx == 0) {
509
+ load_v.template step<true>(k_tile_iter, load_state_v, smem_pipe_write, lane_predicate, k_tile_count_tma, mcast_mask_b);
510
+ }
511
+
512
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
513
+ }
514
+
515
+ // Wait for the pipeline MMAs to drain
516
+ warpgroup_wait<0>();
517
+ warpgroup_fence_operand(acc_pv);
518
+
519
+ Tensor lse = softmax.tail(softmax_state, acc_pv, tiled_mma_pv);
520
+
521
+ return make_tuple(acc_pv, lse);
522
+ }
523
+ };
524
+
525
+ } // namespace cutlass::fmha::collective
526
+
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_collective_tma_warpspecialized.hpp ADDED
@@ -0,0 +1,560 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/gemm/collective/collective_builder.hpp"
36
+
37
+ #include "../collective/fmha_common.hpp"
38
+ #include "../collective/fmha_collective_load.hpp"
39
+ #include "../collective/fmha_collective_softmax.hpp"
40
+ #include "../kernel/fmha_options.hpp"
41
+
42
+ namespace cutlass::fmha::collective {
43
+
44
+ using namespace cute;
45
+ using cutlass::fmha::kernel::Tag;
46
+ using cutlass::fmha::kernel::find_option_t;
47
+
48
+ template<
49
+ class Element_,
50
+ class ElementAccumulatorQK_,
51
+ class ElementAccumulatorPV_,
52
+ class TileShape_, // SeqQ, SeqKV, Head
53
+ class LayoutQ_, class LayoutK_, class LayoutV_, // SeqX, Head, (Batches)
54
+ class Fusion,
55
+ class... Options
56
+ >
57
+ struct FmhaMainloopTmaWarpSpecialized {
58
+
59
+ using Element = Element_;
60
+ using ElementAccumulatorQK = ElementAccumulatorQK_;
61
+ using ElementAccumulatorPV = ElementAccumulatorPV_;
62
+ using TileShape = TileShape_;
63
+
64
+ using LayoutQ = LayoutQ_;
65
+ using LayoutK = LayoutK_;
66
+ using LayoutV = LayoutV_;
67
+
68
+ // Options
69
+ static constexpr bool kIsPersistent = find_option_t<Tag::kIsPersistent, false_type, Options...>::value;
70
+ static constexpr bool kIsMainloopLocked = find_option_t<Tag::kIsMainloopLocked, false_type, Options...>::value;
71
+
72
+ static constexpr int NumLoadWarpGroups = 1;
73
+ static constexpr int NumMmaWarpGroups = find_option_t<Tag::kNumMmaWarpGroups, Int<2>, Options...>::value;
74
+ static constexpr int StageCount = find_option_t<Tag::kStagesKV, Int<5>, Options...>::value;
75
+ static constexpr int StageCountQ = find_option_t<Tag::kStagesQ, Int<NumMmaWarpGroups>, Options...>::value;
76
+
77
+ static const int kOuterLoads = 1;
78
+ using StagesQ = cutlass::gemm::collective::StageCount<StageCountQ>;
79
+ using Stages = cutlass::gemm::collective::StageCount<StageCount>;
80
+ using ClusterShape = Shape<_1, _1, _1>;
81
+ static_assert(StagesQ::value >= NumMmaWarpGroups);
82
+ static_assert(Stages::value >= 2);
83
+
84
+ // 16B alignment lets us use TMA
85
+ static constexpr int Alignment = 16 / sizeof(Element);
86
+
87
+ using TileShapeQK = Shape<
88
+ decltype(tuple_element_t<0, TileShape>{} / Int<NumMmaWarpGroups>{}),
89
+ tuple_element_t<1, TileShape>,
90
+ tuple_element_t<2, TileShape>>;
91
+
92
+ using TileShapePV = decltype(select<0,2,1>(TileShapeQK{}));
93
+
94
+ using CollectiveMmaQK = typename cutlass::gemm::collective::CollectiveBuilder<
95
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
96
+ Element, LayoutQ, Alignment,
97
+ Element, LayoutK, Alignment,
98
+ ElementAccumulatorQK,
99
+ TileShapeQK, ClusterShape, Stages,
100
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
101
+
102
+ using CollectiveMmaPV = typename cutlass::gemm::collective::CollectiveBuilder<
103
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
104
+ // the stride for A does not matter since we do not load from smem at all
105
+ Element, LayoutK, Alignment,
106
+ Element, decltype(select<1,0,2>(LayoutV{})), Alignment,
107
+ ElementAccumulatorPV,
108
+ TileShapePV, ClusterShape, Stages,
109
+ cutlass::gemm::KernelTmaWarpSpecialized>::CollectiveOp;
110
+
111
+ using TiledMmaQK = typename CollectiveMmaQK::TiledMma;
112
+ using TiledMmaPV = decltype(convert_to_gmma_rs(typename CollectiveMmaPV::TiledMma{}));
113
+
114
+ using SmemLayoutQ = decltype(unstageSmemLayout(typename CollectiveMmaQK::SmemLayoutA{}, Int<StagesQ::value>{}));
115
+ using SmemLayoutK = typename CollectiveMmaQK::SmemLayoutB;
116
+ using SmemLayoutV = typename CollectiveMmaPV::SmemLayoutB;
117
+
118
+ using MainloopPipeline = cutlass::PipelineTmaAsync<Stages::value>;
119
+ using MainloopPipelineQ = cutlass::PipelineTmaAsync<StagesQ::value>;
120
+
121
+ using PipelineState = typename cutlass::PipelineState<MainloopPipeline::Stages>;
122
+ using PipelineStateQ = typename cutlass::PipelineState<MainloopPipelineQ::Stages>;
123
+
124
+ static constexpr int kInnerLoadBytes = size(SmemLayoutK{}(_,_,_0{})) * sizeof(Element);
125
+ static constexpr int kOuterLoadBytes = size(SmemLayoutQ{}(_,_,_0{})) * sizeof(Element);
126
+
127
+ using TileShapeOut = TileShapePV;
128
+ using TiledMmaOut = TiledMmaPV;
129
+ using ElementOut = ElementAccumulatorPV;
130
+
131
+ struct SharedStorage {
132
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutQ>> smem_q;
133
+ union {
134
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutK>> smem_k;
135
+ cute::array_aligned<Element, cute::cosize_v<SmemLayoutV>> smem_v;
136
+ };
137
+ };
138
+
139
+ struct Arguments {
140
+ const Element* ptr_Q;
141
+ LayoutQ dQ;
142
+ const Element* ptr_K;
143
+ LayoutK dK;
144
+ const Element* ptr_V;
145
+ LayoutV dV;
146
+ };
147
+
148
+ using TMA_Q = typename CollectiveMmaQK::Params::TMA_A;
149
+ using TMA_K = typename CollectiveMmaQK::Params::TMA_B;
150
+ using TMA_V = typename CollectiveMmaPV::Params::TMA_B;
151
+
152
+ struct Params {
153
+ TMA_Q tma_load_q;
154
+ TMA_K tma_load_k;
155
+ TMA_V tma_load_v;
156
+
157
+ float scale_softmax;
158
+ float scale_softmax_log2;
159
+ float rp_dropout;
160
+ };
161
+
162
+ using LoadQ = cutlass::fmha::collective::CollectiveLoadTma<
163
+ cutlass::fmha::collective::LoadKind::kQ,
164
+ MainloopPipelineQ,
165
+ Element,
166
+ SmemLayoutQ,
167
+ TMA_Q
168
+ >;
169
+
170
+ using LoadK = cutlass::fmha::collective::CollectiveLoadTma<
171
+ cutlass::fmha::collective::LoadKind::kK,
172
+ MainloopPipeline,
173
+ Element,
174
+ SmemLayoutK,
175
+ TMA_K
176
+ >;
177
+
178
+ using LoadV = cutlass::fmha::collective::CollectiveLoadTma<
179
+ cutlass::fmha::collective::LoadKind::kV,
180
+ MainloopPipeline,
181
+ Element,
182
+ SmemLayoutV,
183
+ TMA_V
184
+ >;
185
+
186
+ static_assert(size(typename CollectiveMmaQK::TiledMma{}) == size(typename CollectiveMmaPV::TiledMma{}));
187
+
188
+ template<class ProblemShape>
189
+ static bool can_implement(ProblemShape const& problem_size, Arguments const& args) {
190
+ return true
191
+ && (get<4>(problem_size) <= get<2>(TileShape{}))
192
+ && ((get<4>(problem_size) % Alignment) == 0)
193
+ && ((get<2>(problem_size) % Alignment) == 0)
194
+ ;
195
+ }
196
+
197
+ template<class ProblemShape>
198
+ static Params to_underlying_arguments(ProblemShape const& problem_size, Arguments const& args, void* workspace) {
199
+
200
+ auto problem_shape_qk = make_shape(get<2>(problem_size), get<3>(problem_size), get<4>(problem_size), make_shape(get<0>(problem_size), get<1>(problem_size)));
201
+ auto params_qk = CollectiveMmaQK::to_underlying_arguments(problem_shape_qk,
202
+ typename CollectiveMmaQK::Arguments {
203
+ args.ptr_Q, args.dQ,
204
+ args.ptr_K, args.dK,
205
+ }, /*workspace=*/ nullptr);
206
+
207
+ auto problem_shape_pv = select<0,2,1,3>(problem_shape_qk);
208
+ auto params_pv = CollectiveMmaPV::to_underlying_arguments(problem_shape_pv,
209
+ typename CollectiveMmaPV::Arguments {
210
+ args.ptr_K, args.dK, // never used, dummy
211
+ args.ptr_V, select<1,0,2>(args.dV),
212
+ }, /*workspace=*/ nullptr);
213
+
214
+ return Params{
215
+ params_qk.tma_load_a,
216
+ params_qk.tma_load_b,
217
+ params_pv.tma_load_b,
218
+ 1.0f / (float) std::sqrt(get<4>(problem_size)),
219
+ (float) (std::log2(std::exp(1.0)) / std::sqrt(get<4>(problem_size))),
220
+ 1.0f
221
+ };
222
+ }
223
+
224
+ CUTLASS_DEVICE
225
+ static void prefetch_tma_descriptors(Params const& params) {
226
+ cute::prefetch_tma_descriptor(params.tma_load_q.get_tma_descriptor());
227
+ cute::prefetch_tma_descriptor(params.tma_load_k.get_tma_descriptor());
228
+ cute::prefetch_tma_descriptor(params.tma_load_v.get_tma_descriptor());
229
+ }
230
+
231
+ template<bool kLoadQ, class BlkCoord, class ProblemShape, class LoadWarpBarrier>
232
+ CUTLASS_DEVICE void
233
+ load_kv_maybe_q(
234
+ int block_rank_in_cluster,
235
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
236
+ MainloopPipeline& pipeline, PipelineState& smem_pipe_write,
237
+ MainloopPipelineQ& pipeline_q, PipelineStateQ& smem_pipe_write_q,
238
+ SharedStorage& storage,
239
+ LoadWarpBarrier& load_warp_barrier, bool do_barrier)
240
+ {
241
+ int fusion_tile_count = Fusion{}.get_trip_count(blk_coord, TileShape{}, problem_size);
242
+
243
+ int lane_predicate = cute::elect_one_sync();
244
+
245
+ uint16_t mcast_mask_b = 0;
246
+
247
+ if (lane_predicate == 1) {
248
+ if constexpr (cute::is_same_v<typename CollectiveMmaQK::GmemTiledCopyB, SM90_TMA_LOAD_MULTICAST>) {
249
+ auto block_layout = Layout<ClusterShape>{}; // (m,n) -> block_id
250
+ for (int m = 0; m < size<0>(block_layout); ++m) {
251
+ mcast_mask_b |= (uint16_t(1) << block_layout(m,_0{},Int<0>{}));
252
+ }
253
+ }
254
+ }
255
+
256
+ auto q_tile_iter = cute::make_coord_iterator(Int<NumMmaWarpGroups>{});
257
+ [[maybe_unused]] int q_tile_count = NumMmaWarpGroups;
258
+
259
+ auto k_tile_iter = cute::make_coord_iterator(fusion_tile_count);
260
+ int k_tile_count = 2 * fusion_tile_count;
261
+
262
+ LoadQ load_q{params.tma_load_q, pipeline_q, storage.smem_q};
263
+ auto load_state_q = load_q.init_state(_0{}, problem_size, TileShapeQK{}, blk_coord, NumMmaWarpGroups);
264
+
265
+ LoadK load_k{params.tma_load_k, pipeline, storage.smem_k};
266
+ auto load_state_k = load_k.init_state(block_rank_in_cluster, problem_size, TileShapeQK{}, blk_coord, fusion_tile_count);
267
+
268
+ LoadV load_v{params.tma_load_v, pipeline, storage.smem_v};
269
+ auto load_state_v = load_v.init_state(block_rank_in_cluster, problem_size, TileShapePV{}, blk_coord, fusion_tile_count);
270
+
271
+ if constexpr (kLoadQ) {
272
+ load_q.step(q_tile_iter, load_state_q, smem_pipe_write_q, lane_predicate, q_tile_count);
273
+ }
274
+
275
+ load_k.template step<false>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count, mcast_mask_b);
276
+
277
+ if constexpr (kLoadQ) {
278
+ load_q.step(q_tile_iter, load_state_q, smem_pipe_write_q, lane_predicate, q_tile_count);
279
+ }
280
+
281
+ if constexpr (! kLoadQ) {
282
+ if (do_barrier) {
283
+ load_warp_barrier.arrive();
284
+ load_warp_barrier.wait(/*phase=*/ 0);
285
+ do_barrier = false;
286
+ }
287
+ }
288
+
289
+ load_v.template step<true>(k_tile_iter, load_state_v, smem_pipe_write, lane_predicate, k_tile_count, mcast_mask_b);
290
+
291
+ if constexpr (kLoadQ) {
292
+ while (q_tile_count > 0) {
293
+ load_q.step(q_tile_iter, load_state_q, smem_pipe_write_q, lane_predicate, q_tile_count);
294
+ }
295
+ }
296
+
297
+ CUTLASS_PRAGMA_NO_UNROLL
298
+ while (k_tile_count > 0) {
299
+ load_k.template step<false>(k_tile_iter, load_state_k, smem_pipe_write, lane_predicate, k_tile_count, mcast_mask_b);
300
+ load_v.template step<true>(k_tile_iter, load_state_v, smem_pipe_write, lane_predicate, k_tile_count, mcast_mask_b);
301
+ }
302
+ }
303
+
304
+ template<class BlkCoord, class ProblemShape, class LoadWarpBarrier>
305
+ CUTLASS_DEVICE void
306
+ load_maybe_q(
307
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
308
+ MainloopPipelineQ& pipeline_q, PipelineStateQ& smem_pipe_write_q,
309
+ SharedStorage& storage,
310
+ LoadWarpBarrier& load_warp_barrier, bool do_barrier)
311
+ {
312
+ int lane_predicate = cute::elect_one_sync();
313
+
314
+ LoadQ load_q{params.tma_load_q, pipeline_q, storage.smem_q};
315
+ auto load_state_q = load_q.init_state(_0{}, problem_size, TileShapeQK{}, blk_coord, NumMmaWarpGroups);
316
+
317
+ auto q_tile_iter = cute::make_coord_iterator(Int<NumMmaWarpGroups>{});
318
+
319
+ CUTLASS_PRAGMA_UNROLL
320
+ for (int q_tile_count = 0; q_tile_count < NumMmaWarpGroups; q_tile_count++) {
321
+ int count = 1;
322
+ load_q.step(q_tile_iter, load_state_q, smem_pipe_write_q, lane_predicate, count);
323
+ if (q_tile_count == 0 && do_barrier) {
324
+ load_warp_barrier.arrive();
325
+ load_warp_barrier.wait(/*phase=*/ 0);
326
+ do_barrier = false;
327
+ }
328
+ }
329
+ }
330
+
331
+ template<class BlkCoord, class ProblemShape, class MainloopPipelineReducer, class PipelineStateReducer>
332
+ CUTLASS_DEVICE void
333
+ reduce(
334
+ BlkCoord const& blk_coord, Params const& params, ProblemShape const& problem_size,
335
+ MainloopPipelineReducer& pipeline_reducer, PipelineStateReducer& smem_pipe_write_reducer,
336
+ SharedStorage& storage)
337
+ { /* no-op */ }
338
+
339
+ template<class BlkCoord, class ProblemShape, class MainloopPipelineReducer, class PipelineStateReducer, class MathWgOrderBarrier>
340
+ CUTLASS_DEVICE auto
341
+ compute(
342
+ BlkCoord const& blk_coord, BlkCoord const& wg_coord,
343
+ Params const& params, ProblemShape const& problem_size,
344
+ MainloopPipeline& pipeline, PipelineState& smem_pipe_read,
345
+ MainloopPipelineQ& pipeline_q, PipelineStateQ& smem_pipe_read_q,
346
+ MainloopPipelineReducer&, PipelineStateReducer&,
347
+ SharedStorage& storage,
348
+ MathWgOrderBarrier& math_wg_order_barrier)
349
+ {
350
+ int thread_idx = int(threadIdx.x);
351
+
352
+ PipelineState smem_pipe_release = smem_pipe_read;
353
+ PipelineStateQ smem_pipe_release_q = smem_pipe_read_q;
354
+
355
+ TiledMmaQK tiled_mma_qk;
356
+ auto thr_mma_qk = tiled_mma_qk.get_thread_slice(thread_idx);
357
+
358
+ // Mainloop setup QK
359
+ Tensor sQ = make_tensor(make_smem_ptr(storage.smem_q.data()), SmemLayoutQ{});
360
+ Tensor sK = make_tensor(make_smem_ptr(storage.smem_k.data()), SmemLayoutK{});
361
+
362
+ Tensor tSsQ = thr_mma_qk.partition_A(sQ); // (MMA,MMA_M,MMA_K,PIPE)
363
+ Tensor tSsK = thr_mma_qk.partition_B(sK); // (MMA,MMA_N,MMA_K,PIPE)
364
+ Tensor tSrQ = thr_mma_qk.make_fragment_A(tSsQ); // (MMA,MMA_N,MMA_K,PIPE)
365
+ Tensor tSrK = thr_mma_qk.make_fragment_B(tSsK); // (MMA,MMA_M,MMA_N,PIPE)
366
+
367
+ // Prepare: MMA PV
368
+ TiledMmaPV tiled_mma_pv;
369
+ auto thr_mma_pv = tiled_mma_pv.get_thread_slice(thread_idx);
370
+
371
+ // Mainloop setup PV
372
+ Tensor sV = make_tensor(make_smem_ptr(storage.smem_v.data()), SmemLayoutV{});
373
+
374
+ Tensor tOsV = thr_mma_pv.partition_B(sV); // (MMA,MMA_N,MMA_K,PIPE)
375
+ Tensor tOrV = thr_mma_pv.make_fragment_B(tOsV); // (MMA,MMA_M,MMA_N,PIPE)
376
+
377
+ int k_tile_count = Fusion{}.get_unmasked_trip_count(blk_coord, TileShape{}, problem_size);
378
+
379
+ pipeline_q.consumer_wait(smem_pipe_read_q);
380
+
381
+ // mapping into QK accumulator
382
+ Tensor cP = make_identity_tensor(take<0,2>(TileShapeQK{}));
383
+ Tensor tPcP = thr_mma_qk.partition_C(cP);
384
+ int m_block = get<0>(wg_coord);
385
+ tPcP.data() = tPcP.data() + E<0>{} * m_block * get<0>(TileShapeQK{});
386
+
387
+ // Allocate PV acc
388
+ Tensor acc_pv = partition_fragment_C(tiled_mma_pv, take<0, 2>(TileShapePV{}));
389
+
390
+ cutlass::fmha::collective::CollectiveSoftmax<ElementAccumulatorQK, Fusion, decltype(params)> softmax{params};
391
+ auto softmax_state = softmax.init(acc_pv, tiled_mma_pv);
392
+
393
+ if (true)
394
+ {
395
+ --k_tile_count;
396
+ // Allocate QK acc
397
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
398
+
399
+ pipeline.consumer_wait(smem_pipe_read);
400
+ math_wg_order_barrier.wait();
401
+
402
+ // MMA QK
403
+ warpgroup_fence_operand(acc_qk);
404
+ warpgroup_arrive();
405
+
406
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,smem_pipe_read_q.index()), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
407
+ warpgroup_commit_batch();
408
+ math_wg_order_barrier.arrive();
409
+
410
+ ++smem_pipe_read;
411
+
412
+ // Wait for the pipeline MMAs to drain
413
+ warpgroup_wait<0>();
414
+ warpgroup_fence_operand(acc_qk);
415
+
416
+ softmax.step(acc_qk, tiled_mma_qk, tPcP, softmax_state, problem_size);
417
+
418
+ Tensor acc_qk_fixed = make_acc_into_op<Element>(acc_qk, typename TiledMmaPV::LayoutA_TV{});
419
+
420
+ pipeline.consumer_wait(smem_pipe_read);
421
+
422
+ // MMA PV
423
+ warpgroup_fence_operand(acc_pv);
424
+ warpgroup_fence_operand(acc_qk_fixed);
425
+ warpgroup_arrive();
426
+
427
+ gemm_zero_acc(tiled_mma_pv, acc_qk_fixed, tOrV(_,_,_,smem_pipe_read.index()), acc_pv);
428
+ warpgroup_commit_batch();
429
+
430
+ pipeline.consumer_release(smem_pipe_release);
431
+ ++smem_pipe_release;
432
+
433
+ // Advance consumer pipeline
434
+ ++smem_pipe_read;
435
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
436
+ }
437
+
438
+ CUTLASS_PRAGMA_NO_UNROLL
439
+ while (k_tile_count > 0)
440
+ {
441
+ --k_tile_count;
442
+
443
+ // Allocate QK acc
444
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
445
+
446
+ pipeline.consumer_wait(smem_pipe_read);
447
+
448
+ // MMA QK
449
+ warpgroup_fence_operand(acc_qk);
450
+ warpgroup_arrive();
451
+
452
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,smem_pipe_read_q.index()), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
453
+ warpgroup_commit_batch();
454
+
455
+ ++smem_pipe_read;
456
+ auto tok = pipeline.consumer_try_wait(smem_pipe_read);
457
+
458
+ // Wait for the pipeline MMAs to drain
459
+ warpgroup_wait<0>();
460
+ warpgroup_fence_operand(acc_qk);
461
+ warpgroup_fence_operand(acc_pv);
462
+
463
+ if constexpr (kIsMainloopLocked) math_wg_order_barrier.wait();
464
+ softmax.template step<false>(acc_qk, tiled_mma_qk, tPcP, softmax_state, acc_pv, tiled_mma_pv, problem_size);
465
+ if constexpr (kIsMainloopLocked) math_wg_order_barrier.arrive();
466
+
467
+ Tensor acc_qk_fixed = make_acc_into_op<Element>(acc_qk, typename TiledMmaPV::LayoutA_TV{});
468
+
469
+ pipeline.consumer_wait(smem_pipe_read, tok);
470
+
471
+ // MMA PV
472
+ warpgroup_fence_operand(acc_pv);
473
+ warpgroup_fence_operand(acc_qk_fixed);
474
+ warpgroup_arrive();
475
+
476
+ cute::gemm(tiled_mma_pv, acc_qk_fixed, tOrV(_,_,_,smem_pipe_read.index()), acc_pv);
477
+ warpgroup_commit_batch();
478
+
479
+ pipeline.consumer_release(smem_pipe_release);
480
+ ++smem_pipe_release;
481
+
482
+ pipeline.consumer_release(smem_pipe_release);
483
+ ++smem_pipe_release;
484
+
485
+ ++smem_pipe_read;
486
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
487
+ }
488
+
489
+ k_tile_count += Fusion{}.get_masked_trip_count(blk_coord, TileShape{}, problem_size);
490
+
491
+ CUTLASS_PRAGMA_NO_UNROLL
492
+ while (k_tile_count > 0)
493
+ {
494
+ --k_tile_count;
495
+
496
+ // Allocate QK acc
497
+ Tensor acc_qk = partition_fragment_C(tiled_mma_qk, take<0, 2>(TileShapeQK{}));
498
+
499
+ pipeline.consumer_wait(smem_pipe_read);
500
+
501
+ // MMA QK
502
+ warpgroup_fence_operand(acc_qk);
503
+ warpgroup_arrive();
504
+
505
+ gemm_zero_acc(tiled_mma_qk, tSrQ(_,_,_,smem_pipe_read_q.index()), tSrK(_,_,_,smem_pipe_read.index()), acc_qk);
506
+ warpgroup_commit_batch();
507
+
508
+ ++smem_pipe_read;
509
+ auto tok = pipeline.consumer_try_wait(smem_pipe_read);
510
+
511
+ // Wait for the pipeline MMAs to drain
512
+ warpgroup_wait<0>();
513
+ warpgroup_fence_operand(acc_qk);
514
+ warpgroup_fence_operand(acc_pv);
515
+
516
+ //if constexpr (kIsPersistent)
517
+ // if (k_tile_count == 0) pipeline_q.consumer_release(smem_pipe_release_q);
518
+
519
+ if constexpr (kIsMainloopLocked) math_wg_order_barrier.wait();
520
+ softmax.step(acc_qk, tiled_mma_qk, tPcP, softmax_state, acc_pv, tiled_mma_pv, problem_size);
521
+ if constexpr (kIsMainloopLocked) math_wg_order_barrier.arrive();
522
+
523
+ Tensor acc_qk_fixed = make_acc_into_op<Element>(acc_qk, typename TiledMmaPV::LayoutA_TV{});
524
+
525
+ pipeline.consumer_wait(smem_pipe_read, tok);
526
+
527
+ // MMA PV
528
+ warpgroup_fence_operand(acc_pv);
529
+ warpgroup_fence_operand(acc_qk_fixed);
530
+ warpgroup_arrive();
531
+
532
+ cute::gemm(tiled_mma_pv, acc_qk_fixed, tOrV(_,_,_,smem_pipe_read.index()), acc_pv);
533
+ warpgroup_commit_batch();
534
+
535
+ pipeline.consumer_release(smem_pipe_release);
536
+ ++smem_pipe_release;
537
+
538
+ pipeline.consumer_release(smem_pipe_release);
539
+ ++smem_pipe_release;
540
+
541
+ ++smem_pipe_read;
542
+ tPcP.data() = tPcP.data() + E<1>{} * get<1>(TileShapeQK{});
543
+ }
544
+
545
+ if (kIsPersistent) pipeline_q.consumer_release(smem_pipe_release_q);
546
+
547
+ // Wait for the pipeline MMAs to drain
548
+ warpgroup_wait<0>();
549
+ warpgroup_fence_operand(acc_pv);
550
+
551
+ if (kIsPersistent) pipeline.consumer_release(smem_pipe_release);
552
+ ++smem_pipe_release;
553
+
554
+ Tensor lse = softmax.tail(softmax_state, acc_pv, tiled_mma_pv);
555
+
556
+ return make_tuple(acc_pv, lse);
557
+ }
558
+ };
559
+
560
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_common.hpp ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/kernel_hardware_info.h"
35
+ #include "cute/tensor.hpp"
36
+
37
+ namespace cutlass::fmha::collective {
38
+
39
+ using namespace cute;
40
+
41
+ template<typename Atom, typename TA, typename TB, typename TC>
42
+ CUTE_DEVICE void gemm_reset_zero_acc(Atom& atom, TA const& tA, TB const& tB, TC&& tC) {
43
+ constexpr int rA = decltype(rank(tA))::value;
44
+ constexpr int rB = decltype(rank(tB))::value;
45
+ constexpr int rC = decltype(rank(tC))::value;
46
+ if constexpr (rA == 2 && rB == 2 && rC == 1) {
47
+ CUTLASS_PRAGMA_UNROLL
48
+ for (int k_block = 0; k_block < size<1>(tA); k_block++) {
49
+ cute::gemm(atom, tA(_,k_block), tB(_,k_block), tC);
50
+ atom.accumulate_ = GMMA::ScaleOut::One;
51
+ }
52
+ } else {
53
+ static_assert(rA == 3 && rB == 3 && rC == 3);
54
+ CUTLASS_PRAGMA_UNROLL
55
+ for (int k_block = 0; k_block < size<2>(tA); k_block++) {
56
+ cute::gemm(atom, tA(_,_,k_block), tB(_,_,k_block), tC);
57
+ atom.accumulate_ = GMMA::ScaleOut::One;
58
+ }
59
+ }
60
+ }
61
+
62
+ template<typename Atom, typename TA, typename TB, typename TC>
63
+ CUTE_DEVICE void gemm_zero_acc(Atom& atom, TA const& tA, TB const& tB, TC&& tC) {
64
+ atom.accumulate_ = GMMA::ScaleOut::Zero;
65
+ gemm_reset_zero_acc(atom, tA, tB, tC);
66
+ }
67
+
68
+ template<typename T, typename Fn>
69
+ CUTE_DEVICE constexpr typename T::value_type reduce(T const& t, Fn fn) {
70
+ if constexpr (decltype(size(t) % _2{} == _0{})::value) {
71
+ auto partial = make_tensor<typename T::value_type>(size(t) / _2{});
72
+ CUTE_UNROLL
73
+ for (int i = 0; i < size(partial); i++) {
74
+ partial(i) = fn(t(i), t(i + size(partial)));
75
+ }
76
+ return reduce(partial, fn);
77
+ } else {
78
+ auto result = t(_0{});
79
+ CUTE_UNROLL
80
+ for (int i = 1; i < size(t); i++) {
81
+ result = fn(result, t(i));
82
+ }
83
+ return result;
84
+ }
85
+ }
86
+
87
+ struct fmha_max {
88
+ CUTE_DEVICE float operator()(float a, float b) { return ::max(a, b); }
89
+ };
90
+
91
+ template<typename Threshold, typename Source, typename Reference>
92
+ inline auto __device__ constexpr layout_separate(Threshold const& thr,
93
+ Source const& src, Reference const& ref) {
94
+ auto lt = filter(transform_layout(src, ref, [&](auto const& s, auto const& r) {
95
+ if constexpr(decltype(r < thr)::value) {
96
+ return s;
97
+ } else {
98
+ return make_layout(_1{}, _0{});
99
+ }
100
+ }));
101
+ auto ge = filter(transform_layout(src, ref, [&](auto const& s, auto const& r) {
102
+ if constexpr(decltype(r >= thr)::value) {
103
+ return s;
104
+ } else {
105
+ return make_layout(_1{}, _0{});
106
+ }
107
+ }));
108
+ return make_layout(lt, ge);
109
+ }
110
+
111
+ template<typename TiledMma, typename Acc>
112
+ inline auto __device__ constexpr layout_acc_mn(TiledMma const& tiled_mma, Acc const& acc) {
113
+ auto separated = layout_separate(get<0>(typename TiledMma::Shape_MNK{}),
114
+ get<0>(acc), stride<1>(typename TiledMma::LayoutC_TV{}));
115
+ auto V_M = get<0>(separated);
116
+ auto V_N = get<1>(separated);
117
+ return make_layout(make_layout(V_M, get<1>(acc)), make_layout(V_N, get<2>(acc)));
118
+ }
119
+
120
+ template<typename TiledMma, typename Acc>
121
+ inline auto __device__ constexpr layout_op_mk_v(TiledMma const& tiled_mma, Acc const& acc) {
122
+ return layout_separate(get<0>(typename TiledMma::Shape_MNK{}),
123
+ get<0>(acc), stride<1>(typename TiledMma::LayoutA_TV{}));
124
+ }
125
+
126
+ template<typename TiledMma, typename Acc>
127
+ inline auto __device__ constexpr tensor_op_mk_v(TiledMma const& tiled_mma, Acc&& acc) {
128
+ return make_tensor(acc.data(), layout_op_mk_v(tiled_mma, acc.layout()));
129
+ }
130
+
131
+ template<typename TiledMma>
132
+ inline auto __device__ constexpr reduction_target_n(TiledMma const& tiled_mma) {
133
+ auto separated = layout_separate(get<0>(typename TiledMma::Shape_MNK{}),
134
+ make_layout(shape<0>(typename TiledMma::LayoutC_TV{})),
135
+ stride<0>(typename TiledMma::LayoutC_TV{}));
136
+ return get<1>(separated);
137
+ }
138
+
139
+
140
+ template<template<cute::GMMA::Major, cute::GMMA::Major, cute::GMMA::ScaleIn, cute::GMMA::ScaleIn> class Primitive, cute::GMMA::Major tA, cute::GMMA::Major tB, cute::GMMA::ScaleIn sA, cute::GMMA::ScaleIn sB>
141
+ inline auto __device__ constexpr convert_to_gmma_rs(cute::MMA_Atom<Primitive<tA, tB, sA, sB>> const& tiled_mma) {
142
+ using Atom = cute::MMA_Atom<Primitive<tA, tB, sA, sB>>;
143
+ using ElementA = typename Atom::ValTypeA;
144
+ using ElementB = typename Atom::ValTypeB;
145
+ using ElementC = typename Atom::ValTypeC;
146
+ using Shape_MNK = typename Atom::Shape_MNK;
147
+ using RS = decltype(cute::GMMA::rs_op_selector<ElementA, ElementB, ElementC, Shape_MNK, tA, tB, sA, sB>());
148
+ return cute::MMA_Atom<RS>{};
149
+ }
150
+
151
+ template<template<cute::GMMA::ScaleIn, cute::GMMA::ScaleIn> class Primitive, cute::GMMA::ScaleIn sA, cute::GMMA::ScaleIn sB>
152
+ inline auto __device__ constexpr convert_to_gmma_rs(cute::MMA_Atom<Primitive<sA, sB>> const& tiled_mma) {
153
+ using Atom = cute::MMA_Atom<Primitive<sA, sB>>;
154
+ using ElementA = typename Atom::ValTypeA;
155
+ using ElementB = typename Atom::ValTypeB;
156
+ using ElementC = typename Atom::ValTypeC;
157
+ using Shape_MNK = typename Atom::Shape_MNK;
158
+ constexpr auto tA = cute::GMMA::Major::K;
159
+ constexpr auto tB = cute::GMMA::Major::K;
160
+ using RS = decltype(cute::GMMA::rs_op_selector<ElementA, ElementB, ElementC, Shape_MNK, tA, tB, sA, sB>());
161
+ return cute::MMA_Atom<RS>{};
162
+ }
163
+
164
+ template<class Atom, class... Args>
165
+ CUTE_DEVICE auto constexpr convert_to_gmma_rs(cute::TiledMMA<Atom, Args...> const& tiled_mma) {
166
+ return cute::TiledMMA<decltype(convert_to_gmma_rs(Atom{})), Args...>{};
167
+ }
168
+
169
+ template<typename CLayout, typename AValueShape>
170
+ CUTE_DEVICE auto constexpr convert_c_layout_to_a_layout(CLayout const& c, AValueShape const& a) {
171
+ return make_layout(
172
+ make_shape(a, shape<1>(c), make_shape(shape<2>(c), size<0>(c) / size(a))),
173
+ make_stride(stride<0>(c), stride<1>(c), make_stride(stride<2>(c), size<2>(a) * stride<0,2>(c))));
174
+ }
175
+
176
+ template<class Layout, class Stages = _1>
177
+ CUTE_DEVICE constexpr auto unstageSmemLayout(Layout const& layout, Stages stages = {}) {
178
+ return composition(layout, make_tuple(_, _, make_layout(stages)));
179
+ }
180
+
181
+ template<class Element, class Accumulator, class OperandLayout_TV>
182
+ CUTE_DEVICE auto make_acc_into_op(Accumulator const& acc, OperandLayout_TV const& operand_layout_tv) {
183
+ Tensor operand = make_fragment_like<Element>(convert_c_layout_to_a_layout(acc.layout(), shape<1>(operand_layout_tv)));
184
+ Tensor operand_as_acc = make_tensor(operand.data(), acc.layout());
185
+
186
+ cute::copy(acc, operand_as_acc);
187
+
188
+ if constexpr (sizeof(Element) == 1) {
189
+
190
+ // 00 11 22 33 00 11 22 33 acc layout
191
+ // 00 00 11 11 22 22 33 33 operand layout
192
+ // BB AA AA BB AA BB BB AA conflict-free exchange pattern
193
+ // 16-bit exchange; so process two at a time potentially
194
+ int tid = threadIdx.x % 4;
195
+ auto values_u32 = recast<uint32_t>(operand);
196
+
197
+ CUTE_UNROLL
198
+ for (int n = 0; n < size<1>(values_u32); n++) {
199
+ CUTE_UNROLL
200
+ for (int k = 0; k < size<2>(values_u32); k++) {
201
+ CUTE_UNROLL
202
+ for (int ii = 0; ii < 8; ii += 4) {
203
+
204
+ uint32_t values_tmp_0 = values_u32(ii / 2 + 0, n, k);
205
+ uint32_t values_tmp_1 = values_u32(ii / 2 + 1, n, k);
206
+
207
+ // step A:
208
+ // t 1 v 0 -> t 0 v 1
209
+ // t 2 v 0 -> t 1 v 0
210
+ // t 0 v 1 -> t 2 v 0
211
+ // t 3 v 1 -> t 3 v 1
212
+
213
+ int v_to_send = tid == 1 || tid == 2 ? 0 : 1;
214
+ int v_to_recv = v_to_send;
215
+ int t_to_recv_from = (0x3021 >> (tid * 4)) & 0xF;
216
+
217
+ uint32_t values_tmp_a = v_to_send == 0 ? values_tmp_0 : values_tmp_1;
218
+
219
+ values_tmp_a = __shfl_sync(0xFFFFFFFF, values_tmp_a, t_to_recv_from, 4);
220
+
221
+ // step B:
222
+ // t 0 v 0 -> t 0 v 0
223
+ // t 3 v 0 -> t 1 v 1
224
+ // t 1 v 1 -> t 2 v 1
225
+ // t 2 v 1 -> t 3 v 0
226
+
227
+ v_to_send = 1 - v_to_send;
228
+ v_to_recv = 1 - v_to_recv;
229
+ t_to_recv_from = (0x2130 >> (tid * 4)) & 0xF;
230
+
231
+ uint32_t values_tmp_b = v_to_send == 0 ? values_tmp_0 : values_tmp_1;
232
+
233
+ values_tmp_b = __shfl_sync(0xFFFFFFFF, values_tmp_b, t_to_recv_from, 4);
234
+
235
+ values_u32(ii / 2 + 0, n, k) = __byte_perm(values_tmp_a, values_tmp_b, v_to_send == 0 ? 0x1054 : 0x5410);
236
+ values_u32(ii / 2 + 1, n, k) = __byte_perm(values_tmp_a, values_tmp_b, v_to_send == 0 ? 0x3276 : 0x7632);
237
+ }
238
+ }
239
+ }
240
+ }
241
+
242
+ return operand;
243
+ }
244
+
245
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_epilogue.hpp ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/epilogue/collective/collective_builder.hpp"
36
+ #include "cutlass/epilogue/collective/default_epilogue.hpp"
37
+ #include "cutlass/epilogue/thread/linear_combination.h"
38
+
39
+ #include "../collective/fmha_common.hpp"
40
+
41
+ namespace cutlass::fmha::collective {
42
+
43
+ template<class Element, class ElementAccumulator, class TileShape_WG>
44
+ struct FmhaFwdEpilogue {
45
+
46
+ static constexpr int Alignment = 16 / sizeof(Element);
47
+
48
+ using DefaultOperation = cutlass::epilogue::fusion::LinearCombination<Element, ElementAccumulator, void>;
49
+ using CollectiveEpilogueTMA = typename cutlass::epilogue::collective::CollectiveBuilder<
50
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
51
+ TileShape_WG, Shape<_1,_1,_1>, cutlass::epilogue::collective::EpilogueTileAuto,
52
+ ElementAccumulator, ElementAccumulator,
53
+ void, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
54
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
55
+ cutlass::epilogue::TmaWarpSpecialized,
56
+ DefaultOperation
57
+ >::CollectiveOp;
58
+
59
+ struct Arguments {
60
+ Element* ptr_O;
61
+ cute::tuple<int, cute::_1, cute::tuple<int, int>> dO;
62
+
63
+ ElementAccumulator* ptr_LSE;
64
+ cute::tuple<cute::_1, cute::tuple<int, int>> dLSE;
65
+ };
66
+
67
+ struct Params {
68
+ ElementAccumulator* ptr_LSE;
69
+ cute::tuple<cute::_1, cute::tuple<int, int>> dLSE;
70
+
71
+ typename CollectiveEpilogueTMA::Params epilogue_TMA;
72
+ };
73
+
74
+ using TensorStorage = typename CollectiveEpilogueTMA::TensorStorage;
75
+ using PipelineStorage = typename CollectiveEpilogueTMA::PipelineStorage;
76
+ using LoadPipeline = typename CollectiveEpilogueTMA::LoadPipeline;
77
+ static constexpr int TmaTransactionBytes = CollectiveEpilogueTMA::TmaTransactionBytes;
78
+
79
+ template<class ProblemShape>
80
+ static Params to_underlying_arguments(ProblemShape const& problem_size, Arguments const& args, void* workspace = nullptr) {
81
+ auto problem_size_o = make_shape(get<2>(problem_size), get<4>(problem_size), 1,
82
+ make_shape(get<0>(problem_size), get<1>(problem_size)));
83
+ typename CollectiveEpilogueTMA::Arguments args_tma{{}, args.ptr_O, args.dO, args.ptr_O, args.dO};
84
+ return Params{
85
+ args.ptr_LSE, args.dLSE,
86
+ CollectiveEpilogueTMA::to_underlying_arguments(problem_size_o, args_tma, workspace)
87
+ };
88
+ }
89
+
90
+ template<class TileShape, class BlkCoord, class ResultTuple, class TiledMma, class ProblemShape>
91
+ CUTLASS_DEVICE void operator()(
92
+ TileShape const& tile_shape, BlkCoord const& blk_coord,
93
+ ResultTuple const& result, TiledMma const& tiled_mma,
94
+ ProblemShape const& problem_size, Params const& params,
95
+ LoadPipeline epi_load_pipeline,
96
+ TensorStorage& epi_tensor_storage)
97
+ {
98
+ using X = Underscore;
99
+
100
+ auto acc = get<0>(result);
101
+ auto lse = get<1>(result);
102
+
103
+ auto thr_mma = tiled_mma.get_thread_slice(threadIdx.x);
104
+
105
+ int seqlen_q = get<2>(problem_size);
106
+ int num_batch = get<0>(problem_size);
107
+ int num_heads = get<1>(problem_size);
108
+ // Epilogue for lse
109
+ Tensor mLSE = make_tensor(make_gmem_ptr(params.ptr_LSE),
110
+ make_shape(seqlen_q, get<1>(tile_shape), make_shape(num_batch, num_heads)),
111
+ make_stride(_1{}, _0{}, get<1>(params.dLSE)));
112
+ Tensor gLSE_full = local_tile(mLSE, tile_shape, make_coord(_, _, _), Step<_1, _1, X>{});
113
+ Tensor gLSE = gLSE_full(_, _, get<0>(blk_coord), get<1>(blk_coord), get<2>(blk_coord));
114
+ Tensor tOgLSE = thr_mma.partition_C(gLSE);
115
+ Tensor cO = make_identity_tensor(take<0,2>(tile_shape));
116
+ Tensor tOcO = thr_mma.partition_C(cO);
117
+ if (get<1>(tOcO(_0{})) == 0) {
118
+ auto tOgLSE_mn = make_tensor(tOgLSE.data(), layout_acc_mn(tiled_mma, tOgLSE.layout()));
119
+ auto tOcO_mn = make_tensor(tOcO.data(), layout_acc_mn(tiled_mma, tOcO.layout()));
120
+ CUTLASS_PRAGMA_UNROLL
121
+ for (int i = 0; i < size<0>(tOgLSE_mn); i++) {
122
+ if (get<0>(tOcO_mn(i)) + get<0>(blk_coord) * get<0>(tile_shape) < get<2>(problem_size)) {
123
+ tOgLSE_mn(i, _0{}) = lse(i);
124
+ }
125
+ }
126
+ }
127
+ auto problem_size_o = make_shape(get<2>(problem_size), get<4>(problem_size), _,
128
+ make_shape(get<0>(problem_size), get<1>(problem_size)));
129
+
130
+ CollectiveEpilogueTMA epilogue_tma(params.epilogue_TMA, epi_tensor_storage);
131
+
132
+ using EpiStorePipeline = typename CollectiveEpilogueTMA::StorePipeline;
133
+ typename EpiStorePipeline::Params epi_store_pipeline_params;
134
+ epi_store_pipeline_params.always_wait = true;
135
+ EpiStorePipeline epi_store_pipeline(epi_store_pipeline_params);
136
+
137
+ typename CollectiveEpilogueTMA::LoadPipelineState epi_load_pipe_consumer_state;
138
+ PipelineState epi_store_pipe_producer_state = cutlass::make_producer_start_state<EpiStorePipeline>();
139
+
140
+ auto [epi_load_pipe_consumer_state_next, epi_store_pipe_producer_state_next] =
141
+ epilogue_tma.store(
142
+ epi_load_pipeline, epi_load_pipe_consumer_state,
143
+ epi_store_pipeline, epi_store_pipe_producer_state,
144
+ problem_size_o, tile_shape, make_coord(get<0>(blk_coord), _0{}, _, get<2>(blk_coord)),
145
+ acc, tiled_mma, threadIdx.x % cutlass::NumThreadsPerWarpGroup,
146
+ epi_tensor_storage
147
+ );
148
+
149
+ epilogue_tma.store_tail(
150
+ epi_load_pipeline, epi_load_pipe_consumer_state_next,
151
+ epi_store_pipeline, epi_store_pipe_producer_state_next
152
+ );
153
+ }
154
+ };
155
+
156
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_epilogue_bwd.hpp ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/epilogue/collective/default_epilogue.hpp"
36
+
37
+ #include "../collective/fmha_epilogue.hpp"
38
+
39
+ namespace cutlass::fmha::collective {
40
+
41
+ template<class Element, class ElementAccumulator, class TileShape_WG>
42
+ struct FmhaBwdEpilogueKV {
43
+
44
+ static constexpr int Alignment = 16 / sizeof(Element);
45
+
46
+ struct Arguments {
47
+ Element* ptr_K;
48
+ cute::tuple<int, int, int, cute::_1> dK;
49
+
50
+ Element* ptr_V;
51
+ cute::tuple<int, int, int, _1> dV;
52
+ };
53
+
54
+ //using DefaultOperation = cutlass::epilogue::fusion::LinearCombination<Element, ElementAccumulator, void>;
55
+ static constexpr auto RoundStyle = cutlass::FloatRoundStyle::round_to_nearest;
56
+ using DefaultOperation = cutlass::epilogue::fusion::Sm90EVT<
57
+ cutlass::epilogue::fusion::Sm90Compute<cutlass::first, Element, ElementAccumulator, RoundStyle>,
58
+ cutlass::epilogue::fusion::Sm90AccFetch
59
+ >;
60
+ using CollectiveEpilogueTMA = typename cutlass::epilogue::collective::CollectiveBuilder<
61
+ cutlass::arch::Sm90, cutlass::arch::OpClassTensorOp,
62
+ TileShape_WG, Shape<_1,_1,_1>, cutlass::epilogue::collective::EpilogueTileAuto,
63
+ ElementAccumulator, ElementAccumulator,
64
+ void, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
65
+ Element, cute::tuple<int, _1, cute::tuple<int, int>>, Alignment,
66
+ cutlass::epilogue::TmaWarpSpecialized,
67
+ DefaultOperation
68
+ >::CollectiveOp;
69
+
70
+ struct Params {
71
+ typename CollectiveEpilogueTMA::Params epilogue_K;
72
+ typename CollectiveEpilogueTMA::Params epilogue_V;
73
+ };
74
+
75
+
76
+ using TensorStorage = typename CollectiveEpilogueTMA::TensorStorage[2];
77
+ using PipelineStorage = typename CollectiveEpilogueTMA::PipelineStorage;
78
+ using LoadPipeline = typename CollectiveEpilogueTMA::LoadPipeline;
79
+ static constexpr int TmaTransactionBytes = CollectiveEpilogueTMA::TmaTransactionBytes;
80
+
81
+ template<class ProblemShape>
82
+ static Params to_underlying_arguments(ProblemShape const& problem_size, Arguments const& args, void* workspace = nullptr) {
83
+ auto dK = make_stride(get<2>(args.dK), get<3>(args.dK),
84
+ make_stride(get<0>(args.dK), get<1>(args.dK)));
85
+ auto dV = make_stride(get<2>(args.dV), get<3>(args.dV),
86
+ make_stride(get<0>(args.dV), get<1>(args.dV)));
87
+
88
+ auto problem_size_kv = make_shape(get<3>(problem_size), get<4>(problem_size), 1,
89
+ make_shape(get<0>(problem_size), get<1>(problem_size)));
90
+ typename CollectiveEpilogueTMA::Arguments args_k{{}, args.ptr_K, dK, args.ptr_K, dK};
91
+ typename CollectiveEpilogueTMA::Arguments args_v{{}, args.ptr_V, dV, args.ptr_V, dV};
92
+ return Params{
93
+ CollectiveEpilogueTMA::to_underlying_arguments(problem_size_kv, args_k, nullptr),
94
+ CollectiveEpilogueTMA::to_underlying_arguments(problem_size_kv, args_v, nullptr)
95
+ };
96
+ }
97
+
98
+ template<class TileShape, class BlkCoord, class ResultTuple, class TiledMma, class ProblemShape>
99
+ CUTLASS_DEVICE void operator()(
100
+ TileShape const& tile_shape, BlkCoord const& blk_coord,
101
+ ResultTuple const& result, TiledMma const& tiled_mma,
102
+ ProblemShape const& problem_size, Params const& params,
103
+ LoadPipeline epi_load_pipeline, TensorStorage& epi_tensor_storage)
104
+ {
105
+ auto acc_k = get<0>(result);
106
+ auto acc_v = get<1>(result);
107
+
108
+ auto problem_size_kv = make_shape(get<3>(problem_size), get<4>(problem_size), _,
109
+ make_shape(get<0>(problem_size), get<1>(problem_size)));
110
+
111
+ using EpiStorePipeline = typename CollectiveEpilogueTMA::StorePipeline;
112
+ typename EpiStorePipeline::Params epi_store_pipeline_params;
113
+ epi_store_pipeline_params.always_wait = true;
114
+ EpiStorePipeline epi_store_pipeline(epi_store_pipeline_params);
115
+
116
+ typename CollectiveEpilogueTMA::LoadPipelineState epi_load_pipe_consumer_state;
117
+ PipelineState epi_store_pipe_producer_state = cutlass::make_producer_start_state<EpiStorePipeline>();
118
+
119
+ CollectiveEpilogueTMA epilogue_k{params.epilogue_K, epi_tensor_storage[0]};
120
+ CollectiveEpilogueTMA epilogue_v{params.epilogue_V, epi_tensor_storage[1]};
121
+
122
+ {
123
+ auto [epi_load_pipe_consumer_state_next, epi_store_pipe_producer_state_next] =
124
+ epilogue_k.store(
125
+ epi_load_pipeline, epi_load_pipe_consumer_state,
126
+ epi_store_pipeline, epi_store_pipe_producer_state,
127
+ problem_size_kv, tile_shape, make_coord(get<1>(blk_coord), _0{}, _, get<2>(blk_coord)),
128
+ acc_k, tiled_mma, threadIdx.x % cutlass::NumThreadsPerWarpGroup,
129
+ epi_tensor_storage[0]
130
+ );
131
+
132
+ }
133
+
134
+ {
135
+ auto [epi_load_pipe_consumer_state_next, epi_store_pipe_producer_state_next] =
136
+ epilogue_v.store(
137
+ epi_load_pipeline, epi_load_pipe_consumer_state,
138
+ epi_store_pipeline, epi_store_pipe_producer_state,
139
+ problem_size_kv, tile_shape, make_coord(get<1>(blk_coord), _0{}, _, get<2>(blk_coord)),
140
+ acc_v, tiled_mma, threadIdx.x % cutlass::NumThreadsPerWarpGroup,
141
+ epi_tensor_storage[1]
142
+ );
143
+
144
+ epilogue_k.store_tail(
145
+ epi_load_pipeline, epi_load_pipe_consumer_state_next,
146
+ epi_store_pipeline, epi_store_pipe_producer_state_next
147
+ );
148
+
149
+ epilogue_v.store_tail(
150
+ epi_load_pipeline, epi_load_pipe_consumer_state_next,
151
+ epi_store_pipeline, epi_store_pipe_producer_state_next
152
+ );
153
+ }
154
+ }
155
+ };
156
+
157
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/collective/fmha_fusion.hpp ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cute/tensor.hpp"
36
+
37
+ namespace cutlass::fmha::collective {
38
+
39
+ using namespace cute;
40
+
41
+ struct DefaultFusion {
42
+ template<class BlkCoord, class TileShape, class ProblemSize>
43
+ CUTLASS_DEVICE
44
+ int get_trip_count(
45
+ BlkCoord const& blk_coord,
46
+ TileShape const& tile_shape,
47
+ ProblemSize const& problem_size
48
+ ) {
49
+ return ceil_div(get<3>(problem_size), get<1>(tile_shape));
50
+ }
51
+
52
+ template<class BlkCoord, class TileShape, class ProblemSize>
53
+ CUTLASS_DEVICE
54
+ int get_masked_trip_count(
55
+ BlkCoord const& blk_coord,
56
+ TileShape const& tile_shape,
57
+ ProblemSize const& problem_size
58
+ ) {
59
+ return get_trip_count(blk_coord, tile_shape, problem_size);
60
+ }
61
+
62
+ template<class BlkCoord, class TileShape, class ProblemSize>
63
+ CUTLASS_DEVICE
64
+ int get_unmasked_trip_count(
65
+ BlkCoord const& blk_coord,
66
+ TileShape const& tile_shape,
67
+ ProblemSize const& problem_size
68
+ ) {
69
+ return 0;
70
+ }
71
+
72
+ template<class AccQK, class IndexQK, class ProblemSize>
73
+ CUTLASS_DEVICE
74
+ void before_softmax(
75
+ AccQK& acc_qk,
76
+ IndexQK const& index_qk,
77
+ ProblemSize const& problem_size
78
+
79
+ ) {
80
+ return;
81
+ }
82
+ };
83
+
84
+ struct ResidualFusion : DefaultFusion {
85
+
86
+ using Base = DefaultFusion;
87
+
88
+ template<class BlkCoord, class TileShape, class ProblemSize>
89
+ CUTLASS_DEVICE
90
+ int get_masked_trip_count(
91
+ BlkCoord const& blk_coord,
92
+ TileShape const& tile_shape,
93
+ ProblemSize const& problem_size
94
+ ) {
95
+ return 1;
96
+ }
97
+
98
+ template<class BlkCoord, class TileShape, class ProblemSize>
99
+ CUTLASS_DEVICE
100
+ int get_unmasked_trip_count(
101
+ BlkCoord const& blk_coord,
102
+ TileShape const& tile_shape,
103
+ ProblemSize const& problem_size
104
+ ) {
105
+ return get_trip_count(blk_coord, tile_shape, problem_size) - 1;
106
+ }
107
+
108
+ template<class AccQK, class IndexQK, class ProblemSize>
109
+ CUTLASS_DEVICE
110
+ void before_softmax(
111
+ AccQK& acc_qk,
112
+ IndexQK const& index_qk,
113
+ ProblemSize const& problem_size
114
+ ) {
115
+ // This is useful is seqlen_k % kBlockN != 0 since it masks
116
+ // the remaining elements out from softmax.
117
+ // d % kHeadDim != 0 or seqlen_q % kBlockM do not suffer from similar
118
+ // issues as they are transparently taken care of by TMA and the
119
+ // epilogue, if it is instantiated with predication support.
120
+ CUTLASS_PRAGMA_UNROLL
121
+ for (int i = 0; i < size(acc_qk); i++) {
122
+ auto pos = index_qk(i);
123
+ if (get<1>(pos) >= get<3>(problem_size)) {
124
+ acc_qk(i) = -INFINITY;
125
+ }
126
+ }
127
+ }
128
+ };
129
+
130
+ struct CausalFusion : DefaultFusion {
131
+
132
+ using Base = DefaultFusion;
133
+
134
+ template<class BlkCoord, class TileShape, class ProblemSize>
135
+ CUTLASS_DEVICE
136
+ int get_trip_count(
137
+ BlkCoord const& blk_coord,
138
+ TileShape const& tile_shape,
139
+ ProblemSize const& problem_size
140
+ ) {
141
+ // See note below on different ways to think about causal attention
142
+ // Again, we'd add the offset_q into the max_blocks_q calculation
143
+ int max_blocks_k = Base::get_trip_count(blk_coord, tile_shape, problem_size);
144
+ int max_blocks_q = ceil_div((get<0>(blk_coord) + 1) * get<0>(tile_shape), get<1>(tile_shape));
145
+ return std::min(max_blocks_k, max_blocks_q);
146
+ }
147
+
148
+ template<class BlkCoord, class TileShape, class ProblemSize>
149
+ CUTLASS_DEVICE
150
+ int get_masked_trip_count(
151
+ BlkCoord const& blk_coord,
152
+ TileShape const& tile_shape,
153
+ ProblemSize const& problem_size
154
+ ) {
155
+ return ceil_div(get<0>(tile_shape), get<1>(tile_shape));
156
+ }
157
+
158
+ template<class BlkCoord, class TileShape, class ProblemSize>
159
+ CUTLASS_DEVICE
160
+ int get_unmasked_trip_count(
161
+ BlkCoord const& blk_coord,
162
+ TileShape const& tile_shape,
163
+ ProblemSize const& problem_size
164
+ ) {
165
+ return get_trip_count(blk_coord, tile_shape, problem_size) - get_masked_trip_count(blk_coord, tile_shape, problem_size);
166
+ }
167
+
168
+ template<class AccQK, class IndexQK, class ProblemSize>
169
+ CUTLASS_DEVICE
170
+ void before_softmax(
171
+ AccQK& acc_qk,
172
+ IndexQK const& index_qk,
173
+ ProblemSize const& problem_size
174
+ ) {
175
+ // There are two ways to do causal if N_Q != N_K
176
+ // (1) is to assume that the Q is at the beginning of the matrix
177
+ // - this is what we demonstrate here
178
+ // (2) is that it is at the end of the matrix
179
+ // - this is usually what we want for inference settings
180
+ // where we only compute the next row and use cache for the rest
181
+ // - if you'd like this, you only need to add an offset like so:
182
+ // get<0>(pos) + offset_q < get<1>(pos)
183
+ CUTLASS_PRAGMA_UNROLL
184
+ for (int i = 0; i < size(acc_qk); i++) {
185
+ auto pos = index_qk(i);
186
+ if (get<0>(pos) < get<1>(pos)) {
187
+ acc_qk(i) = -INFINITY;
188
+ }
189
+ }
190
+ }
191
+
192
+ };
193
+
194
+ template<class Base>
195
+ struct FusionBwdAdapter {
196
+ template<class BlkCoord, class TileShape, class ProblemSize>
197
+ CUTLASS_DEVICE
198
+ int get_trip_count(
199
+ BlkCoord const& blk_coord,
200
+ TileShape const& tile_shape,
201
+ ProblemSize const& problem_size
202
+ ) {
203
+ return Base{}.get_trip_count(select<1,0,2>(blk_coord), select<1,0,2>(tile_shape), select<0,1,3,2,4>(problem_size));
204
+ }
205
+
206
+ template<class AccQK, class IndexQK, class ProblemSize>
207
+ CUTLASS_DEVICE
208
+ void before_softmax(
209
+ AccQK& acc_qk,
210
+ IndexQK const& index_qk,
211
+ ProblemSize const& problem_size
212
+ ) {
213
+ auto index_base = index_qk(_0{});
214
+ auto index_shape = shape(index_qk);
215
+ auto index_stride = transform_leaf(stride(index_qk), [](auto elem) {
216
+ if constexpr (is_scaled_basis<decltype(elem)>::value) {
217
+ if constexpr(decltype(elem.mode() == _0{})::value) {
218
+ return ScaledBasis<decltype(elem.value()), 1>(elem.value());
219
+ } else {
220
+ return ScaledBasis<decltype(elem.value()), 0>(elem.value());
221
+ }
222
+ } else {
223
+ return elem;
224
+ }
225
+ });
226
+ auto index_qk_bwd = make_tensor(make_inttuple_iter(select<1,0>(index_base)), make_layout(index_shape, index_stride));
227
+ Base{}.before_softmax(acc_qk, index_qk_bwd, problem_size);
228
+ }
229
+
230
+ template<class BlkCoord, class TileShape, class ProblemSize>
231
+ CUTLASS_DEVICE
232
+ bool is_contributing(
233
+ BlkCoord const& blk_coord,
234
+ TileShape const& tile_shape,
235
+ ProblemSize const& problem_size
236
+ ) {
237
+ return true;
238
+ }
239
+ };
240
+
241
+ template<>
242
+ struct FusionBwdAdapter<CausalFusion> {
243
+ template<class BlkCoord, class TileShape, class ProblemSize>
244
+ CUTLASS_DEVICE
245
+ int get_trip_count(
246
+ BlkCoord const& blk_coord,
247
+ TileShape const& tile_shape,
248
+ ProblemSize const& problem_size
249
+ ) {
250
+ return get<2>(problem_size) / get<0>(TileShape{});
251
+ }
252
+
253
+ template<class AccQK, class IndexQK, class ProblemSize>
254
+ CUTLASS_DEVICE
255
+ void before_softmax(
256
+ AccQK& acc_qk,
257
+ IndexQK const& index_qk,
258
+ ProblemSize const& problem_size
259
+
260
+ ) {
261
+ CUTLASS_PRAGMA_UNROLL
262
+ for (int i = 0; i < size(acc_qk); i++) {
263
+ auto pos = index_qk(i);
264
+ if (get<1>(pos) < get<0>(pos)) {
265
+ acc_qk(i) = -INFINITY;
266
+ }
267
+ }
268
+ }
269
+
270
+ template<class BlkCoord, class TileShape, class ProblemSize>
271
+ CUTLASS_DEVICE
272
+ bool is_contributing(
273
+ BlkCoord const& blk_coord,
274
+ TileShape const& tile_shape,
275
+ ProblemSize const& problem_size
276
+ ) {
277
+ int max_q = get<0>(blk_coord) * get<0>(tile_shape) + get<0>(tile_shape);
278
+ int min_k = get<1>(blk_coord) * get<1>(tile_shape);
279
+ return min_k <= max_q;
280
+ }
281
+ };
282
+
283
+ } // namespace cutlass::fmha::collective
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/device/device_universal.hpp ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ /*!
33
+ \file
34
+ \brief An universal device layer for cutlass 3.x-style kernels.
35
+ */
36
+
37
+ #pragma once
38
+
39
+ // common
40
+ #include "cutlass/cutlass.h"
41
+ #include "cutlass/device_kernel.h"
42
+
43
+ #if !defined(__CUDACC_RTC__)
44
+ #include "cutlass/cluster_launch.hpp"
45
+ #include "cutlass/trace.h"
46
+ #endif // !defined(__CUDACC_RTC__)
47
+
48
+ ////////////////////////////////////////////////////////////////////////////////
49
+
50
+ namespace cutlass::device {
51
+
52
+ ////////////////////////////////////////////////////////////////////////////////
53
+ ////////////////////////////// CUTLASS 3.x API /////////////////////////////////
54
+ ////////////////////////////////////////////////////////////////////////////////
55
+
56
+ template <class Kernel_>
57
+ class Universal {
58
+ public:
59
+ using Kernel = Kernel_;
60
+
61
+ static int const kThreadCount = Kernel::MaxThreadsPerBlock;
62
+
63
+ /// Argument structure: User API
64
+ using Arguments = typename Kernel::Arguments;
65
+ /// Argument structure: Kernel API
66
+ using Params = typename Kernel::Params;
67
+
68
+ private:
69
+
70
+ /// Kernel API parameters object
71
+ Params params_;
72
+
73
+ bool is_initialized(bool set = false) {
74
+ static bool initialized = false;
75
+ if (set) initialized = true;
76
+ return initialized;
77
+ }
78
+
79
+ public:
80
+
81
+ /// Access the Params structure
82
+ Params const& params() const {
83
+ return params_;
84
+ }
85
+
86
+ /// Determines whether the GEMM can execute the given problem.
87
+ static Status
88
+ can_implement(Arguments const& args) {
89
+ if (Kernel::can_implement(args)) {
90
+ return Status::kSuccess;
91
+ }
92
+ else {
93
+ return Status::kInvalid;
94
+ }
95
+ }
96
+
97
+ /// Gets the workspace size
98
+ static size_t
99
+ get_workspace_size(Arguments const& args) {
100
+ size_t workspace_bytes = 0;
101
+ workspace_bytes += Kernel::get_workspace_size(args);
102
+ return workspace_bytes;
103
+ }
104
+
105
+ /// Computes the grid shape
106
+ static dim3
107
+ get_grid_shape(Params const& params) {
108
+ return Kernel::get_grid_shape(params);
109
+ }
110
+
111
+ /// Computes the maximum number of active blocks per multiprocessor
112
+ static int maximum_active_blocks(int /* smem_capacity */ = -1) {
113
+ CUTLASS_TRACE_HOST("Universal::maximum_active_blocks()");
114
+ int max_active_blocks = -1;
115
+ int smem_size = Kernel::SharedStorageSize;
116
+
117
+ // first, account for dynamic smem capacity if needed
118
+ cudaError_t result;
119
+ if (smem_size >= (48 << 10)) {
120
+ CUTLASS_TRACE_HOST(" Setting smem size to " << smem_size);
121
+ result = cudaFuncSetAttribute(
122
+ device_kernel<Kernel>,
123
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
124
+ smem_size);
125
+ if (cudaSuccess != result) {
126
+ result = cudaGetLastError(); // to clear the error bit
127
+ CUTLASS_TRACE_HOST(
128
+ " cudaFuncSetAttribute() returned error: "
129
+ << cudaGetErrorString(result));
130
+ return -1;
131
+ }
132
+ }
133
+
134
+ // query occupancy after setting smem size
135
+ result = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
136
+ &max_active_blocks,
137
+ device_kernel<Kernel>,
138
+ Kernel::MaxThreadsPerBlock,
139
+ smem_size);
140
+
141
+ if (cudaSuccess != result) {
142
+ result = cudaGetLastError(); // to clear the error bit
143
+ CUTLASS_TRACE_HOST(
144
+ " cudaOccupancyMaxActiveBlocksPerMultiprocessor() returned error: "
145
+ << cudaGetErrorString(result));
146
+ return -1;
147
+ }
148
+
149
+ CUTLASS_TRACE_HOST(" max_active_blocks: " << max_active_blocks);
150
+ return max_active_blocks;
151
+ }
152
+
153
+ /// Initializes GEMM state from arguments.
154
+ Status
155
+ initialize(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr) {
156
+ CUTLASS_TRACE_HOST("Universal::initialize() - workspace "
157
+ << workspace << ", stream: " << (stream ? "non-null" : "null"));
158
+
159
+ // Initialize the workspace
160
+ Status status = Kernel::initialize_workspace(args, workspace, stream);
161
+ if (status != Status::kSuccess) {
162
+ return status;
163
+ }
164
+
165
+ // Initialize the Params structure
166
+ params_ = Kernel::to_underlying_arguments(args, workspace);
167
+
168
+ if (is_initialized()) return Status::kSuccess;
169
+
170
+ // account for dynamic smem capacity if needed
171
+ int smem_size = Kernel::SharedStorageSize;
172
+ if (smem_size >= (48 << 10)) {
173
+ CUTLASS_TRACE_HOST(" Setting smem size to " << smem_size);
174
+ cudaError_t result = cudaFuncSetAttribute(
175
+ device_kernel<Kernel>,
176
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
177
+ smem_size);
178
+ if (cudaSuccess != result) {
179
+ result = cudaGetLastError(); // to clear the error bit
180
+ CUTLASS_TRACE_HOST(" cudaFuncSetAttribute() returned error: " << cudaGetErrorString(result));
181
+ return Status::kErrorInternal;
182
+ }
183
+ }
184
+
185
+ is_initialized(true);
186
+
187
+ return Status::kSuccess;
188
+ }
189
+
190
+ /// Update API is preserved in 3.0, but does not guarantee a lightweight update of params.
191
+ Status
192
+ update(Arguments const& args, void* workspace = nullptr) {
193
+ CUTLASS_TRACE_HOST("Universal()::update() - workspace: " << workspace);
194
+
195
+ size_t workspace_bytes = get_workspace_size(args);
196
+ if (workspace_bytes > 0 && nullptr == workspace) {
197
+ return Status::kErrorWorkspaceNull;
198
+ }
199
+
200
+ params_ = Kernel::to_underlying_arguments(args, workspace);
201
+ return Status::kSuccess;
202
+ }
203
+
204
+ /// Primary run() entry point API that is static allowing users to create and manage their own params.
205
+ /// Supplied params struct must be construct by calling Kernel::to_underling_arguments()
206
+ static Status
207
+ run(Params& params, cudaStream_t stream = nullptr) {
208
+ CUTLASS_TRACE_HOST("Universal::run()");
209
+ dim3 const block = Kernel::get_block_shape();
210
+ dim3 const grid = get_grid_shape(params);
211
+
212
+ // configure smem size and carveout
213
+ int smem_size = Kernel::SharedStorageSize;
214
+
215
+ Status launch_result;
216
+ // Use extended launch API only for mainloops that use it
217
+ if constexpr(Kernel::ArchTag::kMinComputeCapability >= 90) {
218
+ dim3 cluster(cute::size<0>(typename Kernel::ClusterShape{}),
219
+ cute::size<1>(typename Kernel::ClusterShape{}),
220
+ cute::size<2>(typename Kernel::ClusterShape{}));
221
+ void const* kernel = (void const*) device_kernel<Kernel>;
222
+ void* kernel_params[] = {&params};
223
+ launch_result = ClusterLauncher::launch(grid, cluster, block, smem_size, stream, kernel, kernel_params);
224
+ }
225
+ else {
226
+ launch_result = Status::kSuccess;
227
+ cutlass::arch::synclog_setup();
228
+ device_kernel<Kernel><<<grid, block, smem_size, stream>>>(params);
229
+ }
230
+
231
+ cudaError_t result = cudaGetLastError();
232
+ if (cudaSuccess == result && Status::kSuccess == launch_result) {
233
+ return Status::kSuccess;
234
+ }
235
+ else {
236
+ CUTLASS_TRACE_HOST(" Kernel launch failed. Reason: " << result);
237
+ return Status::kErrorInternal;
238
+ }
239
+ }
240
+
241
+ //
242
+ // Non-static launch overloads that first create and set the internal params struct of this kernel handle.
243
+ //
244
+
245
+ /// Launches the kernel after first constructing Params internal state from supplied arguments.
246
+ Status
247
+ run(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr) {
248
+ Status status = initialize(args, workspace, stream);
249
+ if (Status::kSuccess == status) {
250
+ status = run(params_, stream);
251
+ }
252
+ return status;
253
+ }
254
+
255
+ /// Launches the kernel after first constructing Params internal state from supplied arguments.
256
+ Status
257
+ operator()(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr) {
258
+ return run(args, workspace, stream);
259
+ }
260
+
261
+ /// Overload that allows a user to re-launch the same kernel without updating internal params struct.
262
+ Status
263
+ run(cudaStream_t stream = nullptr) {
264
+ return run(params_, stream);
265
+ }
266
+
267
+ /// Overload that allows a user to re-launch the same kernel without updating internal params struct.
268
+ Status
269
+ operator()(cudaStream_t stream = nullptr) {
270
+ return run(params_, stream);
271
+ }
272
+ };
273
+
274
+ ////////////////////////////////////////////////////////////////////////////////
275
+
276
+ } // namespace cutlass::device
277
+
278
+ ////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/device/fmha_device_bwd.hpp ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ /*!
35
+ \file
36
+ \brief An universal device layer for cutlass 3.x-style kernels.
37
+ */
38
+
39
+ // common
40
+ #include "cutlass/cutlass.h"
41
+
42
+ #include "../device/device_universal.hpp"
43
+ #include "../collective/fmha_collective_bwd_tma_warpspecialized.hpp"
44
+ #include "../collective/fmha_fusion.hpp"
45
+ #include "../collective/fmha_epilogue_bwd.hpp"
46
+ #include "../kernel/fmha_kernel_bwd_sum_OdO.hpp"
47
+ #include "../kernel/fmha_kernel_bwd_convert.hpp"
48
+ #include "../kernel/fmha_kernel_tma_warpspecialized.hpp"
49
+ #include "../kernel/fmha_tile_scheduler.hpp"
50
+
51
+ ////////////////////////////////////////////////////////////////////////////////
52
+
53
+ namespace cutlass::fmha::device {
54
+
55
+ ////////////////////////////////////////////////////////////////////////////////
56
+ ////////////////////////////// CUTLASS 3.x API /////////////////////////////////
57
+ ////////////////////////////////////////////////////////////////////////////////
58
+
59
+ template<class Element, class ElementAccumulator, class TileShape, class Fusion, class... Options>
60
+ class FmhaBwd {
61
+ public:
62
+ /// Argument structure: User API
63
+ struct Arguments {
64
+ cute::tuple<int, int, int, int, int> problem_size;
65
+
66
+ const Element* ptr_Q;
67
+ cute::tuple<int, int, int, cute::_1> stride_Q;
68
+ const Element* ptr_K;
69
+ cute::tuple<int, int, int, cute::_1> stride_K;
70
+ const Element* ptr_V;
71
+ cute::tuple<int, int, int, cute::_1> stride_V;
72
+
73
+ const Element* ptr_O;
74
+ cute::tuple<int, int, int, cute::_1> stride_O;
75
+ const ElementAccumulator* ptr_LSE;
76
+ cute::tuple<int, int, _1> stride_LSE;
77
+
78
+ const Element* ptr_dO;
79
+ cute::tuple<int, int, int, cute::_1> stride_dO;
80
+
81
+ Element* ptr_dQ;
82
+ cute::tuple<int, int, int, cute::_1> stride_dQ;
83
+ Element* ptr_dK;
84
+ cute::tuple<int, int, int, cute::_1> stride_dK;
85
+ Element* ptr_dV;
86
+ cute::tuple<int, int, int, cute::_1> stride_dV;
87
+
88
+ cutlass::KernelHardwareInfo hw_info;
89
+ };
90
+
91
+ using OperationSumOdO = cutlass::device::Universal<cutlass::fmha::kernel::FmhaKernelBwdSumOdO<Element, ElementAccumulator>>;
92
+ using OperationConvert = cutlass::device::Universal<cutlass::fmha::kernel::FmhaKernelBwdConvert<Element, ElementAccumulator>>;
93
+
94
+ using Mainloop = cutlass::fmha::collective::FmhaBwdMainloopTmaWarpSpecialized<
95
+ Element, ElementAccumulator, TileShape,
96
+ cutlass::fmha::collective::FusionBwdAdapter<Fusion>, Options...>;
97
+
98
+ using Epilogue = cutlass::fmha::collective::FmhaBwdEpilogueKV<Element, ElementAccumulator, typename Mainloop::TileShapePV>;
99
+
100
+ using Operation = cutlass::device::Universal<
101
+ cutlass::fmha::kernel::FmhaKernelTmaWarpSpecialized<
102
+ Mainloop,
103
+ Epilogue,
104
+ cutlass::fmha::kernel::TileSchedulerBwdAdapter<cutlass::fmha::kernel::IndividualTileScheduler>, Options...>>;
105
+
106
+ struct Params {
107
+ OperationSumOdO op_sum_OdO;
108
+ Operation op;
109
+ OperationConvert op_convert;
110
+ ElementAccumulator* dQ_acc;
111
+ size_t dQ_acc_size;
112
+ };
113
+
114
+ private:
115
+ Params params_;
116
+
117
+ static typename OperationSumOdO::Arguments to_sum_OdO_arguments(Arguments const& args, ElementAccumulator* dest = nullptr) {
118
+ auto [B, H, Q, K, D] = args.problem_size;
119
+ D = cutlass::round_up(D, 8); // Alignment
120
+ Q = cutlass::round_up(Q, 8); // Alignment
121
+ auto stride_sum_OdO = make_stride(H*Q, Q, _1{});
122
+ return typename OperationSumOdO::Arguments {
123
+ args.problem_size,
124
+ args.ptr_O, args.stride_O,
125
+ args.ptr_dO, args.stride_dO,
126
+ dest, stride_sum_OdO
127
+ };
128
+ }
129
+
130
+ static typename OperationConvert::Arguments to_convert_arguments(Arguments const& args, ElementAccumulator* src = nullptr) {
131
+ auto [B, H, Q, K, D] = args.problem_size;
132
+ D = cutlass::round_up(D, 8); // Alignment
133
+ Q = cutlass::round_up(Q, 8); // Alignment
134
+ auto stride_src_dQ = make_stride(B == 1 ? 0 : (H*Q*D), Q*D, D, _1{});
135
+ return typename OperationConvert::Arguments {
136
+ args.problem_size,
137
+ src, stride_src_dQ,
138
+ nullptr, stride_src_dQ,
139
+ nullptr, stride_src_dQ,
140
+ args.ptr_dQ, args.stride_dQ,
141
+ nullptr, args.stride_dK,
142
+ nullptr, args.stride_dV
143
+ };
144
+ }
145
+
146
+ static typename Operation::Arguments to_bwd_arguments(
147
+ Arguments const& args,
148
+ ElementAccumulator* sum_OdO = nullptr, cute::tuple<int, int, _1> const& stride_sum_OdO = {},
149
+ ElementAccumulator* dQ_acc = nullptr, cute::tuple<int, int, int, _1> const& stride_dQ = {}
150
+ ) {
151
+ return typename Operation::Arguments{
152
+ args.problem_size,
153
+ { args.ptr_Q, args.stride_Q,
154
+ args.ptr_K, args.stride_K,
155
+ args.ptr_V, args.stride_V,
156
+ args.ptr_dO, args.stride_dO,
157
+ args.ptr_LSE, args.stride_LSE,
158
+ sum_OdO, stride_sum_OdO,
159
+ dQ_acc, stride_dQ },
160
+ { args.ptr_dK, args.stride_dK,
161
+ args.ptr_dV, args.stride_dV },
162
+ args.hw_info
163
+ };
164
+ }
165
+
166
+ public:
167
+
168
+ /// Determines whether the GEMM can execute the given problem.
169
+ static Status
170
+ can_implement(Arguments const& args) {
171
+ Status status = Status::kSuccess;
172
+
173
+ status = OperationSumOdO::can_implement(to_sum_OdO_arguments(args));
174
+ if (status != Status::kSuccess) {
175
+ return status;
176
+ }
177
+
178
+ status = OperationConvert::can_implement(to_convert_arguments(args));
179
+ if (status != Status::kSuccess) {
180
+ return status;
181
+ }
182
+
183
+ status = Operation::can_implement(to_bwd_arguments(args));
184
+ if (status != Status::kSuccess) {
185
+ return status;
186
+ }
187
+
188
+ return status;
189
+ }
190
+
191
+ /// Gets the workspace size
192
+ static size_t
193
+ get_workspace_size(Arguments const& args) {
194
+ auto [B, H, Q, K, D] = args.problem_size;
195
+ D = cutlass::round_up(D, 8); // Alignment
196
+ Q = cutlass::round_up(Q, 8); // Alignment
197
+ size_t workspace_bytes = 0;
198
+ // OdO vector
199
+ workspace_bytes += B*H*Q * sizeof(ElementAccumulator);
200
+ // FP32 versions of outputs that are churned (start off with Q only)
201
+ workspace_bytes += B*H*Q*D * sizeof(ElementAccumulator);
202
+ return workspace_bytes;
203
+ }
204
+
205
+ /// Initializes state from arguments.
206
+ Status
207
+ initialize_split(Arguments const& args, void* workspace_dQ, void* workspace_sum_OdO, cudaStream_t stream = nullptr) {
208
+ CUTLASS_TRACE_HOST("Universal::initialize_split() - workspace_dQ="
209
+ << workspace_dQ << ", workspace_sum_OdO=" << workspace_sum_OdO << "stream: " << (stream ? "non-null" : "null"));
210
+
211
+ auto [B, H, Q, K, D] = args.problem_size;
212
+ D = cutlass::round_up(D, 8); // Alignment
213
+ Q = cutlass::round_up(Q, 8); // Alignment
214
+ ElementAccumulator* sum_OdO = reinterpret_cast<ElementAccumulator*>(workspace_sum_OdO);
215
+ ElementAccumulator* dQ_acc = reinterpret_cast<ElementAccumulator*>(workspace_dQ);
216
+ params_.dQ_acc = dQ_acc;
217
+ params_.dQ_acc_size = B*H*Q*D * sizeof(ElementAccumulator);
218
+ auto args_sum_OdO = to_sum_OdO_arguments(args, sum_OdO);
219
+ auto args_convert = to_convert_arguments(args, dQ_acc);
220
+ params_.op_sum_OdO.initialize(args_sum_OdO, nullptr, stream);
221
+ params_.op_convert.initialize(args_convert, nullptr, stream);
222
+ auto args_bwd = to_bwd_arguments(args, sum_OdO, args_sum_OdO.stride_sum_OdO, dQ_acc, args_convert.stride_src_dQ);
223
+ params_.op.initialize(args_bwd, nullptr, stream);
224
+
225
+ return Status::kSuccess;
226
+ }
227
+
228
+ /// Initializes state from arguments.
229
+ Status
230
+ initialize(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr) {
231
+ CUTLASS_TRACE_HOST("Universal::initialize() - workspace "
232
+ << workspace << ", stream: " << (stream ? "non-null" : "null"));
233
+
234
+ auto [B, H, Q, K, D] = args.problem_size;
235
+ D = cutlass::round_up(D, 8); // Alignment
236
+ Q = cutlass::round_up(Q, 8); // Alignment
237
+ char* workspace_chr = reinterpret_cast<char*>(workspace);
238
+ ElementAccumulator* sum_OdO = reinterpret_cast<ElementAccumulator*>(workspace_chr);
239
+ workspace_chr += B*H*Q * sizeof(ElementAccumulator);
240
+ ElementAccumulator* dQ_acc = reinterpret_cast<ElementAccumulator*>(workspace_chr);
241
+ return initialize_split(args, dQ_acc, sum_OdO, stream);
242
+ }
243
+
244
+ /// Primary run() entry point API that is static allowing users to create and manage their own params.
245
+ /// Supplied params struct must be construct by calling Kernel::to_underling_arguments()
246
+ static Status
247
+ run(Params& params, cudaStream_t stream = nullptr) {
248
+ CUTLASS_TRACE_HOST("FmhaDeviceBwd::run()");
249
+
250
+ Status result = Status::kSuccess;
251
+ result = params.op_sum_OdO.run(stream);
252
+ if (result != Status::kSuccess) {
253
+ return result;
254
+ }
255
+
256
+ auto cuda_result = cudaMemsetAsync(params.dQ_acc, 0, params.dQ_acc_size, stream);
257
+ if (cuda_result != cudaSuccess) {
258
+ return Status::kErrorInternal;
259
+ }
260
+ result = params.op.run(stream);
261
+ if (result != Status::kSuccess) {
262
+ return result;
263
+ }
264
+
265
+ result = params.op_convert.run(stream);
266
+ if (result != Status::kSuccess) {
267
+ return result;
268
+ }
269
+
270
+ return Status::kSuccess;
271
+ }
272
+
273
+ //
274
+ // Non-static launch overloads that first create and set the internal params struct of this kernel handle.
275
+ //
276
+
277
+ /// Launches the kernel after first constructing Params internal state from supplied arguments.
278
+ Status
279
+ run(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr) {
280
+ Status status = initialize(args, workspace, stream);
281
+ if (Status::kSuccess == status) {
282
+ status = run(params_, stream);
283
+ }
284
+ return status;
285
+ }
286
+
287
+ /// Overload that allows a user to re-launch the same kernel without updating internal params struct.
288
+ Status
289
+ run(cudaStream_t stream = nullptr) {
290
+ return run(params_, stream);
291
+ }
292
+
293
+ };
294
+
295
+ ////////////////////////////////////////////////////////////////////////////////
296
+
297
+ } // namespace cutlass::fmha::device
298
+
299
+ ////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_builder.hpp ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "../collective/fmha_collective_tma.hpp"
35
+ #include "../collective/fmha_collective_tma_warpspecialized.hpp"
36
+ #include "../collective/fmha_epilogue.hpp"
37
+ #include "../kernel/fmha_kernel_tma.hpp"
38
+ #include "../kernel/fmha_kernel_tma_warpspecialized.hpp"
39
+ #include "../kernel/fmha_options.hpp"
40
+
41
+ namespace cutlass::fmha::kernel {
42
+
43
+ template<
44
+ class Element_,
45
+ class ElementAccumulatorQK_,
46
+ class ElementAccumulatorPV_,
47
+ class TileShape_, // BlockQO, BlockKV, BlockHead
48
+ class LayoutQ_,
49
+ class LayoutK_,
50
+ class LayoutV_,
51
+ class Fusion,
52
+ class DispatchPolicy,
53
+ class... Options
54
+ >
55
+ struct FmhaBuilder;
56
+
57
+ template<
58
+ class Element,
59
+ class ElementAccumulator,
60
+ class TileShape, // BlockQO, BlockKV, BlockHead
61
+ class Fusion,
62
+ class... Options
63
+ >
64
+ struct FmhaBuilder<
65
+ Element,
66
+ ElementAccumulator,
67
+ ElementAccumulator,
68
+ TileShape,
69
+ cute::tuple<int, _1, cute::tuple<int, int>>,
70
+ cute::tuple<int, _1, cute::tuple<int, int>>,
71
+ cute::tuple<int, _1, cute::tuple<int, int>>,
72
+ Fusion,
73
+ cutlass::gemm::KernelTma,
74
+ Options...
75
+ > {
76
+
77
+ using CollectiveMainloop = cutlass::fmha::collective::FmhaMainloopTma<Element, ElementAccumulator, TileShape, Fusion, Options...>;
78
+
79
+ using CollectiveEpilogue = cutlass::fmha::collective::FmhaFwdEpilogue<
80
+ Element, ElementAccumulator, typename CollectiveMainloop::TileShapePV>;
81
+
82
+ using Kernel = cutlass::fmha::kernel::FmhaKernelTma<CollectiveMainloop, CollectiveEpilogue, Options...>;
83
+ };
84
+
85
+ template<
86
+ class Element,
87
+ class ElementAccumulatorQK,
88
+ class ElementAccumulatorPV,
89
+ class TileShape, // BlockQO, BlockKV, BlockHead
90
+ class LayoutQ,
91
+ class LayoutK,
92
+ class LayoutV,
93
+ class Fusion,
94
+ class... Options
95
+ >
96
+ struct FmhaBuilder<
97
+ Element,
98
+ ElementAccumulatorQK,
99
+ ElementAccumulatorPV,
100
+ TileShape,
101
+ LayoutQ,
102
+ LayoutK,
103
+ LayoutV,
104
+ Fusion,
105
+ cutlass::gemm::KernelTmaWarpSpecializedCooperative,
106
+ Options...
107
+ > {
108
+
109
+ using CollectiveMainloop = cutlass::fmha::collective::FmhaMainloopTmaWarpSpecialized<
110
+ Element, ElementAccumulatorQK, ElementAccumulatorPV,
111
+ TileShape, LayoutQ, LayoutK, LayoutV,
112
+ Fusion, Options...>;
113
+
114
+ using CollectiveEpilogue = cutlass::fmha::collective::FmhaFwdEpilogue<
115
+ Element, ElementAccumulatorPV, typename CollectiveMainloop::TileShapePV>;
116
+
117
+ static constexpr bool kIsPersistent = find_option_t<Tag::kIsPersistent, false_type, Options...>::value;
118
+ using TileScheduler = std::conditional_t<kIsPersistent, cutlass::fmha::kernel::PersistentTileScheduler, cutlass::fmha::kernel::IndividualTileScheduler>;
119
+
120
+ using Kernel = cutlass::fmha::kernel::FmhaKernelTmaWarpSpecialized<CollectiveMainloop, CollectiveEpilogue, TileScheduler, Options...>;
121
+ };
122
+
123
+ template<
124
+ class Element,
125
+ class ElementAccumulatorQK,
126
+ class ElementAccumulatorPV,
127
+ class TileShape, // BlockQO, BlockKV, BlockHead
128
+ class LayoutQ,
129
+ class LayoutK,
130
+ class LayoutV,
131
+ class Fusion,
132
+ class... Options
133
+ >
134
+ struct FmhaBuilder<
135
+ Element,
136
+ ElementAccumulatorQK,
137
+ ElementAccumulatorPV,
138
+ TileShape,
139
+ LayoutQ,
140
+ LayoutK,
141
+ LayoutV,
142
+ Fusion,
143
+ cutlass::gemm::KernelTmaWarpSpecializedPingpong,
144
+ Options...
145
+ > {
146
+ using Kernel = typename FmhaBuilder<
147
+ Element, ElementAccumulatorQK, ElementAccumulatorPV,
148
+ TileShape,
149
+ LayoutQ, LayoutK, LayoutV,
150
+ Fusion,
151
+ cutlass::gemm::KernelTmaWarpSpecializedCooperative,
152
+ Options...,
153
+ Option<Tag::kIsPersistent, true_type>,
154
+ Option<Tag::kLoadsQSeparately, true_type>
155
+ >::Kernel;
156
+ };
157
+
158
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_bwd_convert.hpp ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cute/layout.hpp"
36
+
37
+ namespace cutlass::fmha::kernel {
38
+
39
+ using namespace cute;
40
+
41
+ template<class Element, class ElementAccumulator>
42
+ struct FmhaKernelBwdConvert {
43
+
44
+ struct Arguments {
45
+ tuple<int, int, int, int, int> problem_size;
46
+
47
+ const ElementAccumulator* ptr_src_dQ;
48
+ tuple<int, int, int, _1> stride_src_dQ;
49
+ const ElementAccumulator* ptr_src_dK;
50
+ tuple<int, int, int, _1> stride_src_dK;
51
+ const ElementAccumulator* ptr_src_dV;
52
+ tuple<int, int, int, _1> stride_src_dV;
53
+
54
+ Element* ptr_dest_dQ;
55
+ tuple<int, int, int, _1> stride_dest_dQ;
56
+ Element* ptr_dest_dK;
57
+ tuple<int, int, int, _1> stride_dest_dK;
58
+ Element* ptr_dest_dV;
59
+ tuple<int, int, int, _1> stride_dest_dV;
60
+ };
61
+
62
+ using Params = Arguments;
63
+
64
+ using ClusterShape = Shape<_1, _1, _1>;
65
+ static constexpr int SharedStorageSize = 0;
66
+
67
+ static const int MinBlocksPerMultiprocessor = 1;
68
+ static const int MaxThreadsPerBlock = 128;
69
+ using ArchTag = cutlass::arch::Sm90;
70
+
71
+ static const int kBlockSeq = 8;
72
+
73
+ static size_t get_workspace_size(Arguments const& args) { return 0; }
74
+ static cutlass::Status initialize_workspace(Arguments const&, void*, cudaStream_t) {
75
+ return cutlass::Status::kSuccess;
76
+ }
77
+
78
+ static const int kNumThreadsD = 16;
79
+ static const int kNumThreadsSeq = MaxThreadsPerBlock / kNumThreadsD;
80
+ static const int kElementsPerLoad = 4;
81
+
82
+ static const int kIterationsSeq = kBlockSeq / kNumThreadsSeq;
83
+
84
+ static bool can_implement(Arguments const& args) {
85
+ return get<4>(args.problem_size) % kElementsPerLoad == 0;
86
+ }
87
+
88
+ static dim3 get_grid_shape(Params const& params) {
89
+ dim3 grid(size<0>(params.problem_size), size<1>(params.problem_size), ceil_div(std::max(size<2>(params.problem_size), size<3>(params.problem_size)), kBlockSeq));
90
+ return grid;
91
+ }
92
+
93
+ static dim3 get_block_shape() {
94
+ dim3 block(kNumThreadsD, kNumThreadsSeq, 1);
95
+ return block;
96
+ }
97
+
98
+ static Params to_underlying_arguments(Arguments const& args, void* workspace) {
99
+ return args;
100
+ }
101
+
102
+ template<class StrideSrc, class StrideDest>
103
+ CUTLASS_DEVICE void copy(Params const& params, const ElementAccumulator* ptr_src, StrideSrc const& stride_src, Element* ptr_dest, StrideDest const& stride_dest, int count) {
104
+ auto ptr_src_bh = ptr_src + get<0>(stride_src) * blockIdx.x + get<1>(stride_src) * blockIdx.y;
105
+ auto ptr_dest_bh = ptr_dest + get<0>(stride_dest) * blockIdx.x + get<1>(stride_dest) * blockIdx.y;
106
+
107
+ for (int idx_s_t = threadIdx.y; idx_s_t < kBlockSeq; idx_s_t += kNumThreadsSeq) {
108
+ int idx_s = idx_s_t + kBlockSeq * blockIdx.z;
109
+ if (idx_s >= count) continue;
110
+ auto ptr_src_bhs = ptr_src_bh + idx_s * get<2>(stride_src);
111
+ auto ptr_dest_bhs = ptr_dest_bh + idx_s * get<2>(stride_dest);
112
+
113
+ for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < get<4>(params.problem_size); idx_d += kElementsPerLoad * kNumThreadsD) {
114
+ ElementAccumulator value_src[kElementsPerLoad];
115
+ Element value_dest[kElementsPerLoad];
116
+
117
+ using VecSrc = uint_bit_t<sizeof_bits_v<ElementAccumulator> * kElementsPerLoad>;
118
+ using VecDest = uint_bit_t<sizeof_bits_v<Element> * kElementsPerLoad>;
119
+ *reinterpret_cast<VecSrc*>(value_src) = *reinterpret_cast<const VecSrc*>(&ptr_src_bhs[idx_d]);
120
+
121
+ for (int v = 0; v < kElementsPerLoad; v++) {
122
+ value_dest[v] = value_src[v];
123
+ }
124
+
125
+ *reinterpret_cast<VecDest*>(&ptr_dest_bhs[idx_d]) = *reinterpret_cast<const VecDest*>(value_dest);
126
+ }
127
+ }
128
+ }
129
+
130
+ CUTLASS_DEVICE void operator()(const Params &params, char* smem) {
131
+ if (params.ptr_src_dQ != nullptr) {
132
+ copy(params, params.ptr_src_dQ, params.stride_src_dQ, params.ptr_dest_dQ, params.stride_dest_dQ, get<2>(params.problem_size));
133
+ }
134
+ if (params.ptr_src_dK != nullptr) {
135
+ copy(params, params.ptr_src_dK, params.stride_src_dK, params.ptr_dest_dK, params.stride_dest_dK, get<3>(params.problem_size));
136
+ }
137
+ if (params.ptr_src_dV != nullptr) {
138
+ copy(params, params.ptr_src_dV, params.stride_src_dV, params.ptr_dest_dV, params.stride_dest_dV, get<3>(params.problem_size));
139
+ }
140
+ }
141
+ };
142
+
143
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_bwd_sum_OdO.hpp ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cute/layout.hpp"
36
+
37
+ namespace cutlass::fmha::kernel {
38
+
39
+ using namespace cute;
40
+
41
+ template<class Element, class ElementAccumulator>
42
+ struct FmhaKernelBwdSumOdO {
43
+
44
+ struct Arguments {
45
+ cute::tuple<int, int, int, int, int> problem_size;
46
+
47
+ const Element* ptr_O;
48
+ cute::tuple<int, int, int, cute::_1> stride_O;
49
+ const Element* ptr_dO;
50
+ cute::tuple<int, int, int, cute::_1> stride_dO;
51
+
52
+ ElementAccumulator* ptr_sum_OdO;
53
+ cute::tuple<int, int, _1> stride_sum_OdO;
54
+ };
55
+
56
+ using Params = Arguments;
57
+
58
+ using ClusterShape = Shape<_1, _1, _1>;
59
+ static constexpr int SharedStorageSize = 0;
60
+
61
+ static const int MinBlocksPerMultiprocessor = 1;
62
+ static const int MaxThreadsPerBlock = 128;
63
+ using ArchTag = cutlass::arch::Sm90;
64
+
65
+ static size_t get_workspace_size(Arguments const& args) { return 0; }
66
+ static cutlass::Status initialize_workspace(Arguments const&, void*, cudaStream_t) {
67
+ return cutlass::Status::kSuccess;
68
+ }
69
+
70
+ static const int kBlockQ = 16;
71
+
72
+ static const int kNumThreadsD = 8;
73
+ static const int kNumThreadsQ = MaxThreadsPerBlock / kNumThreadsD;
74
+ static const int kElementsPerLoad = 2;
75
+
76
+ static const int kIterationsQ = kBlockQ / kNumThreadsQ;
77
+
78
+ static bool can_implement(Arguments const& args) {
79
+ return get<4>(args.problem_size) % kElementsPerLoad == 0;
80
+ }
81
+
82
+ static dim3 get_grid_shape(Params const& params) {
83
+ dim3 grid(ceil_div(size<2>(params.problem_size), kBlockQ), size<1>(params.problem_size), size<0>(params.problem_size));
84
+ return grid;
85
+ }
86
+
87
+ static dim3 get_block_shape() {
88
+ dim3 block(kNumThreadsD, kNumThreadsQ, 1);
89
+ return block;
90
+ }
91
+
92
+ static Params to_underlying_arguments(Arguments const& args, void* workspace) {
93
+ return args;
94
+ }
95
+
96
+ CUTLASS_DEVICE void operator()(const Params &params, char* smem) {
97
+ auto ptr_O_bh = params.ptr_O + blockIdx.y * get<1>(params.stride_O) + blockIdx.z * get<0>(params.stride_O);
98
+ auto ptr_dO_bh = params.ptr_dO + blockIdx.y * get<1>(params.stride_dO) + blockIdx.z * get<0>(params.stride_dO);
99
+ auto ptr_sum_OdO_bh = params.ptr_sum_OdO + blockIdx.y * get<1>(params.stride_sum_OdO) + blockIdx.z * get<0>(params.stride_sum_OdO);
100
+
101
+ CUTLASS_PRAGMA_UNROLL
102
+ for (int idx_q_t = threadIdx.y; idx_q_t < kBlockQ; idx_q_t += kNumThreadsQ) {
103
+ int idx_q = idx_q_t + kBlockQ * blockIdx.x;
104
+ if (idx_q >= get<2>(params.problem_size)) continue;
105
+ ElementAccumulator acc = 0;
106
+ auto ptr_O_bhq = ptr_O_bh + idx_q * get<2>(params.stride_O);
107
+ auto ptr_dO_bhq = ptr_dO_bh + idx_q * get<2>(params.stride_dO);
108
+ auto ptr_sum_OdO_bhq = ptr_sum_OdO_bh + idx_q * get<2>(params.stride_sum_OdO);
109
+
110
+ for (int idx_d = threadIdx.x * kElementsPerLoad; idx_d < get<4>(params.problem_size); idx_d += kElementsPerLoad * kNumThreadsD) {
111
+ Element value_O[kElementsPerLoad];
112
+ Element value_dO[kElementsPerLoad];
113
+
114
+ using Vec = uint_bit_t<sizeof_bits_v<Element> * kElementsPerLoad>;
115
+ *reinterpret_cast<Vec*>(value_O) = *reinterpret_cast<const Vec*>(&ptr_O_bhq[idx_d]);
116
+ *reinterpret_cast<Vec*>(value_dO) = *reinterpret_cast<const Vec*>(&ptr_dO_bhq[idx_d]);
117
+
118
+ for (int v = 0; v < kElementsPerLoad; v++) {
119
+ acc += value_O[v] * value_dO[v];
120
+ }
121
+ }
122
+
123
+ for (int i = 1; i < kNumThreadsD; i *= 2) {
124
+ acc += __shfl_xor_sync((uint32_t)-1, acc, i, kNumThreadsD);
125
+ }
126
+
127
+ if (threadIdx.x == 0) {
128
+ *ptr_sum_OdO_bhq = acc;
129
+ }
130
+ }
131
+ }
132
+ };
133
+
134
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_tma.hpp ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/pipeline/pipeline.hpp"
36
+ #include "cutlass/arch/arch.h"
37
+
38
+ #include "../kernel/fmha_tile_scheduler.hpp"
39
+ #include "../kernel/fmha_options.hpp"
40
+
41
+ namespace cutlass::fmha::kernel {
42
+
43
+ template<
44
+ class CollectiveMainloop,
45
+ class CollectiveEpilogue,
46
+ class... Options
47
+ >
48
+ struct FmhaKernelTma {
49
+
50
+ // Options
51
+ static constexpr int kBlocksPerSM = find_option_t<Tag::kBlocksPerSM, Int<2>, Options...>::value;
52
+
53
+ using Element = typename CollectiveMainloop::Element;
54
+ using ElementAccumulator = typename CollectiveMainloop::ElementAccumulator;
55
+
56
+ using TileScheduler = IndividualTileScheduler;
57
+
58
+ using StagesQ = typename CollectiveMainloop::StagesQ;
59
+ using Stages = typename CollectiveMainloop::Stages;
60
+
61
+ using TileShape = typename CollectiveMainloop::TileShape;
62
+ using ClusterShape = typename CollectiveMainloop::ClusterShape;
63
+
64
+ using MainloopPipeline = typename CollectiveMainloop::MainloopPipeline;
65
+ using MainloopPipelineQ = typename CollectiveMainloop::MainloopPipelineQ;
66
+
67
+ using SmemLayoutQ = typename CollectiveMainloop::SmemLayoutQ;
68
+ using SmemLayoutK = typename CollectiveMainloop::SmemLayoutK;
69
+
70
+ struct SharedStorage {
71
+ union {
72
+ typename CollectiveMainloop::SharedStorage mainloop;
73
+ typename CollectiveEpilogue::TensorStorage epilogue;
74
+ };
75
+
76
+ using PipelineStorage = typename MainloopPipeline::SharedStorage;
77
+ using PipelineStorageQ = typename MainloopPipelineQ::SharedStorage;
78
+ alignas(16) PipelineStorage pipeline_storage;
79
+ alignas(16) PipelineStorageQ pipeline_storage_q;
80
+
81
+ using EpiLoadPipelineStorage = typename CollectiveEpilogue::PipelineStorage;
82
+ alignas(16) EpiLoadPipelineStorage epi_load;
83
+ };
84
+
85
+ static constexpr int SharedStorageSize = sizeof(SharedStorage);
86
+
87
+ using ProblemShape = cute::tuple<int, int, int, int, int>;
88
+
89
+ struct Arguments {
90
+ ProblemShape problem_size;
91
+ typename CollectiveMainloop::Arguments mainloop;
92
+ typename CollectiveEpilogue::Arguments epilogue;
93
+ KernelHardwareInfo hw_info;
94
+ };
95
+
96
+ struct Params {
97
+ ProblemShape problem_size;
98
+ typename CollectiveMainloop::Params mainloop;
99
+ typename CollectiveEpilogue::Params epilogue;
100
+ typename TileScheduler::Params tile_scheduler;
101
+ };
102
+
103
+ using PipelineParams = typename MainloopPipeline::Params;
104
+ using PipelineState = typename cutlass::PipelineState<MainloopPipeline::Stages>;
105
+ using PipelineParamsQ = typename MainloopPipelineQ::Params;
106
+ using PipelineStateQ = typename cutlass::PipelineState<MainloopPipelineQ::Stages>;
107
+
108
+ static const int MinBlocksPerMultiprocessor = kBlocksPerSM;
109
+ static const int MaxThreadsPerBlock = CollectiveMainloop::MaxThreadsPerBlock;
110
+ using ArchTag = cutlass::arch::Sm90;
111
+
112
+ static size_t get_workspace_size(Arguments const& args) { return 0; }
113
+ static cutlass::Status initialize_workspace(Arguments const&, void*, cudaStream_t) {
114
+ return cutlass::Status::kSuccess;
115
+ }
116
+
117
+ static bool can_implement(Arguments const& args) {
118
+ return CollectiveMainloop::can_implement(args.problem_size, args.mainloop);
119
+ }
120
+
121
+ static dim3 get_grid_shape(Params const& params) {
122
+ return TileScheduler::get_grid_shape(params.tile_scheduler);
123
+ }
124
+
125
+ static dim3 get_block_shape() {
126
+ dim3 block(MaxThreadsPerBlock, 1, 1);
127
+ return block;
128
+ }
129
+
130
+ static Params to_underlying_arguments(Arguments const& args, void* workspace) {
131
+ return Params{
132
+ args.problem_size,
133
+ CollectiveMainloop::to_underlying_arguments(args.problem_size, args.mainloop, workspace),
134
+ CollectiveEpilogue::to_underlying_arguments(args.problem_size, args.epilogue, workspace),
135
+ TileScheduler::to_underlying_arguments(args.problem_size, args.hw_info, ClusterShape{}, TileShape{})
136
+ };
137
+ }
138
+
139
+ CUTLASS_DEVICE void operator()(const Params &params, char* smem) {
140
+ #if ! defined(CUTLASS_ARCH_MMA_SM90A_ENABLED)
141
+ printf("ERROR : Arch conditional MMA instruction used without targeting appropriate compute capability. Aborting.\n");
142
+ #else
143
+ TileScheduler tile_scheduler{params.tile_scheduler};
144
+
145
+ // Shared memory.
146
+ auto& storage = *reinterpret_cast<SharedStorage*>(smem);
147
+
148
+ int thread_idx = int(threadIdx.x);
149
+
150
+ uint32_t block_rank_in_cluster = cute::block_rank_in_cluster();
151
+
152
+ int warp_idx = cutlass::canonical_warp_idx_sync();
153
+ int warp_group_thread_idx = thread_idx % cutlass::NumThreadsPerWarpGroup;
154
+ int lane_predicate = cute::elect_one_sync();
155
+
156
+ // Issue Tma Descriptor Prefetch from a single thread
157
+ if ((warp_idx == 0) && lane_predicate) {
158
+ CollectiveMainloop::prefetch_tma_descriptors(params.mainloop);
159
+ }
160
+
161
+
162
+ PipelineParamsQ pipeline_params_q;
163
+ pipeline_params_q.transaction_bytes = size(SmemLayoutQ{}(_,_,_0{})) * sizeof(Element); // Q
164
+ pipeline_params_q.role = MainloopPipelineQ::ThreadCategory::ProducerConsumer;
165
+ pipeline_params_q.is_leader = warp_group_thread_idx == 0;
166
+ pipeline_params_q.num_consumers = cutlass::NumThreadsPerWarpGroup;
167
+
168
+ PipelineParams pipeline_params;
169
+ pipeline_params.transaction_bytes = size(SmemLayoutK{}(_,_,_0{})) * sizeof(Element); // KV
170
+ pipeline_params.role = MainloopPipeline::ThreadCategory::ProducerConsumer;
171
+ pipeline_params.is_leader = warp_group_thread_idx == 0;
172
+ pipeline_params.num_consumers = cutlass::NumThreadsPerWarpGroup;
173
+
174
+ MainloopPipelineQ pipeline_q(storage.pipeline_storage_q, pipeline_params_q, Shape<_1, _1, _1>{});
175
+ MainloopPipeline pipeline(storage.pipeline_storage, pipeline_params, ClusterShape{});
176
+
177
+ using EpiLoadPipeline = typename CollectiveEpilogue::LoadPipeline;
178
+ typename EpiLoadPipeline::Params epi_load_pipeline_params;
179
+ epi_load_pipeline_params.role = EpiLoadPipeline::ThreadCategory::ProducerConsumer;
180
+ epi_load_pipeline_params.dst_blockid = cute::block_rank_in_cluster();
181
+ epi_load_pipeline_params.producer_arv_count = NumThreadsPerWarp;
182
+ epi_load_pipeline_params.consumer_arv_count = NumThreadsPerWarpGroup;
183
+ epi_load_pipeline_params.transaction_bytes = CollectiveEpilogue::TmaTransactionBytes;
184
+ EpiLoadPipeline epi_load_pipeline(storage.epi_load, epi_load_pipeline_params);
185
+
186
+ // State variables used for iterating the circular buffer
187
+ // smem_pipe_read / release is used by the consumer of SMEM data - i.e MMA
188
+ // smem_pipe_write is used by the producer of SMEM data - i.e TMA
189
+ PipelineState smem_pipe_read;
190
+ PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();
191
+
192
+ PipelineStateQ smem_pipe_read_q;
193
+ PipelineStateQ smem_pipe_write_q = cutlass::make_producer_start_state<MainloopPipelineQ>();
194
+
195
+ // We need this to guarantee that the Pipeline init is visible
196
+ // To all producers and consumer blocks in the Cluster
197
+ // and to finish smem init
198
+ if constexpr (size(ClusterShape{}) > 1) {
199
+ cute::cluster_arrive_relaxed();
200
+ cute::cluster_wait();
201
+ }
202
+ else {
203
+ __syncthreads();
204
+ }
205
+
206
+ auto blk_coord = tile_scheduler.get_block_coord();
207
+
208
+ CollectiveMainloop collective_mainloop;
209
+ auto result = collective_mainloop.compute(
210
+ block_rank_in_cluster,
211
+ blk_coord, params.mainloop, params.problem_size,
212
+ pipeline, smem_pipe_read, smem_pipe_write,
213
+ pipeline_q, smem_pipe_read_q, smem_pipe_write_q,
214
+ storage.mainloop
215
+ );
216
+
217
+ CollectiveEpilogue epilogue;
218
+ epilogue(typename CollectiveMainloop::TileShapePV{}, blk_coord,
219
+ result, typename CollectiveMainloop::TiledMmaPV{},
220
+ params.problem_size, params.epilogue,
221
+ epi_load_pipeline, storage.epilogue);
222
+ #endif
223
+ }
224
+ };
225
+
226
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_kernel_tma_warpspecialized.hpp ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/arch/reg_reconfig.h"
36
+ #include "cutlass/pipeline/pipeline.hpp"
37
+ #include "cutlass/arch/arch.h"
38
+
39
+ #include "../kernel/fmha_options.hpp"
40
+
41
+ namespace cutlass::fmha::kernel {
42
+
43
+ using namespace cute;
44
+
45
+ template<
46
+ class CollectiveMainloop,
47
+ class CollectiveEpilogue,
48
+ class TileScheduler,
49
+ class... Options
50
+ >
51
+ struct FmhaKernelTmaWarpSpecialized {
52
+
53
+ // Options
54
+ static constexpr bool kIsEpilogueLocked = find_option_t<Tag::kIsEpilogueLocked, false_type, Options...>::value;
55
+ static constexpr bool kLoadsQSeparately = find_option_t<Tag::kLoadsQSeparately, false_type, Options...>::value;
56
+
57
+
58
+ static const int NumLoadWarpGroups = 1;
59
+ static constexpr int NumMmaWarpGroups = CollectiveMainloop::NumMmaWarpGroups;
60
+
61
+ using TileShape = typename CollectiveMainloop::TileShape;
62
+ using ClusterShape = typename CollectiveMainloop::ClusterShape;
63
+
64
+ using MainloopPipelineOuter = typename CollectiveMainloop::MainloopPipelineQ;
65
+ using MainloopPipelineInner = typename CollectiveMainloop::MainloopPipeline;
66
+ using MainloopPipelineReducer = cutlass::PipelineAsync<2>;
67
+
68
+ static constexpr uint32_t StagesPerMathWarpGroup = 2;
69
+ using MathWarpGroupOrderBarrier = cutlass::OrderedSequenceBarrier<
70
+ StagesPerMathWarpGroup, NumMmaWarpGroups>;
71
+
72
+ struct TensorStorageStruct {
73
+ typename CollectiveMainloop::SharedStorage mainloop;
74
+ typename CollectiveEpilogue::TensorStorage epilogue[NumMmaWarpGroups];
75
+ };
76
+ union TensorStorageUnion {
77
+ typename CollectiveMainloop::SharedStorage mainloop;
78
+ typename CollectiveEpilogue::TensorStorage epilogue[NumMmaWarpGroups];
79
+ };
80
+ using TensorStorage = std::conditional_t<CollectiveMainloop::kIsPersistent, TensorStorageStruct, TensorStorageUnion>;
81
+
82
+ struct SharedStorage {
83
+ TensorStorage tensors;
84
+
85
+ using PipelineStorageInner = typename MainloopPipelineInner::SharedStorage;
86
+ using PipelineStorageOuter = typename MainloopPipelineOuter::SharedStorage;
87
+ using PipelineStorageReducer = typename MainloopPipelineReducer::SharedStorage;
88
+
89
+ alignas(16) PipelineStorageInner pipeline_storage_inner;
90
+ alignas(16) PipelineStorageOuter pipeline_storage_outer;
91
+ alignas(16) PipelineStorageReducer pipeline_storage_reducer;
92
+
93
+ using MathWarpGroupOrderBarrierStorage = typename MathWarpGroupOrderBarrier::SharedStorage;
94
+ alignas(16) MathWarpGroupOrderBarrierStorage math_wg_order;
95
+
96
+ alignas(16) cutlass::arch::ClusterBarrier load_warp_barrier;
97
+
98
+ using EpiLoadPipelineStorage = typename CollectiveEpilogue::PipelineStorage;
99
+ alignas(16) EpiLoadPipelineStorage epi_load;
100
+ };
101
+
102
+ static constexpr int SharedStorageSize = sizeof(SharedStorage);
103
+
104
+ using ProblemShape = cute::tuple<int, int, int, int, int>;
105
+
106
+ struct Arguments {
107
+ ProblemShape problem_size;
108
+ typename CollectiveMainloop::Arguments mainloop;
109
+ typename CollectiveEpilogue::Arguments epilogue;
110
+ KernelHardwareInfo hw_info;
111
+ };
112
+
113
+ struct Params {
114
+ ProblemShape problem_size;
115
+ typename CollectiveMainloop::Params mainloop;
116
+ typename CollectiveEpilogue::Params epilogue;
117
+ typename TileScheduler::Params tile_scheduler;
118
+ };
119
+
120
+ using PipelineParamsInner = typename MainloopPipelineInner::Params;
121
+ using PipelineStateInner = typename cutlass::PipelineState<MainloopPipelineInner::Stages>;
122
+ using PipelineParamsOuter = typename MainloopPipelineOuter::Params;
123
+ using PipelineStateOuter = typename cutlass::PipelineState<MainloopPipelineOuter::Stages>;
124
+ using PipelineParamsReducer = typename MainloopPipelineReducer::Params;
125
+ using PipelineStateReducer = typename cutlass::PipelineState<MainloopPipelineReducer::Stages>;
126
+
127
+ static const int MinBlocksPerMultiprocessor = 1;
128
+ static const int MaxThreadsPerBlock = (NumMmaWarpGroups + NumLoadWarpGroups) * cutlass::NumThreadsPerWarpGroup;
129
+ using ArchTag = cutlass::arch::Sm90;
130
+
131
+ static constexpr uint32_t LoadRegisterRequirement = 40 - 2 * 8;
132
+ static constexpr uint32_t TotalRegisterSupply = (64*1024 / MaxThreadsPerBlock / MinBlocksPerMultiprocessor / 8) * 8 * MaxThreadsPerBlock / cutlass::NumThreadsPerWarpGroup;
133
+ static constexpr uint32_t MmaRegisterRequirement = ((TotalRegisterSupply - LoadRegisterRequirement) / NumMmaWarpGroups / 8) * 8;
134
+
135
+ static size_t get_workspace_size(Arguments const& args) { return 0; }
136
+ static cutlass::Status initialize_workspace(Arguments const&, void*, cudaStream_t) {
137
+ return cutlass::Status::kSuccess;
138
+ }
139
+
140
+ static bool can_implement(Arguments const& args) {
141
+ return CollectiveMainloop::can_implement(args.problem_size, args.mainloop);
142
+ }
143
+
144
+ static dim3 get_grid_shape(Params const& params) {
145
+ return TileScheduler::get_grid_shape(params.tile_scheduler);
146
+ }
147
+
148
+ static dim3 get_block_shape() {
149
+ dim3 block(MaxThreadsPerBlock, 1, 1);
150
+ return block;
151
+ }
152
+
153
+ static Params to_underlying_arguments(Arguments const& args, void* workspace) {
154
+ return Params{
155
+ args.problem_size,
156
+ CollectiveMainloop::to_underlying_arguments(args.problem_size, args.mainloop, workspace),
157
+ CollectiveEpilogue::to_underlying_arguments(args.problem_size, args.epilogue, workspace),
158
+ TileScheduler::to_underlying_arguments(args.problem_size, args.hw_info, ClusterShape{}, TileShape{})
159
+ };
160
+ }
161
+
162
+ CUTLASS_DEVICE void operator()(const Params &params, char* smem) {
163
+ #if ! defined(CUTLASS_ARCH_MMA_SM90A_ENABLED)
164
+ printf("ERROR : Arch conditional MMA instruction used without targeting appropriate compute capability. Aborting.\n");
165
+ #else
166
+
167
+ enum class WarpGroupRole {
168
+ Producer = 0,
169
+ Consumer0 = 1,
170
+ Consumer1 = 2,
171
+ Consumer2 = 3,
172
+ Consumer3 = 4,
173
+ };
174
+ enum class ProducerWarpRole {
175
+ LoadKV = 1,
176
+ Reducer = 0,
177
+ MaybeLoadQ = 2, // is kLoadsQSeparately is true, this warp loads Q (otherwise warp 0 does it)
178
+ MainloopEpilogue = 3,
179
+ };
180
+
181
+ static constexpr ProducerWarpRole WarpRoleLoadQ = kLoadsQSeparately ? ProducerWarpRole::MaybeLoadQ : ProducerWarpRole::LoadKV;
182
+
183
+ TileScheduler tile_scheduler{params.tile_scheduler};
184
+
185
+ // Shared memory.
186
+ auto& storage = *reinterpret_cast<SharedStorage*>(smem);
187
+
188
+ int lane_idx = cutlass::canonical_lane_idx();
189
+ int warp_idx = cutlass::canonical_warp_idx_sync();
190
+ int warp_idx_in_warp_group = warp_idx % cutlass::NumWarpsPerWarpGroup;
191
+ int warp_group_idx = cutlass::canonical_warp_group_idx();
192
+ auto warp_group_role = WarpGroupRole(warp_group_idx);
193
+ auto producer_warp_role = ProducerWarpRole(warp_idx_in_warp_group);
194
+ int consumer_warp_group_idx = warp_group_idx - (int) WarpGroupRole::Consumer0;
195
+ int lane_predicate = cute::elect_one_sync();
196
+ uint32_t block_rank_in_cluster = cute::block_rank_in_cluster();
197
+
198
+ // Issue Tma Descriptor Prefetch from a single thread
199
+ if ((warp_idx == 0) && lane_predicate) {
200
+ CollectiveMainloop::prefetch_tma_descriptors(params.mainloop);
201
+ }
202
+
203
+ PipelineParamsOuter pipeline_params_outer;
204
+ pipeline_params_outer.transaction_bytes = CollectiveMainloop::kOuterLoadBytes;
205
+ pipeline_params_outer.is_leader = lane_predicate && (producer_warp_role == WarpRoleLoadQ);
206
+ pipeline_params_outer.num_consumers = cutlass::NumThreadsPerWarpGroup;
207
+
208
+ PipelineParamsInner pipeline_params_inner;
209
+ pipeline_params_inner.transaction_bytes = CollectiveMainloop::kInnerLoadBytes;
210
+ pipeline_params_inner.is_leader = lane_predicate && (producer_warp_role == ProducerWarpRole::LoadKV);
211
+ pipeline_params_inner.num_consumers = NumMmaWarpGroups * cutlass::NumThreadsPerWarpGroup;
212
+
213
+ PipelineParamsReducer pipeline_params_reducer;
214
+ pipeline_params_reducer.producer_arv_count = NumMmaWarpGroups * cutlass::NumThreadsPerWarpGroup;
215
+ pipeline_params_reducer.consumer_arv_count = cutlass::NumThreadsPerWarp;
216
+
217
+ using EpiLoadPipeline = typename CollectiveEpilogue::LoadPipeline;
218
+ typename EpiLoadPipeline::Params epi_load_pipeline_params;
219
+
220
+ if (warp_group_role == WarpGroupRole::Producer && producer_warp_role == ProducerWarpRole::MainloopEpilogue) {
221
+ epi_load_pipeline_params.role = EpiLoadPipeline::ThreadCategory::Producer;
222
+ }
223
+ if (warp_group_role == WarpGroupRole::Producer && producer_warp_role == ProducerWarpRole::LoadKV) {
224
+ pipeline_params_inner.role = MainloopPipelineInner::ThreadCategory::Producer;
225
+ }
226
+ if (warp_group_role == WarpGroupRole::Producer && producer_warp_role == WarpRoleLoadQ) {
227
+ pipeline_params_outer.role = MainloopPipelineOuter::ThreadCategory::Producer;
228
+ }
229
+ if (warp_group_role == WarpGroupRole::Producer && producer_warp_role == ProducerWarpRole::Reducer) {
230
+ pipeline_params_reducer.role = MainloopPipelineReducer::ThreadCategory::Consumer;
231
+ }
232
+ if (warp_group_role == WarpGroupRole::Consumer0 ||
233
+ warp_group_role == WarpGroupRole::Consumer1 ||
234
+ warp_group_role == WarpGroupRole::Consumer2 ||
235
+ warp_group_role == WarpGroupRole::Consumer3
236
+ ) {
237
+ pipeline_params_inner.role = MainloopPipelineInner::ThreadCategory::Consumer;
238
+ pipeline_params_outer.role = MainloopPipelineOuter::ThreadCategory::Consumer;
239
+ pipeline_params_reducer.role = MainloopPipelineReducer::ThreadCategory::Producer;
240
+ epi_load_pipeline_params.role = EpiLoadPipeline::ThreadCategory::Consumer;
241
+ }
242
+
243
+ MainloopPipelineOuter pipeline_outer(storage.pipeline_storage_outer, pipeline_params_outer, Shape<_1, _1, _1>{});
244
+ MainloopPipelineInner pipeline_inner(storage.pipeline_storage_inner, pipeline_params_inner, ClusterShape{});
245
+ MainloopPipelineReducer pipeline_reducer(storage.pipeline_storage_reducer, pipeline_params_reducer);
246
+
247
+ // State variables used for iterating the circular buffer
248
+ // smem_pipe_read / release is used by the consumer of SMEM data - i.e MMA
249
+ // smem_pipe_write is used by the producer of SMEM data - i.e TMA
250
+ PipelineStateInner smem_pipe_read_inner;
251
+ PipelineStateInner smem_pipe_write_inner = cutlass::make_producer_start_state<MainloopPipelineInner>();
252
+
253
+ PipelineStateOuter smem_pipe_read_outer;
254
+ PipelineStateOuter smem_pipe_write_outer = cutlass::make_producer_start_state<MainloopPipelineOuter>();
255
+
256
+ PipelineStateReducer smem_pipe_read_reducer;
257
+ PipelineStateReducer smem_pipe_write_reducer = cutlass::make_producer_start_state<MainloopPipelineReducer>();
258
+
259
+ typename MathWarpGroupOrderBarrier::Params params_math_wg_order_barrier;
260
+ // DMA Load WG will not participate in these Ordered Barrier syncs
261
+ params_math_wg_order_barrier.group_id = consumer_warp_group_idx;
262
+ params_math_wg_order_barrier.group_size = cutlass::NumThreadsPerWarpGroup; // Number of threads / participants in a group
263
+ MathWarpGroupOrderBarrier math_wg_order_barrier(storage.math_wg_order, params_math_wg_order_barrier);
264
+
265
+ // Epilogue Load pipeline
266
+ epi_load_pipeline_params.dst_blockid = cute::block_rank_in_cluster();
267
+ epi_load_pipeline_params.producer_arv_count = NumThreadsPerWarp;
268
+ epi_load_pipeline_params.consumer_arv_count = NumThreadsPerWarpGroup;
269
+ epi_load_pipeline_params.transaction_bytes = CollectiveEpilogue::TmaTransactionBytes;
270
+ EpiLoadPipeline epi_load_pipeline(storage.epi_load, epi_load_pipeline_params);
271
+
272
+ if constexpr (kLoadsQSeparately) {
273
+ if ((warp_idx == 0) && lane_predicate) {
274
+ storage.load_warp_barrier.init(2 * cutlass::NumThreadsPerWarp);
275
+ }
276
+ cutlass::arch::fence_barrier_init();
277
+ }
278
+
279
+ // We need this to guarantee that the Pipeline init is visible
280
+ // To all producers and consumer blocks in the Cluster
281
+ // and to finish smem init
282
+ if constexpr (size(ClusterShape{}) > 1) {
283
+ cute::cluster_arrive_relaxed();
284
+ cute::cluster_wait();
285
+ }
286
+ else {
287
+ __syncthreads();
288
+ }
289
+
290
+ CollectiveMainloop collective_mainloop;
291
+
292
+ if (warp_group_role == WarpGroupRole::Producer) {
293
+ cutlass::arch::warpgroup_reg_dealloc<LoadRegisterRequirement>();
294
+ if (producer_warp_role == ProducerWarpRole::LoadKV) {
295
+ bool do_barrier = kLoadsQSeparately;
296
+
297
+ CUTLASS_PRAGMA_NO_UNROLL
298
+ for (; tile_scheduler.is_valid(); ++tile_scheduler) {
299
+ auto blk_coord = tile_scheduler.get_block_coord();
300
+ collective_mainloop.template load_kv_maybe_q<!kLoadsQSeparately>(
301
+ block_rank_in_cluster,
302
+ blk_coord, params.mainloop, params.problem_size,
303
+ pipeline_inner, smem_pipe_write_inner,
304
+ pipeline_outer, smem_pipe_write_outer,
305
+ storage.tensors.mainloop,
306
+ storage.load_warp_barrier, do_barrier
307
+ );
308
+ do_barrier = false;
309
+ }
310
+ }
311
+ else if (kLoadsQSeparately && (producer_warp_role == ProducerWarpRole::MaybeLoadQ)) {
312
+ bool do_barrier = true;
313
+
314
+ CUTLASS_PRAGMA_NO_UNROLL
315
+ for (; tile_scheduler.is_valid(); ++tile_scheduler) {
316
+ auto blk_coord = tile_scheduler.get_block_coord();
317
+ collective_mainloop.load_maybe_q(
318
+ blk_coord, params.mainloop, params.problem_size,
319
+ pipeline_outer, smem_pipe_write_outer,
320
+ storage.tensors.mainloop,
321
+ storage.load_warp_barrier, do_barrier
322
+ );
323
+ do_barrier = false;
324
+ }
325
+ } else if (producer_warp_role == ProducerWarpRole::Reducer) {
326
+ for (; tile_scheduler.is_valid(); ++tile_scheduler) {
327
+ auto blk_coord = tile_scheduler.get_block_coord();
328
+ collective_mainloop.reduce(
329
+ blk_coord, params.mainloop, params.problem_size,
330
+ pipeline_reducer, smem_pipe_read_reducer,
331
+ storage.tensors.mainloop
332
+ );
333
+ }
334
+ }
335
+ }
336
+ else if (
337
+ warp_group_role == WarpGroupRole::Consumer0 ||
338
+ warp_group_role == WarpGroupRole::Consumer1 ||
339
+ warp_group_role == WarpGroupRole::Consumer2 ||
340
+ warp_group_role == WarpGroupRole::Consumer3
341
+ ) {
342
+ cutlass::arch::warpgroup_reg_alloc<MmaRegisterRequirement>();
343
+ CUTLASS_PRAGMA_NO_UNROLL
344
+ for (; tile_scheduler.is_valid(); ++tile_scheduler) {
345
+ auto blk_coord = tile_scheduler.get_block_coord();
346
+ auto wg_coord = blk_coord;
347
+
348
+ constexpr int kOuterLoads = CollectiveMainloop::kOuterLoads;
349
+
350
+ if (warp_group_role == WarpGroupRole::Consumer0) {
351
+ smem_pipe_read_outer.advance(0 * kOuterLoads);
352
+ }
353
+ else if (warp_group_role == WarpGroupRole::Consumer1) {
354
+ smem_pipe_read_outer.advance(1 * kOuterLoads);
355
+ }
356
+ else if (warp_group_role == WarpGroupRole::Consumer2) {
357
+ smem_pipe_read_outer.advance(2 * kOuterLoads);
358
+ }
359
+ else if (warp_group_role == WarpGroupRole::Consumer3) {
360
+ smem_pipe_read_outer.advance(3 * kOuterLoads);
361
+ }
362
+
363
+ constexpr int wg_dim = is_constant<0, decltype(get<1>(wg_coord))>::value ? 0 : 1;
364
+ auto& wg_block = get<wg_dim>(wg_coord);
365
+ if (warp_group_role == WarpGroupRole::Consumer0) {
366
+ wg_block = NumMmaWarpGroups * wg_block + 0;
367
+ }
368
+ else if (warp_group_role == WarpGroupRole::Consumer1) {
369
+ wg_block = NumMmaWarpGroups * wg_block + 1;
370
+ }
371
+ else if (warp_group_role == WarpGroupRole::Consumer2) {
372
+ wg_block = NumMmaWarpGroups * wg_block + 2;
373
+ }
374
+ else if (warp_group_role == WarpGroupRole::Consumer3) {
375
+ wg_block = NumMmaWarpGroups * wg_block + 3;
376
+ }
377
+
378
+ auto result = collective_mainloop.compute(
379
+ blk_coord, wg_coord,
380
+ params.mainloop, params.problem_size,
381
+ pipeline_inner, smem_pipe_read_inner,
382
+ pipeline_outer, smem_pipe_read_outer,
383
+ pipeline_reducer, smem_pipe_write_reducer,
384
+ storage.tensors.mainloop,
385
+ math_wg_order_barrier
386
+ );
387
+
388
+ if (warp_group_role == WarpGroupRole::Consumer0) {
389
+ smem_pipe_read_outer.advance(kOuterLoads * (NumMmaWarpGroups - 0));
390
+ }
391
+ if constexpr (NumMmaWarpGroups >= 2) {
392
+ if (warp_group_role == WarpGroupRole::Consumer1) {
393
+ smem_pipe_read_outer.advance(kOuterLoads * (NumMmaWarpGroups - 1));
394
+ }
395
+ }
396
+ if constexpr (NumMmaWarpGroups >= 3) {
397
+ if (warp_group_role == WarpGroupRole::Consumer2) {
398
+ smem_pipe_read_outer.advance(kOuterLoads * (NumMmaWarpGroups - 2));
399
+ }
400
+ }
401
+ if constexpr (NumMmaWarpGroups >= 4) {
402
+ if (warp_group_role == WarpGroupRole::Consumer3) {
403
+ smem_pipe_read_outer.advance(kOuterLoads * (NumMmaWarpGroups - 3));
404
+ }
405
+ }
406
+
407
+ if constexpr (kIsEpilogueLocked) ; math_wg_order_barrier.wait();
408
+
409
+ CollectiveEpilogue epilogue;
410
+ epilogue(typename CollectiveMainloop::TileShapePV{}, wg_coord,
411
+ result, typename CollectiveMainloop::TiledMmaPV{},
412
+ params.problem_size, params.epilogue,
413
+ epi_load_pipeline, storage.tensors.epilogue[consumer_warp_group_idx]);
414
+
415
+ if constexpr (kIsEpilogueLocked) ; math_wg_order_barrier.arrive();
416
+ }
417
+ }
418
+ #endif
419
+ }
420
+ };
421
+
422
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_options.hpp ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+
36
+ namespace cutlass::fmha::kernel {
37
+
38
+ template<auto kTag, typename Default, typename... Options>
39
+ struct find_option;
40
+
41
+ template<auto kTag, typename Default>
42
+ struct find_option<kTag, Default> {
43
+ using option_value = Default;
44
+ };
45
+
46
+ template<auto kTag, typename Default, typename Option, typename... Options>
47
+ struct find_option<kTag, Default, Option, Options...> :
48
+ std::conditional_t<
49
+ Option::tag == kTag,
50
+ Option,
51
+ find_option<kTag, Default, Options...>
52
+ >
53
+ {};
54
+
55
+ template<auto kTag, typename Default, typename... Options>
56
+ using find_option_t = typename find_option<kTag, Default, Options...>::option_value;
57
+
58
+ enum class Tag {
59
+ kIsPersistent,
60
+ kNumMmaWarpGroups,
61
+ kLoadsQSeparately,
62
+
63
+ kIsMainloopLocked,
64
+ kIsEpilogueLocked,
65
+
66
+ kStagesQ,
67
+ kStagesKV,
68
+
69
+ kEpilogueKind,
70
+
71
+ kBlocksPerSM,
72
+ kClusterM,
73
+
74
+ kAccQK
75
+ };
76
+
77
+ template<auto kTag, class Value>
78
+ struct Option {
79
+ static constexpr auto tag = kTag;
80
+ using option_value = Value;
81
+ };
82
+
83
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/kernel/fmha_tile_scheduler.hpp ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+
34
+ #include "cutlass/cutlass.h"
35
+ #include "cutlass/fast_math.h"
36
+ #include "cutlass/kernel_hardware_info.h"
37
+
38
+ namespace cutlass::fmha::kernel {
39
+
40
+ ////////////////////////////////////////////////////////////////////////////////
41
+
42
+ struct IndividualTileScheduler {
43
+
44
+ struct Params {
45
+ dim3 grid;
46
+ };
47
+
48
+ bool valid_ = true;
49
+
50
+ CUTLASS_DEVICE
51
+ IndividualTileScheduler(Params const&) {}
52
+
53
+ template<class ProblemSize, class ClusterShape, class TileShape>
54
+ static Params to_underlying_arguments(
55
+ ProblemSize const& problem_size, KernelHardwareInfo hw_info,
56
+ ClusterShape const& cluster_shape, TileShape const& tile_shape)
57
+ {
58
+ using namespace cute;
59
+ dim3 grid(round_up(ceil_div(size<2>(problem_size), size<0>(tile_shape)), size<0>(cluster_shape)), size<0>(problem_size), size<1>(problem_size));
60
+ return Params{ grid };
61
+ }
62
+
63
+ static dim3 get_grid_shape(Params const& params) {
64
+ return params.grid;
65
+ }
66
+
67
+ CUTLASS_DEVICE
68
+ bool is_valid() {
69
+ return valid_;
70
+ }
71
+
72
+ CUTLASS_DEVICE
73
+ auto get_block_coord() {
74
+ using namespace cute;
75
+ return make_coord(blockIdx.x, _0{}, make_coord(blockIdx.y, blockIdx.z));
76
+ }
77
+
78
+ CUTLASS_DEVICE
79
+ IndividualTileScheduler& operator++() {
80
+ valid_ = false;
81
+ return *this;
82
+ }
83
+ };
84
+
85
+ ////////////////////////////////////////////////////////////////////////////////
86
+
87
+ struct PersistentTileScheduler {
88
+
89
+ struct Params {
90
+ int num_blocks;
91
+ FastDivmod divmod_m_block;
92
+ FastDivmod divmod_b;
93
+ FastDivmod divmod_h;
94
+
95
+ KernelHardwareInfo hw_info;
96
+ };
97
+
98
+ int block_idx = 0;
99
+ Params params;
100
+
101
+ CUTLASS_DEVICE
102
+ PersistentTileScheduler(Params const& params) : block_idx(blockIdx.x), params(params) {}
103
+
104
+ template<class ProblemSize, class ClusterShape, class TileShape>
105
+ static Params to_underlying_arguments(
106
+ ProblemSize const& problem_size, KernelHardwareInfo hw_info,
107
+ ClusterShape const& cluster_shape, TileShape const& tile_shape)
108
+ {
109
+ using namespace cute;
110
+ // Get SM count if needed, otherwise use user supplied SM count
111
+ int sm_count = hw_info.sm_count;
112
+ if (sm_count <= 0) {
113
+ CUTLASS_TRACE_HOST(" WARNING: Arguments do not include a valid SM count.\n"
114
+ " For optimal performance, populate the arguments KernelHardwareInfo struct with the SM count.");
115
+ sm_count = KernelHardwareInfo::query_device_multiprocessor_count(hw_info.device_id);
116
+ }
117
+
118
+ CUTLASS_TRACE_HOST("to_underlying_arguments(): Setting persistent grid SM count to " << sm_count);
119
+ hw_info.sm_count = sm_count;
120
+
121
+ int num_m_blocks = cutlass::round_up(ceil_div(size<2>(problem_size), size<0>(tile_shape)), size<0>(cluster_shape));
122
+ int num_blocks = num_m_blocks * size<0>(problem_size) * size<1>(problem_size);
123
+
124
+ return Params {
125
+ num_blocks,
126
+ { num_m_blocks}, { size<0>(problem_size) }, { size<1>(problem_size) },
127
+ hw_info
128
+ };
129
+ }
130
+
131
+ static dim3 get_grid_shape(Params const& params) {
132
+ dim3 grid(std::min(params.num_blocks, params.hw_info.sm_count), 1, 1);
133
+ return grid;
134
+ }
135
+
136
+ CUTLASS_DEVICE
137
+ bool is_valid() {
138
+ return block_idx < params.num_blocks;
139
+ }
140
+
141
+ CUTLASS_DEVICE
142
+ auto get_block_coord() {
143
+ using namespace cute;
144
+ int block_decode = block_idx;
145
+ int m_block, bidb, bidh;
146
+ params.divmod_m_block(block_decode, m_block, block_decode);
147
+ params.divmod_b(block_decode, bidb, block_decode);
148
+ params.divmod_h(block_decode, bidh, block_decode);
149
+ return make_coord(m_block, _0{}, make_coord(bidb, bidh));
150
+ }
151
+
152
+ CUTLASS_DEVICE
153
+ PersistentTileScheduler& operator++() {
154
+ block_idx += gridDim.x;
155
+ return *this;
156
+ }
157
+ };
158
+
159
+ ////////////////////////////////////////////////////////////////////////////////
160
+
161
+ template<typename Base>
162
+ struct TileSchedulerBwdAdapter {
163
+
164
+ using Params = typename Base::Params;
165
+
166
+ Base base_;
167
+
168
+ CUTLASS_DEVICE
169
+ TileSchedulerBwdAdapter(Params const& params) : base_(params) {}
170
+
171
+ template<class ProblemSize, class ClusterShape, class TileShape>
172
+ static Params to_underlying_arguments(
173
+ ProblemSize const& problem_size, KernelHardwareInfo hw_info,
174
+ ClusterShape const& cluster_shape, TileShape const& tile_shape)
175
+ {
176
+ using namespace cute;
177
+ return Base::to_underlying_arguments(select<0,1,3,2,4>(problem_size), hw_info, select<1,0,2>(cluster_shape), select<1,0,2>(tile_shape));
178
+ }
179
+
180
+ static dim3 get_grid_shape(Params const& params) {
181
+ return Base::get_grid_shape(params);
182
+ }
183
+
184
+ CUTLASS_DEVICE
185
+ bool is_valid() {
186
+ return base_.is_valid();
187
+ }
188
+
189
+ CUTLASS_DEVICE
190
+ auto get_block_coord() {
191
+ using namespace cute;
192
+ return select<1,0,2>(base_.get_block_coord());
193
+ }
194
+
195
+ CUTLASS_DEVICE
196
+ TileSchedulerBwdAdapter& operator++() {
197
+ ++base_;
198
+ return *this;
199
+ }
200
+ };
201
+
202
+ ////////////////////////////////////////////////////////////////////////////////
203
+
204
+ } // namespace cutlass::fmha::kernel
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/fmha_bwd_reference.hpp ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2025 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+
33
+ #pragma once
34
+
35
+ #include "cute/tensor.hpp"
36
+
37
+ /////////////////////////////////////////////////////////////////////////////////////////////////
38
+
39
+ template<
40
+ class ProblemShape,
41
+ class TensorQ, class TensorK, class TensorV,
42
+ class TensorO, class TensorLSE, class TensorDO,
43
+ class TensorDQ, /* class TensorDK, class TensorDV, */
44
+ class Fusion
45
+ >
46
+ void __global__ fmha_bwd_reference_dQ_kernel(
47
+ ProblemShape problem_shape,
48
+ TensorQ mQ, TensorK mK, TensorV mV,
49
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
50
+ TensorDQ mDQ, /* TensorDK mDK, TensorDV mDV, */
51
+ Fusion fusion
52
+ ) {
53
+ using namespace cute;
54
+
55
+ using Element = typename TensorO::value_type;
56
+ using ElementAccumulator = typename TensorLSE::value_type;
57
+
58
+ extern __shared__ char mS_mem[];
59
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
60
+
61
+ Element softmax_scale = static_cast<Element>(1.0 / sqrt(1.0 * size<1>(mO)));
62
+
63
+ for (int idx_L = blockIdx.y; idx_L < size<2>(mDQ); idx_L += gridDim.y) {
64
+ for (int idx_Q = blockIdx.x; idx_Q < size<0>(mDQ); idx_Q += gridDim.x) {
65
+
66
+ for (int idx_K = threadIdx.x; idx_K < size<0>(mK); idx_K += blockDim.x) {
67
+ ElementAccumulator acc_qk = 0;
68
+ ElementAccumulator acc_dov = 0;
69
+ ElementAccumulator acc_doo = 0;
70
+ for (int idx_D0 = 0; idx_D0 < size<1>(mK); idx_D0++) {
71
+ acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
72
+ acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
73
+ acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
74
+ }
75
+
76
+ auto id = make_identity_tensor(make_shape(1, 1));
77
+ auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
78
+ frag(0) = acc_qk;
79
+ fusion.before_softmax(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
80
+ acc_qk = frag(0);
81
+
82
+ mS[idx_K] = static_cast<Element>(exp(softmax_scale * acc_qk - mLSE(idx_Q, idx_L)) * softmax_scale * (acc_dov - acc_doo));
83
+ }
84
+
85
+ __syncthreads();
86
+
87
+ for (int idx_D = threadIdx.x; idx_D < size<1>(mDQ); idx_D += blockDim.x) {
88
+ ElementAccumulator acc = 0;
89
+ for (int idx_K = 0; idx_K < size<0>(mK); idx_K++) {
90
+ acc += mS[idx_K] * mK(idx_K, idx_D, idx_L);
91
+ }
92
+ mDQ(idx_Q, idx_D, idx_L) = acc;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ /////////////////////////////////////////////////////////////////////////////////////////////////
99
+
100
+ template<
101
+ class ProblemShape,
102
+ class TensorQ, class TensorK, class TensorV,
103
+ class TensorO, class TensorLSE, class TensorDO,
104
+ /* class TensorDQ, */ class TensorDK, /* class TensorDV, */
105
+ class Fusion
106
+ >
107
+ void __global__ fmha_bwd_reference_dK_kernel(
108
+ ProblemShape problem_shape,
109
+ TensorQ mQ, TensorK mK, TensorV mV,
110
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
111
+ /* TensorDQ mDQ, */ TensorDK mDK, /* TensorDV mDV, */
112
+ Fusion fusion
113
+ ) {
114
+ using namespace cute;
115
+
116
+ using Element = typename TensorO::value_type;
117
+ using ElementAccumulator = typename TensorLSE::value_type;
118
+
119
+ extern __shared__ char mS_mem[];
120
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
121
+
122
+ Element softmax_scale = static_cast<Element>(1.0 / sqrt(1.0 * size<1>(mO)));
123
+
124
+ for (int idx_L = blockIdx.y; idx_L < size<2>(mDK); idx_L += gridDim.y) {
125
+ for (int idx_K = blockIdx.x; idx_K < size<0>(mDK); idx_K += gridDim.x) {
126
+
127
+ for (int idx_Q = threadIdx.x; idx_Q < size<0>(mDO); idx_Q += blockDim.x) {
128
+ ElementAccumulator acc_qk = 0;
129
+ ElementAccumulator acc_dov = 0;
130
+ ElementAccumulator acc_doo = 0;
131
+ for (int idx_D0 = 0; idx_D0 < size<1>(mK); idx_D0++) {
132
+ acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
133
+ acc_dov += mDO(idx_Q, idx_D0, idx_L) * mV(idx_K, idx_D0, idx_L);
134
+ acc_doo += mDO(idx_Q, idx_D0, idx_L) * mO(idx_Q, idx_D0, idx_L);
135
+ }
136
+
137
+ auto id = make_identity_tensor(make_shape(1, 1));
138
+ auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
139
+ frag(0) = acc_qk;
140
+ fusion.before_softmax(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
141
+ acc_qk = frag(0);
142
+
143
+ mS[idx_Q] = static_cast<Element>(exp(softmax_scale * acc_qk - mLSE(idx_Q, idx_L)) * softmax_scale * (acc_dov - acc_doo));
144
+ }
145
+
146
+ __syncthreads();
147
+
148
+ for (int idx_D = threadIdx.x; idx_D < size<1>(mDK); idx_D += blockDim.x) {
149
+ ElementAccumulator acc = 0;
150
+ for (int idx_Q = 0; idx_Q < size<0>(mDO); idx_Q++) {
151
+ acc += mS[idx_Q] * mQ(idx_Q, idx_D, idx_L);
152
+ }
153
+ mDK(idx_K, idx_D, idx_L) = acc;
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ /////////////////////////////////////////////////////////////////////////////////////////////////
160
+
161
+ template<
162
+ class ProblemShape,
163
+ class TensorQ, class TensorK, class TensorV,
164
+ class TensorO, class TensorLSE, class TensorDO,
165
+ /* class TensorDQ, class TensorDK, */ class TensorDV,
166
+ class Fusion
167
+ >
168
+ void __global__ fmha_bwd_reference_dV_kernel(
169
+ ProblemShape problem_shape,
170
+ TensorQ mQ, TensorK mK, TensorV mV,
171
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
172
+ /* TensorDQ mDQ, TensorDK mDK, */ TensorDV mDV,
173
+ Fusion fusion
174
+ ) {
175
+ using namespace cute;
176
+
177
+ using Element = typename TensorO::value_type;
178
+ using ElementAccumulator = typename TensorLSE::value_type;
179
+
180
+ extern __shared__ char mS_mem[];
181
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
182
+
183
+ Element softmax_scale = static_cast<Element>(1.0 / sqrt(1.0 * size<1>(mO)));
184
+
185
+ for (int idx_L = blockIdx.y; idx_L < size<2>(mDV); idx_L += gridDim.y) {
186
+ for (int idx_K = blockIdx.x; idx_K < size<0>(mDV); idx_K += gridDim.x) {
187
+
188
+ for (int idx_Q = threadIdx.x; idx_Q < size<0>(mDO); idx_Q += blockDim.x) {
189
+ ElementAccumulator acc_qk = 0;
190
+ for (int idx_D0 = 0; idx_D0 < size<1>(mK); idx_D0++) {
191
+ acc_qk += mQ(idx_Q, idx_D0, idx_L) * mK(idx_K, idx_D0, idx_L);
192
+ }
193
+
194
+ auto id = make_identity_tensor(make_shape(1, 1));
195
+ auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
196
+ frag(0) = acc_qk;
197
+ fusion.before_softmax(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
198
+ acc_qk = frag(0);
199
+
200
+ mS[idx_Q] = static_cast<Element>(exp(softmax_scale * acc_qk - mLSE(idx_Q, idx_L)));
201
+ }
202
+
203
+ __syncthreads();
204
+
205
+ for (int idx_D = threadIdx.x; idx_D < size<1>(mDV); idx_D += blockDim.x) {
206
+ ElementAccumulator acc = 0;
207
+ for (int idx_Q = 0; idx_Q < size<0>(mDO); idx_Q++) {
208
+ acc += mS[idx_Q] * mDO(idx_Q, idx_D, idx_L);
209
+ }
210
+ mDV(idx_K, idx_D, idx_L) = acc;
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ /////////////////////////////////////////////////////////////////////////////////////////////////
217
+
218
+ template<
219
+ class ProblemShape,
220
+ class TensorQ, class TensorK, class TensorV,
221
+ class TensorO, class TensorLSE, class TensorDO,
222
+ /**/ class TensorDQ, /** / class TensorDK, / ** / class TensorDV, / **/
223
+ class Fusion
224
+ >
225
+ void fmha_bwd_reference_dQ(
226
+ ProblemShape problem_shape,
227
+ TensorQ mQ, TensorK mK, TensorV mV,
228
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
229
+ /**/ TensorDQ mDQ, /** / TensorDK mDK, / ** / TensorDV mDV, / **/
230
+ Fusion fusion
231
+ ) {
232
+ using namespace cute;
233
+
234
+ dim3 grid(size<0>(mDQ), size<2>(mDQ), 1);
235
+ dim3 block(256);
236
+ int shared_mem = size<0>(mK) * sizeof(typename TensorO::value_type);
237
+
238
+ if (shared_mem >= (48 << 10)) {
239
+ CUTLASS_TRACE_HOST(" Setting smem size to " << shared_mem);
240
+ auto result = cudaFuncSetAttribute(
241
+ fmha_bwd_reference_dQ_kernel<ProblemShape, TensorQ, TensorK, TensorV, TensorO, TensorLSE, TensorDO, TensorDQ, Fusion>,
242
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
243
+ shared_mem);
244
+ if (cudaSuccess != result) {
245
+ result = cudaGetLastError(); // to clear the error bit
246
+ CUTLASS_TRACE_HOST(
247
+ " cudaFuncSetAttribute() returned error: "
248
+ << cudaGetErrorString(result));
249
+ return;
250
+ }
251
+ }
252
+
253
+ fmha_bwd_reference_dQ_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, fusion);
254
+ }
255
+
256
+ /////////////////////////////////////////////////////////////////////////////////////////////////
257
+
258
+ template<
259
+ class ProblemShape,
260
+ class TensorQ, class TensorK, class TensorV,
261
+ class TensorO, class TensorLSE, class TensorDO,
262
+ /** / class TensorDQ, / **/ class TensorDK, /** / class TensorDV, / **/
263
+ class Fusion
264
+ >
265
+ void fmha_bwd_reference_dK(
266
+ ProblemShape problem_shape,
267
+ TensorQ mQ, TensorK mK, TensorV mV,
268
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
269
+ /** / TensorDQ mDQ, / **/ TensorDK mDK, /** / TensorDV mDV, / **/
270
+ Fusion fusion
271
+ ) {
272
+ using namespace cute;
273
+
274
+ dim3 grid(size<0>(mDK), size<2>(mDK), 1);
275
+ dim3 block(256);
276
+ int shared_mem = size<0>(mDO) * sizeof(typename TensorO::value_type);
277
+
278
+ if (shared_mem >= (48 << 10)) {
279
+ CUTLASS_TRACE_HOST(" Setting smem size to " << shared_mem);
280
+ auto result = cudaFuncSetAttribute(
281
+ fmha_bwd_reference_dK_kernel<ProblemShape, TensorQ, TensorK, TensorV, TensorO, TensorLSE, TensorDO, TensorDK, Fusion>,
282
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
283
+ shared_mem);
284
+ if (cudaSuccess != result) {
285
+ result = cudaGetLastError(); // to clear the error bit
286
+ CUTLASS_TRACE_HOST(
287
+ " cudaFuncSetAttribute() returned error: "
288
+ << cudaGetErrorString(result));
289
+ return;
290
+ }
291
+ }
292
+
293
+ fmha_bwd_reference_dK_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDK, fusion);
294
+ }
295
+
296
+ /////////////////////////////////////////////////////////////////////////////////////////////////
297
+
298
+ template<
299
+ class ProblemShape,
300
+ class TensorQ, class TensorK, class TensorV,
301
+ class TensorO, class TensorLSE, class TensorDO,
302
+ /** / class TensorDQ, / ** / class TensorDK, / **/ class TensorDV, /**/
303
+ class Fusion
304
+ >
305
+ void fmha_bwd_reference_dV(
306
+ ProblemShape problem_shape,
307
+ TensorQ mQ, TensorK mK, TensorV mV,
308
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
309
+ /** / TensorDQ mDQ, / ** / TensorDK mDK, / **/ TensorDV mDV, /**/
310
+ Fusion fusion
311
+ ) {
312
+ using namespace cute;
313
+
314
+ dim3 grid(size<0>(mDV), size<2>(mDV), 1);
315
+ dim3 block(256);
316
+ int shared_mem = size<0>(mDO) * sizeof(typename TensorO::value_type);
317
+
318
+ if (shared_mem >= (48 << 10)) {
319
+ CUTLASS_TRACE_HOST(" Setting smem size to " << shared_mem);
320
+ auto result = cudaFuncSetAttribute(
321
+ fmha_bwd_reference_dV_kernel<ProblemShape, TensorQ, TensorK, TensorV, TensorO, TensorLSE, TensorDO, TensorDV, Fusion>,
322
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
323
+ shared_mem);
324
+ if (cudaSuccess != result) {
325
+ result = cudaGetLastError(); // to clear the error bit
326
+ CUTLASS_TRACE_HOST(
327
+ " cudaFuncSetAttribute() returned error: "
328
+ << cudaGetErrorString(result));
329
+ return;
330
+ }
331
+ }
332
+
333
+ fmha_bwd_reference_dV_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDV, fusion);
334
+ }
335
+
336
+ /////////////////////////////////////////////////////////////////////////////////////////////////
337
+
338
+ template<
339
+ class ProblemShape,
340
+ class TensorQ, class TensorK, class TensorV,
341
+ class TensorO, class TensorLSE, class TensorDO,
342
+ class TensorDQ, class TensorDK, class TensorDV,
343
+ class Fusion
344
+ >
345
+ void fmha_bwd_reference(
346
+ ProblemShape problem_shape,
347
+ TensorQ mQ, TensorK mK, TensorV mV,
348
+ TensorO mO, TensorLSE mLSE, TensorDO mDO,
349
+ TensorDQ mDQ, TensorDK mDK, TensorDV mDV,
350
+ Fusion fusion
351
+ ) {
352
+ fmha_bwd_reference_dQ(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDQ, fusion);
353
+ fmha_bwd_reference_dK(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDK, fusion);
354
+ fmha_bwd_reference_dV(problem_shape, mQ, mK, mV, mO, mLSE, mDO, mDV, fusion);
355
+ }
356
+
357
+ /////////////////////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/fmha_reference.hpp ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include "cute/tensor.hpp"
34
+
35
+ /////////////////////////////////////////////////////////////////////////////////////////////////
36
+
37
+ template<
38
+ class ProblemShape,
39
+ class TensorQ,
40
+ class TensorK,
41
+ class TensorV,
42
+ class TensorO,
43
+ class TensorLSE,
44
+ class Fusion
45
+ >
46
+ void __global__ fmha_reference_kernel(
47
+ ProblemShape problem_shape,
48
+ TensorQ mQ, TensorK mK, TensorV mV,
49
+ TensorO mO, TensorLSE mLSE,
50
+ Fusion fusion
51
+ ) {
52
+ using namespace cute;
53
+
54
+ using Element = typename TensorO::value_type;
55
+ using ElementAccumulator = typename TensorLSE::value_type;
56
+
57
+ extern __shared__ char mS_mem[];
58
+ Element* mS = reinterpret_cast<Element*>(mS_mem);
59
+
60
+ ElementAccumulator softmax_scale = static_cast<ElementAccumulator>(1.0 / sqrt(1.0 * size<1>(mO)));
61
+
62
+ auto id = make_identity_tensor(make_shape(1, 1));
63
+ for (int idx_L = blockIdx.y; idx_L < size<2>(mO); idx_L += gridDim.y) {
64
+ for (int idx_Q = blockIdx.x; idx_Q < size<0>(mO); idx_Q += gridDim.x) {
65
+ for (int idx_K = threadIdx.x; idx_K < size<0>(mK); idx_K += blockDim.x) {
66
+ ElementAccumulator acc = 0;
67
+ for (int idx_D = 0; idx_D < size<1>(mK); idx_D++) {
68
+ acc += mQ(idx_Q, idx_D, idx_L) * mK(idx_K, idx_D, idx_L);
69
+ }
70
+ auto frag = make_tensor<ElementAccumulator>(Shape<_1, _1>{});
71
+ frag(0) = acc;
72
+ fusion.before_softmax(frag, make_tensor(id.data() + make_arithmetic_tuple(idx_Q, idx_K), id.layout()), problem_shape);
73
+ mS[idx_K] = static_cast<Element>(frag(0) * softmax_scale);
74
+ }
75
+
76
+ __syncthreads();
77
+
78
+ ElementAccumulator maxS = -std::numeric_limits<ElementAccumulator>::infinity();
79
+ for (int idx_K = 0; idx_K < size<0>(mK); idx_K++) {
80
+ maxS = std::max<ElementAccumulator>(maxS, mS[idx_K]);
81
+ }
82
+ if (maxS == -std::numeric_limits<ElementAccumulator>::infinity()) maxS = 0;
83
+
84
+ __syncthreads();
85
+
86
+ for (int idx_K = threadIdx.x; idx_K < size<0>(mK); idx_K += blockDim.x) {
87
+ mS[idx_K] = static_cast<Element>(exp(mS[idx_K] - maxS));
88
+ }
89
+
90
+ __syncthreads();
91
+
92
+ ElementAccumulator sum = 0;
93
+ for (int idx_K = 0; idx_K < size<0>(mK); idx_K++) {
94
+ sum += mS[idx_K];
95
+ }
96
+
97
+ Element scale = static_cast<Element>(1.0 / sum);
98
+
99
+ for (int idx_D = threadIdx.x; idx_D < size<1>(mO); idx_D += blockDim.x) {
100
+ ElementAccumulator acc = 0;
101
+ for (int idx_K = 0; idx_K < size<0>(mK); idx_K++) {
102
+ acc += mS[idx_K] * mV(idx_K, idx_D, idx_L) * scale;
103
+ }
104
+ mO(idx_Q, idx_D, idx_L) = static_cast<Element>(acc);
105
+ }
106
+
107
+ if (threadIdx.x == 0) {
108
+ mLSE(idx_Q, idx_L) = log(sum) + maxS;
109
+ }
110
+
111
+ }
112
+ }
113
+ }
114
+
115
+ /////////////////////////////////////////////////////////////////////////////////////////////////
116
+
117
+ template<
118
+ class ProblemShape,
119
+ class TensorQ,
120
+ class TensorK,
121
+ class TensorV,
122
+ class TensorO,
123
+ class TensorLSE,
124
+ class Fusion
125
+ >
126
+ void fmha_reference(
127
+ ProblemShape problem_shape,
128
+ TensorQ mQ, TensorK mK, TensorV mV,
129
+ TensorO mO, TensorLSE mLSE,
130
+ Fusion fusion
131
+ ) {
132
+ using namespace cute;
133
+
134
+ dim3 grid(size<0>(mO), size<2>(mO), 1);
135
+ dim3 block(256);
136
+ int shared_mem = size<0>(mK) * sizeof(typename TensorO::value_type);
137
+
138
+ if (shared_mem >= (48 << 10)) {
139
+ CUTLASS_TRACE_HOST(" Setting smem size to " << shared_mem);
140
+ auto result = cudaFuncSetAttribute(
141
+ fmha_reference_kernel<ProblemShape, TensorQ, TensorK, TensorV, TensorO, TensorLSE, Fusion>,
142
+ cudaFuncAttributeMaxDynamicSharedMemorySize,
143
+ shared_mem);
144
+ if (cudaSuccess != result) {
145
+ result = cudaGetLastError(); // to clear the error bit
146
+ CUTLASS_TRACE_HOST(
147
+ " cudaFuncSetAttribute() returned error: "
148
+ << cudaGetErrorString(result));
149
+ return;
150
+ }
151
+ }
152
+
153
+ fmha_reference_kernel<<<grid, block, shared_mem>>>(problem_shape, mQ, mK, mV, mO, mLSE, fusion);
154
+ }
155
+
156
+ /////////////////////////////////////////////////////////////////////////////////////////////////
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/88_hopper_fmha/reference/reference_abs_error.hpp ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+
33
+
34
+ #pragma once
35
+
36
+ #include <cmath>
37
+ #include "cutlass/util/device_memory.h"
38
+
39
+ template<typename Element>
40
+ __global__ void reference_abs_diff_kernel(
41
+ Element* data, Element* data_ref, size_t count,
42
+ double* max_diff, double* sum_diff,
43
+ bool print_diff
44
+ ) {
45
+ double thread_max_diff = 0;
46
+ double thread_sum_diff = 0;
47
+
48
+ __shared__ double block_max_diff;
49
+ __shared__ double block_sum_diff;
50
+
51
+ for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < count; i += blockDim.x * gridDim.x) {
52
+ double diff = fabs(data[i] - data_ref[i]);
53
+ if (print_diff) if (diff != diff || diff > 0.01f) printf("difference at %lld: %f ... %f vs %f\n", static_cast<long long int>(i), diff, (double)data[i], (double)data_ref[i]);
54
+ thread_max_diff = fmax(diff, thread_max_diff);
55
+ thread_sum_diff += diff;
56
+ }
57
+
58
+ for (int i = 0; i < blockDim.x; i++) {
59
+ if (i == threadIdx.x) {
60
+ if (i == 0) {
61
+ block_max_diff = thread_max_diff;
62
+ block_sum_diff = thread_sum_diff;
63
+ } else {
64
+ block_max_diff = fmax(block_max_diff, thread_max_diff);
65
+ block_sum_diff += thread_sum_diff;
66
+ }
67
+ }
68
+ __syncthreads();
69
+ }
70
+
71
+ if (threadIdx.x == 0) {
72
+ atomicAdd(sum_diff, block_sum_diff);
73
+
74
+ for (;;) {
75
+ unsigned long long prev = *reinterpret_cast<unsigned long long*>(max_diff);
76
+ double prev_diff = reinterpret_cast<double const&>(prev);
77
+ double new_max_diff = fmax(block_max_diff, prev_diff);
78
+ unsigned long long found = atomicCAS(reinterpret_cast<unsigned long long*>(max_diff), prev, reinterpret_cast<unsigned long long const&>(new_max_diff));
79
+ if (found == prev) break;
80
+ }
81
+ }
82
+ }
83
+
84
+ template<typename Element>
85
+ void reference_abs_diff(
86
+ cutlass::DeviceAllocation<Element> const& data,
87
+ cutlass::DeviceAllocation<Element> const& data_ref,
88
+ double& max_diff, double& mean_diff
89
+ ) {
90
+ static bool kPrintDiff = getenv("REF_PRINT_DIFF") && atoi(getenv("REF_PRINT_DIFF")) == 1;
91
+
92
+ cutlass::DeviceAllocation<double> result;
93
+ result.reset(2);
94
+ assert(data.size() == data_ref.size());
95
+
96
+ cudaError_t err = cudaMemset(result.get(), 0, result.size() * sizeof(double));
97
+ if (err != cudaSuccess) {
98
+ std::cerr << "Memset failed. Last CUDA error: "
99
+ << cudaGetErrorString(err) << std::endl;
100
+ max_diff = mean_diff = 1e20;
101
+ return;
102
+ }
103
+
104
+ dim3 block(256, 1, 1);
105
+ dim3 grid(1024, 1, 1);
106
+ reference_abs_diff_kernel<<<block, grid>>>(
107
+ data.get(), data_ref.get(), data.size(),
108
+ result.get(), result.get() + 1, kPrintDiff);
109
+
110
+ err = cudaDeviceSynchronize();
111
+ if (err != cudaSuccess) {
112
+ std::cerr << "Difference kernel failed. Last CUDA error: "
113
+ << cudaGetErrorString(err) << std::endl;
114
+ max_diff = mean_diff = 1e20;
115
+ return;
116
+ }
117
+
118
+ double result_host[2];
119
+ err = cudaMemcpy(result_host, result.get(), result.size() * sizeof(double), cudaMemcpyDefault);
120
+ if (err != cudaSuccess) {
121
+ std::cerr << "Copy failed. Last CUDA error: "
122
+ << cudaGetErrorString(err) << std::endl;
123
+ max_diff = mean_diff = 1e20;
124
+ return;
125
+ }
126
+
127
+ max_diff = result_host[0];
128
+ mean_diff = result_host[1] / static_cast<double>(data.size());
129
+ }
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/dist_gemm_helpers.h ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ /*! \file
33
+ \brief Benchmark helpers for Distributed GEMM
34
+
35
+ A delay kernel to gate all GEMMs across devices, controlled by a flag that
36
+ the host will set off once it launches DistGEMM across all devices.
37
+
38
+ DistGpuTimer extends cutlass's existing cudaEvent-based timer to multiple devices.
39
+ */
40
+
41
+ #pragma once
42
+ #include "cutlass/cutlass.h"
43
+ #include <iostream>
44
+ #include <cuda/atomic>
45
+ #include CUDA_STD_HEADER(atomic)
46
+
47
+ #include "cute/layout.hpp"
48
+ #include "cute/tensor.hpp"
49
+ #include "cutlass/cuda_host_adapter.hpp"
50
+
51
+
52
+ namespace cutlass {
53
+
54
+ /////////////////////////////////////////////////////////////////////////////////////////////////
55
+ /// Delay kernel
56
+ /////////////////////////////////////////////////////////////////////////////////////////////////
57
+
58
+ using AtomicBoolean = cuda::atomic<bool>;
59
+
60
+ __global__ void delay_kernel(const AtomicBoolean* atomic_flag_ptr) {
61
+ while (not atomic_flag_ptr->load()) {
62
+ __nanosleep(40);
63
+ }
64
+ }
65
+
66
+
67
+ /////////////////////////////////////////////////////////////////////////////////////////////////
68
+ /// Distributed GPU Timer
69
+ /// Sets up cuda events for multiple processors.
70
+ /////////////////////////////////////////////////////////////////////////////////////////////////
71
+ template <int NP>
72
+ struct DistGpuTimer {
73
+ int _primary_device;
74
+ cudaEvent_t _start[NP];
75
+ cudaEvent_t _stop[NP];
76
+
77
+ /// Constructor
78
+ DistGpuTimer()
79
+ {
80
+ CUDA_CHECK(cudaGetDevice(&_primary_device));
81
+ for (int device = 0; device < NP; ++device) {
82
+ CUDA_CHECK(cudaSetDevice(device));
83
+ CUDA_CHECK(cudaEventCreate(&_start[device]));
84
+ CUDA_CHECK(cudaEventCreate(&_stop[device]));
85
+ }
86
+ CUDA_CHECK(cudaSetDevice(_primary_device));
87
+ }
88
+
89
+ /// Destructor
90
+ ~DistGpuTimer()
91
+ {
92
+ for (int device = 0; device < NP; ++device) {
93
+ CUDA_CHECK(cudaSetDevice(device));
94
+ CUDA_CHECK(cudaEventDestroy(_start[device]));
95
+ CUDA_CHECK(cudaEventDestroy(_stop[device]));
96
+ }
97
+ CUDA_CHECK(cudaSetDevice(_primary_device));
98
+ }
99
+
100
+ /// Start the timer for a given stream (defaults to the default stream)
101
+ void start(int device, cudaStream_t stream) {
102
+ assert(device >= 0 && device < NP);
103
+ CUDA_CHECK(cudaEventRecord(_start[device], stream));
104
+ }
105
+
106
+ /// Stop the timer
107
+ void stop(int device, cudaStream_t stream) {
108
+ assert(device >= 0 && device < NP);
109
+ CUDA_CHECK(cudaEventRecord(_stop[device], stream));
110
+ }
111
+
112
+ /// Return the elapsed time (in milliseconds)
113
+ float elapsed_millis(int device) {
114
+ assert(device >= 0 && device < NP);
115
+ float elapsed = 0.0;
116
+ CUDA_CHECK(cudaEventSynchronize(_stop[device]));
117
+ CUDA_CHECK(cudaEventElapsedTime(&elapsed, _start[device], _stop[device]));
118
+ return elapsed;
119
+ }
120
+ };
121
+
122
+ /////////////////////////////////////////////////////////////////////////////////////////////////
123
+ /// Generic device-to-device data movement kernel based for CuTe tensors.
124
+ ///
125
+ /// NOTE: this kernel assigns one element copy to every thread, and is by no means
126
+ /// an efficient way of copying tensors. It should only be used for convenience in
127
+ /// reference checks.
128
+ /////////////////////////////////////////////////////////////////////////////////////////////////
129
+
130
+ template <typename TensorSource, typename TensorDestination>
131
+ void device_copy(TensorSource tensor_source,
132
+ TensorDestination tensor_destination,
133
+ cudaStream_t stream);
134
+
135
+
136
+ template <typename TensorSource, typename TensorDestination>
137
+ __global__ void device_copy_kernel(TensorSource const tensor_source,
138
+ TensorDestination tensor_destination) {
139
+ auto linear_idx = blockIdx.x * blockDim.x + threadIdx.x;
140
+ using ElementSrc = typename TensorSource::value_type;
141
+ using ElementDst = typename TensorDestination::value_type;
142
+ NumericConverter<ElementDst, ElementSrc> converter;
143
+ if (linear_idx < size(tensor_source)) {
144
+ tensor_destination(linear_idx) = converter(tensor_source(linear_idx));
145
+ }
146
+ }
147
+
148
+ template <typename TensorSource, typename TensorDestination>
149
+ void device_copy(TensorSource tensor_source,
150
+ TensorDestination tensor_destination,
151
+ cudaStream_t stream) {
152
+
153
+ assert(tensor_source.size() == tensor_destination.size());
154
+
155
+ auto numel = tensor_source.size();
156
+ static constexpr int NumThreads = 128;
157
+ auto grid_size = cute::ceil_div(numel, NumThreads);
158
+
159
+ dim3 grid(grid_size);
160
+ dim3 block(NumThreads);
161
+ device_copy_kernel<<<grid, block, 0, stream>>>(tensor_source, tensor_destination);
162
+ }
163
+
164
+ } //namespace cutlass
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/gather_tensor.hpp ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2023 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include "cute/layout.hpp"
34
+ #include "cute/tensor.hpp"
35
+ #include "cute/util/print.hpp"
36
+
37
+ namespace example {
38
+
39
+ using namespace cute;
40
+
41
+ // Empty type used to disable gather/scatter for a GEMM argument
42
+ struct NoGather
43
+ {
44
+ template<class... Ts>
45
+ NoGather(Ts...) {};
46
+ };
47
+
48
+ /// Function object that applies an index to its argument
49
+ template <class Index>
50
+ struct IndexedGather
51
+ {
52
+ CUTE_HOST_DEVICE constexpr
53
+ IndexedGather(Index const *indices = {}): indices_(indices) {}
54
+
55
+ template <typename I>
56
+ CUTE_HOST_DEVICE constexpr
57
+ Index
58
+ operator()(I i) const { return indices_[i]; }
59
+
60
+ CUTE_HOST_DEVICE friend
61
+ void
62
+ print(IndexedGather const &s) {
63
+ cute::print("Indexed");
64
+ }
65
+
66
+ Index const *indices_;
67
+ };
68
+
69
+ /// Function object that applies a stride to its argument
70
+ /// Example: StridedFunc<int,_2> gathers every other row/column
71
+ template <class Stride>
72
+ struct StridedGather
73
+ {
74
+ CUTE_HOST_DEVICE constexpr
75
+ StridedGather(Stride stride = {}): stride_(stride) {}
76
+
77
+ template <class I>
78
+ CUTE_HOST_DEVICE constexpr
79
+ auto
80
+ operator()(I i) const { return i * stride_; }
81
+
82
+ CUTE_HOST_DEVICE friend
83
+ void
84
+ print(StridedGather const &s) {
85
+ cute::print("Strided{");
86
+ print(s.stride_);
87
+ cute::print("}");
88
+ }
89
+
90
+ Stride stride_;
91
+ };
92
+
93
+ /// Custom stride object that applies a function followed by a stride
94
+ template <class Func, class Stride>
95
+ struct CustomStride
96
+ {
97
+ CUTE_HOST_DEVICE constexpr
98
+ CustomStride(Func const &func, Stride const &stride): func_(func), stride_(stride) {}
99
+
100
+ template <class I>
101
+ CUTE_HOST_DEVICE constexpr friend
102
+ auto
103
+ operator*(I i, CustomStride const &s) { return s.func_(i) * s.stride_; }
104
+
105
+ template <class I>
106
+ CUTE_HOST_DEVICE constexpr friend
107
+ auto
108
+ operator*(CustomStride const &s, I i) { return s.func_(i) * s.stride_; }
109
+
110
+ CUTE_HOST_DEVICE friend
111
+ void
112
+ print(CustomStride const & s) {
113
+ cute::print("Custom{");
114
+ print(s.func_);
115
+ cute::print(",");
116
+ print(s.stride_);
117
+ cute::print("}");
118
+ }
119
+
120
+ template<class Div>
121
+ CUTE_HOST_DEVICE constexpr friend
122
+ auto
123
+ safe_div(CustomStride const &s, Div const &div)
124
+ {
125
+ return CustomStride<Func, decltype(safe_div(s.stride_, div))>(s.func_, safe_div(s.stride_, div));
126
+ }
127
+
128
+ // Circumvent the requirement on make_layout that shape and stride are integral
129
+ template <class Shape>
130
+ CUTE_HOST_DEVICE constexpr friend
131
+ auto
132
+ make_layout(Shape const &shape, CustomStride const &stride)
133
+ {
134
+ return Layout<Shape, CustomStride>(shape, stride);
135
+ }
136
+
137
+ Func func_;
138
+ Stride stride_;
139
+ };
140
+
141
+ template<class Stride, class Func>
142
+ CUTLASS_HOST_DEVICE
143
+ auto
144
+ make_custom_stride_layout(Stride const &stride, Func&& func)
145
+ {
146
+ // Use a dummy shape and replace the first non-unit stride with a custom gather stride
147
+ auto idx = find_if(stride, [](auto x){ return not is_constant<1, decltype(x)>{}; });
148
+ constexpr int I = decltype(idx)::value;
149
+ return make_layout(repeat_like(stride, _1{}),
150
+ replace<I>(stride, CustomStride{static_cast<Func&&>(func), get<I>(stride)}));
151
+ }
152
+
153
+ /// Helper function to optionally create a gather tensor
154
+ template<class Iterator, class Shape, class Stride, class Func>
155
+ CUTLASS_HOST_DEVICE
156
+ auto
157
+ make_gather_tensor(Iterator iter, Shape const &shape, Stride const &stride, Func &&func)
158
+ {
159
+ if constexpr (not cutlass::platform::is_same<remove_cvref_t<Func>, NoGather>::value) {
160
+ Layout matrix_layout = make_identity_layout(shape);
161
+ auto offset = as_arithmetic_tuple(repeat_like(shape, _0{}));
162
+ Layout gather_layout = make_custom_stride_layout(stride, static_cast<Func&&>(func));
163
+ return make_tensor(iter, ComposedLayout{gather_layout, offset, matrix_layout});
164
+ } else {
165
+ return make_tensor(iter, shape, stride);
166
+ }
167
+ }
168
+
169
+ } // namespace example
170
+
171
+ namespace cute
172
+ {
173
+
174
+ template<int N, int I, class Shape, class Stride>
175
+ CUTE_HOST_DEVICE constexpr
176
+ auto
177
+ upcast(Shape const& shape, Stride const& stride)
178
+ {
179
+ if constexpr (is_tuple<Shape>::value) {
180
+ return transform_layout(shape, stride, [](auto const& s, auto const& d) { return upcast<N,I>(s,d); });
181
+ } else if constexpr (is_scaled_basis<Stride>::value) {
182
+ if constexpr (Stride::mode() == I) {
183
+ return make_layout(ceil_div(shape, Int<N>{}), ceil_div(stride, Int<N>{}));
184
+ } else {
185
+ return make_layout(shape, stride);
186
+ }
187
+ } else {
188
+ return upcast<N>(shape, stride);
189
+ }
190
+
191
+ CUTE_GCC_UNREACHABLE;
192
+ }
193
+
194
+ template <int N, class OuterShape, class OuterStride, class Offset, class Shape, class Stride>
195
+ CUTE_HOST_DEVICE constexpr
196
+ auto
197
+ upcast(ComposedLayout<Layout<OuterShape,OuterStride>,Offset,Layout<Shape,Stride>> const& layout)
198
+ {
199
+ // Find index of the stride-1 mode - that is the only one that requires updating inner shape and offset
200
+ auto idx = find_if(layout.layout_a().stride(), [](auto x){ return is_constant<1, decltype(x)>{}; });
201
+ constexpr int I = decltype(idx)::value;
202
+
203
+ // Upcast the outer layout (works as expected)
204
+ auto outer = upcast<N>(layout.layout_a());
205
+
206
+ // Upcast the accumulated offset along stride-1 mode
207
+ auto offset = as_arithmetic_tuple(replace<I>(layout.offset(), upcast<N>(get<I>(layout.offset()))));
208
+
209
+ // Upcast the inner layout's shape along stride-1 mode
210
+ auto inner = upcast<N,I>(layout.layout_b().shape(), layout.layout_b().stride());
211
+
212
+ return composition(outer, offset, inner);
213
+ }
214
+
215
+ } // namespace example
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/common/helper.h ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2017 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+ #pragma once
32
+
33
+ #include "cuda_runtime.h"
34
+ #include <iostream>
35
+
36
+ /**
37
+ * Panic wrapper for unwinding CUTLASS errors
38
+ */
39
+ #define CUTLASS_CHECK(status) \
40
+ { \
41
+ cutlass::Status error = status; \
42
+ if (error != cutlass::Status::kSuccess) { \
43
+ std::cerr << "Got cutlass error: " << cutlassGetStatusString(error) << " at: " << __LINE__ \
44
+ << std::endl; \
45
+ exit(EXIT_FAILURE); \
46
+ } \
47
+ }
48
+
49
+
50
+ /**
51
+ * Panic wrapper for unwinding CUDA runtime errors
52
+ */
53
+ #define CUDA_CHECK(status) \
54
+ { \
55
+ cudaError_t error = status; \
56
+ if (error != cudaSuccess) { \
57
+ std::cerr << "Got bad cuda status: " << cudaGetErrorString(error) \
58
+ << " at line: " << __LINE__ << std::endl; \
59
+ exit(EXIT_FAILURE); \
60
+ } \
61
+ }
62
+
63
+
64
+ /**
65
+ * GPU timer for recording the elapsed time across kernel(s) launched in GPU stream
66
+ */
67
+ struct GpuTimer
68
+ {
69
+ cudaStream_t _stream_id;
70
+ cudaEvent_t _start;
71
+ cudaEvent_t _stop;
72
+
73
+ /// Constructor
74
+ GpuTimer() : _stream_id(0)
75
+ {
76
+ CUDA_CHECK(cudaEventCreate(&_start));
77
+ CUDA_CHECK(cudaEventCreate(&_stop));
78
+ }
79
+
80
+ /// Destructor
81
+ ~GpuTimer()
82
+ {
83
+ CUDA_CHECK(cudaEventDestroy(_start));
84
+ CUDA_CHECK(cudaEventDestroy(_stop));
85
+ }
86
+
87
+ /// Start the timer for a given stream (defaults to the default stream)
88
+ void start(cudaStream_t stream_id = 0)
89
+ {
90
+ _stream_id = stream_id;
91
+ CUDA_CHECK(cudaEventRecord(_start, _stream_id));
92
+ }
93
+
94
+ /// Stop the timer
95
+ void stop()
96
+ {
97
+ CUDA_CHECK(cudaEventRecord(_stop, _stream_id));
98
+ }
99
+
100
+ /// Return the elapsed time (in milliseconds)
101
+ float elapsed_millis()
102
+ {
103
+ float elapsed = 0.0;
104
+ CUDA_CHECK(cudaEventSynchronize(_stop));
105
+ CUDA_CHECK(cudaEventElapsedTime(&elapsed, _start, _stop));
106
+ return elapsed;
107
+ }
108
+ };
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/cute/tutorial/blackwell/example_utils.hpp ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***************************************************************************************************
2
+ * Copyright (c) 2024 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * 3. Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ **************************************************************************************************/
31
+
32
+ #pragma once
33
+ #include <cute/tensor.hpp> // CuTe tensor implementation
34
+ #include <cute/arch/copy_sm90_desc.hpp>
35
+
36
+ template <class AccType,
37
+ class TensorA, class TensorB,
38
+ class TensorC, class TensorD,
39
+ class Alpha, class Beta>
40
+ void
41
+ reference_gemm(TensorA const& tensor_A, TensorB const& tensor_B,
42
+ TensorC const& tensor_C, TensorD & tensor_D,
43
+ Alpha alpha, Beta beta)
44
+ {
45
+ using namespace cute;
46
+ for (int m = 0; m < size<0>(tensor_D); ++m) {
47
+ for (int n = 0; n < size<1>(tensor_D); ++n) {
48
+ AccType c = AccType(0.f);
49
+ for (int k = 0; k < size<1>(tensor_A); ++k) {
50
+ c += tensor_A(m,k) * tensor_B(n,k);
51
+ }
52
+ tensor_D(m,n) = alpha * c + beta * tensor_C(m,n);
53
+ }
54
+ }
55
+ }
56
+
57
+ template <class TensorA, class TensorB,
58
+ class TensorC, class TensorD,
59
+ class RefTensorD>
60
+ bool
61
+ compare_results(TensorA const& tensor_A, TensorB const& tensor_B,
62
+ TensorC const& tensor_C, TensorD const& tensor_D,
63
+ RefTensorD const& ref_tensor_D,
64
+ bool print_diff = false)
65
+ {
66
+ using namespace cute;
67
+ auto norm_A = matrix_inf_norm(tensor_A);
68
+ auto norm_B = matrix_inf_norm(tensor_B);
69
+ auto norm_C = matrix_inf_norm(tensor_C);
70
+ auto norm_D = matrix_inf_norm(tensor_D);
71
+ auto norm_ref_D = matrix_inf_norm(ref_tensor_D);
72
+ auto norm_diff = matrix_diff_inf_norm(tensor_D, ref_tensor_D);
73
+
74
+ if (print_diff) {
75
+ for (int m = 0; m < size<0>(tensor_D); ++m) {
76
+ for (int n = 0; n < size<1>(tensor_D); ++n) {
77
+ std::cout << m << "," << n << " : " << tensor_D(m,n) << " vs. " << ref_tensor_D(m,n) << std::endl;
78
+ }
79
+ }
80
+ }
81
+
82
+ std::cout << "norm (A) : " << norm_A.inf_norm << std::endl;
83
+ std::cout << "norm (B) : " << norm_B.inf_norm << std::endl;
84
+ std::cout << "norm (C) : " << norm_C.inf_norm << std::endl;
85
+ std::cout << "norm (D) : " << norm_D.inf_norm << std::endl;
86
+ std::cout << "norm (ref_D) : " << norm_ref_D.inf_norm << std::endl;
87
+ std::cout << "norm (D-ref_D) : " << norm_diff.inf_norm << std::endl;
88
+
89
+ return (!norm_A.found_nan) && (!norm_B.found_nan) &&
90
+ (!norm_C.found_nan) && (!norm_D.found_nan) && (!norm_ref_D.found_nan) && // There are no NaNs
91
+ (norm_A.inf_norm > 0.0) && (norm_B.inf_norm > 0.0) &&
92
+ (norm_C.inf_norm > 0.0) && (norm_D.inf_norm > 0.0) && (norm_ref_D.inf_norm > 0.0) && // Values in tensors aren't zeros
93
+ (norm_diff.inf_norm <= 0.0); // Diff (ref_D-D) == 0
94
+ }
95
+
96
+ template <class Tensor>
97
+ void
98
+ initialize_tensor(Tensor& tensor, cute::tuple<int, int> value_range = {-2, 2})
99
+ {
100
+ using DataType = typename Tensor::element_type;
101
+ auto [min, max] = value_range;
102
+ for (int i = 0; i < cute::size(tensor); i++) {
103
+ tensor(i) = DataType(int((max-min)*(rand() / double(RAND_MAX)) + min));
104
+ }
105
+ }
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/call_bypass_dlpack.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import sys
30
+ import os
31
+ from typing import Tuple
32
+ import torch
33
+
34
+ import cutlass
35
+ import cutlass.cute as cute
36
+ from cutlass.cute.runtime import make_ptr
37
+
38
+
39
+ """
40
+ An Example demonstrating how to call off-the-shelf kernel by-passing dlpack protocol
41
+
42
+ The example shows how to directly pass pointers from PyTorch tensors to off-the-shelf kernels
43
+ written by CuTe DSL with a thin customized wrapper jit function. The jit function will be
44
+ compiled with inline without introducing overhead.
45
+
46
+ To run this example:
47
+
48
+ .. code-block:: bash
49
+
50
+ python examples/ampere/call_bypass_dlpack.py
51
+
52
+
53
+ It's worth to mention that by-passing dlpack protocol can resolve the issue that dlpack doesn't handle shape-1
54
+ mode correctly. For example, the following code will fail, because dlpack will convert the shape-1 mode
55
+ with stride-1 which propagate alignment incorrectly.
56
+
57
+ .. code-block:: python
58
+
59
+ @cute.kernel
60
+ def fails_kernel(gX: cute.Tensor):
61
+ bidx, _, _ = cute.arch.block_idx()
62
+ mX = gX[None, bidx, None] # We wish to retain alignment
63
+ # assert mX.iterator.alignment == 16
64
+
65
+
66
+ @cute.jit
67
+ def fails(gX_: cute.Tensor):
68
+ gX = gX_
69
+ fails_kernel(gX).launch(grid=(1, 1, 1), block=(128, 1, 1))
70
+
71
+
72
+ gX_torch = torch.rand((128, 1, 128), device="cuda", dtype=torch.bfloat16)
73
+ fails(from_dlpack(gX_torch, assumed_align=16))
74
+
75
+ """
76
+
77
+ # Add the current directory to sys.path
78
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
79
+ from tensorop_gemm import TensorOpGemm
80
+
81
+
82
+ @cute.jit
83
+ def tensor_op_gemm_wrapper(
84
+ a_ptr: cute.Pointer,
85
+ b_ptr: cute.Pointer,
86
+ c_ptr: cute.Pointer,
87
+ m: cutlass.Int32,
88
+ n: cutlass.Int32,
89
+ k: cutlass.Int32,
90
+ l: cutlass.Int32,
91
+ ):
92
+ print(f"\n[DSL INFO] Input Parameters:")
93
+ print(f"[DSL INFO] mnkl: {(m, n, k, l)}")
94
+
95
+ # Assume alignment of shape to call tensorop_gemm example
96
+ m = cute.assume(m, divby=8)
97
+ n = cute.assume(n, divby=8)
98
+
99
+ # Torch is row major
100
+ a_layout = cute.make_ordered_layout((m, k, l), order=(0, 1, 2))
101
+ b_layout = cute.make_ordered_layout((n, k, l), order=(0, 1, 2))
102
+ c_layout = cute.make_ordered_layout((m, n, l), order=(1, 0, 2))
103
+ mA = cute.make_tensor(a_ptr, layout=a_layout)
104
+ mB = cute.make_tensor(b_ptr, layout=b_layout)
105
+ mC = cute.make_tensor(c_ptr, layout=c_layout)
106
+
107
+ print(f"[DSL INFO] mA: {mA}")
108
+ print(f"[DSL INFO] mB: {mB}")
109
+ print(f"[DSL INFO] mC: {mC}")
110
+
111
+ tensor_op_gemm = TensorOpGemm(
112
+ a_ptr.value_type, c_ptr.value_type, cutlass.Float32, (2, 2, 1)
113
+ )
114
+ print(f"\n[DSL INFO] Created TensorOpGemm instance")
115
+ print(f"[DSL INFO] Input dtype: {a_ptr.value_type}")
116
+ print(f"[DSL INFO] Output dtype: {c_ptr.value_type}")
117
+ print(f"[DSL INFO] Accumulation dtype: {cutlass.Float32}")
118
+ print(f"[DSL INFO] Atom layout: {(2, 2, 1)}")
119
+
120
+ # No need to compile inside jit function
121
+ tensor_op_gemm(mA, mB, mC)
122
+ print(f"\n[DSL INFO] Executed TensorOpGemm")
123
+
124
+
125
+ def run_tensor_op_gemm_wrapper(mnkl: Tuple[int, int, int, int]):
126
+ print(f"\nRunning TensorOpGemm test with:")
127
+ print(f"Tensor dimensions: {mnkl}")
128
+
129
+ # (M,K,L)
130
+ a = torch.randn(
131
+ mnkl[3], mnkl[2], mnkl[0], dtype=torch.float16, device="cuda"
132
+ ).permute(2, 1, 0)
133
+ # (N,K,L)
134
+ b = torch.randn(
135
+ mnkl[3], mnkl[2], mnkl[1], dtype=torch.float16, device="cuda"
136
+ ).permute(2, 1, 0)
137
+ # (N,M,L)
138
+ c = torch.randn(
139
+ mnkl[3], mnkl[0], mnkl[1], dtype=torch.float16, device="cuda"
140
+ ).permute(1, 2, 0)
141
+
142
+ print(f"Input tensor shapes:")
143
+ print(f"a: {a.shape}, dtype: {a.dtype}")
144
+ print(f"b: {b.shape}, dtype: {b.dtype}")
145
+ print(f"c: {c.shape}, dtype: {c.dtype}\n")
146
+
147
+ a_ptr = make_ptr(
148
+ cutlass.Float16, a.data_ptr(), cute.AddressSpace.gmem, assumed_align=32
149
+ )
150
+ b_ptr = make_ptr(
151
+ cutlass.Float16, b.data_ptr(), cute.AddressSpace.gmem, assumed_align=32
152
+ )
153
+ c_ptr = make_ptr(
154
+ cutlass.Float16, c.data_ptr(), cute.AddressSpace.gmem, assumed_align=32
155
+ )
156
+ tensor_op_gemm_wrapper(a_ptr, b_ptr, c_ptr, *mnkl)
157
+ torch.cuda.synchronize()
158
+
159
+ ref = torch.einsum("mkl,nkl->mnl", a, b)
160
+ torch.testing.assert_close(c, ref, atol=1e-05, rtol=1e-05)
161
+ print(f"\n[DSL INFO] Results verified successfully!")
162
+ print(f"First few elements of result: \n{c[:3, :3, :3]}")
163
+
164
+
165
+ if __name__ == "__main__":
166
+ run_tensor_op_gemm_wrapper((512, 256, 128, 16))
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/call_from_jit.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ """
30
+ Demonstrating JIT GEMM Implementation with Static Shape Wrapper
31
+
32
+ This example illustrates how to invoke a JIT-compiled GEMM implementation through a wrapper function
33
+ with static shapes. It showcases the integration between PyTorch and CuTe tensors in a JIT context.
34
+
35
+ Key features demonstrated:
36
+ 1. Seamless conversion between PyTorch and CuTe tensors using the JitArgument protocol
37
+ 2. Integration of static shape GEMM operations within a JIT-compiled wrapper function
38
+
39
+ Core components:
40
+ - BufferWithLayout: Handles memory buffer management with configurable stride ordering
41
+ - tensor_op_gemm_wrapper: JIT-compiled entry point that orchestrates the GEMM operation
42
+
43
+ Usage:
44
+
45
+ .. code-block:: bash
46
+
47
+ python examples/ampere/call_from_jit.py
48
+
49
+ Default configuration:
50
+ - Batch dimension (L): 16
51
+ - Matrix dimensions: M=512, N=256, K=128
52
+ - Precision: Float16 inputs with Float32 accumulation
53
+
54
+ Requirements:
55
+ - CUDA-capable GPU
56
+ - PyTorch with CUDA support
57
+ """
58
+
59
+ import os
60
+ import sys
61
+ from typing import Type, Tuple
62
+
63
+ import torch
64
+
65
+ import cutlass
66
+ import cutlass.cute as cute
67
+ from cutlass.torch import dtype as torch_dtype
68
+ from cutlass.cute.runtime import make_ptr
69
+
70
+
71
+ # Add the current directory to sys.path
72
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
73
+ from tensorop_gemm import TensorOpGemm
74
+
75
+
76
+ class BufferWithLayout:
77
+ def __init__(self, ptr: cute.Pointer, stride_order: tuple[int, int, int]):
78
+ self.ptr = ptr
79
+
80
+ # static properties
81
+ self.stride_order = stride_order
82
+
83
+ def to_tensor(
84
+ self, shape: tuple[int, int, int], *, loc=None, ip=None
85
+ ) -> cute.Tensor:
86
+ assert len(shape) == len(self.stride_order), (
87
+ f"Shape {shape} and stride_order {self.stride_order} must have the "
88
+ "same rank."
89
+ )
90
+ layout = cute.make_ordered_layout(shape, self.stride_order)
91
+ # permute (l, mn, k) -> (mn, k, l)
92
+ res = cute.make_tensor(self.ptr, cute.select(layout, mode=[1, 2, 0]))
93
+ return res
94
+
95
+ # Implement JitArgument Protocol and DynamicExpression Protocol
96
+
97
+ def __c_pointers__(self):
98
+ """Get the C pointers for the underlying pointer.
99
+
100
+ This method is part of the JitArgument Protocol and returns the C pointers
101
+ from the underlying pointer object.
102
+
103
+ This is required for user to define a custom data type which can pass to JIT function.
104
+ When JIT compiled function is called, JIT executor will call this method to get raw pointers
105
+ to underlying data object.
106
+
107
+ Following condition must be satisfied:
108
+
109
+ len(__c_pointers__()) == len(__get_mlir_types__()) == len(__extract_mlir_values__())
110
+
111
+ :return: The C pointers from the underlying pointer object
112
+ :rtype: Any
113
+ """
114
+ return self.ptr.__c_pointers__()
115
+
116
+ def __get_mlir_types__(self):
117
+ """Get the MLIR types for the underlying pointer.
118
+
119
+ This method is part of the JitArgument Protocol and returns the MLIR types
120
+ used for compiler to generate code. It must match the type of the underlying pointers
121
+ returned by __c_pointers__().
122
+
123
+ :return: The MLIR types from the underlying pointer object
124
+ :rtype: Any
125
+ """
126
+ return self.ptr.__get_mlir_types__()
127
+
128
+ def __extract_mlir_values__(self):
129
+ """Extract MLIR values from the underlying pointer.
130
+
131
+ This method is part of the DynamicExpression Protocol and extracts MLIR values
132
+ from the underlying pointer object.
133
+
134
+ It is used by compiler to generate function call in MLIR to another JIT function.
135
+ It must match the types returned by __get_mlir_types__().
136
+
137
+ :return: The MLIR values extracted from the underlying pointer object
138
+ :rtype: Any
139
+ """
140
+ return self.ptr.__extract_mlir_values__()
141
+
142
+ def __new_from_mlir_values__(self, values):
143
+ """Create a new BufferWithLayout instance from MLIR values.
144
+
145
+ This method is part of the JitArgument & DynamicExpression Protocol and creates a new
146
+ BufferWithLayout instance with pointer initialized from the given MLIR values.
147
+
148
+ It is used by compiler to generate function body in MLIR called by JIT function.
149
+ It must match the types returned by __c_pointers__() and __get_mlir_types__().
150
+ code generator takes function arguments and reconstructs python object which is legal
151
+ inside function body.
152
+
153
+ :param values: MLIR values to initialize the underlying pointer
154
+ :type values: Any
155
+ :return: A new BufferWithLayout instance with pointer initialized from values
156
+ :rtype: BufferWithLayout
157
+ """
158
+ return BufferWithLayout(
159
+ self.ptr.__new_from_mlir_values__(values), self.stride_order
160
+ )
161
+
162
+
163
+ @cute.jit
164
+ def tensor_op_gemm_wrapper(
165
+ buffer_a: BufferWithLayout,
166
+ buffer_b: BufferWithLayout,
167
+ buffer_c: BufferWithLayout,
168
+ mnkl: cutlass.Constexpr[tuple[int, int, int, int]],
169
+ acc_dtype: Type[cutlass.Numeric],
170
+ atom_layout_mnk: cutlass.Constexpr[tuple[int, int, int]],
171
+ ):
172
+ print(f"\n[DSL INFO] Input Parameters:")
173
+ print(f"[DSL INFO] mnkl: {mnkl}")
174
+ print(f"[DSL INFO] buffer_a: {buffer_a}")
175
+ print(f"[DSL INFO] buffer_b: {buffer_b}")
176
+ print(f"[DSL INFO] buffer_c: {buffer_c}")
177
+ print(f"[DSL INFO] acc_dtype: {acc_dtype}")
178
+ print(f"[DSL INFO] atom_layout_mnk: {atom_layout_mnk}")
179
+
180
+ mA = buffer_a.to_tensor(cute.select(mnkl, mode=[3, 0, 2]))
181
+ mB = buffer_b.to_tensor(cute.select(mnkl, mode=[3, 1, 2]))
182
+ mC = buffer_c.to_tensor(cute.select(mnkl, mode=[3, 0, 1]))
183
+
184
+ print(f"\n[DSL INFO] Created Tensors:")
185
+ print(f"[DSL INFO] mA = {mA}")
186
+ print(f"[DSL INFO] mB = {mB}")
187
+ print(f"[DSL INFO] mC = {mC}")
188
+
189
+ tensor_op_gemm = TensorOpGemm(
190
+ buffer_a.ptr.value_type,
191
+ buffer_c.ptr.value_type,
192
+ acc_dtype,
193
+ atom_layout_mnk,
194
+ )
195
+ print(f"\n[DSL INFO] Created TensorOpGemm instance")
196
+ print(f"[DSL INFO] Input dtype: {buffer_a.ptr.value_type}")
197
+ print(f"[DSL INFO] Output dtype: {buffer_c.ptr.value_type}")
198
+ print(f"[DSL INFO] Accumulation dtype: {acc_dtype}")
199
+ print(f"[DSL INFO] Atom layout: {atom_layout_mnk}")
200
+
201
+ # No need to compile inside jit function
202
+ tensor_op_gemm(mA, mB, mC)
203
+ print(f"\n[DSL INFO] Executed TensorOpGemm")
204
+
205
+
206
+ def run_tensor_op_gemm_wrapper(mnkl: Tuple[int, int, int, int]):
207
+ print(f"\nRunning TensorOpGemm test with:")
208
+ print(f"Tensor dimensions: {mnkl}")
209
+
210
+ ab_dtype = cutlass.Float16
211
+ c_dtype = cutlass.Float16
212
+
213
+ a = torch.randn(
214
+ mnkl[3], mnkl[0], mnkl[2], dtype=torch_dtype(ab_dtype), device="cuda"
215
+ )
216
+ b = torch.randn(
217
+ mnkl[3], mnkl[1], mnkl[2], dtype=torch_dtype(ab_dtype), device="cuda"
218
+ )
219
+ c = torch.randn(
220
+ mnkl[3], mnkl[0], mnkl[1], dtype=torch_dtype(c_dtype), device="cuda"
221
+ )
222
+
223
+ print(f"Input tensor shapes:")
224
+ print(f"a: {a.shape}, dtype: {a.dtype}")
225
+ print(f"b: {b.shape}, dtype: {b.dtype}")
226
+ print(f"c: {c.shape}, dtype: {c.dtype}\n")
227
+
228
+ buffer_a = BufferWithLayout(
229
+ make_ptr(ab_dtype, a.data_ptr(), cute.AddressSpace.gmem, assumed_align=32),
230
+ (2, 1, 0),
231
+ )
232
+ buffer_b = BufferWithLayout(
233
+ make_ptr(ab_dtype, b.data_ptr(), cute.AddressSpace.gmem, assumed_align=32),
234
+ (2, 1, 0),
235
+ )
236
+ buffer_c = BufferWithLayout(
237
+ make_ptr(c_dtype, c.data_ptr(), cute.AddressSpace.gmem, assumed_align=32),
238
+ (2, 1, 0),
239
+ )
240
+
241
+ tensor_op_gemm_wrapper(
242
+ buffer_a,
243
+ buffer_b,
244
+ buffer_c,
245
+ mnkl, # pass shape as static value
246
+ # no stride passing
247
+ cutlass.Float32,
248
+ (2, 2, 1),
249
+ )
250
+ torch.cuda.synchronize()
251
+
252
+ ref = torch.einsum("lmk,lnk->lmn", a, b)
253
+ torch.testing.assert_close(c, ref, atol=1e-05, rtol=1e-05)
254
+ print(f"\n[DSL INFO] Results verified successfully!")
255
+ print(f"First few elements of result: \n{c[:3, :3, :3]}")
256
+
257
+
258
+ if __name__ == "__main__":
259
+ run_tensor_op_gemm_wrapper((512, 256, 128, 16))
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/dynamic_smem_size.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import cutlass.cute as cute
30
+ import cutlass
31
+
32
+ """
33
+ Example of automatic shared memory size computation for configuring kernel launch
34
+
35
+ This example demonstrates how to let the DSL automatically set shared memory
36
+ size for a kernel launch rather explicitly configuring it at launch time,
37
+ provided that developers are using `SmemAllocator` for all allocations.
38
+
39
+ Usage:
40
+ python dynamic_smem_size.py # Show auto inference
41
+ """
42
+
43
+
44
+ @cute.struct
45
+ class SharedData:
46
+ """A struct to demonstrate shared memory allocation."""
47
+
48
+ values: cute.struct.MemRange[cutlass.Float32, 64] # 256 bytes
49
+ counter: cutlass.Int32 # 4 bytes
50
+ flag: cutlass.Int8 # 1 byte
51
+
52
+
53
+ @cute.kernel
54
+ def kernel():
55
+ """
56
+ Example kernel that allocates shared memory.
57
+ The total allocation will be automatically calculated when smem=None.
58
+ """
59
+ allocator = cutlass.utils.SmemAllocator()
60
+
61
+ # Allocate various types of shared memory
62
+ shared_data = allocator.allocate(SharedData)
63
+ raw_buffer = allocator.allocate(512, byte_alignment=64)
64
+ int_array = allocator.allocate_array(element_type=cutlass.Int32, num_elems=128)
65
+ tensor_smem = allocator.allocate_tensor(
66
+ element_type=cutlass.Float16,
67
+ layout=cute.make_layout((32, 16)),
68
+ byte_alignment=16,
69
+ swizzle=None,
70
+ )
71
+ return
72
+
73
+
74
+ @cute.kernel
75
+ def kernel_no_smem():
76
+ """
77
+ Example kernel that does not allocates shared memory.
78
+ The total allocation will be automatically calculated as 0 when smem=None.
79
+ """
80
+ tidx, _, _ = cute.arch.block_idx()
81
+ if tidx == 0:
82
+ cute.printf("Hello world")
83
+ return
84
+
85
+
86
+ if __name__ == "__main__":
87
+ # Initialize CUDA context
88
+ cutlass.cuda.initialize_cuda_context()
89
+
90
+ print("Launching kernel with auto smem size. (launch config `smem=None`)")
91
+
92
+ # Compile the example
93
+ @cute.jit
94
+ def launch_kernel1():
95
+ k = kernel()
96
+ k.launch(
97
+ grid=(1, 1, 1),
98
+ block=(1, 1, 1),
99
+ )
100
+ print(f"Kernel recorded internal smem usage: {k.smem_usage()}")
101
+
102
+ @cute.jit
103
+ def launch_kernel2():
104
+ k = kernel_no_smem()
105
+ k.launch(
106
+ grid=(1, 1, 1),
107
+ block=(1, 1, 1),
108
+ )
109
+ print(f"Kernel recorded internal smem usage: {k.smem_usage()}")
110
+
111
+ cute.compile(launch_kernel1)
112
+ cute.compile(launch_kernel2)
113
+
114
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/elementwise_add.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+
30
+ import argparse
31
+ import time
32
+ from typing import Type
33
+
34
+ import cuda.bindings.driver as cuda
35
+ import torch
36
+
37
+ import cutlass
38
+ import cutlass.cute as cute
39
+ import cutlass.cute.testing as testing
40
+ import cutlass.torch as cutlass_torch
41
+ from cutlass.cute.runtime import from_dlpack
42
+
43
+ """
44
+ An Elementwise Addition Example using CuTe DSL.
45
+
46
+ This example kernel copies data from global memory to register memory (rmem), performs the elementwise
47
+ addition operation, and stores the result back to global memory.
48
+
49
+ Primary goals of this example are to demonstrate how basic global memory copies can be expressed in
50
+ CuTe DSL and illustrate canonical partitioning patterns in CuTe. It also implements canonical
51
+ predication for tensors whose shape is not multiple of tile size to guard OOB reads.
52
+
53
+ Thread-value (or TV) layouts are central to canonical partitioning patterns in CuTe. They provide a
54
+ mapping from thread and a thread's value to the set of coordinates within a tile that we have sliced
55
+ out from a data tensor.
56
+
57
+ The input tensors are row-major layout, that leading dimension is the right most dimension. In order
58
+ to efficiently copy data from global memory, we must map threads contiguously on row dimension.
59
+
60
+ Thread ID mapping to 2D coordinates with layout `(4,32):(32,1)`:
61
+
62
+ +----+----+----+----+-----+----+
63
+ | | 0 | 1 | 2 | ... | 31 |
64
+ +----+----+----+----+-----+----+
65
+ | 0 | T0 | T1 | T2 | ... | T31|
66
+ +----+----+----+----+-----+----+
67
+ | 1 |T32 |T33 |T34 | ... |T63 |
68
+ +----+----+----+----+-----+----+
69
+ | 2 |T64 |T65 |T66 | ... |T95 |
70
+ +----+----+----+----+-----+----+
71
+ | 3 |T96 |T97 |T98 | ... |T127|
72
+ +----+----+----+----+-----+----+
73
+
74
+ As Ampere GPU supports a maximum of 128bit per load/store instruction and each element is 32bit, we
75
+ can load 4 elements per instruction. Having additional contiguous values allows for vectorization
76
+ across threads (coalesced accesses) and is required for saturating the memory bandwidth.
77
+
78
+ We use `(4,4):(4,1)` as the val layout in this example. Notice that the major mode is the same as
79
+ the major mode of the input tensor - without which vectorization would not be possible.
80
+
81
+ If you already know the TV layout you want to use for your tiled copy, CuTe DSL provides utility
82
+ `cute.make_layout_tv` to build the tiled copy type around it and the atom of your choice.
83
+
84
+ .. code-block:: python
85
+
86
+ thr_layout = cute.make_layout((4, 32), stride=(32, 1))
87
+ val_layout = cute.make_layout((4, 4), stride=(4, 1))
88
+ tiler_mn, tv_layout = cute.make_layout_tv(thr_layout, val_layout)
89
+
90
+ # Tile input tensor to thread blocks: ((TileM,TileN),(RestM,RestN))
91
+ gA = cute.zipped_divide(mA, tiler_mn)
92
+
93
+ Then we can build tiled copy for input and output tensors with `cute.make_tiled_copy_tv` utility, which
94
+ infers the tiler and tv layout for the tiled copy automatically, where `tiler` is the tile size per thread
95
+ block and `tv_layout` is the TV layout which maps thread index and inter-thread index of data array per
96
+ thread to logical coordinates of elements in input and output tensors.
97
+
98
+ .. code-block:: python
99
+
100
+ blkA = gA[((None, None), bidx)] # (TileM,TileN)
101
+
102
+ copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
103
+ tiled_copy_A = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
104
+
105
+ # get slice of tiled_copy_A for current thread
106
+ thr_copy_A = tiled_copy_A.get_slice(tidx)
107
+
108
+ # partition per thread block tensor as source of tiled copy
109
+ thrA = thr_copy_A.partition_S(blkA)
110
+
111
+ # allocate fragment for gmem->rmem
112
+ frgA = cute.make_fragment_like(thrA)
113
+
114
+ # copy data from global memory to register memory
115
+ cute.copy(copy_atom_load, thrA, frgA)
116
+
117
+
118
+ To run this example:
119
+
120
+ .. code-block:: bash
121
+
122
+ python examples/ampere/elementwise_add.py --M 3 --N 12
123
+ python examples/ampere/elementwise_add.py --M 1024 --N 512
124
+ python examples/ampere/elementwise_add.py --M 1024 --N 1024 --benchmark --warmup_iterations 2 --iterations 1000
125
+
126
+ To collect performance with NCU profiler:
127
+
128
+ .. code-block:: bash
129
+
130
+ # Don't iterate too many times when profiling with ncu
131
+ ncu python examples/ampere/elementwise_add.py --M 2048 --N 2048 --benchmark --iterations 10 --skip_ref_check
132
+ """
133
+
134
+
135
+ @cute.kernel
136
+ def elementwise_add_kernel(
137
+ gA: cute.Tensor,
138
+ gB: cute.Tensor,
139
+ gC: cute.Tensor,
140
+ cC: cute.Tensor, # coordinate tensor
141
+ shape: cute.Shape,
142
+ thr_layout: cute.Layout,
143
+ val_layout: cute.Layout,
144
+ ):
145
+ tidx, _, _ = cute.arch.thread_idx()
146
+ bidx, _, _ = cute.arch.block_idx()
147
+
148
+ # slice for CTAs
149
+ # logical id -> address
150
+ blk_coord = ((None, None), bidx)
151
+ blkA = gA[blk_coord] # (TileM,TileN)
152
+ blkB = gB[blk_coord] # (TileM,TileN)
153
+ blkC = gC[blk_coord] # (TileM,TileN)
154
+ blkCrd = cC[blk_coord] # (TileM, TileN)
155
+
156
+ # Note: these prints only run at compile/jit time
157
+ print(f"[DSL INFO] Sliced Tensors per thread block:")
158
+ print(f"[DSL INFO] blkA = {blkA.type}")
159
+ print(f"[DSL INFO] blkB = {blkB.type}")
160
+ print(f"[DSL INFO] blkC = {blkC.type}")
161
+ print(f"[DSL INFO] blkCrd = {blkCrd.type}")
162
+
163
+ # # declare the atoms which will be used later for memory copy
164
+ copy_atom_load = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gA.element_type)
165
+ copy_atom_store = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), gC.element_type)
166
+
167
+ tiled_copy_A = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
168
+ tiled_copy_B = cute.make_tiled_copy_tv(copy_atom_load, thr_layout, val_layout)
169
+ tiled_copy_C = cute.make_tiled_copy_tv(copy_atom_store, thr_layout, val_layout)
170
+
171
+ thr_copy_A = tiled_copy_A.get_slice(tidx)
172
+ thr_copy_B = tiled_copy_B.get_slice(tidx)
173
+ thr_copy_C = tiled_copy_C.get_slice(tidx)
174
+
175
+ thrA = thr_copy_A.partition_S(blkA)
176
+ thrB = thr_copy_B.partition_S(blkB)
177
+ thrC = thr_copy_C.partition_S(blkC)
178
+
179
+ # allocate fragments for gmem->rmem
180
+ frgA = cute.make_fragment_like(thrA)
181
+ frgB = cute.make_fragment_like(thrB)
182
+ frgC = cute.make_fragment_like(thrC)
183
+
184
+ thrCrd = thr_copy_C.partition_S(blkCrd)
185
+ frgPred = cute.make_fragment(thrCrd.shape, cutlass.Boolean)
186
+
187
+ print(f"[DSL INFO] Sliced Tensors per thread:")
188
+ print(f"[DSL INFO] thrA = {thrA.type}")
189
+ print(f"[DSL INFO] thrB = {thrB.type}")
190
+ print(f"[DSL INFO] thrC = {thrC.type}")
191
+ print(f"[DSL INFO] thrCrd = {thrCrd.type}")
192
+
193
+ for i in range(0, cute.size(frgPred), 1):
194
+ val = cute.elem_less(thrCrd[i], shape)
195
+ frgPred[i] = val
196
+
197
+ # Print per thread predicate mask
198
+ # if tidx == 0 and bidx == 0:
199
+ # cute.printf("block_dim = {}", cute.arch.grid_dim())
200
+ # cute.printf("shape = {}", shape)
201
+ # cute.print_tensor(thrA)
202
+ # cute.print_tensor(thrB)
203
+ # cute.print_tensor(frgPred)
204
+
205
+ ##########################################################
206
+ # Move data to reg address space
207
+ ##########################################################
208
+
209
+ cute.copy(copy_atom_load, thrA, frgA, pred=frgPred)
210
+ cute.copy(copy_atom_load, thrB, frgB, pred=frgPred)
211
+
212
+ # if tidx == 0 and bidx == 0:
213
+ # cute.print_tensor(frgA)
214
+ # cute.print_tensor(frgB)
215
+
216
+ # Load data before use. The compiler will optimize the copy and load
217
+ # operations to convert some memory ld/st into register uses.
218
+ result = frgA.load() + frgB.load()
219
+
220
+ # Save the results back to registers. Here we reuse b's registers.
221
+ frgC.store(result)
222
+
223
+ # Copy the results back to c
224
+ cute.copy(copy_atom_store, frgC, thrC, pred=frgPred)
225
+
226
+
227
+ @cute.jit
228
+ def elementwise_add(mA, mB, mC, copy_bits: cutlass.Constexpr = 128):
229
+ dtype = mA.element_type
230
+ vector_size = copy_bits // dtype.width
231
+
232
+ thr_layout = cute.make_ordered_layout((4, 32), order=(1, 0))
233
+ val_layout = cute.make_ordered_layout((4, vector_size), order=(1, 0))
234
+ tiler_mn, tv_layout = cute.make_layout_tv(thr_layout, val_layout)
235
+
236
+ print(f"[DSL INFO] Input Tensors:")
237
+ print(f"[DSL INFO] mA = {mA.type}")
238
+ print(f"[DSL INFO] mB = {mB.type}")
239
+
240
+ print(f"[DSL INFO] Tiling Parameters:")
241
+ print(f"[DSL INFO] tiler_mn = {tiler_mn} per thread block")
242
+ print(f"[DSL INFO] tv_layout = {tv_layout}")
243
+
244
+ gA = cute.zipped_divide(mA, tiler_mn) # ((TileM,TileN),(RestM,RestN))
245
+ gB = cute.zipped_divide(mB, tiler_mn) # ((TileM,TileN),(RestM,RestN))
246
+ gC = cute.zipped_divide(mC, tiler_mn) # ((TileM,TileN),(RestM,RestN))
247
+ print(f"[DSL INFO] Tiled Tensors:")
248
+ print(f"[DSL INFO] gA = {gA.type}")
249
+ print(f"[DSL INFO] gB = {gB.type}")
250
+ print(f"[DSL INFO] gC = {gC.type}")
251
+
252
+ idC = cute.make_identity_tensor(mC.shape)
253
+ cC = cute.zipped_divide(idC, tiler=tiler_mn)
254
+ print(f"[DSL INFO] coord tensor = {cC.type}")
255
+
256
+ elementwise_add_kernel(gA, gB, gC, cC, mC.shape, thr_layout, val_layout).launch(
257
+ grid=[cute.size(gC, mode=[1]), 1, 1],
258
+ block=[cute.size(tv_layout, mode=[0]), 1, 1],
259
+ )
260
+
261
+
262
+ def run_elementwise_add(
263
+ M,
264
+ N,
265
+ dtype: Type[cutlass.Numeric],
266
+ is_a_dynamic_layout=False,
267
+ is_b_dynamic_layout=False,
268
+ is_result_dynamic_layout=False,
269
+ skip_ref_check=False,
270
+ benchmark=True,
271
+ warmup_iterations=2,
272
+ iterations=200,
273
+ ):
274
+ print(f"\nRunning Elementwise Add test with:")
275
+ print(f"Tensor dimensions: [{M}, {N}]")
276
+ print(f"Input and Output Data type: {dtype}")
277
+
278
+ torch_dtype = cutlass_torch.dtype(dtype)
279
+ if dtype.is_integer:
280
+ a = torch.randint(0, 10, (M, N), device=torch.device("cuda"), dtype=torch_dtype)
281
+ b = torch.randint(0, 10, (M, N), device=torch.device("cuda"), dtype=torch_dtype)
282
+ else:
283
+ a = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
284
+ b = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
285
+
286
+ c = torch.zeros_like(a)
287
+
288
+ print(f"Input tensor shapes:")
289
+ print(f"a: {a.shape}, dtype: {a.dtype}")
290
+ print(f"b: {b.shape}, dtype: {b.dtype}")
291
+ print(f"c: {c.shape}, dtype: {c.dtype}\n")
292
+
293
+ if not is_a_dynamic_layout:
294
+ a_tensor = from_dlpack(a).mark_layout_dynamic()
295
+ else:
296
+ a_tensor = a
297
+
298
+ if not is_b_dynamic_layout:
299
+ b_tensor = from_dlpack(b).mark_layout_dynamic()
300
+ else:
301
+ b_tensor = b
302
+
303
+ if not is_result_dynamic_layout:
304
+ c_tensor = from_dlpack(c).mark_layout_dynamic()
305
+ else:
306
+ c_tensor = c
307
+
308
+ print("Compiling kernel with cute.compile ...")
309
+ start_time = time.time()
310
+ compiled_func = cute.compile(elementwise_add, a_tensor, b_tensor, c_tensor)
311
+ compilation_time = time.time() - start_time
312
+ print(f"Compilation time: {compilation_time:.4f} seconds")
313
+
314
+ print("Executing vector add kernel...")
315
+
316
+ # Get current CUstream from torch
317
+ current_stream = cutlass_torch.current_stream()
318
+
319
+ if not skip_ref_check:
320
+ compiled_func(a_tensor, b_tensor, c_tensor)
321
+ print("Verifying results...")
322
+ torch.testing.assert_close(a + b, c)
323
+ print("Results verified successfully!")
324
+
325
+ if not benchmark:
326
+ return
327
+
328
+ def generate_tensors():
329
+ if dtype.is_integer:
330
+ a = torch.randint(
331
+ 0, 10, (M, N), device=torch.device("cuda"), dtype=torch_dtype
332
+ )
333
+ b = torch.randint(
334
+ 0, 10, (M, N), device=torch.device("cuda"), dtype=torch_dtype
335
+ )
336
+ else:
337
+ a = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
338
+ b = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
339
+
340
+ c = torch.zeros_like(a)
341
+
342
+ if not is_a_dynamic_layout:
343
+ a_tensor = from_dlpack(a).mark_layout_dynamic()
344
+ else:
345
+ a_tensor = a
346
+
347
+ if not is_b_dynamic_layout:
348
+ b_tensor = from_dlpack(b).mark_layout_dynamic()
349
+ else:
350
+ b_tensor = b
351
+
352
+ if not is_result_dynamic_layout:
353
+ c_tensor = from_dlpack(c).mark_layout_dynamic()
354
+ else:
355
+ c_tensor = c
356
+
357
+ return testing.JitArguments(a_tensor, b_tensor, c_tensor)
358
+
359
+ avg_time_us = testing.benchmark(
360
+ compiled_func,
361
+ workspace_generator=generate_tensors,
362
+ workspace_count=10,
363
+ warmup_iterations=warmup_iterations,
364
+ iterations=iterations,
365
+ )
366
+
367
+ # Print execution results
368
+ print(f"Kernel execution time: {avg_time_us / 1e3:.4f} ms")
369
+ print(
370
+ f"Achieved memory throughput: {(3 * a.numel() * dtype.width // 8) / (avg_time_us / 1e6) / 1e9:.2f} GB/s"
371
+ )
372
+ print(f"First few elements of result: \n{c[:3, :3]}")
373
+
374
+
375
+ if __name__ == "__main__":
376
+ parser = argparse.ArgumentParser(
377
+ description="example of elementwise add to demonstrate the numpy/pytorch as input for kernels"
378
+ )
379
+ parser.add_argument("--M", default=1024, type=int)
380
+ parser.add_argument("--N", default=1024, type=int)
381
+ parser.add_argument("--warmup_iterations", default=2, type=int)
382
+ parser.add_argument("--iterations", default=100, type=int)
383
+ parser.add_argument("--skip_ref_check", action="store_true")
384
+ parser.add_argument("--benchmark", action="store_true")
385
+
386
+ args = parser.parse_args()
387
+
388
+ if not torch.cuda.is_available():
389
+ raise RuntimeError(f"Ampere GPU is required to run this example!")
390
+
391
+ run_elementwise_add(
392
+ args.M,
393
+ args.N,
394
+ dtype=cutlass.Float32,
395
+ is_a_dynamic_layout=True,
396
+ is_b_dynamic_layout=True,
397
+ is_result_dynamic_layout=True,
398
+ skip_ref_check=args.skip_ref_check,
399
+ benchmark=args.benchmark,
400
+ warmup_iterations=args.warmup_iterations,
401
+ iterations=args.iterations,
402
+ )
403
+ print("\nPASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/elementwise_apply.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+
30
+ import argparse
31
+ import operator
32
+ import time
33
+ from typing import Type, List
34
+
35
+ import cuda.bindings.driver as cuda
36
+ import torch
37
+
38
+ import cutlass
39
+ import cutlass.cute as cute
40
+ import cutlass.cute.testing as testing
41
+ import cutlass.torch as cutlass_torch
42
+ from cutlass.cute.runtime import from_dlpack
43
+
44
+ """
45
+ An Elementwise Apply Example using CuTe DSL.
46
+
47
+ This example kernel demonstrates the meta-programming capability of the CuTe DSL by allowing
48
+ customization of elementwise operations through lambda functions. The kernel copies data from
49
+ global memory to register memory (rmem), applies a user-defined operation to the elements,
50
+ and stores the result back to global memory.
51
+
52
+ Primary goals of this example:
53
+ 1. Demonstrate meta-programming capability by passing lambda functions to customize elementwise operations
54
+ 2. Show how to apply different operations (add, multiply, etc.) using the same kernel structure
55
+ 3. Illustrate how to parameterize CUDA kernels with operation types at compile time
56
+
57
+ To run this example:
58
+
59
+ .. code-block:: bash
60
+
61
+ # Run with addition operation
62
+ python examples/ampere/elementwise_apply.py --M 1024 --N 512 --op add
63
+
64
+ # Run with multiplication operation
65
+ python examples/ampere/elementwise_apply.py --M 1024 --N 512 --op mul
66
+
67
+ # Run with subtraction operation
68
+ python examples/ampere/elementwise_apply.py --M 1024 --N 512 --op sub
69
+
70
+ # Benchmark performance
71
+ python examples/ampere/elementwise_apply.py --M 2048 --N 2048 --op add --benchmark --warmup_iterations 2 --iterations 10
72
+
73
+ The example demonstrates how to express complex CUDA kernels with customizable operations
74
+ while maintaining high performance through efficient memory access patterns.
75
+ """
76
+
77
+
78
+ @cute.kernel
79
+ def elementwise_apply_kernel(
80
+ op: cutlass.Constexpr,
81
+ inputs: List[cute.Tensor],
82
+ gC: cute.Tensor,
83
+ cC: cute.Tensor, # coordinate tensor
84
+ shape: cute.Shape,
85
+ tv_layout: cute.Layout, # (tid, vid) -> logic coord
86
+ ):
87
+ tidx, _, _ = cute.arch.thread_idx()
88
+ bidx, _, _ = cute.arch.block_idx()
89
+
90
+ # slice for CTAs
91
+ cta_coord = ((None, None), bidx)
92
+ # logical coord -> address
93
+ # Leverage the meta-programming capability of the DSL to slice the tensors for each input
94
+ # All for loops below on input tensors would be fully unrolled automatically at compile time
95
+ ctaInputs = [t[cta_coord] for t in inputs] # (TileM, TileN)
96
+ ctaC = gC[cta_coord] # (TileM, TileN)
97
+ ctaCrd = cC[cta_coord] # (TileM, TileN)
98
+
99
+ print(f"[DSL INFO] Sliced Tensors per thread block:")
100
+ for i in cutlass.range_constexpr(len(ctaInputs)):
101
+ print(f"[DSL INFO] ctaInputs{i} = {ctaInputs[i].type}")
102
+ print(f"[DSL INFO] ctaC = {ctaC.type}")
103
+ print(f"[DSL INFO] ctaCrd = {ctaCrd.type}")
104
+
105
+ # compose with CTA TV layout
106
+ # (tid, vid) -> address
107
+ tidfrgInputs = [cute.composition(t, tv_layout) for t in ctaInputs]
108
+ tidfrgC = cute.composition(ctaC, tv_layout)
109
+ tidfrgCrd = cute.composition(ctaCrd, tv_layout)
110
+ # print(f"{tv_layout = }")
111
+ # print(f"{tidfrgAB[0] = }")
112
+
113
+ thr_coord = (tidx, (None, None))
114
+
115
+ # slice for threads
116
+ # vid -> address
117
+ thrInputs = [t[thr_coord] for t in tidfrgInputs] # (V)
118
+ thrC = tidfrgC[thr_coord] # (V)
119
+ thrCrd = tidfrgCrd[thr_coord]
120
+
121
+ print(f"[DSL INFO] Sliced Tensors per thread:")
122
+ for i in cutlass.range_constexpr(len(thrInputs)):
123
+ print(f"[DSL INFO] thrInputs{i} = {thrInputs[i].type}")
124
+ print(f"[DSL INFO] thrC = {thrC.type}")
125
+ print(f"[DSL INFO] thrCrd = {thrCrd.type}")
126
+
127
+ # allocate fragments for gmem->rmem
128
+ frgInputs = [cute.make_fragment_like(t, t.element_type) for t in thrInputs]
129
+ frgC = cute.make_fragment_like(thrC, gC.element_type)
130
+ frgPred = cute.make_fragment(thrCrd.shape, cutlass.Boolean)
131
+
132
+ for i in cutlass.range(cute.size(frgPred), unroll=1):
133
+ frgPred[i] = cute.elem_less(thrCrd[i], shape)
134
+
135
+ # if tidx == 0 and bidx == 0:
136
+ # cute.print_tensor(frgPred)
137
+
138
+ ##########################################################
139
+ # Move data to reg address space
140
+ ##########################################################
141
+
142
+ # declare the atoms which will be used later for memory copy
143
+ # Compile time validation: expect same element type for all input tensors so as to reuse the copy atom for load
144
+ assert all(t.element_type == inputs[0].element_type for t in inputs)
145
+
146
+ copy_atom_load = cute.make_copy_atom(
147
+ cute.nvgpu.CopyUniversalOp(),
148
+ inputs[0].element_type,
149
+ num_bits_per_copy=inputs[0].element_type.width,
150
+ )
151
+ copy_atom_store = cute.make_copy_atom(
152
+ cute.nvgpu.CopyUniversalOp(),
153
+ gC.element_type,
154
+ num_bits_per_copy=gC.element_type.width,
155
+ )
156
+
157
+ for thrInput, frgInput in zip(thrInputs, frgInputs):
158
+ cute.copy(copy_atom_load, thrInput, frgInput, pred=frgPred)
159
+
160
+ # Load data before use. The compiler will optimize the copy and load
161
+ # operations to convert some memory ld/st into register uses.
162
+ result = op(*[frgInput.load() for frgInput in frgInputs])
163
+
164
+ # Save the results back to registers. Here we reuse b's registers.
165
+ frgC.store(result)
166
+
167
+ # Copy the results back to c
168
+ cute.copy(copy_atom_store, frgC, thrC, pred=frgPred)
169
+
170
+
171
+ @cute.jit
172
+ def elementwise_apply(
173
+ op: cutlass.Constexpr,
174
+ a: cute.Tensor,
175
+ b: cute.Tensor,
176
+ result: cute.Tensor,
177
+ stream: cuda.CUstream,
178
+ ):
179
+ """CUDA kernel applying binary operator on each element of two n-D input tensors in
180
+ CuTe Python and store to result tensor.
181
+
182
+ :param op: Binary operator or lambda function to apply element-wise
183
+ :type op: cutlass.Constexpr
184
+ :param a: First input tensor
185
+ :type a: cute.Tensor
186
+ :param b: Second input tensor
187
+ :type b: cute.Tensor
188
+ :param result: Output tensor to store the results of op(a, b)
189
+ :type result: cute.Tensor
190
+ :return: None
191
+ :rtype: None
192
+
193
+ .. code-block:: python
194
+
195
+ # Example 1: Adding two tensors
196
+ x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, device="cuda")
197
+ y = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32, device="cuda")
198
+ result = torch.empty_like(x)
199
+ elementwise_apply(operator.add, from_dlpack(x), from_dlpack(y), from_dlpack(result))
200
+ # result:
201
+ # tensor([[6.0, 8.0],
202
+ # [10.0, 12.0]], device='cuda:0')
203
+
204
+ # Example 2: Using a lambda function
205
+ elementwise_apply(lambda a, b: a * a + b * b, from_dlpack(x), from_dlpack(y), from_dlpack(result))
206
+ # result:
207
+ # tensor([[ 2., 8.],
208
+ # [ 54., 512.]], device='cuda:0')
209
+ """
210
+
211
+ # Baseline: naive TV layout
212
+ # * mA layout: (4096, 4096):(4096, 1)
213
+ # * TV layout map to (512, 4) tile
214
+ # * tidx maps to mode-0 but input layout is contiguous on mode-1, performance will be bad
215
+ # tv_layout = cute.make_layout((128, (4, 4)), stride=(4, (512, 1)))
216
+ # cta_tiler = (512, 4)
217
+
218
+ # Opt-1: better TV layout with better 1D thread layout (SOL with 1D thread layout)
219
+ # * mA layout: (4096, 4096):(4096, 1)
220
+ # * TV layout map to (4, 512) tile
221
+ # * tidx maps to mode-1 which is leading mode of input tensor for coalesced load
222
+ # tv_layout = cute.make_layout((128, (4, 4)), stride=(16, (4, 1)))
223
+ # cta_tiler = (4, 512)
224
+
225
+ # Opt-2: 2D tile but worse
226
+ # * mA layout: (4096, 4096):(4096, 1)
227
+ # * TV layout map to (128, 16) logical tile
228
+ # * V layout is bad as contiguous mode is not on right-most
229
+ # * `cute.copy` only supports vectorize when stride-1 of v-layout on right-most )
230
+ # tv_layout = cute.make_layout(((32, 4), (4, 4)), stride=((4, 512), (1, 128)))
231
+ # cta_tiler = (128, 16)
232
+
233
+ # Opt-3: SOL with 2D thread tile
234
+ # * mA layout: (4096, 4096):(4096, 1)
235
+ # * TV layout map to (16, 128) logical tile
236
+ # * tidx maps to mode-1 and input layout is contiguous on mode-1 for coalesced load-store
237
+ thr_layout = cute.make_layout((4, 32), stride=(32, 1))
238
+ val_layout = cute.make_layout((4, 4), stride=(4, 1))
239
+ tiler_mn, tv_layout = cute.make_layout_tv(thr_layout, val_layout)
240
+
241
+ print(f"[DSL INFO] Input Tensors:")
242
+ print(f"[DSL INFO] a = {a.type}")
243
+ print(f"[DSL INFO] b = {b.type}")
244
+ print(f"[DSL INFO] result = {result.type}")
245
+
246
+ print(f"[DSL INFO] Tiling Parameters:")
247
+ print(f"[DSL INFO] tiler_mn = {tiler_mn} per thread block")
248
+ print(f"[DSL INFO] tv_layout = {tv_layout}")
249
+
250
+ gA = cute.zipped_divide(a, tiler_mn) # ((TileM, TileN), (RestM, RestN))
251
+ gB = cute.zipped_divide(b, tiler_mn) # ((TileM, TileN), (RestM, RestN))
252
+ gC = cute.zipped_divide(result, tiler_mn) # ((TileM, TileN), (RestM, RestN))
253
+
254
+ print(f"[DSL INFO] Tiled Tensors:")
255
+ print(f"[DSL INFO] gA = {gA.type}")
256
+ print(f"[DSL INFO] gB = {gB.type}")
257
+ print(f"[DSL INFO] gC = {gC.type}")
258
+
259
+ idC = cute.make_identity_tensor(result.shape)
260
+ cC = cute.zipped_divide(idC, tiler=tiler_mn)
261
+ print(f"[DSL INFO] coord tensor = {cC.type}")
262
+
263
+ # Launch the kernel asynchronously
264
+ # Async token(s) can also be specified as dependencies
265
+ elementwise_apply_kernel(
266
+ op,
267
+ [gA, gB], # Group input tensors into a list as a single argument
268
+ gC,
269
+ cC,
270
+ result.shape,
271
+ tv_layout,
272
+ ).launch(
273
+ grid=[cute.size(gC, mode=[1]), 1, 1],
274
+ block=[cute.size(tv_layout, mode=[0]), 1, 1],
275
+ stream=stream,
276
+ )
277
+
278
+
279
+ def run_elementwise_apply_and_verify(
280
+ op,
281
+ M,
282
+ N,
283
+ dtype: Type[cutlass.Numeric],
284
+ skip_ref_check=False,
285
+ benchmark=True,
286
+ warmup_iterations=2,
287
+ iterations=100,
288
+ ):
289
+ if not torch.cuda.is_available():
290
+ raise RuntimeError(f"Ampere GPU is required to run this example!")
291
+
292
+ # Create non default CUDA stream from PyTorch
293
+ torch_stream = torch.cuda.Stream()
294
+ # Get the raw stream pointer as a CUstream
295
+ current_stream = cuda.CUstream(torch_stream.cuda_stream)
296
+
297
+ print(f"\nRunning Elementwise Apply test with:")
298
+ print(f"Tensor dimensions: [{M}, {N}]")
299
+ print(f"Input and Output Data type: {dtype}")
300
+ print(f"Warmup iterations: {warmup_iterations}")
301
+ print(f"Measurement iterations: {iterations}\n")
302
+
303
+ torch_dtype = cutlass_torch.dtype(dtype)
304
+
305
+ # Allocate tensors with random values.
306
+ a = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
307
+ b = torch.randn(M, N, device=torch.device("cuda"), dtype=torch_dtype)
308
+ c = torch.zeros_like(a)
309
+
310
+ print(f"Input tensor shapes:")
311
+ print(f"a: {a.shape}, dtype: {a.dtype}")
312
+ print(f"b: {b.shape}, dtype: {b.dtype}")
313
+ print(f"c: {c.shape}, dtype: {c.dtype}\n")
314
+
315
+ epsilon = 1.2
316
+ if op in (operator.truediv, operator.floordiv):
317
+ b = torch.where(b == 0, torch.tensor(epsilon), b)
318
+
319
+ print("Executing elementwise apply kernel...")
320
+
321
+ if not skip_ref_check:
322
+ elementwise_apply(
323
+ op,
324
+ from_dlpack(a),
325
+ from_dlpack(b),
326
+ from_dlpack(c).mark_layout_dynamic(),
327
+ current_stream,
328
+ )
329
+ print("Verifying results...")
330
+ torch.testing.assert_close(op(a, b), c)
331
+ print("Results verified successfully!")
332
+
333
+ if not benchmark:
334
+ return
335
+
336
+ compiled_func = cute.compile(
337
+ elementwise_apply,
338
+ op,
339
+ from_dlpack(a),
340
+ from_dlpack(b),
341
+ from_dlpack(c).mark_layout_dynamic(),
342
+ current_stream,
343
+ )
344
+
345
+ # When compiled we inlined op in the kernel, so we do not pass it when benchmarking
346
+
347
+ avg_time_us = testing.benchmark(
348
+ compiled_func,
349
+ kernel_arguments=testing.JitArguments(
350
+ from_dlpack(a),
351
+ from_dlpack(b),
352
+ from_dlpack(c).mark_layout_dynamic(),
353
+ current_stream,
354
+ ),
355
+ warmup_iterations=warmup_iterations,
356
+ iterations=iterations,
357
+ use_cuda_graphs=True,
358
+ stream=current_stream,
359
+ )
360
+
361
+ avg_time = avg_time_us / 1e3
362
+
363
+ # Print execution results
364
+ print(f"Kernel execution time: {avg_time:.4f} ms")
365
+ print(
366
+ f"Achieved memory throughput: {(3 * a.numel() * dtype.width // 8) / (avg_time / 1000) / 1e9:.2f} GB/s"
367
+ )
368
+ print(f"First few elements of result: \n{c[:3, :3]}")
369
+
370
+
371
+ if __name__ == "__main__":
372
+ parser = argparse.ArgumentParser(
373
+ description="example of elementwise apply to demonstrate building elementwise kernels"
374
+ )
375
+ parser.add_argument("--M", default=128, type=int)
376
+ parser.add_argument("--N", default=128, type=int)
377
+ parser.add_argument("--op", default="add", type=str)
378
+ parser.add_argument("--warmup_iterations", default=2, type=int)
379
+ parser.add_argument("--iterations", default=100, type=int)
380
+ parser.add_argument("--skip_ref_check", action="store_true")
381
+ parser.add_argument("--benchmark", action="store_true")
382
+ args = parser.parse_args()
383
+ run_elementwise_apply_and_verify(
384
+ getattr(operator, args.op),
385
+ args.M,
386
+ args.N,
387
+ dtype=cutlass.Float32,
388
+ warmup_iterations=args.warmup_iterations,
389
+ iterations=args.iterations,
390
+ skip_ref_check=args.skip_ref_check,
391
+ benchmark=args.benchmark,
392
+ )
393
+ print("\nPASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/flash_attention_v2.py ADDED
@@ -0,0 +1,1334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ from types import SimpleNamespace
31
+ from typing import Type, Union, Callable
32
+
33
+ import torch
34
+ import cuda.bindings.driver as cuda
35
+ import cutlass.cute.testing as testing
36
+ import cutlass
37
+ import cutlass.cute as cute
38
+ from cutlass.cute.nvgpu import cpasync, warp
39
+ import cutlass.torch as cutlass_torch
40
+ from cutlass.cute.runtime import from_dlpack
41
+ import cutlass.utils as utils
42
+
43
+ """
44
+ A flash attention v2 forward pass example for NVIDIA Ampere SM80 architecture using CUTE DSL.
45
+
46
+ - Matrix Q is BxSqxNxH, B is batch dimension, Sq is query sequence length, N is number of heads, H is head dimension
47
+ - Matrix K is BxSkxNxH, B is batch dimension, Sk is key sequence length, N is number of heads, H is head dimension
48
+ - Matrix V is BxSkxNxH, B is batch dimension, Sk is key sequence length, N is number of heads, H is head dimension
49
+ - Matrix O is BxSqxNxH, B is batch dimension, Sq is query sequence length, N is number of heads, H is head dimension
50
+
51
+ This kernel supports the following features:
52
+ - Utilizes CpAsync for efficient memory operations
53
+ - Utilizes Ampere's tensor core for matrix multiply-accumulate (MMA) operations
54
+ - Utilizes register pipeline to overlap shared memory-to-register transfers with computations.
55
+ - Leverages DSL to implement an integrated online softmax fusion pattern.
56
+
57
+ This kernel works as follows:
58
+ 1. Load Q and K matrices from global memory (GMEM) to shared memory (SMEM) using CpAsync operations.
59
+ 2. Perform matrix multiply-accumulate (MMA) operations using tensor core instructions to compute intermediate result S.
60
+ 3. Apply padding mask or causal mask to S during initial iterations.
61
+ 4. Apply online softmax to S and rescale O using results from previous iteration.
62
+ 5. Load V matrices and perform matrix multiply-accumulate (MMA) operations to compute final result O.
63
+ 6. Normalize O after all iterations complete and store result back to global memory (GMEM).
64
+
65
+ To run this example:
66
+
67
+ .. code-block:: bash
68
+
69
+ python examples/ampere/flash_attention_v2.py \
70
+ --dtype Float16 --head_dim 128 --m_block_size 128 --n_block_size 128 \
71
+ --num_threads 128 --batch_size 1 --seqlen_q 1280 --seqlen_k 1536 \
72
+ --num_head 16 --softmax_scale 1.0 --is_causal
73
+
74
+ The above command configures the model to use float16 for inputs and outputs. The problem dimensions
75
+ are set to: batch size of 1, query sequence length of 1280, key sequence length of 1536, head dimension
76
+ of 128, and 16 attention heads. The softmax scale is set to 1.0 and causal masking is enabled. The computation
77
+ uses tiles of size 128x128 for m and n dimensions, and utilizes 128 parallel threads.
78
+
79
+ To collect the performance with NCU profiler:
80
+
81
+ .. code-block:: bash
82
+
83
+ ncu python examples/ampere/flash_attention_v2.py \
84
+ --dtype Float16 --head_dim 128 --m_block_size 128 --n_block_size 128 \
85
+ --num_threads 128 --batch_size 1 --seqlen_q 1280 --seqlen_k 1536 \
86
+ --num_head 16 --softmax_scale 1.0 --is_causal --skip_ref_check
87
+
88
+ There are some constraints for this example:
89
+ * Only fp16 and bf16 data types are supported.
90
+ * The contiguous dimension of each tensor must be at least 16 bytes aligned.
91
+ * The log-sum-exp(for training) is not computed in the kernel.
92
+ * The values of `m_block_size`, `n_block_size`, and `head_dim` must be selected to stay within shared memory capacity limits.
93
+ * `m_block_size * 2` must be divisible by `num_threads`, otherwise the kernel will not be able to get the correct result.
94
+ """
95
+
96
+
97
+ class FlashAttentionForwardAmpere:
98
+ def __init__(
99
+ self,
100
+ head_dim: int,
101
+ m_block_size: int = 128,
102
+ n_block_size: int = 128,
103
+ num_threads: int = 128,
104
+ is_causal: bool = False,
105
+ ):
106
+ """Initializes the configuration for a flash attention v2 kernel.
107
+
108
+ All contiguous dimensions must be at least 16 bytes aligned which indicates the head dimension
109
+ should be a multiple of 8.
110
+
111
+ :param head_dim: head dimension
112
+ :type head_dim: int
113
+ :param m_block_size: m block size
114
+ :type m_block_size: int
115
+ :param n_block_size: n block size
116
+ :type n_block_size: int
117
+ :param num_threads: number of threads
118
+ :type num_threads: int
119
+ :param is_causal: is causal
120
+ """
121
+ self._head_dim = head_dim
122
+ self._m_block_size = m_block_size
123
+ self._n_block_size = n_block_size
124
+ # padding head_dim to a multiple of 32 as k_block_size
125
+ self._head_dim_padded = (head_dim + 31) // 32 * 32
126
+ self._num_threads = num_threads
127
+ self._is_causal = is_causal
128
+
129
+ @staticmethod
130
+ def can_implement(
131
+ dtype, head_dim, m_block_size, n_block_size, num_threads, is_causal
132
+ ) -> bool:
133
+ """Check if the kernel can be implemented with the given parameters.
134
+
135
+ :param dtype: data type
136
+ :type dtype: cutlass.Numeric
137
+ :param head_dim: head dimension
138
+ :type head_dim: int
139
+ :param m_block_size: m block size
140
+ :type m_block_size: int
141
+ :param n_block_size: n block size
142
+ :type n_block_size: int
143
+ :param num_threads: number of threads
144
+ :type num_threads: int
145
+ :param is_causal: is causal
146
+ :type is_causal: bool
147
+
148
+ :return: True if the kernel can be implemented, False otherwise
149
+ :rtype: bool
150
+ """
151
+ # Check if data type is fp16 or bf16
152
+ if dtype != cutlass.Float16 and dtype != cutlass.BFloat16:
153
+ return False
154
+
155
+ # Check if head dimension is a multiple of 8
156
+ if head_dim % 8 != 0:
157
+ return False
158
+
159
+ # Check if number of threads is a multiple of 32
160
+ if num_threads % 32 != 0:
161
+ return False
162
+
163
+ # Check if block size setting is out of shared memory capacity
164
+ # Shared memory usage: Q tile + (K tile + V tile) where K and V use the same tile size
165
+ smem_usage = (m_block_size * head_dim + n_block_size * head_dim * 2) * 2
166
+ smem_capacity = utils.get_smem_capacity_in_bytes("sm_80")
167
+ if smem_usage > smem_capacity:
168
+ return False
169
+
170
+ # Check if twice the block size is divisible by the number of threads
171
+ if (m_block_size * 2) % num_threads != 0:
172
+ return False
173
+
174
+ return True
175
+
176
+ @cute.jit
177
+ def __call__(
178
+ self,
179
+ mQ: cute.Tensor,
180
+ mK: cute.Tensor,
181
+ mV: cute.Tensor,
182
+ mO: cute.Tensor,
183
+ softmax_scale: cutlass.Float32,
184
+ stream: cuda.CUstream,
185
+ ):
186
+ """Configures and launches the flash attention v2 kernel.
187
+
188
+ mQ/mK/mV/mO has same data types(supports fp16 and bf16) and same layout:
189
+ (batch_size, seqlen_q, num_head, head_dim):(seqlen_q * num_head * head_dim, num_head * head_dim, head_dim, 1)
190
+
191
+ Prepares the shared memory layout, tiled copy atoms, tiled mma and shared memory storage.
192
+ Then launches the kernel function with the prepared parameters.
193
+
194
+ :param mQ: query tensor
195
+ :type mQ: cute.Tensor
196
+ :param mK: key tensor
197
+ :type mK: cute.Tensor
198
+ :param mV: value tensor
199
+ :type mV: cute.Tensor
200
+ :param mO: output tensor
201
+ :type mO: cute.Tensor
202
+ :param softmax_scale: softmax scale
203
+ :type softmax_scale: cutlass.Float32
204
+ """
205
+ # Get the data type and check if it is fp16 or bf16
206
+ if cutlass.const_expr(
207
+ not (
208
+ mQ.element_type == mK.element_type == mV.element_type == mO.element_type
209
+ )
210
+ ):
211
+ raise TypeError("All tensors must have the same data type")
212
+ if cutlass.const_expr(
213
+ not (
214
+ mQ.element_type == cutlass.Float16
215
+ or mQ.element_type == cutlass.BFloat16
216
+ )
217
+ ):
218
+ raise TypeError("Only Float16 or BFloat16 is supported")
219
+ self._dtype: Type[cutlass.Numeric] = mQ.element_type
220
+ # ///////////////////////////////////////////////////////////////////////////////
221
+ # Shared memory layout: Q/K/V
222
+ # ///////////////////////////////////////////////////////////////////////////////
223
+ smem_k_block_size = 64 if self._head_dim_padded % 64 == 0 else 32
224
+ swizzle_bits = 3 if smem_k_block_size == 64 else 2
225
+ sQ_layout_atom = cute.make_composed_layout(
226
+ cute.make_swizzle(swizzle_bits, 3, 3),
227
+ 0,
228
+ cute.make_layout((8, smem_k_block_size), stride=(smem_k_block_size, 1)),
229
+ )
230
+ sQ_layout = cute.tile_to_shape(
231
+ sQ_layout_atom,
232
+ (self._m_block_size, self._head_dim_padded),
233
+ (0, 1),
234
+ )
235
+
236
+ sKV_layout_atom = sQ_layout_atom
237
+ sKV_layout = cute.tile_to_shape(
238
+ sKV_layout_atom,
239
+ (self._n_block_size, self._head_dim_padded),
240
+ (0, 1),
241
+ )
242
+
243
+ sO_layout = sQ_layout
244
+
245
+ @cute.struct
246
+ class SharedStorage:
247
+ sQ: cute.struct.Align[
248
+ cute.struct.MemRange[self._dtype, cute.cosize(sQ_layout)], 1024
249
+ ]
250
+ sK: cute.struct.Align[
251
+ cute.struct.MemRange[self._dtype, cute.cosize(sKV_layout)], 1024
252
+ ]
253
+ sV: cute.struct.Align[
254
+ cute.struct.MemRange[self._dtype, cute.cosize(sKV_layout)], 1024
255
+ ]
256
+
257
+ # ///////////////////////////////////////////////////////////////////////////////
258
+ # GMEM Tiled copy:
259
+ # ///////////////////////////////////////////////////////////////////////////////
260
+ # Thread layouts for copies
261
+ universal_copy_bits = 128
262
+ async_copy_elems = universal_copy_bits // self._dtype.width
263
+ # atom_async_copy: async copy atom for QKV load
264
+ atom_async_copy = cute.make_copy_atom(
265
+ cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
266
+ self._dtype,
267
+ num_bits_per_copy=universal_copy_bits,
268
+ )
269
+ # atom_universal_copy: universal copy atom for O store
270
+ atom_universal_copy = cute.make_copy_atom(
271
+ cute.nvgpu.CopyUniversalOp(),
272
+ self._dtype,
273
+ num_bits_per_copy=universal_copy_bits,
274
+ )
275
+ # tQKV_layout: thread layout for QKV load
276
+ tQKV_shape_dim_1 = sQ_layout_atom.outer.shape[1] // async_copy_elems
277
+ tQKV_layout = cute.make_layout(
278
+ (self._num_threads // tQKV_shape_dim_1, tQKV_shape_dim_1),
279
+ stride=(tQKV_shape_dim_1, 1),
280
+ )
281
+ # tO_layout: thread layout for O store
282
+ tO_layout = tQKV_layout
283
+
284
+ # Value layouts for copies
285
+ vQKV_layout = cute.make_layout((1, async_copy_elems))
286
+ vO_layout = vQKV_layout
287
+
288
+ # gmem_tiled_copy_QKV: tiled copy for QKV load
289
+ gmem_tiled_copy_QKV = cute.make_tiled_copy_tv(
290
+ atom_async_copy, tQKV_layout, vQKV_layout
291
+ )
292
+ # gmem_tiled_copy_O: tiled copy for O store
293
+ gmem_tiled_copy_O = cute.make_tiled_copy_tv(
294
+ atom_universal_copy, tO_layout, vO_layout
295
+ )
296
+
297
+ # ///////////////////////////////////////////////////////////////////////////////
298
+ # Tiled mma
299
+ # ///////////////////////////////////////////////////////////////////////////////
300
+ tiled_mma = cute.make_tiled_mma(
301
+ warp.MmaF16BF16Op(self._dtype, cutlass.Float32, (16, 8, 16)),
302
+ (self._num_threads // 32, 1, 1),
303
+ permutation_mnk=(self._num_threads // 32 * 16, 16, 16),
304
+ )
305
+
306
+ # grid_dim: (m_block, batch_size, num_head)
307
+ grid_dim = (
308
+ cute.ceil_div(mQ.shape[1], self._m_block_size),
309
+ cute.size(mQ.shape[0]),
310
+ cute.size(mQ.shape[2]),
311
+ )
312
+ LOG2_E = 1.4426950408889634074
313
+ softmax_scale_log2 = softmax_scale * LOG2_E
314
+ self.kernel(
315
+ mQ,
316
+ mK,
317
+ mV,
318
+ mO,
319
+ softmax_scale_log2,
320
+ sQ_layout,
321
+ sKV_layout,
322
+ sO_layout,
323
+ gmem_tiled_copy_QKV,
324
+ gmem_tiled_copy_O,
325
+ tiled_mma,
326
+ SharedStorage,
327
+ ).launch(
328
+ grid=grid_dim,
329
+ block=[self._num_threads, 1, 1],
330
+ stream=stream,
331
+ )
332
+
333
+ @cute.kernel
334
+ def kernel(
335
+ self,
336
+ mQ: cute.Tensor,
337
+ mK: cute.Tensor,
338
+ mV: cute.Tensor,
339
+ mO: cute.Tensor,
340
+ softmax_scale_log2: cutlass.Float32,
341
+ sQ_layout: cute.ComposedLayout,
342
+ sKV_layout: cute.ComposedLayout,
343
+ sO_layout: cute.ComposedLayout,
344
+ gmem_tiled_copy_QKV: cute.TiledCopy,
345
+ gmem_tiled_copy_O: cute.TiledCopy,
346
+ tiled_mma: cute.TiledMma,
347
+ SharedStorage: cutlass.Constexpr,
348
+ ):
349
+ """Kernel function for flash attention v2.
350
+
351
+ :param mQ: query tensor
352
+ :type mQ: cute.Tensor
353
+ :param mK: key tensor
354
+ :type mK: cute.Tensor
355
+ :param mV: value tensor
356
+ :type mV: cute.Tensor
357
+ :param mO: output tensor
358
+ :type mO: cute.Tensor
359
+ :param softmax_scale_log2: softmax scale log2
360
+ :type softmax_scale_log2: cutlass.Float32
361
+ :param sQ_layout: query layout
362
+ :type sQ_layout: cute.ComposedLayout
363
+ :param sKV_layout: key/value layout
364
+ :type sKV_layout: cute.ComposedLayout
365
+ :param sO_layout: output layout
366
+ :type sO_layout: cute.ComposedLayout
367
+ :param gmem_tiled_copy_QKV: tiled copy for QKV load
368
+ :type gmem_tiled_copy_QKV: cute.TiledCopy
369
+ :param gmem_tiled_copy_O: tiled copy for O store
370
+ :type gmem_tiled_copy_O: cute.TiledCopy
371
+ :param tiled_mma: tiled mma
372
+ :type tiled_mma: cute.TiledMma
373
+ :param SharedStorage: shared storage
374
+ :type SharedStorage: cutlass.Constexpr
375
+ """
376
+ # Thread index, block index
377
+ tidx, _, _ = cute.arch.thread_idx()
378
+ m_block, batch_size, num_head = cute.arch.block_idx()
379
+
380
+ n_block_max = cute.ceil_div(mK.shape[1], self._n_block_size)
381
+ if self._is_causal:
382
+ n_block_max = min(
383
+ cute.ceil_div(
384
+ (m_block + 1) * self._m_block_size,
385
+ self._n_block_size,
386
+ ),
387
+ n_block_max,
388
+ )
389
+ n_block = n_block_max - 1
390
+
391
+ # ///////////////////////////////////////////////////////////////////////////////
392
+ # Get the appropriate tiles for this thread block.
393
+ # ///////////////////////////////////////////////////////////////////////////////
394
+ # (m_block_size, head_dim)
395
+ gQ = cute.local_tile(
396
+ mQ[batch_size, None, num_head, None],
397
+ (self._m_block_size, self._head_dim_padded),
398
+ (m_block, 0),
399
+ )
400
+ # (n_block_size, head_dim, n_block)
401
+ gK = cute.local_tile(
402
+ mK[batch_size, None, num_head, None],
403
+ (self._n_block_size, self._head_dim_padded),
404
+ (None, 0),
405
+ )
406
+ # (n_block_size, head_dim, n_block)
407
+ gV = cute.local_tile(
408
+ mV[batch_size, None, num_head, None],
409
+ (self._n_block_size, self._head_dim_padded),
410
+ (None, 0),
411
+ )
412
+
413
+ # ///////////////////////////////////////////////////////////////////////////////
414
+ # Get shared memory buffer
415
+ # ///////////////////////////////////////////////////////////////////////////////
416
+ smem = cutlass.utils.SmemAllocator()
417
+
418
+ storage = smem.allocate(SharedStorage)
419
+ sQ = storage.sQ.get_tensor(sQ_layout)
420
+ sK = storage.sK.get_tensor(sKV_layout)
421
+ sV = storage.sV.get_tensor(sKV_layout)
422
+
423
+ # Transpose view of V to tensor with layout (head_dim, n_block_size) for tiled mma
424
+ sVt = cute.composition(
425
+ sV,
426
+ cute.make_layout(
427
+ (self._head_dim_padded, self._n_block_size),
428
+ stride=(self._n_block_size, 1),
429
+ ),
430
+ )
431
+
432
+ gmem_thr_copy_QKV = gmem_tiled_copy_QKV.get_slice(tidx)
433
+ # (CPY_Atom, CPY_M, CPY_K)
434
+ tQgQ = gmem_thr_copy_QKV.partition_S(gQ)
435
+ tQsQ = gmem_thr_copy_QKV.partition_D(sQ)
436
+ # (CPY_Atom, CPY_N, CPY_K, n_block)
437
+ tKgK = gmem_thr_copy_QKV.partition_S(gK)
438
+ tKsK = gmem_thr_copy_QKV.partition_D(sK)
439
+ # (CPY_Atom, CPY_N, CPY_K, n_block)
440
+ tVgV = gmem_thr_copy_QKV.partition_S(gV)
441
+ tVsV = gmem_thr_copy_QKV.partition_D(sV)
442
+
443
+ # ///////////////////////////////////////////////////////////////////////////////
444
+ # Tile MMA compute thread partitions and allocate accumulators
445
+ # ///////////////////////////////////////////////////////////////////////////////
446
+ thr_mma = tiled_mma.get_slice(tidx)
447
+ tSrQ = thr_mma.make_fragment_A(thr_mma.partition_A(sQ))
448
+ tSrK = thr_mma.make_fragment_B(thr_mma.partition_B(sK))
449
+ tOrVt = thr_mma.make_fragment_B(thr_mma.partition_B(sVt))
450
+ acc_shape_O = thr_mma.partition_shape_C(
451
+ (self._m_block_size, self._head_dim_padded)
452
+ )
453
+ acc_O = cute.make_fragment(acc_shape_O, cutlass.Float32)
454
+ acc_O.fill(0.0)
455
+
456
+ # ///////////////////////////////////////////////////////////////////////////////
457
+ # Smem copy atom tiling
458
+ # ///////////////////////////////////////////////////////////////////////////////
459
+ smem_copy_atom_Q = cute.make_copy_atom(
460
+ warp.LdMatrix8x8x16bOp(transpose=False, num_matrices=4),
461
+ self._dtype,
462
+ )
463
+ smem_copy_atom_K = cute.make_copy_atom(
464
+ warp.LdMatrix8x8x16bOp(transpose=False, num_matrices=4),
465
+ self._dtype,
466
+ )
467
+ smem_copy_atom_V = cute.make_copy_atom(
468
+ warp.LdMatrix8x8x16bOp(transpose=True, num_matrices=4),
469
+ self._dtype,
470
+ )
471
+ smem_tiled_copy_Q = cute.make_tiled_copy_A(smem_copy_atom_Q, tiled_mma)
472
+ smem_tiled_copy_K = cute.make_tiled_copy_B(smem_copy_atom_K, tiled_mma)
473
+ smem_tiled_copy_V = cute.make_tiled_copy_B(smem_copy_atom_V, tiled_mma)
474
+
475
+ smem_thr_copy_Q = smem_tiled_copy_Q.get_slice(tidx)
476
+ smem_thr_copy_K = smem_tiled_copy_K.get_slice(tidx)
477
+ smem_thr_copy_V = smem_tiled_copy_V.get_slice(tidx)
478
+
479
+ tSsQ = smem_thr_copy_Q.partition_S(sQ)
480
+ tSrQ_copy_view = smem_thr_copy_Q.retile(tSrQ)
481
+ tSsK = smem_thr_copy_K.partition_S(sK)
482
+ tSrK_copy_view = smem_thr_copy_K.retile(tSrK)
483
+ tOsVt = smem_thr_copy_V.partition_S(sVt)
484
+ tOrVt_copy_view = smem_thr_copy_V.retile(tOrVt)
485
+
486
+ # ///////////////////////////////////////////////////////////////////////////////
487
+ # Predicate: Mark indices that need to copy when problem_shape isn't a multiple
488
+ # of tile_shape
489
+ # ///////////////////////////////////////////////////////////////////////////////
490
+ # Construct identity layout for Q and KV
491
+ mcQ = cute.make_identity_tensor(mQ.layout.shape)
492
+ mcKV = cute.make_identity_tensor(mK.layout.shape)
493
+ cQ = cute.local_tile(
494
+ mcQ[batch_size, None, num_head, None],
495
+ (self._m_block_size, self._head_dim_padded),
496
+ (m_block, 0),
497
+ )
498
+ cKV = cute.local_tile(
499
+ mcKV[batch_size, None, num_head, None],
500
+ (self._n_block_size, self._head_dim_padded),
501
+ (n_block, 0),
502
+ )
503
+
504
+ # Repeat the partitioning with identity layouts
505
+ tQcQ = gmem_thr_copy_QKV.partition_S(cQ)
506
+ tKVcKV = gmem_thr_copy_QKV.partition_S(cKV)
507
+ # Allocate predicate tensors for m and n, here we only allocate the tile of k, and do special process for mn.
508
+ # This is to reduce register pressure and gets 2-3% performance gain compared with allocating the whole tile.
509
+ tQpQ = cute.make_fragment(
510
+ cute.make_layout(
511
+ (
512
+ tQsQ.shape[0][1],
513
+ cute.size(tQsQ, mode=[1]),
514
+ cute.size(tQsQ, mode=[2]),
515
+ ),
516
+ stride=(cute.size(tQsQ, mode=[2]), 0, 1),
517
+ ),
518
+ cutlass.Boolean,
519
+ )
520
+ tKVpKV = cute.make_fragment(
521
+ cute.make_layout(
522
+ (
523
+ tKsK.shape[0][1],
524
+ cute.size(tKsK, mode=[1]),
525
+ cute.size(tKsK, mode=[2]),
526
+ ),
527
+ stride=(cute.size(tKsK, mode=[2]), 0, 1),
528
+ ),
529
+ cutlass.Boolean,
530
+ )
531
+ # Set predicates for head_dim bounds, seqlen_q/k bounds is processed at the first tile.
532
+ for rest_v in cutlass.range_constexpr(tQpQ.shape[0]):
533
+ for rest_k in cutlass.range_constexpr(tQpQ.shape[2]):
534
+ tQpQ[rest_v, 0, rest_k] = cute.elem_less(
535
+ tQcQ[(0, rest_v), 0, rest_k][3], mQ.layout.shape[3]
536
+ )
537
+ for rest_v in cutlass.range_constexpr(tKVpKV.shape[0]):
538
+ for rest_k in cutlass.range_constexpr(tKVpKV.shape[2]):
539
+ tKVpKV[rest_v, 0, rest_k] = cute.elem_less(
540
+ tKVcKV[(0, rest_v), 0, rest_k][3], mK.layout.shape[3]
541
+ )
542
+ # ///////////////////////////////////////////////////////////////////////////////
543
+ # Prefetch Prologue
544
+ # ///////////////////////////////////////////////////////////////////////////////
545
+ # Start async loads of the last mn-tile, where we take care of the mn residue
546
+ for m in cutlass.range_constexpr(cute.size(tQsQ.shape[1])):
547
+ if cute.elem_less(tQcQ[0, m, 0][1], mQ.layout.shape[1]):
548
+ cute.copy(
549
+ gmem_tiled_copy_QKV,
550
+ tQgQ[None, m, None],
551
+ tQsQ[None, m, None],
552
+ pred=tQpQ[None, m, None],
553
+ )
554
+ else:
555
+ # Clear the smem tiles to account for predicated off loads
556
+ tQsQ[None, m, None].fill(0)
557
+ for n in cutlass.range_constexpr(cute.size(tKsK.shape[1])):
558
+ if cute.elem_less(tKVcKV[0, n, 0][1], mK.layout.shape[1]):
559
+ cute.copy(
560
+ gmem_tiled_copy_QKV,
561
+ tKgK[None, n, None, n_block],
562
+ tKsK[None, n, None],
563
+ pred=tKVpKV[None, n, None],
564
+ )
565
+ else:
566
+ # Clear the smem tiles to account for predicated off loads
567
+ tKsK[None, n, None].fill(0)
568
+
569
+ cute.arch.cp_async_commit_group()
570
+ # ///////////////////////////////////////////////////////////////////////////////
571
+ # Softmax intermediate result: row_max and row_sum
572
+ # ///////////////////////////////////////////////////////////////////////////////
573
+ # shape: (atom_v_m * rest_m)
574
+ row_max = cute.make_fragment(
575
+ (acc_O.shape[0][0] * acc_O.shape[1]), cutlass.Float32
576
+ )
577
+ # shape: (atom_v_m * rest_m)
578
+ row_sum = cute.make_fragment(
579
+ (acc_O.shape[0][0] * acc_O.shape[1]), cutlass.Float32
580
+ )
581
+ row_max.fill(-cutlass.Float32.inf)
582
+ row_sum.fill(0.0)
583
+
584
+ # group parameters for compute_one_n_block
585
+ basic_params = SimpleNamespace(
586
+ m_block=m_block,
587
+ n_block=n_block,
588
+ mQ=mQ,
589
+ mK=mK,
590
+ batch_size=batch_size,
591
+ num_head=num_head,
592
+ )
593
+ mma_params = SimpleNamespace(
594
+ thr_mma=thr_mma,
595
+ tiled_mma=tiled_mma,
596
+ tSrQ=tSrQ,
597
+ tSrK=tSrK,
598
+ tOrVt=tOrVt,
599
+ acc_O=acc_O,
600
+ )
601
+ gmem_copy_params = SimpleNamespace(
602
+ gmem_tiled_copy_QKV=gmem_tiled_copy_QKV,
603
+ tKVcKV=tKVcKV,
604
+ tKgK=tKgK,
605
+ tKsK=tKsK,
606
+ tVgV=tVgV,
607
+ tVsV=tVsV,
608
+ tKVpKV=tKVpKV,
609
+ )
610
+ smem_copy_params = SimpleNamespace(
611
+ smem_tiled_copy_Q=smem_tiled_copy_Q,
612
+ smem_tiled_copy_K=smem_tiled_copy_K,
613
+ smem_tiled_copy_V=smem_tiled_copy_V,
614
+ tSsQ=tSsQ,
615
+ tSrQ_copy_view=tSrQ_copy_view,
616
+ tSsK=tSsK,
617
+ tSrK_copy_view=tSrK_copy_view,
618
+ tOsVt=tOsVt,
619
+ tOrVt_copy_view=tOrVt_copy_view,
620
+ )
621
+ softmax_params = SimpleNamespace(
622
+ row_max=row_max,
623
+ row_sum=row_sum,
624
+ softmax_scale_log2=softmax_scale_log2,
625
+ )
626
+
627
+ # Start processing of the first n-block.
628
+ # For performance reason, we separate out two kinds of iterations:
629
+ # those that need masking on S, and those that don't.
630
+ # We need masking on S for the very last block when K and V has length not multiple of n_block_size.
631
+ # We also need masking on S if it's causal, for the last ceil_div(m_block_size, n_block_size) blocks.
632
+ # We will have at least 1 "masking" iteration.
633
+ mask_steps = 1
634
+ if cutlass.const_expr(self._is_causal):
635
+ mask_steps = cute.ceil_div(self._m_block_size, self._n_block_size)
636
+
637
+ for n_tile in cutlass.range_constexpr(mask_steps):
638
+ n_block = n_block_max - n_tile - 1
639
+ basic_params.n_block = n_block
640
+ if cutlass.const_expr(self._is_causal):
641
+ if n_block >= 0:
642
+ self.compute_one_n_block(
643
+ basic_params,
644
+ mma_params,
645
+ gmem_copy_params,
646
+ smem_copy_params,
647
+ softmax_params,
648
+ is_first_n_block=(n_tile == 0),
649
+ in_mask_steps=True,
650
+ )
651
+ else:
652
+ self.compute_one_n_block(
653
+ basic_params,
654
+ mma_params,
655
+ gmem_copy_params,
656
+ smem_copy_params,
657
+ softmax_params,
658
+ is_first_n_block=True,
659
+ in_mask_steps=True,
660
+ )
661
+
662
+ # Start async loads of rest k-tiles in reverse order, no k-residue handling needed
663
+ for n_tile in range(mask_steps, n_block_max, 1):
664
+ n_block = n_block_max - n_tile - 1
665
+ basic_params.n_block = n_block
666
+ self.compute_one_n_block(
667
+ basic_params,
668
+ mma_params,
669
+ gmem_copy_params,
670
+ smem_copy_params,
671
+ softmax_params,
672
+ is_first_n_block=False,
673
+ in_mask_steps=False,
674
+ )
675
+
676
+ # ///////////////////////////////////////////////////////////////////////////////
677
+ # Epilogue
678
+ # ///////////////////////////////////////////////////////////////////////////////
679
+ # normalize acc_O by row_sum and calculate the lse
680
+ self.normalize_softmax(acc_O, row_sum)
681
+ # store acc_O
682
+ rO = cute.make_fragment_like(acc_O, self._dtype)
683
+ rO.store(acc_O.load().to(self._dtype))
684
+ # reuse sQ's data iterator
685
+ sO = cute.make_tensor(sQ.iterator, sO_layout)
686
+
687
+ # smem copy atom for O
688
+ smem_copy_atom_O = cute.make_copy_atom(
689
+ cute.nvgpu.CopyUniversalOp(), self._dtype
690
+ )
691
+ # tiled copy atom for O
692
+ smem_tiled_copy_O = cute.make_tiled_copy_C(smem_copy_atom_O, tiled_mma)
693
+ smem_thr_copy_O = smem_tiled_copy_O.get_slice(tidx)
694
+ taccOrO = smem_thr_copy_O.retile(rO)
695
+ taccOsO = smem_thr_copy_O.partition_D(sO)
696
+ # copy acc O from rmem to smem with the smem copy atom
697
+ cute.copy(
698
+ smem_copy_atom_O,
699
+ taccOrO,
700
+ taccOsO,
701
+ )
702
+ gO = cute.local_tile(
703
+ mO[batch_size, None, num_head, None],
704
+ (self._m_block_size, self._head_dim_padded),
705
+ (m_block, 0),
706
+ )
707
+
708
+ gmem_thr_copy_O = gmem_tiled_copy_O.get_slice(tidx)
709
+ tOsO = gmem_thr_copy_O.partition_S(sO)
710
+ tOgO = gmem_thr_copy_O.partition_D(gO)
711
+ tOrO = cute.make_fragment_like(tOgO, self._dtype)
712
+ # sync before all smem stores are done.
713
+ cute.arch.barrier()
714
+ # load acc O from smem to rmem for wider vectorization
715
+ cute.copy(
716
+ gmem_tiled_copy_O,
717
+ tOsO,
718
+ tOrO,
719
+ )
720
+ mcO = cute.make_identity_tensor(mO.layout.shape)
721
+ cO = cute.local_tile(
722
+ mcO[batch_size, None, num_head, None],
723
+ (self._m_block_size, self._head_dim_padded),
724
+ (m_block, 0),
725
+ )
726
+ tOcO = gmem_thr_copy_O.partition_D(cO)
727
+ tOpO = cute.make_fragment(
728
+ cute.make_layout(
729
+ (tOgO.shape[0][1], tOgO.shape[1], tOgO.shape[2]),
730
+ stride=(tOgO.shape[2], 0, 1),
731
+ ),
732
+ cutlass.Boolean,
733
+ )
734
+ for rest_v in cutlass.range_constexpr(tOpO.shape[0]):
735
+ for rest_n in cutlass.range_constexpr(cute.size(tOpO.shape[2])):
736
+ tOpO[rest_v, 0, rest_n] = cute.elem_less(
737
+ tOcO[(0, rest_v), 0, rest_n][3], mO.layout.shape[3]
738
+ )
739
+ # copy acc O from rmem to gmem
740
+ for rest_m in cutlass.range_constexpr(cute.size(tOpO.shape[1])):
741
+ if cute.elem_less(tOcO[0, rest_m, 0][1], mO.layout.shape[1]):
742
+ cute.copy(
743
+ gmem_tiled_copy_O,
744
+ tOrO[None, rest_m, None],
745
+ tOgO[None, rest_m, None],
746
+ pred=tOpO[None, rest_m, None],
747
+ )
748
+
749
+ @cute.jit
750
+ def compute_one_n_block(
751
+ self,
752
+ basic_params: SimpleNamespace,
753
+ mma_params: SimpleNamespace,
754
+ gmem_copy_params: SimpleNamespace,
755
+ smem_copy_params: SimpleNamespace,
756
+ softmax_params: SimpleNamespace,
757
+ is_first_n_block: cutlass.Constexpr,
758
+ in_mask_steps: cutlass.Constexpr,
759
+ ):
760
+ """Compute one n_block of S/O.
761
+
762
+ This function provides different variants for processing the first n block versus subsequent blocks,
763
+ as well as variants for handling masked and unmasked steps.
764
+
765
+ :param basic_params: basic parameters
766
+ :type basic_params: SimpleNamespace
767
+ :param mma_params: mma parameters
768
+ :type mma_params: SimpleNamespace
769
+ :param gmem_copy_params: gmem copy parameters
770
+ :type gmem_copy_params: SimpleNamespace
771
+ :param smem_copy_params: smem copy parameters
772
+ :type smem_copy_params: SimpleNamespace
773
+ :param softmax_params: softmax parameters
774
+ :type softmax_params: SimpleNamespace
775
+ :param is_first_n_block: is first n block
776
+ :type is_first_n_block: cutlass.Constexpr
777
+ """
778
+ acc_shape_S = mma_params.thr_mma.partition_shape_C(
779
+ (self._m_block_size, self._n_block_size)
780
+ )
781
+ acc_S = cute.make_fragment(acc_shape_S, cutlass.Float32)
782
+ acc_S.fill(0.0)
783
+
784
+ # wait for smem tile QK before mma calculation for S
785
+ cute.arch.cp_async_wait_group(0)
786
+ cute.arch.barrier()
787
+ # load smem tile V for O, special process for the first tile to avoid loading nan.
788
+ # The `if` here is a constexpr, won't be generated in the IR.
789
+ if is_first_n_block:
790
+ for n in cutlass.range_constexpr(cute.size(gmem_copy_params.tVsV.shape[1])):
791
+ if cute.elem_less(
792
+ gmem_copy_params.tKVcKV[0, n, 0][1],
793
+ basic_params.mK.layout.shape[1],
794
+ ):
795
+ cute.copy(
796
+ gmem_copy_params.gmem_tiled_copy_QKV,
797
+ gmem_copy_params.tVgV[None, n, None, basic_params.n_block],
798
+ gmem_copy_params.tVsV[None, n, None],
799
+ pred=gmem_copy_params.tKVpKV[None, n, None],
800
+ )
801
+ else:
802
+ gmem_copy_params.tVsV[None, n, None].fill(0.0)
803
+ else:
804
+ cute.copy(
805
+ gmem_copy_params.gmem_tiled_copy_QKV,
806
+ gmem_copy_params.tVgV[None, None, None, basic_params.n_block],
807
+ gmem_copy_params.tVsV,
808
+ pred=gmem_copy_params.tKVpKV,
809
+ )
810
+
811
+ cute.arch.cp_async_commit_group()
812
+ # ///////////////////////////////////////////////////////////////////////////////
813
+ # S gemm calculation
814
+ # ///////////////////////////////////////////////////////////////////////////////
815
+ # load first QK k-block from smem to rmem for mma
816
+ cute.copy(
817
+ smem_copy_params.smem_tiled_copy_Q,
818
+ smem_copy_params.tSsQ[None, None, 0],
819
+ smem_copy_params.tSrQ_copy_view[None, None, 0],
820
+ )
821
+ cute.copy(
822
+ smem_copy_params.smem_tiled_copy_K,
823
+ smem_copy_params.tSsK[None, None, 0],
824
+ smem_copy_params.tSrK_copy_view[None, None, 0],
825
+ )
826
+ # mma for S
827
+ for k in cutlass.range_constexpr(cute.size(smem_copy_params.tSsQ.shape[2])):
828
+ # load next QK k-block from smem to rmem for mma
829
+ k_next = (k + 1) % cute.size(smem_copy_params.tSsQ.shape[2])
830
+ cute.copy(
831
+ smem_copy_params.smem_tiled_copy_Q,
832
+ smem_copy_params.tSsQ[None, None, k_next],
833
+ smem_copy_params.tSrQ_copy_view[None, None, k_next],
834
+ )
835
+ cute.copy(
836
+ smem_copy_params.smem_tiled_copy_K,
837
+ smem_copy_params.tSsK[None, None, k_next],
838
+ smem_copy_params.tSrK_copy_view[None, None, k_next],
839
+ )
840
+ cute.gemm(
841
+ mma_params.tiled_mma,
842
+ acc_S,
843
+ mma_params.tSrQ[None, None, k],
844
+ mma_params.tSrK[None, None, k],
845
+ acc_S,
846
+ )
847
+
848
+ # wait for smem tile V for O
849
+ cute.arch.cp_async_wait_group(0)
850
+ cute.arch.barrier()
851
+
852
+ if basic_params.n_block > 0:
853
+ cute.copy(
854
+ gmem_copy_params.gmem_tiled_copy_QKV,
855
+ gmem_copy_params.tKgK[None, None, None, basic_params.n_block - 1],
856
+ gmem_copy_params.tKsK,
857
+ pred=gmem_copy_params.tKVpKV,
858
+ )
859
+ cute.arch.cp_async_commit_group()
860
+ # ///////////////////////////////////////////////////////////////////////////////
861
+ # online softmax
862
+ # ///////////////////////////////////////////////////////////////////////////////
863
+ self.softmax_rescale_O(
864
+ basic_params,
865
+ mma_params,
866
+ softmax_params,
867
+ acc_S,
868
+ is_first_n_block,
869
+ in_mask_steps,
870
+ )
871
+
872
+ rP = cute.make_fragment_like(acc_S, self._dtype)
873
+ rP.store(acc_S.load().to(self._dtype))
874
+ # ///////////////////////////////////////////////////////////////////////////////
875
+ # O gemm calculation
876
+ # ///////////////////////////////////////////////////////////////////////////////
877
+ # Convert layout of acc_S to gemm O accept layout.
878
+ # Due to the mma instruction shape is 16x8x16, we need to convert from (4, MMA_M, MMA_N) to ((4, 2), MMA_M, MMA_N / 2)
879
+ # (4, MMA_M, MMA_N) -> (4, MMA_M, (2, MMA_N / 2))
880
+ rP_layout_divided = cute.logical_divide(rP.layout, (None, None, 2))
881
+ rP_mma_view = cute.make_layout(
882
+ (
883
+ (rP_layout_divided.shape[0], rP_layout_divided.shape[2][0]),
884
+ rP_layout_divided.shape[1],
885
+ rP_layout_divided.shape[2][1],
886
+ ),
887
+ stride=(
888
+ (rP_layout_divided.stride[0], rP_layout_divided.stride[2][0]),
889
+ rP_layout_divided.stride[1],
890
+ rP_layout_divided.stride[2][1],
891
+ ),
892
+ )
893
+ tOrS = cute.make_tensor(rP.iterator, rP_mma_view)
894
+
895
+ # load first V k-block from smem to rmem for mma
896
+ cute.copy(
897
+ smem_copy_params.smem_tiled_copy_V,
898
+ smem_copy_params.tOsVt[None, None, 0],
899
+ smem_copy_params.tOrVt_copy_view[None, None, 0],
900
+ )
901
+ # mma for O
902
+ for k in cutlass.range_constexpr(cute.size(tOrS.shape[2])):
903
+ # load next V k-block from smem to rmem for mma
904
+ k_next = (k + 1) % cute.size(tOrS.shape[2])
905
+ cute.copy(
906
+ smem_copy_params.smem_tiled_copy_V,
907
+ smem_copy_params.tOsVt[None, None, k_next],
908
+ smem_copy_params.tOrVt_copy_view[None, None, k_next],
909
+ )
910
+ cute.gemm(
911
+ mma_params.tiled_mma,
912
+ mma_params.acc_O,
913
+ tOrS[None, None, k],
914
+ mma_params.tOrVt[None, None, k],
915
+ mma_params.acc_O,
916
+ )
917
+
918
+ @cute.jit
919
+ def softmax_rescale_O(
920
+ self,
921
+ basic_params: SimpleNamespace,
922
+ mma_params: SimpleNamespace,
923
+ softmax_params: SimpleNamespace,
924
+ acc_S: cute.Tensor,
925
+ is_first_n_block: cutlass.Constexpr,
926
+ in_mask_steps: cutlass.Constexpr,
927
+ ):
928
+ """Apply online softmax and rescale acc_O.
929
+
930
+ This function provides different variants for processing the first n block versus subsequent blocks,
931
+ as well as variants for handling masked and unmasked steps.
932
+
933
+ :param basic_params: basic parameters
934
+ :type basic_params: SimpleNamespace
935
+ :param mma_params: mma parameters
936
+ :type mma_params: SimpleNamespace
937
+ :param softmax_params: softmax parameters
938
+ :type softmax_params: SimpleNamespace
939
+ :param acc_S: acc_S tensor
940
+ :type acc_S: cute.Tensor
941
+ :param is_first_n_block: is first n_block
942
+ :type is_first_n_block: cutlass.Constexpr
943
+ :param in_mask_steps: in mask steps
944
+ :type in_mask_steps: cutlass.Constexpr
945
+ """
946
+ # Change acc_S to M,N layout view.
947
+ acc_S_mn = self._make_acc_tensor_mn_view(acc_S)
948
+ acc_O_mn = self._make_acc_tensor_mn_view(mma_params.acc_O)
949
+ row_max_prev = None
950
+ # if it is not the first tile, load the row r of previous row_max and compare with row_max_cur_row.
951
+ if cutlass.const_expr(not is_first_n_block):
952
+ row_max_prev = cute.make_fragment_like(
953
+ softmax_params.row_max, cutlass.Float32
954
+ )
955
+ cute.basic_copy(softmax_params.row_max, row_max_prev)
956
+ # if it is the first tile, create a mask for residual of S to -inf for softmax.
957
+ tScS_mn = None
958
+ if cutlass.const_expr(in_mask_steps):
959
+ mcS = cute.make_identity_tensor(
960
+ (
961
+ basic_params.mQ.shape[0],
962
+ basic_params.mQ.shape[1],
963
+ basic_params.mQ.shape[2],
964
+ basic_params.mK.shape[1],
965
+ )
966
+ )
967
+ cS = cute.local_tile(
968
+ mcS[basic_params.batch_size, None, basic_params.num_head, None],
969
+ (self._m_block_size, self._n_block_size),
970
+ (basic_params.m_block, basic_params.n_block),
971
+ )
972
+ tScS = mma_params.thr_mma.partition_C(cS)
973
+ tScS_mn = self._make_acc_tensor_mn_view(tScS)
974
+
975
+ # Each iteration processes one row of acc_S
976
+ for r in cutlass.range_constexpr(cute.size(softmax_params.row_max)):
977
+ # mask residual of S with -inf
978
+ if cutlass.const_expr(in_mask_steps):
979
+ if cutlass.const_expr(not self._is_causal):
980
+ # traverse column index.
981
+ for c in cutlass.range_constexpr(cute.size(tScS_mn.shape[1])):
982
+ if cute.elem_less(
983
+ basic_params.mK.shape[1], tScS_mn[0, c][3] + 1
984
+ ):
985
+ acc_S_mn[r, c] = -cutlass.Float32.inf
986
+ else:
987
+ # get the column index limit based on current row. Only consider the row index, so the column index sets to 0.
988
+ col_idx_limit = cutlass.min(
989
+ tScS_mn[r, 0][1] + 1, basic_params.mK.shape[1]
990
+ )
991
+ # traverse column index.
992
+ for c in cutlass.range_constexpr(cute.size(tScS_mn.shape[1])):
993
+ # only consider the column index, so the row index sets to 0.
994
+ if cute.elem_less(col_idx_limit, tScS_mn[0, c][3] + 1):
995
+ acc_S_mn[r, c] = -cutlass.Float32.inf
996
+
997
+ # (n_block_size)
998
+ acc_S_row = acc_S_mn[r, None].load()
999
+ # row_max_cur_row => f32
1000
+ row_max_cur_row = acc_S_row.reduce(
1001
+ cute.ReductionOp.MAX, -cutlass.Float32.inf, 0
1002
+ )
1003
+ # quad reduction for row_max
1004
+ row_max_cur_row = self._threadquad_reduce_max(row_max_cur_row)
1005
+ row_max_prev_row = None
1006
+ # if it is not the first tile, load the row r of previous row_max and compare with row_max_cur_row.
1007
+ if cutlass.const_expr(not is_first_n_block):
1008
+ row_max_prev_row = row_max_prev[r]
1009
+ row_max_cur_row = cute.arch.fmax(row_max_prev_row, row_max_cur_row)
1010
+ if cutlass.const_expr(self._is_causal):
1011
+ row_max_cur_row = (
1012
+ 0.0 if row_max_cur_row == -cutlass.Float32.inf else row_max_cur_row
1013
+ )
1014
+
1015
+ # compute exp(x - max) using exp2(x * log_2(e) - max * log_2(e))
1016
+ acc_S_row_exp = cute.math.exp2(
1017
+ acc_S_row * softmax_params.softmax_scale_log2
1018
+ - row_max_cur_row * softmax_params.softmax_scale_log2,
1019
+ fastmath=True,
1020
+ )
1021
+ # acc_S_row_sum => f32
1022
+ acc_S_row_sum = acc_S_row_exp.reduce(
1023
+ cute.ReductionOp.ADD, cutlass.Float32.zero, 0
1024
+ )
1025
+ # if it is not the first tile, load the row r of previous row_max and minus row_max_cur_row to update row_sum.
1026
+ if cutlass.const_expr(not is_first_n_block):
1027
+ prev_minus_cur_exp = cute.math.exp2(
1028
+ row_max_prev_row * softmax_params.softmax_scale_log2
1029
+ - row_max_cur_row * softmax_params.softmax_scale_log2,
1030
+ fastmath=True,
1031
+ )
1032
+ acc_S_row_sum = (
1033
+ acc_S_row_sum + softmax_params.row_sum[r] * prev_minus_cur_exp
1034
+ )
1035
+ acc_O_mn[r, None] = acc_O_mn[r, None].load() * prev_minus_cur_exp
1036
+ # update row_max, row_sum and acc_S
1037
+ softmax_params.row_max[r] = row_max_cur_row
1038
+ softmax_params.row_sum[r] = acc_S_row_sum
1039
+ acc_S_mn[r, None] = acc_S_row_exp
1040
+
1041
+ @cute.jit
1042
+ def normalize_softmax(
1043
+ self,
1044
+ acc_O: cute.Tensor,
1045
+ row_sum: cute.Tensor,
1046
+ ):
1047
+ """Normalize acc_O by row_sum.
1048
+
1049
+ :param acc_O: input tensor
1050
+ :type acc_O: cute.Tensor
1051
+ :param row_sum: row_sum tensor
1052
+ :type row_sum: cute.Tensor
1053
+ """
1054
+ # do quad reduction for row_sum.
1055
+ acc_O_mn = self._make_acc_tensor_mn_view(acc_O)
1056
+ for r in cutlass.range_constexpr(cute.size(row_sum)):
1057
+ row_sum[r] = self._threadquad_reduce_sum(row_sum[r])
1058
+ # if row_sum is zero or nan, set acc_O_mn_row to 1.0
1059
+ acc_O_mn_row_is_zero_or_nan = row_sum[r] == 0.0 or row_sum[r] != row_sum[r]
1060
+
1061
+ scale = (
1062
+ 1.0 if acc_O_mn_row_is_zero_or_nan else cute.arch.rcp_approx(row_sum[r])
1063
+ )
1064
+
1065
+ acc_O_mn[r, None] = acc_O_mn[r, None].load() * scale
1066
+
1067
+ def _make_acc_tensor_mn_view(self, acc: cute.Tensor) -> cute.Tensor:
1068
+ """make acc tensor as mn layout view
1069
+
1070
+ :param acc: input tensor
1071
+ :type acc: cute.Tensor
1072
+ :return: acc tensor mn layout view
1073
+ :rtype: cute.Tensor
1074
+ """
1075
+ acc_layout_col_major = cute.make_layout(acc.layout.shape)
1076
+ acc_layout_mn = cute.make_layout(
1077
+ (
1078
+ (
1079
+ acc_layout_col_major.shape[0][1],
1080
+ acc_layout_col_major.shape[1],
1081
+ ), # MMA_M
1082
+ (
1083
+ acc_layout_col_major.shape[0][0],
1084
+ acc_layout_col_major.shape[2],
1085
+ ), # MMA_N
1086
+ ),
1087
+ stride=(
1088
+ (
1089
+ acc_layout_col_major.stride[0][1],
1090
+ acc_layout_col_major.stride[1],
1091
+ ), # MMA_M
1092
+ (
1093
+ acc_layout_col_major.stride[0][0],
1094
+ acc_layout_col_major.stride[2],
1095
+ ), # MMA_N
1096
+ ),
1097
+ )
1098
+ acc_layout_mn = cute.composition(acc.layout, acc_layout_mn)
1099
+ return cute.make_tensor(acc.iterator, acc_layout_mn)
1100
+
1101
+ def _threadquad_reduce(self, val: cutlass.Float32, op: Callable) -> cutlass.Float32:
1102
+ """thread quad reduction
1103
+
1104
+ :param val: register value
1105
+ :type val: cutlass.Float32
1106
+ :param op: binary operator
1107
+ :type op: Callable
1108
+ :return: reduced value
1109
+ :rtype: cutlass.Float32
1110
+ """
1111
+ val = op(
1112
+ val,
1113
+ cute.arch.shuffle_sync_bfly(val, offset=2, mask=-1, mask_and_clamp=31),
1114
+ )
1115
+ val = op(
1116
+ val,
1117
+ cute.arch.shuffle_sync_bfly(val, offset=1, mask=-1, mask_and_clamp=31),
1118
+ )
1119
+ return val
1120
+
1121
+ def _threadquad_reduce_max(self, val: cutlass.Float32) -> cutlass.Float32:
1122
+ """thread quad reduction max
1123
+
1124
+ :param val: register value
1125
+ :type val: cutlass.Float32
1126
+ :return: max value
1127
+ :rtype: cutlass.Float32
1128
+ """
1129
+ return self._threadquad_reduce(val, lambda x, y: cute.arch.fmax(x, y))
1130
+
1131
+ def _threadquad_reduce_sum(self, val: cutlass.Float32) -> cutlass.Float32:
1132
+ """thread quad reduction sum
1133
+
1134
+ :param val: register value
1135
+ :type val: cutlass.Float32
1136
+ :return: sum value
1137
+ :rtype: cutlass.Float32
1138
+ """
1139
+ return self._threadquad_reduce(val, lambda x, y: x + y)
1140
+
1141
+
1142
+ def run(
1143
+ dtype: Type[cutlass.Numeric],
1144
+ batch_size: int,
1145
+ seqlen_q: int,
1146
+ seqlen_k: int,
1147
+ num_head: int,
1148
+ head_dim: int,
1149
+ softmax_scale: float = 1.0,
1150
+ m_block_size: int = 128,
1151
+ n_block_size: int = 128,
1152
+ num_threads: int = 128,
1153
+ is_causal: bool = False,
1154
+ warmup_iterations: int = 0,
1155
+ iterations: int = 1,
1156
+ skip_ref_check: bool = False,
1157
+ use_cold_l2: bool = False,
1158
+ **kwargs,
1159
+ ):
1160
+ # Skip unsupported testcase
1161
+ if not FlashAttentionForwardAmpere.can_implement(
1162
+ dtype,
1163
+ head_dim,
1164
+ m_block_size,
1165
+ n_block_size,
1166
+ num_threads,
1167
+ is_causal,
1168
+ ):
1169
+ raise TypeError(
1170
+ f"Unsupported testcase {dtype}, {head_dim}, {m_block_size}, {n_block_size}, {num_threads}, {is_causal}"
1171
+ )
1172
+
1173
+ print(f"Running Ampere SM80 FlashAttentionForward test with:")
1174
+ print(f" dtype: {dtype}")
1175
+ print(f" batch_size: {batch_size}")
1176
+ print(f" seqlen_q: {seqlen_q}")
1177
+ print(f" seqlen_k: {seqlen_k}")
1178
+ print(f" num_head: {num_head}")
1179
+ print(f" head_dim: {head_dim}")
1180
+ print(f" softmax_scale: {softmax_scale}")
1181
+ print(f" m_block_size: {m_block_size}")
1182
+ print(f" n_block_size: {n_block_size}")
1183
+ print(f" num_threads: {num_threads}")
1184
+ print(f" is_causal: {is_causal}")
1185
+ print(f" warmup_iterations: {warmup_iterations}")
1186
+ print(f" iterations: {iterations}")
1187
+ print(f" skip_ref_check: {skip_ref_check}")
1188
+ print(f" use_cold_l2: {use_cold_l2}")
1189
+
1190
+ # Create tensor Q/K/V/O
1191
+ def create_tensor(
1192
+ batch_size: int,
1193
+ seqlen: int,
1194
+ num_head: int,
1195
+ head_dim: int,
1196
+ dtype: Type[cutlass.Numeric],
1197
+ ) -> cute.Tensor:
1198
+ # (batch_size, seqlen, num_head, head_dim)
1199
+ shape = (batch_size, seqlen, num_head, head_dim)
1200
+ torch_tensor = (
1201
+ torch.empty(*shape, dtype=torch.int32)
1202
+ .random_(-2, 2)
1203
+ .to(dtype=cutlass_torch.dtype(dtype))
1204
+ .cuda()
1205
+ )
1206
+ # assume input is 16B aligned.
1207
+ cute_tensor = (
1208
+ from_dlpack(torch_tensor, assumed_align=16)
1209
+ .mark_layout_dynamic(leading_dim=3)
1210
+ .mark_compact_shape_dynamic(
1211
+ mode=3,
1212
+ stride_order=torch_tensor.dim_order(),
1213
+ divisibility=(128 // dtype.width),
1214
+ )
1215
+ )
1216
+ return cute_tensor, torch_tensor
1217
+
1218
+ q, q_torch = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
1219
+ k, k_torch = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
1220
+ v, v_torch = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
1221
+ o, o_torch = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
1222
+
1223
+ fa2_fwd = FlashAttentionForwardAmpere(
1224
+ head_dim,
1225
+ m_block_size,
1226
+ n_block_size,
1227
+ num_threads,
1228
+ is_causal,
1229
+ )
1230
+
1231
+ # Get current CUDA stream from PyTorch
1232
+ torch_stream = torch.cuda.current_stream()
1233
+ # Get the raw stream pointer as a CUstream
1234
+ current_stream = cuda.CUstream(torch_stream.cuda_stream)
1235
+ # compile the fa2 forward pass
1236
+ compiled_fa2_fwd = cute.compile(fa2_fwd, q, k, v, o, softmax_scale, current_stream)
1237
+
1238
+ if not skip_ref_check:
1239
+ compiled_fa2_fwd(q, k, v, o, softmax_scale, current_stream)
1240
+ torch.cuda.synchronize()
1241
+ q_ref = q_torch.permute(0, 2, 1, 3)
1242
+ k_ref = k_torch.permute(0, 2, 1, 3)
1243
+ v_ref = v_torch.permute(0, 2, 1, 3)
1244
+ torch.backends.cuda.enable_flash_sdp(enabled=True)
1245
+ ref_o = torch.nn.functional.scaled_dot_product_attention(
1246
+ q_ref, k_ref, v_ref, scale=softmax_scale, is_causal=is_causal
1247
+ ).permute(0, 2, 1, 3)
1248
+ torch.testing.assert_close(o_torch.cpu(), ref_o.cpu(), atol=1e-02, rtol=1e-04)
1249
+ print("Results verified successfully!")
1250
+
1251
+ def generate_tensors():
1252
+ q_workspace, _ = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
1253
+ k_workspace, _ = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
1254
+ v_workspace, _ = create_tensor(batch_size, seqlen_k, num_head, head_dim, dtype)
1255
+ o_workspace, _ = create_tensor(batch_size, seqlen_q, num_head, head_dim, dtype)
1256
+ return testing.JitArguments(
1257
+ q_workspace,
1258
+ k_workspace,
1259
+ v_workspace,
1260
+ o_workspace,
1261
+ softmax_scale,
1262
+ current_stream,
1263
+ )
1264
+
1265
+ workspace_count = 1
1266
+ if use_cold_l2:
1267
+ one_workspace_bytes = (
1268
+ q_torch.numel() * q_torch.element_size()
1269
+ + k_torch.numel() * k_torch.element_size()
1270
+ + v_torch.numel() * v_torch.element_size()
1271
+ + o_torch.numel() * o_torch.element_size()
1272
+ )
1273
+ workspace_count = testing.get_workspace_count(
1274
+ one_workspace_bytes, warmup_iterations, iterations
1275
+ )
1276
+
1277
+ avg_time_us = testing.benchmark(
1278
+ compiled_fa2_fwd,
1279
+ workspace_generator=generate_tensors,
1280
+ workspace_count=workspace_count,
1281
+ stream=current_stream,
1282
+ warmup_iterations=warmup_iterations,
1283
+ iterations=iterations,
1284
+ )
1285
+
1286
+ return avg_time_us # Return execution time in microseconds
1287
+
1288
+ if __name__ == "__main__":
1289
+ parser = argparse.ArgumentParser(
1290
+ description="example of flash attention v2 with CuTe on GPU"
1291
+ )
1292
+ parser.add_argument("--dtype", type=cutlass.dtype, default=cutlass.BFloat16)
1293
+ parser.add_argument("--batch_size", type=int, default=4)
1294
+ parser.add_argument("--seqlen_q", type=int, default=8192)
1295
+ parser.add_argument("--seqlen_k", type=int, default=8192)
1296
+ parser.add_argument("--num_head", type=int, default=16)
1297
+ parser.add_argument("--head_dim", type=int, default=128)
1298
+ parser.add_argument("--softmax_scale", type=float, default=0.5)
1299
+ parser.add_argument("--m_block_size", type=int, default=128)
1300
+ parser.add_argument("--n_block_size", type=int, default=64)
1301
+ parser.add_argument("--num_threads", type=int, default=128)
1302
+ parser.add_argument("--is_causal", action="store_true", help="Enable causal mask")
1303
+ parser.add_argument("--warmup_iterations", type=int, default=3)
1304
+ parser.add_argument("--iterations", type=int, default=10)
1305
+ parser.add_argument(
1306
+ "--skip_ref_check", action="store_true", help="Skip reference check"
1307
+ )
1308
+ parser.add_argument(
1309
+ "--use_cold_l2",
1310
+ action="store_true",
1311
+ default=False,
1312
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
1313
+ )
1314
+
1315
+ args = parser.parse_args()
1316
+ run(
1317
+ args.dtype,
1318
+ args.batch_size,
1319
+ args.seqlen_q,
1320
+ args.seqlen_k,
1321
+ args.num_head,
1322
+ args.head_dim,
1323
+ args.softmax_scale,
1324
+ args.m_block_size,
1325
+ args.n_block_size,
1326
+ args.num_threads,
1327
+ args.is_causal,
1328
+ args.warmup_iterations,
1329
+ args.iterations,
1330
+ args.skip_ref_check,
1331
+ args.use_cold_l2,
1332
+ )
1333
+
1334
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/sgemm.py ADDED
@@ -0,0 +1,866 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ import time
31
+ from typing import Tuple
32
+
33
+ import cuda.bindings.driver as cuda
34
+ import torch
35
+
36
+ import cutlass
37
+ import cutlass.cute as cute
38
+ import cutlass.cute.testing as testing
39
+ import cutlass.torch as cutlass_torch
40
+ import cutlass.utils as utils
41
+ from cutlass.cute.runtime import from_dlpack
42
+
43
+ """
44
+ A dense FP32 SIMT GEMM (C = A * B) example using CUTE DSL.
45
+ - Matrix A is MxK, A can be row-major("K") or column-major("M")
46
+ - Matrix B is NxK, B can be row-major("N") or column-major("K")
47
+ - Matrix C is MxN, C can be row-major("N") or column-major("M")
48
+
49
+ This GEMM kernel supports the following features:
50
+ - Utilizes FPU for matrix multiply-accumulate (MMA) operations
51
+ - Use multistage pipeline to overlap computation and memory access
52
+ * Shared memory pipeline: hides gmem-to-smem latency.
53
+ * Register pipeline: overlaps shared memory-to-register transfers with
54
+ computations and eliminates false data dependencies for
55
+ better parallelism.
56
+ - Use vectorized copies
57
+ - Add padding to reduce bank conflicts in global -> shared memory copies
58
+ - Use predication to avoid unnecessary copies or copies of stale data
59
+
60
+ This GEMM works as follows:
61
+ 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using asynchronous copies.
62
+ 2. Perform matrix multiply-accumulate (MMA) operations using simple fused multiply-add atomics.
63
+ 3. Store results from registers (RMEM) to global memory (GMEM).
64
+
65
+ To run this example:
66
+
67
+ .. code-block:: bash
68
+
69
+ python examples/ampere/sgemm.py \
70
+ --mnk 8192,8192,8192 \
71
+ --a_major m --b_major n --c_major n
72
+
73
+ To collect performance with NCU profiler:
74
+
75
+ .. code-block:: bash
76
+
77
+ ncu python examples/ampere/sgemm.py \
78
+ --mnk 8192,8192,8192 \
79
+ --a_major m --b_major n --c_major n \
80
+ --skip_ref_check --iterations 2
81
+
82
+ Constraints:
83
+ * Supported input, output, and accumulator data types: fp32
84
+ * Default tile shape is set to be 128x128x8
85
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned
86
+ """
87
+
88
+
89
+ class SGemm:
90
+ def __init__(
91
+ self,
92
+ cta_tiler: Tuple[int, int, int] = (128, 128, 8),
93
+ num_stages: int = 3,
94
+ num_threads: int = 256,
95
+ ):
96
+ self._cta_tiler = cta_tiler
97
+ self._num_stages = num_stages
98
+ self._num_threads = num_threads
99
+ assert num_threads > 0, "needs at least one thread"
100
+ assert num_threads % 16 == 0, "multiples of 16 required for MMA thread layout"
101
+
102
+ self._bM, self._bN, self._bK = self._cta_tiler
103
+ assert self._bM % 16 == 0, "multiple of 16 required for tile dimension M"
104
+ assert self._bN % 16 == 0, "multiple of 16 required for tile dimension N"
105
+ assert self._num_stages >= 3, "num_stages must be greater than or equal to 3"
106
+
107
+ @cute.jit
108
+ def __call__(
109
+ self,
110
+ mA: cute.Tensor,
111
+ mB: cute.Tensor,
112
+ mC: cute.Tensor,
113
+ epilogue_op: cutlass.Constexpr = lambda x: x,
114
+ stream: cuda.CUstream = cuda.CUstream(cuda.CUstream_flags.CU_STREAM_DEFAULT),
115
+ ):
116
+ self.a_major_mode = utils.LayoutEnum.from_tensor(mA)
117
+ self.b_major_mode = utils.LayoutEnum.from_tensor(mB)
118
+ self.c_major_mode = utils.LayoutEnum.from_tensor(mC)
119
+
120
+ # ///////////////////////////////////////////////////////////////////////////////
121
+ # Create layouts for shared memory for A and B:
122
+ # - sA/sB is m/n-major to vectorized copies from shared
123
+ # memory to registers. This is because the MMA layouts
124
+ # for sA/sB are also m/n-major
125
+ # - When gA/gB is k-major, pad 4 elements to reduce bank conflicts
126
+ # ///////////////////////////////////////////////////////////////////////////////
127
+
128
+ padding_a = 4 if self.a_major_mode == utils.LayoutEnum.ROW_MAJOR else 0
129
+ padding_b = 4 if self.b_major_mode == utils.LayoutEnum.ROW_MAJOR else 0
130
+ sA_layout = cute.make_layout(
131
+ (self._bM, self._bK, self._num_stages),
132
+ stride=(1, (self._bM + padding_a), self._bK * (self._bM + padding_a)),
133
+ )
134
+ sB_layout = cute.make_layout(
135
+ (self._bN, self._bK, self._num_stages),
136
+ stride=(1, (self._bN + padding_b), self._bK * (self._bN + padding_b)),
137
+ )
138
+
139
+ # ///////////////////////////////////////////////////////////////////////////////
140
+ # Create copy layouts that will be used for asynchronous
141
+ # global memory -> shared memory copies:
142
+ # - The majorness of tA/tB follows the majorness of gA/gB
143
+ # - For k-major, these layouts will copy values one-by-one from
144
+ # from global memory, without vectorizing
145
+ # - For m/n-major, it will vectorize to a 128bit copy for faster
146
+ # data transfer between global and shared memory, as long
147
+ # as the alignment of the tensor allows it. Otherwise, it
148
+ # defaults to a non-vectorized copy
149
+ # ///////////////////////////////////////////////////////////////////////////////
150
+
151
+ tA = cute.make_layout(
152
+ (self._num_threads // self._bK, self._bK), stride=(self._bK, 1)
153
+ )
154
+ tB = cute.make_layout(
155
+ (self._num_threads // self._bK, self._bK), stride=(self._bK, 1)
156
+ )
157
+ vA = cute.make_layout((1, 1))
158
+ vB = cute.make_layout((1, 1))
159
+ atom_async_copy_A = cute.make_copy_atom(
160
+ cute.nvgpu.cpasync.CopyG2SOp(),
161
+ mA.element_type,
162
+ num_bits_per_copy=mA.element_type.width,
163
+ )
164
+ atom_async_copy_B = cute.make_copy_atom(
165
+ cute.nvgpu.cpasync.CopyG2SOp(),
166
+ mA.element_type,
167
+ num_bits_per_copy=mB.element_type.width,
168
+ )
169
+
170
+ if cutlass.const_expr(self.a_major_mode == utils.LayoutEnum.COL_MAJOR):
171
+ num_vectorized = 4 if (mA.layout.max_alignment % 16 == 0) else 1
172
+ atom_async_copy_A = cute.make_copy_atom(
173
+ cute.nvgpu.cpasync.CopyG2SOp(),
174
+ mA.element_type,
175
+ num_bits_per_copy=mA.element_type.width * num_vectorized,
176
+ )
177
+ major_mode_size = self._bM // num_vectorized
178
+ tA = cute.make_layout(
179
+ (major_mode_size, self._num_threads // major_mode_size),
180
+ stride=(1, major_mode_size),
181
+ )
182
+ vA = cute.make_layout((num_vectorized, 1))
183
+
184
+ if cutlass.const_expr(self.b_major_mode == utils.LayoutEnum.COL_MAJOR):
185
+ num_vectorized = 4 if (mB.layout.max_alignment % 16 == 0) else 1
186
+ atom_async_copy_B = cute.make_copy_atom(
187
+ cute.nvgpu.cpasync.CopyG2SOp(),
188
+ mA.element_type,
189
+ num_bits_per_copy=mB.element_type.width * num_vectorized,
190
+ )
191
+ major_mode_size = self._bN // num_vectorized
192
+ tB = cute.make_layout(
193
+ (major_mode_size, self._num_threads // major_mode_size),
194
+ stride=(1, major_mode_size),
195
+ )
196
+ vB = cute.make_layout((num_vectorized, 1))
197
+
198
+ tiled_copy_A = cute.make_tiled_copy_tv(atom_async_copy_A, tA, vA)
199
+ tiled_copy_B = cute.make_tiled_copy_tv(atom_async_copy_B, tB, vB)
200
+
201
+ # ///////////////////////////////////////////////////////////////////////////////
202
+ # Create layouts for GEMM:
203
+ # We tile an MMA atom across a tensor. `atoms_layout` is the layout
204
+ # of atoms in the tiled MMA. (Because we use an `MmaUniversalOp`,
205
+ # which has a trivial 1x1x1 MMA trait, `atoms_layout` is also
206
+ # simply the thread layout for C.) `permutation_tiler` reorders the
207
+ # elements of the tensor that the tiled MMA is applied to.
208
+ # Different combinations of `atoms_layout` and `permutation_tiler`
209
+ # values can create different MMA thread-value patterns.
210
+ #
211
+ # Here, the MMA layout is set so that each thread copies four
212
+ # consecutive elements from shared memory to registers.
213
+ # `permutation_tiler_M/N` maps the elements handled by each thread
214
+ # to the permuted element in the tensor.
215
+ # For increasing indices in the tensor, the thread ID that reads it is:
216
+ # - (without permutation) ==>
217
+ # 0 1 2 ... 15 0 1 2 ... 15 0 1 2 ... 15 0 1 2 ... 15 ......
218
+ # - (with permutation) ==>
219
+ # 0 0 0 0 1 1 1 1 2 2 2 2 ... 15 15 15 15 0 0 0 0 1 1 1 1 ......
220
+ # ///////////////////////////////////////////////////////////////////////////////
221
+ atoms_layout = cute.make_layout(
222
+ (self._num_threads // 16, 16, 1), stride=(16, 1, 0)
223
+ )
224
+ if cutlass.const_expr(self.c_major_mode == utils.LayoutEnum.COL_MAJOR):
225
+ atoms_layout = cute.make_layout(
226
+ (16, self._num_threads // 16, 1), stride=(1, 16, 0)
227
+ )
228
+ op = cute.nvgpu.MmaUniversalOp(cutlass.Float32)
229
+ permutation_tiler_M = cute.make_layout(
230
+ (atoms_layout.shape[0], 4), stride=(4, 1)
231
+ )
232
+ permutation_tiler_N = cute.make_layout(
233
+ (atoms_layout.shape[1], 4), stride=(4, 1)
234
+ )
235
+ tiled_mma = cute.make_tiled_mma(
236
+ op,
237
+ atoms_layout,
238
+ permutation_mnk=(permutation_tiler_M, permutation_tiler_N, None),
239
+ )
240
+
241
+ # grid_dim: ((m + BLK_M - 1) // BLK_M, (n + BLK_N - 1) // BLK_N, 1)
242
+ grid_dim = *cute.ceil_div(mC.shape, (self._bM, self._bN)), 1
243
+
244
+ self.kernel(
245
+ mA,
246
+ mB,
247
+ mC,
248
+ sA_layout,
249
+ sB_layout,
250
+ tiled_copy_A,
251
+ tiled_copy_B,
252
+ tiled_mma,
253
+ epilogue_op,
254
+ ).launch(
255
+ grid=grid_dim,
256
+ block=[cute.size(atoms_layout), 1, 1],
257
+ stream=stream,
258
+ )
259
+
260
+ @cute.kernel
261
+ def kernel(
262
+ self,
263
+ mA: cute.Tensor,
264
+ mB: cute.Tensor,
265
+ mC: cute.Tensor,
266
+ sA_layout: cute.Layout,
267
+ sB_layout: cute.Layout,
268
+ tiled_copy_A: cute.TiledCopy,
269
+ tiled_copy_B: cute.TiledCopy,
270
+ tiled_mma: cute.TiledMma,
271
+ epilogue_op: cutlass.Constexpr = lambda x: x,
272
+ ):
273
+ # Thread and block indices
274
+ tidx, tidy, tidz = cute.arch.thread_idx()
275
+ bidx, bidy, bidz = cute.arch.block_idx()
276
+ tiler_coord = (bidx, bidy, None)
277
+ thr_mma = tiled_mma.get_slice(tidx)
278
+
279
+ # ///////////////////////////////////////////////////////////////////////////////
280
+ # Get the appropriate tiles for this thread block.
281
+ # gA: (BLK_M, BLK_K, k), gB: (BLK_N, BLK_K, k), gC: (BLK_M, BLK_N)
282
+ # ///////////////////////////////////////////////////////////////////////////////
283
+ gA = cute.local_tile(
284
+ mA, tiler=self._cta_tiler, coord=tiler_coord, proj=(1, None, 1)
285
+ )
286
+ gB = cute.local_tile(
287
+ mB, tiler=self._cta_tiler, coord=tiler_coord, proj=(None, 1, 1)
288
+ )
289
+ gC = cute.local_tile(
290
+ mC, tiler=self._cta_tiler, coord=tiler_coord, proj=(1, 1, None)
291
+ )
292
+
293
+ # Move the pointer of gA/gB in the `-k`` direction, making the first
294
+ # tile (instead of the last one) irregular in shape when k is irregular.
295
+ # We first handle the irregular tile to avoid checking for this
296
+ # condition within the mainloop.
297
+ residue_k = mA.shape[1] - cutlass.Int32(self._bK) * gA.shape[2]
298
+ gA = cute.domain_offset((0, residue_k, 0), gA)
299
+ gB = cute.domain_offset((0, residue_k, 0), gB)
300
+
301
+ # ///////////////////////////////////////////////////////////////////////////////
302
+ # Get the appropriate tiles for this thread.
303
+ # sA: (BLK_M, BLK_K, PIPE) , sB: (BLK_N, BLK_K, PIPE)
304
+ # tAgA: (CPY, CPY_M, CPY_K, k) , tBgB: (CPY, CPY_N, CPY_K, k)
305
+ # tAsA: (CPY, CPY_M, CPY_K, PIPE) , tBsB: (CPY, CPY_N, CPY_K, PIPE)
306
+ # ///////////////////////////////////////////////////////////////////////////////
307
+ # Create shared memory buffer
308
+ smem = cutlass.utils.SmemAllocator()
309
+ sA = smem.allocate_tensor(mA.element_type, sA_layout, 16)
310
+ sB = smem.allocate_tensor(mB.element_type, sB_layout, 16)
311
+ thr_copy_A = tiled_copy_A.get_slice(tidx)
312
+ thr_copy_B = tiled_copy_B.get_slice(tidx)
313
+ tAgA = thr_copy_A.partition_S(gA)
314
+ tAsA = thr_copy_A.partition_D(sA)
315
+ tBgB = thr_copy_B.partition_S(gB)
316
+ tBsB = thr_copy_B.partition_D(sB)
317
+
318
+ # ///////////////////////////////////////////////////////////////////////////////
319
+ # Predicate: Mark indices that need to copy when the problem shape
320
+ # isn't a multiple of the tile shape. If tApA/B[i] is 0, then do not
321
+ # do the copy atom associated with index i.
322
+ # cA: (BLK_M, BLK_K) => (blk_m, blk_k)
323
+ # cB: (BLK_N, BLK_K) => (blk_n, blk_k)
324
+ # tAcA: (CPY, CPY_M, CPY_K) => (blk_m, blk_k)
325
+ # tBcB: (CPY, CPY_N, CPY_K) => (blk_n, blk_k)
326
+ # tApA: (rest_v, CPY_M, CPY_K), stride=(..., ..., 0)
327
+ # tBpB: (rest_v, CPY_N, CPY_K), stride=(..., ..., 0)
328
+ # CPY = (atom_v, rest_v)
329
+ # ///////////////////////////////////////////////////////////////////////////////
330
+ # Construct identity layout for sA and sB, used for predication
331
+ mcA = cute.make_identity_tensor(mA.shape)
332
+ mcB = cute.make_identity_tensor(mB.shape)
333
+ cA = cute.local_tile(
334
+ mcA, tiler=self._cta_tiler, coord=tiler_coord, proj=(1, None, 1)
335
+ )
336
+ cB = cute.local_tile(
337
+ mcB, tiler=self._cta_tiler, coord=tiler_coord, proj=(None, 1, 1)
338
+ )
339
+ cA = cute.domain_offset((0, residue_k, 0), cA)
340
+ cB = cute.domain_offset((0, residue_k, 0), cB)
341
+ # Repeat the partitioning with identity layouts
342
+ tAcA = thr_copy_A.partition_S(cA)
343
+ tBcB = thr_copy_B.partition_S(cB)
344
+ # Allocate predicate tensors for m and n
345
+ tApA = cute.make_fragment(
346
+ cute.make_layout(
347
+ (
348
+ tAsA.shape[0][1],
349
+ cute.size(tAsA, mode=[1]),
350
+ cute.size(tAsA, mode=[2]),
351
+ ),
352
+ stride=(cute.size(tAsA, mode=[1]), 1, 0),
353
+ ),
354
+ cutlass.Boolean,
355
+ )
356
+ tBpB = cute.make_fragment(
357
+ cute.make_layout(
358
+ (
359
+ tBsB.shape[0][1],
360
+ cute.size(tBsB, mode=[1]),
361
+ cute.size(tBsB, mode=[2]),
362
+ ),
363
+ stride=(cute.size(tBsB, mode=[1]), 1, 0),
364
+ ),
365
+ cutlass.Boolean,
366
+ )
367
+ # Allocate predicate tensors for m, n and k for residue k-tile
368
+ tApA_residue_k = cute.make_fragment(
369
+ cute.make_layout(
370
+ (
371
+ tAsA.shape[0][1],
372
+ cute.size(tAsA, mode=[1]),
373
+ cute.size(tAsA, mode=[2]),
374
+ ),
375
+ stride=(
376
+ cute.size(tAsA, mode=[1]) * cute.size(tAsA, mode=[2]),
377
+ cute.size(tAsA, mode=[2]),
378
+ 1,
379
+ ),
380
+ ),
381
+ cutlass.Boolean,
382
+ )
383
+ tBpB_residue_k = cute.make_fragment(
384
+ cute.make_layout(
385
+ (
386
+ tBsB.shape[0][1],
387
+ cute.size(tBsB, mode=[1]),
388
+ cute.size(tBsB, mode=[2]),
389
+ ),
390
+ stride=(
391
+ cute.size(tBsB, mode=[1]) * cute.size(tBsB, mode=[2]),
392
+ cute.size(tBsB, mode=[2]),
393
+ 1,
394
+ ),
395
+ ),
396
+ cutlass.Boolean,
397
+ )
398
+ # Set predicates for m/n bounds for mainloop
399
+ for rest_v in range(tApA.shape[0]):
400
+ for m in range(tApA.shape[1]):
401
+ tApA[rest_v, m, 0] = cute.elem_less(
402
+ tAcA[(0, rest_v), m, 0, 0][0], mA.shape[0]
403
+ )
404
+ for rest_v in range(tBpB.shape[0]):
405
+ for n in range(tBpB.shape[1]):
406
+ tBpB[rest_v, n, 0] = cute.elem_less(
407
+ tBcB[(0, rest_v), n, 0, 0][0], mB.shape[0]
408
+ )
409
+
410
+ # Set predicates for m/n/k bounds for residue k tile
411
+ for rest_v in range(tApA_residue_k.shape[0]):
412
+ for m in range(tApA_residue_k.shape[1]):
413
+ for k in range(tApA_residue_k.shape[2]):
414
+ coord_A = tAcA[(0, rest_v), m, k, 0]
415
+ tApA_residue_k[rest_v, m, k] = cute.elem_less(
416
+ (coord_A[0], cutlass.Int32(-1)), (mA.shape[0], coord_A[1])
417
+ )
418
+ for rest_v in range(tBpB_residue_k.shape[0]):
419
+ for n in range(tBpB_residue_k.shape[1]):
420
+ for k in range(tBpB_residue_k.shape[2]):
421
+ coord_B = tBcB[(0, rest_v), n, k, 0]
422
+ tBpB_residue_k[rest_v, n, k] = cute.elem_less(
423
+ (coord_B[0], cutlass.Int32(-1)), (mB.shape[0], coord_B[1])
424
+ )
425
+
426
+ # ///////////////////////////////////////////////////////////////////////////////
427
+ # Prefetch Prologue
428
+ # ///////////////////////////////////////////////////////////////////////////////
429
+ # Start async loads for 0th k-tile, where we take care of the k-residue
430
+ k_pipe_max = cute.size(tAsA, mode=[3])
431
+ k_tile_count = cute.size(tAgA, mode=[3])
432
+ gmem_pipe_read = cutlass.Int32(0)
433
+ cute.copy(
434
+ tiled_copy_A,
435
+ tAgA[None, None, None, gmem_pipe_read],
436
+ tAsA[None, None, None, 0],
437
+ pred=tApA_residue_k,
438
+ )
439
+ cute.copy(
440
+ tiled_copy_B,
441
+ tBgB[None, None, None, gmem_pipe_read],
442
+ tBsB[None, None, None, 0],
443
+ pred=tBpB_residue_k,
444
+ )
445
+ cute.arch.cp_async_commit_group()
446
+ gmem_pipe_read = (
447
+ gmem_pipe_read + 1
448
+ if gmem_pipe_read + 1 < k_tile_count
449
+ else cutlass.Int32(0)
450
+ )
451
+ # Start async loads for 1st k-tile onwards, no k-residue handling needed
452
+ for k_tile in range(1, k_pipe_max - 1):
453
+ if k_tile < k_tile_count:
454
+ cute.copy(
455
+ tiled_copy_A,
456
+ tAgA[None, None, None, gmem_pipe_read],
457
+ tAsA[None, None, None, k_tile],
458
+ pred=tApA,
459
+ )
460
+ cute.copy(
461
+ tiled_copy_B,
462
+ tBgB[None, None, None, gmem_pipe_read],
463
+ tBsB[None, None, None, k_tile],
464
+ pred=tBpB,
465
+ )
466
+
467
+ gmem_pipe_read = (
468
+ gmem_pipe_read + 1
469
+ if gmem_pipe_read + 1 < k_tile_count
470
+ else cutlass.Int32(0)
471
+ )
472
+ cute.arch.cp_async_commit_group()
473
+
474
+ # all tiles have been copied from global memory, so clear the
475
+ # predicate tensor
476
+ if k_tile_count < k_pipe_max:
477
+ for rest_v in range(tApA.shape[0]):
478
+ for m in range(tApA.shape[1]):
479
+ tApA[rest_v, m, 0] = cutlass.Boolean(0)
480
+ for rest_v in range(tBpB.shape[0]):
481
+ for n in range(tBpB.shape[1]):
482
+ tBpB[rest_v, n, 0] = cutlass.Boolean(0)
483
+
484
+ # ///////////////////////////////////////////////////////////////////////////////
485
+ # Define A/B partitioning and C accumulators.
486
+ # ///////////////////////////////////////////////////////////////////////////////
487
+ tCsA = thr_mma.partition_A(sA)
488
+ tCsB = thr_mma.partition_B(sB)
489
+ tCgC = thr_mma.partition_C(gC)
490
+ tCrA = tiled_mma.make_fragment_A(tCsA[None, None, None, 0])
491
+ tCrB = tiled_mma.make_fragment_B(tCsB[None, None, None, 0])
492
+ tCrC = tiled_mma.make_fragment_C(tCgC)
493
+ # Clear the accumulator
494
+ tCrC.fill(0.0)
495
+
496
+ # Current pipe index in smem to read from / write to
497
+ smem_pipe_read = cutlass.Int32(0)
498
+ smem_pipe_write = cutlass.Int32(k_pipe_max - 1)
499
+
500
+ tCsA_p = tCsA[None, None, None, smem_pipe_read]
501
+ tCsB_p = tCsB[None, None, None, smem_pipe_read]
502
+
503
+ # ///////////////////////////////////////////////////////////////////////////////
504
+ # PREFETCH register pipeline
505
+ # ///////////////////////////////////////////////////////////////////////////////
506
+ k_block_max = cute.size(tCrA, mode=[2])
507
+
508
+ if k_block_max > 1:
509
+ # Wait until our first prefetched tile is loaded in
510
+ cute.arch.cp_async_wait_group(k_pipe_max - 2)
511
+ cute.arch.barrier()
512
+ # Prefetch the first rmem from the first k-tile
513
+ cute.autovec_copy(tCsA_p[None, None, 0], tCrA[None, None, 0])
514
+ cute.autovec_copy(tCsB_p[None, None, 0], tCrB[None, None, 0])
515
+
516
+ # ///////////////////////////////////////////////////////////////////////////////
517
+ # Mainloop
518
+ # 1. Shared memory pipeline (gmem -> smem):
519
+ # The default smem pipeline depth is 3, meaning that for shared
520
+ # memory buffers, we allocate three times the size described by the
521
+ # CTA tiler. We prefetch 2 of these buffers before entering the main
522
+ # loop. Considering only the transfer from global memory to shared
523
+ # memory, the general structure of the mainloop is:
524
+ # (1) copy k-tile from gmem to smem;
525
+ # (2) perform gemm computation on k-tile;
526
+ # (3) wait for the next copy to finish.
527
+ # The `cute.arch.cp_async_wait_group(num_smem_stages - 2)` command
528
+ # waits for the number of unfinished 'copy' to be <= 1. The advantage
529
+ # of this approach is that it allows for simultaneous production
530
+ # (i.e., step (1)) and consumption (i.e., step (2)) of smem.
531
+ # A common misconception is to prefetch N buffers and rewrite
532
+ # the pipeline logic to wait on N-1 pending copies. The disadvantage
533
+ # of this approach is that it requires fully consuming a buffer in
534
+ # order to open an empty buffer for the next copy.
535
+ # 2. Register pipeline (smem -> register):
536
+ # Similarly, the register pipeline produces i+1, consumes i, and
537
+ # produces i+2... Notably, i and i+1 do not use the same register,
538
+ # eliminating dependencies on the same register for better parallelism.
539
+ # 3. Combining the smem and register pipelines results in the mainloop.
540
+ # ///////////////////////////////////////////////////////////////////////////////
541
+
542
+ for _ in range(k_tile_count):
543
+ for k_block in range(k_block_max, unroll_full=True):
544
+ if k_block == k_block_max - 1:
545
+ tCsA_p = tCsA[None, None, None, smem_pipe_read]
546
+ tCsB_p = tCsB[None, None, None, smem_pipe_read]
547
+ cute.arch.cp_async_wait_group(k_pipe_max - 2)
548
+ cute.arch.barrier()
549
+
550
+ # Load A, B from shared memory to registers for k_block + 1
551
+ k_block_next = (k_block + 1) % k_block_max # static
552
+ cute.autovec_copy(
553
+ tCsA_p[None, None, k_block_next],
554
+ tCrA[None, None, k_block_next],
555
+ )
556
+ cute.autovec_copy(
557
+ tCsB_p[None, None, k_block_next],
558
+ tCrB[None, None, k_block_next],
559
+ )
560
+
561
+ # Fetch next A: To better interleave global memory access and
562
+ # compute instructions, we intentionally use the sequence:
563
+ # copy A, perform GEMM, then copy B.
564
+ if k_block == 0:
565
+ cute.copy(
566
+ tiled_copy_A,
567
+ tAgA[None, None, None, gmem_pipe_read],
568
+ tAsA[None, None, None, smem_pipe_write],
569
+ # Use predicates because the m-mode may be irregular
570
+ pred=tApA,
571
+ )
572
+
573
+ # Thread-level register gemm for k_block
574
+ cute.gemm(
575
+ tiled_mma,
576
+ tCrC,
577
+ tCrA[None, None, k_block],
578
+ tCrB[None, None, k_block],
579
+ tCrC,
580
+ )
581
+
582
+ # Fetch next B and update smem pipeline read/write
583
+ if k_block == 0:
584
+ cute.copy(
585
+ tiled_copy_B,
586
+ tBgB[None, None, None, gmem_pipe_read],
587
+ tBsB[None, None, None, smem_pipe_write],
588
+ # Use predicates because the n-mode may be irregular
589
+ pred=tBpB,
590
+ )
591
+ cute.arch.cp_async_commit_group()
592
+ smem_pipe_write = smem_pipe_read
593
+ smem_pipe_read = smem_pipe_read + 1
594
+ if smem_pipe_read == k_pipe_max:
595
+ smem_pipe_read = cutlass.Int32(0)
596
+ # After copying all tiles, we avoid clearing the predicate
597
+ # tensor in the `mainloop` to prevent increasing its
598
+ # instruction count. Instead, we continue copying the
599
+ # first tile, though it won't be used. The 0-th tile is not
600
+ # copied due to its irregular shape, which could lead to
601
+ # illegal memory accesses.
602
+ gmem_pipe_read = (
603
+ gmem_pipe_read + 1
604
+ if gmem_pipe_read + 1 < k_tile_count
605
+ else cutlass.Int32(1)
606
+ )
607
+
608
+ # ///////////////////////////////////////////////////////////////////////////////
609
+ # Epilogue
610
+ # Applies the epilogue operation to the accumulated results and copies
611
+ # them without vectorization.
612
+ # ///////////////////////////////////////////////////////////////////////////////
613
+ cute.arch.cp_async_wait_group(0)
614
+ cute.arch.barrier()
615
+ tCrC.store(epilogue_op(tCrC.load()))
616
+
617
+ # predicate
618
+ cC = cute.make_identity_tensor(gC.shape)
619
+ tCpC = thr_mma.partition_C(cC)
620
+ predC = cute.make_fragment(tCrC.layout, cutlass.Boolean)
621
+ residue_m = mC.shape[0] - cutlass.Int32(self._bM) * bidx
622
+ residue_n = mC.shape[1] - cutlass.Int32(self._bN) * bidy
623
+ for i in range(cute.size(tCrC.shape)):
624
+ predC[i] = cute.elem_less(tCpC[i], (residue_m, residue_n))
625
+ numIterM = cute.size(tCrC, mode=[1])
626
+ numIterN = cute.size(tCrC, mode=[2])
627
+ atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), mC.element_type)
628
+ cute.copy(atom, tCrC, tCgC, pred=predC)
629
+ return
630
+
631
+
632
+ def run(
633
+ mnk: Tuple[int, int, int],
634
+ a_major: str,
635
+ b_major: str,
636
+ c_major: str,
637
+ static_shape: bool = False,
638
+ warmup_iterations: int = 2,
639
+ iterations: int = 100,
640
+ skip_ref_check: bool = False,
641
+ use_cold_l2: bool = False,
642
+ **kwargs,
643
+ ):
644
+ """Execute SIMT GEMM operation and benchmark performance.
645
+
646
+ :param mnk: GEMM problem size (M, N, K, L)
647
+ :type mnk: Tuple[int, int, int, int]
648
+ :param a_major: Memory layout of tensor A
649
+ :type a_major: str
650
+ :param b_major: Memory layout of tensor B
651
+ :type b_major: str
652
+ :param c_major: Memory layout of tensor C
653
+ :type c_major: str
654
+ :param static_shape: Whether to use static shape optimization, defaults to False
655
+ :type static_shape: bool, optional
656
+ :param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 2
657
+ :type warmup_iterations: int, optional
658
+ :param iterations: Number of benchmark iterations to run, defaults to 100
659
+ :type iterations: int, optional
660
+ :param skip_ref_check: Skip validation against reference implementation, defaults to False
661
+ :type skip_ref_check: bool, optional
662
+ :param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
663
+ :type use_cold_l2: bool, optional
664
+ :return: Execution time of the GEMM kernel in microseconds
665
+ :rtype: float
666
+ """
667
+ print(f"Running Ampere SIMT GEMM example:")
668
+ print(f"mnk: {mnk}")
669
+ print(f"A major: {a_major}, B major: {b_major}, C major: {c_major}")
670
+ print(f"Static shape: {static_shape}")
671
+ print(f"Warmup iterations: {warmup_iterations}")
672
+ print(f"Iterations: {iterations}")
673
+ print(f"Skip reference checking: {skip_ref_check}")
674
+ print(f"Use cold L2: {use_cold_l2}")
675
+ M, N, K = mnk
676
+
677
+ # Create and permute tensor A/B/C
678
+ def create_and_permute_tensor(mode0, mode1, is_mode0_major, dtype):
679
+ # is_mode0_major: (mode1, mode0) -> (mode0, mode1)
680
+ # else: (mode0, mode1) -> (mode0, mode1)
681
+ shape = (mode1, mode0) if is_mode0_major else (mode0, mode1)
682
+ permute_order = (1, 0) if is_mode0_major else (0, 1)
683
+
684
+ return (
685
+ torch.empty(*shape, dtype=torch.int32)
686
+ .random_(-5, 5)
687
+ .to(dtype=dtype)
688
+ .permute(permute_order)
689
+ .cuda()
690
+ )
691
+
692
+ a = create_and_permute_tensor(M, K, a_major == "m", torch.float32)
693
+ b = create_and_permute_tensor(N, K, b_major == "n", torch.float32)
694
+ c = create_and_permute_tensor(M, N, c_major == "m", torch.float32)
695
+
696
+ divisibility_a = a.shape[1] if a_major == "k" else a.shape[0]
697
+ divisibility_b = b.shape[1] if b_major == "k" else b.shape[0]
698
+ divisibility_c = c.shape[1] if c_major == "n" else c.shape[0]
699
+
700
+ a_tensor = (
701
+ from_dlpack(a, assumed_align=16)
702
+ .mark_layout_dynamic(leading_dim=(1 if a_major == "k" else 0))
703
+ .mark_compact_shape_dynamic(
704
+ mode=(1 if a_major == "k" else 0),
705
+ divisibility=divisibility_a,
706
+ )
707
+ )
708
+
709
+ b_tensor = (
710
+ from_dlpack(b, assumed_align=16)
711
+ .mark_layout_dynamic(leading_dim=(1 if b_major == "k" else 0))
712
+ .mark_compact_shape_dynamic(
713
+ mode=(1 if b_major == "k" else 0),
714
+ divisibility=divisibility_b,
715
+ )
716
+ )
717
+
718
+ c_tensor = (
719
+ from_dlpack(c, assumed_align=16)
720
+ .mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
721
+ .mark_compact_shape_dynamic(
722
+ mode=(1 if c_major == "n" else 0),
723
+ divisibility=divisibility_c,
724
+ )
725
+ )
726
+
727
+ sgemm = SGemm()
728
+
729
+ # Get current CUDA stream from PyTorch
730
+ torch_stream = torch.cuda.current_stream()
731
+ # Get the raw stream pointer as a CUstream
732
+ current_stream = cuda.CUstream(torch_stream.cuda_stream)
733
+
734
+ print("Compiling kernel with cute.compile ...")
735
+ start_time = time.time()
736
+ compiled_fn = cute.compile(
737
+ sgemm,
738
+ a_tensor,
739
+ b_tensor,
740
+ c_tensor,
741
+ stream=current_stream,
742
+ )
743
+ compilation_time = time.time() - start_time
744
+ print(f"Compilation time: {compilation_time:.4f} seconds")
745
+
746
+ print("Executing GEMM kernel...")
747
+
748
+ if not skip_ref_check:
749
+ compiled_fn(a_tensor, b_tensor, c_tensor)
750
+ torch.cuda.synchronize()
751
+ print("Verifying results...")
752
+ ref = torch.einsum("mk,nk->mn", a, b)
753
+ torch.testing.assert_close(c.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
754
+ print("Results verified successfully!")
755
+
756
+ def generate_tensors():
757
+ # Create new tensors for each workspace to ensure cold L2 cache
758
+ a_workspace = create_and_permute_tensor(M, K, a_major == "m", torch.float32)
759
+ b_workspace = create_and_permute_tensor(N, K, b_major == "n", torch.float32)
760
+ c_workspace = create_and_permute_tensor(M, N, c_major == "m", torch.float32)
761
+
762
+ if static_shape:
763
+ a_tensor_workspace = (
764
+ from_dlpack(a_workspace, assumed_align=16)
765
+ .mark_layout_dynamic(leading_dim=(1 if a_major == "k" else 0))
766
+ .mark_compact_shape_dynamic(
767
+ mode=(1 if a_major == "k" else 0),
768
+ divisibility=divisibility_a,
769
+ )
770
+ )
771
+ else:
772
+ a_tensor_workspace = from_dlpack(a_workspace, assumed_align=16)
773
+
774
+ b_tensor_workspace = (
775
+ from_dlpack(b_workspace, assumed_align=16)
776
+ .mark_layout_dynamic(leading_dim=(1 if b_major == "k" else 0))
777
+ .mark_compact_shape_dynamic(
778
+ mode=(1 if b_major == "k" else 0),
779
+ divisibility=divisibility_b,
780
+ )
781
+ )
782
+
783
+ c_tensor_workspace = (
784
+ from_dlpack(c_workspace, assumed_align=16)
785
+ .mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
786
+ .mark_compact_shape_dynamic(
787
+ mode=(1 if c_major == "n" else 0),
788
+ divisibility=divisibility_c,
789
+ )
790
+ )
791
+
792
+ return testing.JitArguments(
793
+ a_tensor_workspace, b_tensor_workspace, c_tensor_workspace, current_stream
794
+ )
795
+
796
+ workspace_count = 1
797
+ if use_cold_l2:
798
+ one_workspace_bytes = (
799
+ a.numel() * a.element_size()
800
+ + b.numel() * b.element_size()
801
+ + c.numel() * c.element_size()
802
+ )
803
+ workspace_count = testing.get_workspace_count(
804
+ one_workspace_bytes, warmup_iterations, iterations
805
+ )
806
+
807
+ avg_time_us = testing.benchmark(
808
+ compiled_fn,
809
+ workspace_generator=generate_tensors,
810
+ workspace_count=workspace_count,
811
+ stream=current_stream,
812
+ warmup_iterations=warmup_iterations,
813
+ iterations=iterations,
814
+ )
815
+
816
+ # Print execution results
817
+ print(f"Kernel execution time: {avg_time_us / 1e3:.4f} ms")
818
+
819
+ return avg_time_us # Return execution time in microseconds
820
+
821
+
822
+ if __name__ == "__main__":
823
+
824
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
825
+ try:
826
+ return tuple(int(x.strip()) for x in s.split(","))
827
+ except ValueError:
828
+ raise argparse.ArgumentTypeError(
829
+ "Invalid format. Expected comma-separated integers."
830
+ )
831
+
832
+ parser = argparse.ArgumentParser()
833
+ parser.add_argument(
834
+ "--mnk", type=parse_comma_separated_ints, default=(256, 256, 64)
835
+ )
836
+ parser.add_argument("--a_major", choices=["k", "m"], default="k")
837
+ parser.add_argument("--b_major", choices=["k", "n"], default="k")
838
+ parser.add_argument("--c_major", choices=["n", "m"], default="n")
839
+ parser.add_argument("--warmup_iterations", default=2, type=int)
840
+ parser.add_argument("--iterations", default=100, type=int)
841
+ parser.add_argument("--static_shape", action="store_true")
842
+ parser.add_argument("--skip_ref_check", action="store_true")
843
+ parser.add_argument(
844
+ "--use_cold_l2",
845
+ action="store_true",
846
+ default=False,
847
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
848
+ )
849
+
850
+ args = parser.parse_args()
851
+ print("Running SIMT GEMM example:")
852
+
853
+ torch.manual_seed(1024)
854
+
855
+ run(
856
+ args.mnk,
857
+ args.a_major,
858
+ args.b_major,
859
+ args.c_major,
860
+ args.static_shape,
861
+ args.warmup_iterations,
862
+ args.iterations,
863
+ args.skip_ref_check,
864
+ args.use_cold_l2,
865
+ )
866
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/smem_allocator.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import cutlass.cute as cute
30
+ import cutlass
31
+ import torch
32
+ import numpy as np
33
+ from cutlass.cute.runtime import from_dlpack
34
+
35
+ """
36
+ A Shared Memory Allocator Example on NVIDIA Ampere architecture using CuTe DSL.
37
+
38
+ This example demonstrates how to allocate and manage shared memory in JIT kernels by using the SmemAllocator in CuTe DSL.
39
+ It shows various ways to allocate different data structures in shared memory:
40
+
41
+ 1. Struct allocation with natural and strict alignment
42
+ 2. Raw memory block allocation with custom alignment
43
+ 3. Array allocation with automatic alignment
44
+ 4. Tensor allocation with layout specification
45
+
46
+ The example includes:
47
+ - Shared storage struct with mixed alignment requirements
48
+ - Memory allocation patterns for different data types
49
+ - Tensor operations on allocated memory
50
+
51
+ To run this example:
52
+
53
+ .. code-block:: bash
54
+
55
+ python examples/ampere/smem_allocator.py
56
+
57
+ The example will allocate shared memory, perform tensor operations, and verify the results.
58
+ """
59
+
60
+
61
+ @cute.struct
62
+ class complex:
63
+ real: cutlass.Float32
64
+ imag: cutlass.Float32
65
+
66
+
67
+ # SharedStorage size is 512, alignment is 128
68
+ @cute.struct
69
+ class SharedStorage:
70
+ # struct elements with natural alignment
71
+ a: cute.struct.MemRange[cutlass.Float32, 32] # array
72
+ b: cutlass.Int64 # saclar
73
+ c: complex # nested struct
74
+ # struct elements with strict alignment
75
+ x: cute.struct.Align[
76
+ cute.struct.MemRange[cutlass.Float32, 32],
77
+ 128,
78
+ ]
79
+ y: cute.struct.Align[cutlass.Int32, 8]
80
+ z: cute.struct.Align[complex, 16]
81
+
82
+
83
+ @cute.kernel
84
+ def kernel(
85
+ const_a: cutlass.Constexpr,
86
+ dst_a: cute.Tensor,
87
+ const_b: cutlass.Constexpr,
88
+ dst_b: cute.Tensor,
89
+ const_c: cutlass.Constexpr,
90
+ dst_c: cute.Tensor,
91
+ ):
92
+ # Note: SMEM_SIZE bytes (specified in kernel().launch(smem=...)) can be reserved for developer to utilize
93
+ # Note: alignment of inital allocator base ptr is 1024
94
+ allocator = cutlass.utils.SmemAllocator()
95
+ # base ptr of allocator points at: SMEM_ADDR_START (the starting address of available shared memory)
96
+
97
+ # -- Allocate a struct --
98
+ # Note: when specified alignment, max(alignment, alignof(struct)) will be applied
99
+ # reserves the section of struct in smem, elements in the struct can be accessed by ptr
100
+ struct_in_smem = allocator.allocate(SharedStorage)
101
+ # base ptr of allocator now points at: SMEM_ADDR_AFTER_STRUCT = SMEM_ADDR_START + aligned_size(struct)
102
+
103
+ # -- Allocate a block of memory --
104
+ # reserves a section of 64 bytes in smem, align to 128 bytes, returns the section base ptr
105
+ section_in_smem = allocator.allocate(64, byte_alignment=128)
106
+ # base ptr of allocator now points at: SMEM_ADDR_AFTER_SECTION = SMEM_ADDR_AFTER_STRUCT + aligned_size(section)
107
+
108
+ # -- Allocate an array --
109
+ # reserves an int64 array of size 14 in smem, returns the array base ptr
110
+ array_in_smem = allocator.allocate_array(element_type=cutlass.Int64, num_elems=14)
111
+ # base ptr of allocator now points at: SMEM_ADDR_AFTER_ARRAY = SMEM_ADDR_AFTER_SECTION + aligned_size(array)
112
+
113
+ # -- Allocate a tensor --
114
+ # Note: use cute.ComposedLayout or cute.Layout to specify layout of tensor
115
+ # Note: iterator swizzle with swizzle layout is currently not supported
116
+ layout = cute.make_layout((16, 2))
117
+ tensor_in_smem = allocator.allocate_tensor(
118
+ element_type=cutlass.Float32, layout=layout, byte_alignment=32, swizzle=None
119
+ )
120
+ # base ptr of allocator now points at: SMEM_ADDR_AFTER_TENSOR = SMEM_ADDR_AFTER_ARRAY + aligned_size(tensor)
121
+
122
+ # ptr<f16, smem, align<1024>>
123
+ # ptr<i64, smem, align<128>>
124
+ # ptr<f32, smem, align<8>>
125
+ print(struct_in_smem.a.data_ptr())
126
+ print(struct_in_smem.b)
127
+ print(struct_in_smem.c.real)
128
+ # ptr<i8, smem, align<512>>
129
+ print(section_in_smem)
130
+ # ptr<i64, smem, align<64>>
131
+ print(array_in_smem)
132
+ # tensor<ptr<f16, smem, align<32>> o (16,4):(1,16)>
133
+ print(tensor_in_smem)
134
+
135
+ # fill MemRange tensor in struct and copy to dst
136
+ a_tensor = struct_in_smem.a.get_tensor(cute.make_layout((8, 4)))
137
+ a_tensor.fill(const_a)
138
+ cute.printf("cute.struct.MemRange: {}", a_tensor)
139
+ dst_a.store(a_tensor.load())
140
+
141
+ # convert block of smem to fill tensor and copy to dst
142
+ layout = cute.make_layout((8, 2))
143
+ sec_ptr = cute.recast_ptr(section_in_smem, dtype=cutlass.Float32)
144
+ sec_tensor = cute.make_tensor(sec_ptr, layout)
145
+ sec_tensor.fill(const_b)
146
+ cute.printf("block of memory: {}", sec_tensor)
147
+ dst_b.store(sec_tensor.load())
148
+
149
+ # fill allocated tensor in smem and copy to dst
150
+ tensor_in_smem.fill(const_c)
151
+ cute.printf("tensor in smem: {}", tensor_in_smem)
152
+ dst_c.store(tensor_in_smem.load())
153
+
154
+
155
+ @cute.jit
156
+ def run_allocation_kernel(
157
+ const_a: cutlass.Constexpr,
158
+ dst_a: cute.Tensor,
159
+ const_b: cutlass.Constexpr,
160
+ dst_b: cute.Tensor,
161
+ const_c: cutlass.Constexpr,
162
+ dst_c: cute.Tensor,
163
+ ):
164
+ # additional size for the example, 64(section) + 112(array) + 128(tensor) < 384
165
+ addtional_bytes = 384
166
+ # Note: launch shared memory size is: SMEM_SIZE = 512 + 384 = 896 bytes
167
+ kernel(const_a, dst_a, const_b, dst_b, const_c, dst_c).launch(
168
+ grid=(1, 1, 1),
169
+ block=(1, 1, 1),
170
+ smem=SharedStorage.size_in_bytes() + addtional_bytes,
171
+ )
172
+
173
+
174
+ def veify_allocation_kernel(const_a, const_b, const_c):
175
+ dst_a = torch.zeros((8, 4), dtype=torch.float32, device="cuda")
176
+ dst_b = torch.zeros((8, 2), dtype=torch.float32, device="cuda")
177
+ dst_c = torch.zeros((16, 2), dtype=torch.float32, device="cuda")
178
+
179
+ run_allocation_kernel(
180
+ const_a,
181
+ from_dlpack(dst_a),
182
+ const_b,
183
+ from_dlpack(dst_b),
184
+ const_c,
185
+ from_dlpack(dst_c),
186
+ )
187
+
188
+ np.testing.assert_equal(const_a, dst_a.detach().cpu().numpy()[0])
189
+ np.testing.assert_equal(const_b, dst_b.detach().cpu().numpy()[0])
190
+ np.testing.assert_equal(const_c, dst_c.detach().cpu().numpy()[0])
191
+
192
+
193
+ if __name__ == "__main__":
194
+ # prepare cuda context
195
+ cutlass.cuda.initialize_cuda_context()
196
+ # An example for shared memory allocation
197
+ const_a = 0.5
198
+ const_b = 1.0
199
+ const_c = 2.0
200
+ veify_allocation_kernel(const_a, const_b, const_c)
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/ampere/tensorop_gemm.py ADDED
@@ -0,0 +1,1012 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ import math
31
+ import time
32
+ from typing import Tuple, Type
33
+
34
+ import cuda.bindings.driver as cuda
35
+ import torch
36
+
37
+ import cutlass
38
+ import cutlass.cute as cute
39
+ import cutlass.cute.testing as testing
40
+ import cutlass.torch as cutlass_torch
41
+ import cutlass.utils as utils
42
+ from cutlass.cute.runtime import from_dlpack
43
+
44
+ """
45
+ A dense GEMM (C = A * B) example for the NVIDIA Ampere architecture using CUTE DSL.
46
+ - Matrix A is MxKxL, L is batch dimension, A can be row-major("K") or column-major("M")
47
+ - Matrix B is NxKxL, L is batch dimension, B can be row-major("N") or column-major("K")
48
+ - Matrix C is MxNxL, L is batch dimension, C can be row-major("N") or column-major("M")
49
+
50
+ This GEMM kernel supports the following features:
51
+ - Utilizes Ampere's tensor cores for matrix multiply-accumulate (MMA) operations
52
+ - Threadblock rasterization to improve data re-use
53
+ - Supports multi-stage pipeline to overlap computation and memory access
54
+ - Implements shared memory buffering for epilogue to increase coalesced global memory access
55
+
56
+ This GEMM works as follows:
57
+ 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using asynchronous copies.
58
+ 2. Perform matrix multiply-accumulate (MMA) operations.
59
+ 3. Store results from registers (RMEM) to shared memory (SMEM), then to global memory (GMEM).
60
+
61
+ The Ampere tensor core instruction used operates as follows:
62
+ - Read matrix A from SMEM
63
+ - Read matrix B from SMEM
64
+ - Perform MMA operation and store the result in Accumulator(register)
65
+
66
+ To run this example:
67
+
68
+ .. code-block:: bash
69
+
70
+ python examples/ampere/tensorop_gemm.py \
71
+ --mnkl 8192,8192,8192,1 --atom_layout_mnk 2,2,1 \
72
+ --ab_dtype Float16 \
73
+ --c_dtype Float16 --acc_dtype Float32 \
74
+ --a_major m --b_major n --c_major n
75
+
76
+ The above example command computes with M=8192, N=8192, K=8192,
77
+ batch_count=1. The atom layout's shape is 2x2x1 and the input, mma
78
+ accumulator, and output data type are set as fp16, fp32 and fp16,
79
+ respectively.
80
+
81
+ To collect performance with NCU profiler:
82
+
83
+ .. code-block:: bash
84
+
85
+ ncu python examples/ampere/tensorop_gemm.py \
86
+ --mnkl 8192,8192,8192,1 --atom_layout_mnk 2,2,1 \
87
+ --ab_dtype Float16 \
88
+ --c_dtype Float16 --acc_dtype Float32 \
89
+ --a_major m --b_major n --c_major n \
90
+ --skip_ref_check --iterations 2
91
+
92
+ Constraints:
93
+ * Supported input and output data types: fp16
94
+ * Support accumulator data types: f32
95
+ * Default tile shape is set to be 128x128x32
96
+ * Atom layout's MNK shape is set so that tile shape can be divided by MMA
97
+ instruction shape
98
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned,
99
+ i.e, number of elements is a multiple of 8
100
+ """
101
+
102
+
103
+ class TensorOpGemm:
104
+ def __init__(
105
+ self,
106
+ ab_dtype: Type[cutlass.Numeric],
107
+ c_dtype: Type[cutlass.Numeric],
108
+ acc_dtype: Type[cutlass.Numeric],
109
+ atom_layout_mnk: Tuple[int, int, int],
110
+ ):
111
+ self.ab_dtype = ab_dtype
112
+ self.c_dtype = c_dtype
113
+ self.acc_dtype = acc_dtype
114
+ self.cta_tiler = (128, 128, 32)
115
+ self.num_stages = 3
116
+ self.atom_layout_mnk = atom_layout_mnk
117
+ atom_lay_M, atom_lay_N, atom_lay_K = self.atom_layout_mnk
118
+ self.num_threads = atom_lay_M * atom_lay_N * atom_lay_K * 32
119
+
120
+ self.bM, self.bN, self.bK = self.cta_tiler
121
+ self.mma_inst_shape = (16, 8, 16)
122
+ mmaM, mmaN, mmaK = self.mma_inst_shape
123
+
124
+ assert (
125
+ self.bM % (atom_lay_M * mmaM) == 0
126
+ ), "bM must be divisible by MMA instruction"
127
+ assert (
128
+ self.bN % (atom_lay_N * mmaN) == 0
129
+ ), "bN must be divisible by MMA instruction"
130
+ assert atom_lay_K == 1, "this example does not support atom layout K > 1"
131
+ assert self.bK % mmaK == 0, "bK must be divisible by MMA instruction"
132
+ assert self.num_stages >= 3, "num_stages must be greater than or equal to 3"
133
+
134
+ @cute.jit
135
+ def __call__(
136
+ self,
137
+ mA: cute.Tensor,
138
+ mB: cute.Tensor,
139
+ mC: cute.Tensor,
140
+ epilogue_op: cutlass.Constexpr = lambda x: x,
141
+ ):
142
+ # The grid divides the problems's M, N, and L dimensions by the
143
+ # respective modes of the tile shape (bM, bN, 1). The K dimension is
144
+ # handled within a block via a multistage process.
145
+
146
+ self.a_major_mode = utils.LayoutEnum.from_tensor(mA)
147
+ self.b_major_mode = utils.LayoutEnum.from_tensor(mB)
148
+ self.c_major_mode = utils.LayoutEnum.from_tensor(mC)
149
+
150
+ # ///////////////////////////////////////////////////////////////////////////////
151
+ # Shared memory layout:
152
+ # ///////////////////////////////////////////////////////////////////////////////
153
+
154
+ # Creates a layout with the size required for the provided tile
155
+ # size and num stages (stages are used for K dimension) that is also
156
+ # sectioned into 64x8 or 8x32 layout atoms. The swizzle is set so that
157
+ # the atom for the shared memory -> register copy does not encounter
158
+ # bank conflicts
159
+
160
+ # assume the input is 16B align
161
+ ab_copy_bits = 128
162
+ sA_layout = self._make_smem_layout_AB(
163
+ mA.element_type,
164
+ self.a_major_mode,
165
+ ab_copy_bits,
166
+ (self.cta_tiler[0], self.cta_tiler[2], self.num_stages),
167
+ )
168
+ sB_layout = self._make_smem_layout_AB(
169
+ mB.element_type,
170
+ self.b_major_mode,
171
+ ab_copy_bits,
172
+ (self.cta_tiler[1], self.cta_tiler[2], self.num_stages),
173
+ )
174
+
175
+ # Creates a similar layout but without num_stages or layout atoms
176
+ sC_layout = self._make_smem_layout_C(
177
+ mC.element_type,
178
+ self.c_major_mode,
179
+ ab_copy_bits,
180
+ (self.cta_tiler[0], self.cta_tiler[1]),
181
+ )
182
+
183
+ # Shared memory allocated for operations with A, B will be
184
+ # overwritten for operations on C. This is to improve performance
185
+ # by reducing the size of shared memory requested by each block
186
+ smem_size = max(
187
+ cute.size_in_bytes(mC.element_type, sC_layout),
188
+ cute.size_in_bytes(mA.element_type, sA_layout)
189
+ + cute.size_in_bytes(mB.element_type, sB_layout),
190
+ )
191
+
192
+ # ///////////////////////////////////////////////////////////////////////////////
193
+ # Tiled copy:
194
+ # The majorness of tA/tB/tC follows the majorness of gA/gB/gC,
195
+ # enabling merged accesses to global memory for faster data
196
+ # transfer between global and shared memory.
197
+ # ///////////////////////////////////////////////////////////////////////////////
198
+
199
+ # Create a copy atom for a global to shared memory asynchronous copy
200
+ atom_async_copy = cute.make_copy_atom(
201
+ cute.nvgpu.cpasync.CopyG2SOp(
202
+ cache_mode=cute.nvgpu.cpasync.LoadCacheMode.GLOBAL
203
+ ),
204
+ mA.element_type,
205
+ num_bits_per_copy=ab_copy_bits,
206
+ )
207
+
208
+ # Create thread layouts for tiled copy from the copy atom where the
209
+ # thread layout simply follows the leading dimension of the tensor
210
+ tiled_copy_A = self._make_gmem_tiled_copy_AB(
211
+ atom_async_copy, mA.element_type, self.a_major_mode, ab_copy_bits
212
+ )
213
+ tiled_copy_B = self._make_gmem_tiled_copy_AB(
214
+ atom_async_copy, mB.element_type, self.b_major_mode, ab_copy_bits
215
+ )
216
+
217
+ # Creates a synchronous copy atom and thread layouts for the epilogue
218
+ c_copy_bits = 128
219
+ atom_sync_copy = cute.make_copy_atom(
220
+ cute.nvgpu.CopyUniversalOp(),
221
+ mC.element_type,
222
+ num_bits_per_copy=c_copy_bits,
223
+ )
224
+ tiled_copy_C = self._make_gmem_tiled_copy_C(
225
+ atom_sync_copy, mC.element_type, self.c_major_mode, c_copy_bits
226
+ )
227
+
228
+ # ///////////////////////////////////////////////////////////////////////////////
229
+ # Tiled MMA
230
+ # ///////////////////////////////////////////////////////////////////////////////
231
+
232
+ # Creates a mma atom with 16x8x16 shape for MNK
233
+ op = cute.nvgpu.warp.MmaF16BF16Op(
234
+ self.ab_dtype, self.acc_dtype, self.mma_inst_shape
235
+ )
236
+
237
+ permutation_mnk = (
238
+ self.atom_layout_mnk[0] * self.mma_inst_shape[0],
239
+ # if atom layout's N-mode is 1, to leverage the largest coalesced
240
+ # shared memory -> register copy, set the tiled mma's N mode to 16
241
+ self.atom_layout_mnk[1] * self.mma_inst_shape[1] * 2,
242
+ self.atom_layout_mnk[2] * self.mma_inst_shape[2],
243
+ )
244
+
245
+ # Created a tiled mma that tiles the atom according to specified layout.
246
+ # For a 2x2x1 atom layout, the mma atom is duplicated 4 times, twice
247
+ # across M and twice across N
248
+ tC = cute.make_layout(self.atom_layout_mnk)
249
+ tiled_mma = cute.make_tiled_mma(
250
+ op,
251
+ tC,
252
+ permutation_mnk=permutation_mnk,
253
+ )
254
+
255
+ # grid_dim: ((m + BLK_M - 1) // BLK_M, (n + BLK_N - 1) // BLK_N, l)
256
+ grid_dim = cute.ceil_div(mC.shape, (self.bM, self.bN, 1))
257
+
258
+ # Add threadblock rasterization to improve re-use of data
259
+ raster_factor = 1
260
+ grid_dim_n = cute.size(grid_dim[1])
261
+ # Thresholds picked so that it doesn't cause too many no-op CTAs
262
+ if grid_dim_n > 5:
263
+ raster_factor = 8
264
+ elif grid_dim_n > 2:
265
+ raster_factor = 4
266
+ elif grid_dim_n > 1:
267
+ raster_factor = 2
268
+ rasterization_remap_grid_dim = (
269
+ cute.size(grid_dim[0]) * raster_factor,
270
+ (cute.size(grid_dim[1]) + raster_factor - 1) // raster_factor,
271
+ cute.size(grid_dim[2]),
272
+ )
273
+
274
+ self.kernel(
275
+ mA,
276
+ mB,
277
+ mC,
278
+ sA_layout,
279
+ sB_layout,
280
+ sC_layout,
281
+ tiled_copy_A,
282
+ tiled_copy_B,
283
+ tiled_copy_C,
284
+ tiled_mma,
285
+ raster_factor,
286
+ epilogue_op,
287
+ ).launch(
288
+ grid=rasterization_remap_grid_dim,
289
+ block=[self.num_threads, 1, 1],
290
+ smem=smem_size,
291
+ )
292
+
293
+ @cute.kernel
294
+ def kernel(
295
+ self,
296
+ mA: cute.Tensor,
297
+ mB: cute.Tensor,
298
+ mC: cute.Tensor,
299
+ sA_layout: cute.ComposedLayout,
300
+ sB_layout: cute.ComposedLayout,
301
+ sC_layout: cute.ComposedLayout,
302
+ tiled_copy_A: cute.TiledCopy,
303
+ tiled_copy_B: cute.TiledCopy,
304
+ tiled_copy_C: cute.TiledCopy,
305
+ tiled_mma: cute.TiledMma,
306
+ rasterization_factor: cutlass.Int32,
307
+ epilogue_op: cutlass.Constexpr = lambda x: x,
308
+ ):
309
+ # Thread index, block index
310
+ tidx, _, _ = cute.arch.thread_idx()
311
+ bidx, bidy, bidz = cute.arch.block_idx()
312
+ grid_dim = cute.ceil_div(mC.shape, (self.bM, self.bN, 1))
313
+ offset_tile_x, offset_tile_y = self.raster_tile(
314
+ bidx, bidy, rasterization_factor
315
+ )
316
+ # Early exit if CTA is out of range
317
+ if grid_dim[0] <= offset_tile_x or grid_dim[1] <= offset_tile_y:
318
+ pass
319
+ else:
320
+ tiler_coord = (offset_tile_x, offset_tile_y, None)
321
+
322
+ # ///////////////////////////////////////////////////////////////////////////////
323
+ # Get the appropriate tiles for this thread block.
324
+ # gA: (BLK_M, BLK_N, k), gB: (BLK_N, BLK_K, k), gC: (BLK_M, BLK_N)
325
+ # ///////////////////////////////////////////////////////////////////////////////
326
+ gA = cute.local_tile(
327
+ mA[None, None, bidz],
328
+ tiler=self.cta_tiler,
329
+ coord=tiler_coord,
330
+ proj=(1, None, 1),
331
+ )
332
+ gB = cute.local_tile(
333
+ mB[None, None, bidz],
334
+ tiler=self.cta_tiler,
335
+ coord=tiler_coord,
336
+ proj=(None, 1, 1),
337
+ )
338
+ gC = cute.local_tile(
339
+ mC[None, None, bidz],
340
+ tiler=self.cta_tiler,
341
+ coord=tiler_coord,
342
+ proj=(1, 1, None),
343
+ )
344
+
345
+ # By default, if the tensor k mode does not divide into the tile k
346
+ # size, then last tiles in the k dimension are irregular.
347
+ # Instead, make the first tiles irregular when k is irregular.
348
+ # This allows us to handle the irregular tile first to avoid
349
+ # checking for this condition within the mainloop.
350
+
351
+ # residual_k is a negative number indicating the amount needed to
352
+ # shift the pointer by in dimension k
353
+ residual_k = cute.size(mA, mode=[1]) - cutlass.Int32(self.bK) * cute.size(
354
+ gA, mode=[2]
355
+ )
356
+
357
+ # move the pointer of gA/gB in the `-k` direction
358
+ gA = cute.domain_offset((0, residual_k, 0), gA)
359
+ gB = cute.domain_offset((0, residual_k, 0), gB)
360
+ # input is 16B aligned
361
+ gA = cute.make_tensor(gA.iterator.align(16), gA.layout)
362
+ gB = cute.make_tensor(gB.iterator.align(16), gB.layout)
363
+
364
+ # Construct identity layout for sA and sB (mirrors global tensors,
365
+ # used for predication only)
366
+ mcA = cute.make_identity_tensor(mA.layout.shape)
367
+ mcB = cute.make_identity_tensor(mB.layout.shape)
368
+ cA = cute.local_tile(
369
+ mcA[None, None, bidz],
370
+ tiler=self.cta_tiler,
371
+ coord=tiler_coord,
372
+ proj=(1, None, 1),
373
+ )
374
+ cB = cute.local_tile(
375
+ mcB[None, None, bidz],
376
+ tiler=self.cta_tiler,
377
+ coord=tiler_coord,
378
+ proj=(None, 1, 1),
379
+ )
380
+
381
+ cA = cute.domain_offset((0, residual_k, 0), cA)
382
+ cB = cute.domain_offset((0, residual_k, 0), cB)
383
+
384
+ # ///////////////////////////////////////////////////////////////////////////////
385
+ # Create shared memory buffers and get the appropriate fragments for this thread.
386
+ # sA: (BLK_M, BLK_K, PIPE) , sB: (BLK_N, BLK_K, PIPE)
387
+ # tAgA: (CPY, CPY_M, CPY_K, k) , tBgB: (CPY, CPY_N, CPY_K, k)
388
+ # tAsA: (CPY, CPY_M, CPY_K, PIPE) , tBsB: (CPY, CPY_N, CPY_K, PIPE)
389
+ # ///////////////////////////////////////////////////////////////////////////////
390
+ # Shared memory buffer
391
+ smem = cutlass.utils.SmemAllocator()
392
+
393
+ sA = smem.allocate_tensor(mA.element_type, sA_layout, 16)
394
+ sB = smem.allocate_tensor(mB.element_type, sB_layout, 16)
395
+ sC = cute.make_tensor(
396
+ cute.recast_ptr(sA.iterator, dtype=self.c_dtype), sC_layout
397
+ )
398
+
399
+ thr_copy_A = tiled_copy_A.get_slice(tidx)
400
+ thr_copy_B = tiled_copy_B.get_slice(tidx)
401
+ thr_copy_C = tiled_copy_C.get_slice(tidx)
402
+ tAgA = thr_copy_A.partition_S(gA)
403
+ tAsA = thr_copy_A.partition_D(sA)
404
+ tBgB = thr_copy_B.partition_S(gB)
405
+ tBsB = thr_copy_B.partition_D(sB)
406
+ tCsC_epilogue = thr_copy_C.partition_S(sC)
407
+ tCgC_epilogue = thr_copy_C.partition_D(gC)
408
+
409
+ # Repeat the partitioning with identity layouts
410
+ tAcA = thr_copy_A.partition_S(cA)
411
+ tBcB = thr_copy_B.partition_S(cB)
412
+
413
+ # ///////////////////////////////////////////////////////////////////////////////
414
+ # Predicate: Mark indices that need to copy when problem_shape isn't a multiple
415
+ # of tile_shape
416
+ # ///////////////////////////////////////////////////////////////////////////////
417
+
418
+ # For predication over the tensors A (M/K), B (N/K), and (in the
419
+ # epilogue) C (M/N), we will compute it in a fashion similar to an
420
+ # outer product. The predication along one of the dimensions is
421
+ # evaluated and stored in a predication tensor. Then, the
422
+ # predication for the remaining dimension is handled later via an
423
+ # if/else branch at the copy.
424
+ # For A and B, predication booleans along M/N are stored in a
425
+ # predication tensor and along K is handled via a if/else branch.
426
+
427
+ # Allocate predicate tensors for M and N. Predication is checked
428
+ # at the granularity of a copy atom, so the predicate tensor does not
429
+ # need separate booleans for individual elements within a copy
430
+ # atom (for example, the elements of tAgA.shape[0][0].)
431
+ tApA = cute.make_fragment(
432
+ cute.make_layout(
433
+ (
434
+ tAgA.shape[0][1],
435
+ cute.size(tAgA, mode=[1]),
436
+ cute.size(tAgA, mode=[2]),
437
+ ),
438
+ stride=(cute.size(tAgA, mode=[1]), 1, 0),
439
+ ),
440
+ cutlass.Boolean,
441
+ )
442
+ tBpB = cute.make_fragment(
443
+ cute.make_layout(
444
+ (
445
+ tBsB.shape[0][1],
446
+ cute.size(tBsB, mode=[1]),
447
+ cute.size(tBsB, mode=[2]),
448
+ ),
449
+ stride=(cute.size(tBsB, mode=[1]), 1, 0),
450
+ ),
451
+ cutlass.Boolean,
452
+ )
453
+ # Set predicates for M/N bounds
454
+ for rest_v in range(tApA.shape[0]):
455
+ for m in range(tApA.shape[1]):
456
+ tApA[rest_v, m, 0] = cute.elem_less(
457
+ tAcA[(0, rest_v), m, 0, 0][0], mA.shape[0]
458
+ )
459
+ for rest_v in range(tBpB.shape[0]):
460
+ for n in range(tBpB.shape[1]):
461
+ tBpB[rest_v, n, 0] = cute.elem_less(
462
+ tBcB[(0, rest_v), n, 0, 0][0], mB.shape[0]
463
+ )
464
+
465
+ # ///////////////////////////////////////////////////////////////////////////////
466
+ # Prefetch Prologue
467
+ # ///////////////////////////////////////////////////////////////////////////////
468
+ # Clear the smem tiles to account for predicated off loads
469
+ tAsA.fill(0)
470
+ tBsB.fill(0)
471
+ cute.arch.sync_threads()
472
+ # Start async loads for the first k-tile. Here we take care of the k residue
473
+ # via if/else check along the k dimension. Because we shifted the identity tensor
474
+ # by the residue_k and because the identity tensor is a coord tensor, the
475
+ # values of any identity tensor element that is poison is less than -1
476
+ num_smem_stages = cute.size(tAsA, mode=[3])
477
+ k_tile_count = cute.size(tAgA, mode=[3])
478
+ k_tile_index = cutlass.Int32(0)
479
+
480
+ for k in range(tApA.shape[2]):
481
+ if cute.elem_less(cutlass.Int32(-1), tAcA[0, 0, k, 0][1]):
482
+ cute.copy(
483
+ tiled_copy_A,
484
+ tAgA[None, None, k, k_tile_index],
485
+ tAsA[None, None, k, 0],
486
+ pred=tApA[None, None, k],
487
+ )
488
+ for k in range(tBpB.shape[2]):
489
+ if cute.elem_less(cutlass.Int32(-1), tBcB[0, 0, k, 0][1]):
490
+ cute.copy(
491
+ tiled_copy_B,
492
+ tBgB[None, None, k, k_tile_index],
493
+ tBsB[None, None, k, 0],
494
+ pred=tBpB[None, None, k],
495
+ )
496
+ k_tile_index = k_tile_index + 1
497
+ cute.arch.cp_async_commit_group()
498
+
499
+ # Start async loads for rest of the k-tiles
500
+ for k_tile in range(1, num_smem_stages - 1):
501
+ if k_tile == k_tile_count:
502
+ tApA.fill(0)
503
+ tBpB.fill(0)
504
+ cute.copy(
505
+ tiled_copy_A,
506
+ tAgA[None, None, None, k_tile_index],
507
+ tAsA[None, None, None, k_tile],
508
+ pred=tApA,
509
+ )
510
+ cute.copy(
511
+ tiled_copy_B,
512
+ tBgB[None, None, None, k_tile_index],
513
+ tBsB[None, None, None, k_tile],
514
+ pred=tBpB,
515
+ )
516
+ k_tile_index = k_tile_index + 1
517
+ cute.arch.cp_async_commit_group()
518
+
519
+ # ///////////////////////////////////////////////////////////////////////////////
520
+ # Tile MMA compute thread partitions and allocate accumulators
521
+ # ///////////////////////////////////////////////////////////////////////////////
522
+ thr_mma = tiled_mma.get_slice(tidx)
523
+ tCsA = thr_mma.partition_A(sA)
524
+ tCsB = thr_mma.partition_B(sB)
525
+ tCsC = thr_mma.partition_C(sC)
526
+ tCgC = thr_mma.partition_C(gC)
527
+ tCrA = tiled_mma.make_fragment_A(tCsA[None, None, None, 0])
528
+ tCrB = tiled_mma.make_fragment_B(tCsB[None, None, None, 0])
529
+ tCrC = tiled_mma.make_fragment_C(tCgC)
530
+ # Clear the accumulator
531
+ tCrC.fill(0.0)
532
+
533
+ # ///////////////////////////////////////////////////////////////////////////////
534
+ # Copy Atom A/B retiling
535
+ # ///////////////////////////////////////////////////////////////////////////////
536
+
537
+ # Create the copy atoms for the copy from shared memory to register
538
+ atom_copy_s2r_A = cute.make_copy_atom(
539
+ cute.nvgpu.warp.LdMatrix8x8x16bOp(
540
+ self.a_major_mode != utils.LayoutEnum.ROW_MAJOR, 4
541
+ ),
542
+ mA.element_type,
543
+ )
544
+ atom_copy_s2r_B = cute.make_copy_atom(
545
+ cute.nvgpu.warp.LdMatrix8x8x16bOp(
546
+ self.b_major_mode != utils.LayoutEnum.ROW_MAJOR, 4
547
+ ),
548
+ mB.element_type,
549
+ )
550
+
551
+ # Creates the tiled copy so that it matches the thread-value layout
552
+ # expected by the tiled mma
553
+ tiled_copy_s2r_A = cute.make_tiled_copy_A(atom_copy_s2r_A, tiled_mma)
554
+ tiled_copy_s2r_B = cute.make_tiled_copy_B(atom_copy_s2r_B, tiled_mma)
555
+
556
+ thr_copy_ldmatrix_A = tiled_copy_s2r_A.get_slice(tidx)
557
+ thr_copy_ldmatrix_B = tiled_copy_s2r_B.get_slice(tidx)
558
+ tCsA_copy_view = thr_copy_ldmatrix_A.partition_S(sA)
559
+ tCrA_copy_view = thr_copy_ldmatrix_A.retile(tCrA)
560
+ tCsB_copy_view = thr_copy_ldmatrix_B.partition_S(sB)
561
+ tCrB_copy_view = thr_copy_ldmatrix_B.retile(tCrB)
562
+
563
+ # Current pipe index in smem to read from / write to
564
+ smem_pipe_read = 0
565
+ smem_pipe_write = num_smem_stages - 1
566
+
567
+ tCsA_p = tCsA_copy_view[None, None, None, smem_pipe_read]
568
+ tCsB_p = tCsB_copy_view[None, None, None, smem_pipe_read]
569
+
570
+ # ///////////////////////////////////////////////////////////////////////////////
571
+ # PREFETCH register pipeline
572
+ # ///////////////////////////////////////////////////////////////////////////////
573
+ num_k_block = cute.size(tCrA, mode=[2])
574
+ if num_k_block > 1:
575
+ # Wait until our first prefetched tile is loaded in
576
+ cute.arch.cp_async_wait_group(num_smem_stages - 2)
577
+ cute.arch.sync_threads()
578
+ # Prefetch the first k-block rmem from the first k-tile
579
+ cute.copy(
580
+ tiled_copy_s2r_A,
581
+ tCsA_p[None, None, 0],
582
+ tCrA_copy_view[None, None, 0],
583
+ )
584
+ cute.copy(
585
+ tiled_copy_s2r_B,
586
+ tCsB_p[None, None, 0],
587
+ tCrB_copy_view[None, None, 0],
588
+ )
589
+
590
+ # ///////////////////////////////////////////////////////////////////////////////
591
+ # Mainloop
592
+ # 1. Shared memory pipeline (gmem -> smem):
593
+ # The default smem pipeline depth is 3, meaning that for shared
594
+ # memory buffers, we allocate three times the size described by the
595
+ # CTA tiler. We prefetch 2 of these buffers before entering the main
596
+ # loop. Considering only the transfer from global memory to shared
597
+ # memory, the general structure of the mainloop is:
598
+ # (1) copy k-tile from gmem to smem;
599
+ # (2) perform gemm computation on k-tile;
600
+ # (3) wait for the next copy to finish.
601
+ # The `cute.arch.cp_async_wait_group(num_smem_stages - 2)` command
602
+ # waits for the number of unfinished 'copy' to be <= 1. The advantage
603
+ # of this approach is that it allows for simultaneous production
604
+ # (i.e., step (1)) and consumption (i.e., step (2)) of smem.
605
+ # A common misconception is to prefetch N buffers and rewrite
606
+ # the pipeline logic to wait on N-1 pending copies. The disadvantage
607
+ # of this approach is that it requires fully consuming a buffer in
608
+ # order to open an empty buffer for the next copy.
609
+ # 2. Register pipeline (smem -> register):
610
+ # Similarly, the register pipeline produces i+1, consumes i, and
611
+ # produces i+2... Notably, i and i+1 do not use the same register,
612
+ # eliminating dependencies on the same register for better parallelism.
613
+ # 3. Combining the smem and register pipelines results in the mainloop.
614
+ # ///////////////////////////////////////////////////////////////////////////////
615
+ for k_tile in range(k_tile_count):
616
+ for k_block in cutlass.range(num_k_block, unroll_full=True):
617
+ if k_block == num_k_block - 1:
618
+ tCsA_p = tCsA_copy_view[None, None, None, smem_pipe_read]
619
+ tCsB_p = tCsB_copy_view[None, None, None, smem_pipe_read]
620
+ cute.arch.cp_async_wait_group(num_smem_stages - 2)
621
+ cute.arch.sync_threads()
622
+
623
+ # Load A, B from shared memory to registers for k_block + 1
624
+ k_block_next = (k_block + 1) % num_k_block # static
625
+ cute.copy(
626
+ tiled_copy_s2r_A,
627
+ tCsA_p[None, None, k_block_next],
628
+ tCrA_copy_view[None, None, k_block_next],
629
+ )
630
+ cute.copy(
631
+ tiled_copy_s2r_B,
632
+ tCsB_p[None, None, k_block_next],
633
+ tCrB_copy_view[None, None, k_block_next],
634
+ )
635
+
636
+ # Fetch next A: To better interleave global memory access and compute
637
+ # instructions, we intentionally use the sequence: copy A, perform GEMM,
638
+ # then copy B.
639
+ if k_block == 0:
640
+ if k_tile + num_smem_stages - 1 < k_tile_count:
641
+ cute.copy(
642
+ tiled_copy_A,
643
+ tAgA[None, None, None, k_tile_index],
644
+ tAsA[None, None, None, smem_pipe_write],
645
+ pred=tApA,
646
+ )
647
+
648
+ # Thread-level register gemm for k_block
649
+ cute.gemm(
650
+ tiled_mma,
651
+ tCrC,
652
+ tCrA[None, None, k_block],
653
+ tCrB[None, None, k_block],
654
+ tCrC,
655
+ )
656
+
657
+ # Fetch next B and update smem pipeline read/write
658
+ if k_block == 0:
659
+ if k_tile + num_smem_stages - 1 < k_tile_count:
660
+ cute.copy(
661
+ tiled_copy_B,
662
+ tBgB[None, None, None, k_tile_index],
663
+ tBsB[None, None, None, smem_pipe_write],
664
+ pred=tBpB,
665
+ )
666
+ k_tile_index = k_tile_index + 1
667
+ cute.arch.cp_async_commit_group()
668
+ smem_pipe_write = smem_pipe_read
669
+ smem_pipe_read = smem_pipe_read + 1
670
+ if smem_pipe_read == num_smem_stages:
671
+ smem_pipe_read = 0
672
+
673
+ # Sync before epilogue
674
+ cute.arch.cp_async_wait_group(0)
675
+ cute.arch.sync_threads()
676
+
677
+ # ///////////////////////////////////////////////////////////////////////////////
678
+ # Epilogue with fusion
679
+ # ///////////////////////////////////////////////////////////////////////////////
680
+ tCrD = cute.make_fragment_like(tCrC, self.c_dtype)
681
+ tCrD[None] = epilogue_op(tCrC.load()).to(self.c_dtype)
682
+
683
+ # Copy results of D back to shared memory
684
+ cute.autovec_copy(tCrD, tCsC)
685
+
686
+ # Create coord tensor for C
687
+ ceilM, ceilN, _ = cute.ceil_div(mC.shape, (self.bM, self.bN, 1))
688
+ mcC = cute.make_identity_tensor(
689
+ (
690
+ cute.size(ceilM) * self.cta_tiler[0],
691
+ cute.size(ceilN) * self.cta_tiler[1],
692
+ 1,
693
+ )
694
+ )
695
+ cC = cute.local_tile(
696
+ mcC[None, None, bidz],
697
+ tiler=self.cta_tiler,
698
+ coord=tiler_coord,
699
+ proj=(1, 1, None),
700
+ )
701
+ tCcC = thr_copy_C.partition_S(cC)
702
+
703
+ tCrC_epilogue = cute.make_fragment_like(tCsC_epilogue)
704
+ # Wait for all writes to shared memory to finish before starting copies
705
+ # using the new layouts
706
+ cute.arch.sync_threads()
707
+ cute.autovec_copy(tCsC_epilogue, tCrC_epilogue)
708
+
709
+ # Create predication tensor for m
710
+ tCpC = cute.make_fragment(
711
+ cute.make_layout(
712
+ (
713
+ tCgC_epilogue.shape[0][1],
714
+ cute.size(tCgC_epilogue, mode=[1]),
715
+ cute.size(tCgC_epilogue, mode=[2]),
716
+ ),
717
+ stride=(cute.size(tCgC_epilogue, mode=[1]), 1, 0),
718
+ ),
719
+ cutlass.Boolean,
720
+ )
721
+ for rest_v in range(tCpC.shape[0]):
722
+ for m in range(tCpC.shape[1]):
723
+ tCpC[rest_v, m, 0] = cute.elem_less(
724
+ tCcC[(0, rest_v), m, 0][0], mC.shape[0]
725
+ )
726
+
727
+ # Copy to global memory using better vectorization
728
+ for rest_v in range(tCpC.shape[0]):
729
+ for n in range(tCpC.shape[2]):
730
+ if cute.elem_less(tCcC[(0, rest_v), 0, n][1], mC.shape[1]):
731
+ cute.copy(
732
+ tiled_copy_C,
733
+ tCrC_epilogue[None, None, n],
734
+ tCgC_epilogue[None, None, n],
735
+ pred=tCpC[None, None, n],
736
+ )
737
+ return
738
+
739
+ def _make_smem_layout_AB(self, dtype, major_mode, copy_bits, smem_tiler):
740
+ major_mode_size = (
741
+ smem_tiler[1] if major_mode == utils.LayoutEnum.ROW_MAJOR else smem_tiler[0]
742
+ )
743
+ major_mode_size = 64 if major_mode_size >= 64 else major_mode_size
744
+
745
+ swizzle_bits = int(math.log2(major_mode_size * dtype.width // copy_bits))
746
+ swizzle_bits = min(swizzle_bits, 3)
747
+
748
+ layout_atom_outer = (
749
+ cute.make_layout((8, major_mode_size), stride=(major_mode_size, 1))
750
+ if major_mode == utils.LayoutEnum.ROW_MAJOR
751
+ else cute.make_layout((major_mode_size, 8), stride=(1, major_mode_size))
752
+ )
753
+ layout_atom = cute.make_composed_layout(
754
+ cute.make_swizzle(swizzle_bits, 3, 3),
755
+ 0,
756
+ layout_atom_outer,
757
+ )
758
+ layout = cute.tile_to_shape(layout_atom, smem_tiler, (0, 1, 2))
759
+ return layout
760
+
761
+ def _make_smem_layout_C(self, dtype, major_mode, copy_bits, smem_tiler):
762
+ major_mode_size = (
763
+ smem_tiler[1] if major_mode == utils.LayoutEnum.ROW_MAJOR else smem_tiler[0]
764
+ )
765
+
766
+ swizzle_bits = int(math.log2(major_mode_size * dtype.width // copy_bits))
767
+ swizzle_bits = min(swizzle_bits, 3)
768
+
769
+ layout_atom_outer = (
770
+ cute.make_layout((8, major_mode_size), stride=(major_mode_size, 1))
771
+ if major_mode == utils.LayoutEnum.ROW_MAJOR
772
+ else cute.make_layout((major_mode_size, 8), stride=(1, major_mode_size))
773
+ )
774
+ layout_atom = cute.make_composed_layout(
775
+ cute.make_swizzle(swizzle_bits, 3, 4),
776
+ 0,
777
+ layout_atom_outer,
778
+ )
779
+
780
+ # Due to the thread layout of the mma, remove swizzle in C to
781
+ # prevent shared memory fragments owned by an single thread from
782
+ # holding swizzles
783
+ if major_mode == utils.LayoutEnum.COL_MAJOR:
784
+ layout_atom = cute.make_composed_layout(
785
+ cute.make_swizzle(0, 3, 4), 0, layout_atom_outer
786
+ )
787
+ layout = cute.tile_to_shape(
788
+ layout_atom,
789
+ smem_tiler,
790
+ (0, 1),
791
+ )
792
+ return layout
793
+
794
+ def _make_gmem_tiled_copy_AB(self, atom_copy, dtype, major_mode, copy_bits):
795
+ copy_elems = copy_bits // dtype.width
796
+ shape_dim_1 = cute.size(self.bK) // copy_elems
797
+ # thread layout for copy
798
+ thread_layout = cute.make_layout(
799
+ (self.num_threads // shape_dim_1, shape_dim_1), stride=(shape_dim_1, 1)
800
+ )
801
+ if major_mode != utils.LayoutEnum.ROW_MAJOR:
802
+ shape_dim_0 = cute.size(self.bM) // copy_elems
803
+ thread_layout = cute.make_layout(
804
+ (shape_dim_0, self.num_threads // shape_dim_0), stride=(1, shape_dim_0)
805
+ )
806
+ # Value layout for copy
807
+ value_layout = (
808
+ cute.make_layout((1, copy_elems))
809
+ if major_mode == utils.LayoutEnum.ROW_MAJOR
810
+ else cute.make_layout((copy_elems, 1))
811
+ )
812
+ return cute.make_tiled_copy_tv(atom_copy, thread_layout, value_layout)
813
+
814
+ def _make_gmem_tiled_copy_C(self, atom_copy, dtype, major_mode, copy_bits):
815
+ copy_elems = copy_bits // dtype.width
816
+ shape_dim_1 = cute.size(self.bN) // copy_elems
817
+ # thread layout for copy
818
+ thread_layout = cute.make_layout(
819
+ (self.num_threads // shape_dim_1, shape_dim_1), stride=(shape_dim_1, 1)
820
+ )
821
+ if major_mode != utils.LayoutEnum.ROW_MAJOR:
822
+ shape_dim_0 = cute.size(self.bM) // copy_elems
823
+ thread_layout = cute.make_layout(
824
+ (shape_dim_0, self.num_threads // shape_dim_0), stride=(1, shape_dim_0)
825
+ )
826
+ value_layout = (
827
+ cute.make_layout((1, copy_elems))
828
+ if major_mode == utils.LayoutEnum.ROW_MAJOR
829
+ else cute.make_layout((copy_elems, 1))
830
+ )
831
+ return cute.make_tiled_copy_tv(atom_copy, thread_layout, value_layout)
832
+
833
+ def raster_tile(self, i, j, f):
834
+ new_i = i // f
835
+ new_j = (i % f) + (j * f)
836
+ return (new_i, new_j)
837
+
838
+
839
+ def run(
840
+ a_major: str,
841
+ b_major: str,
842
+ c_major: str,
843
+ ab_dtype: Type[cutlass.Numeric],
844
+ c_dtype: Type[cutlass.Numeric],
845
+ acc_dtype: Type[cutlass.Numeric],
846
+ mnkl: Tuple[int, int, int, int],
847
+ atom_layout_mnk: Tuple[int, int, int],
848
+ warmup_iterations: int = 2,
849
+ iterations: int = 100,
850
+ skip_ref_check: bool = False,
851
+ use_cold_l2: bool = False,
852
+ **kwargs,
853
+ ):
854
+ print(f"Running Ampere tensor core GEMM example:")
855
+ print(f"mnkl: {mnkl}")
856
+ print(
857
+ f"A dtype: {ab_dtype}, B dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}"
858
+ )
859
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
860
+ print(f"Atoms layout: {atom_layout_mnk}")
861
+ print(f"Warmup iterations: {warmup_iterations}")
862
+ print(f"Iterations: {iterations}")
863
+ print(f"Skip reference checking: {skip_ref_check}")
864
+ print(f"Use cold L2: {use_cold_l2}")
865
+ M, N, K, L = mnkl
866
+
867
+ # Create and permute tensor A/B/C
868
+ def create_and_permute_tensor(l, mode0, mode1, is_mode0_major, dtype):
869
+ # is_mode0_major: (l, mode1, mode0) -> (mode0, mode1, l)
870
+ # else: (l, mode0, mode1) -> (mode0, mode1, l)
871
+ shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
872
+ permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
873
+ torch_tensor = (
874
+ torch.empty(*shape, dtype=torch.int32)
875
+ .random_(-2, 2)
876
+ .to(dtype=cutlass_torch.dtype(dtype))
877
+ .permute(permute_order)
878
+ .cuda()
879
+ )
880
+ # assume input is 16B aligned
881
+ cute_tensor = (
882
+ from_dlpack(torch_tensor, assumed_align=16)
883
+ .mark_layout_dynamic(leading_dim=(1 if not is_mode0_major else 0))
884
+ .mark_compact_shape_dynamic(
885
+ mode=(1 if not is_mode0_major else 0),
886
+ stride_order=(2, 0, 1) if not is_mode0_major else (2, 1, 0),
887
+ divisibility=(128 // dtype.width),
888
+ )
889
+ )
890
+ return cute_tensor, torch_tensor
891
+
892
+ mA, a_torch = create_and_permute_tensor(L, M, K, a_major == "m", ab_dtype)
893
+ mB, b_torch = create_and_permute_tensor(L, N, K, b_major == "n", ab_dtype)
894
+ mC, c_torch = create_and_permute_tensor(L, M, N, c_major == "m", c_dtype)
895
+
896
+ tensor_op_gemm = TensorOpGemm(
897
+ ab_dtype,
898
+ c_dtype,
899
+ acc_dtype,
900
+ atom_layout_mnk,
901
+ )
902
+
903
+ print("Compiling kernel with cute.compile ...")
904
+ compiled_gemm = cute.compile(tensor_op_gemm, mA, mB, mC)
905
+
906
+ print("Executing GEMM kernel...")
907
+
908
+ if not skip_ref_check:
909
+ ref = torch.einsum(
910
+ "mkl,nkl->mnl",
911
+ a_torch.to(dtype=torch.float32),
912
+ b_torch.to(dtype=torch.float32),
913
+ ).to(cutlass_torch.dtype(c_dtype))
914
+ compiled_gemm(mA, mB, mC)
915
+ print("Verifying results...")
916
+ torch.testing.assert_close(c_torch.cpu(), ref.cpu(), atol=1e-03, rtol=1e-05)
917
+ print("Results verified successfully!")
918
+
919
+ def generate_tensors():
920
+ a_workspace, _ = create_and_permute_tensor(L, M, K, a_major == "m", ab_dtype)
921
+ b_workspace, _ = create_and_permute_tensor(L, N, K, b_major == "n", ab_dtype)
922
+ c_workspace, _ = create_and_permute_tensor(L, M, N, c_major == "m", c_dtype)
923
+ return testing.JitArguments(a_workspace, b_workspace, c_workspace)
924
+
925
+ workspace_count = 1
926
+ if use_cold_l2:
927
+ one_workspace_bytes = (
928
+ a_torch.numel() * a_torch.element_size()
929
+ + b_torch.numel() * b_torch.element_size()
930
+ + c_torch.numel() * c_torch.element_size()
931
+ )
932
+ workspace_count = testing.get_workspace_count(
933
+ one_workspace_bytes, warmup_iterations, iterations
934
+ )
935
+
936
+ avg_time_us = testing.benchmark(
937
+ compiled_gemm,
938
+ workspace_generator=generate_tensors,
939
+ workspace_count=workspace_count,
940
+ warmup_iterations=warmup_iterations,
941
+ iterations=iterations,
942
+ use_cuda_graphs=False,
943
+ )
944
+
945
+ return avg_time_us # Return execution time in microseconds
946
+
947
+ if __name__ == "__main__":
948
+
949
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
950
+ try:
951
+ return tuple(int(x.strip()) for x in s.split(","))
952
+ except ValueError:
953
+ raise argparse.ArgumentTypeError(
954
+ "Invalid format. Expected comma-separated integers."
955
+ )
956
+
957
+ parser = argparse.ArgumentParser(
958
+ description="example of multistage block matmul with CuTe on GPU"
959
+ )
960
+ parser.add_argument(
961
+ "--mnkl", type=parse_comma_separated_ints, default=(112, 136, 40, 1)
962
+ )
963
+ parser.add_argument(
964
+ "--atom_layout_mnk", type=parse_comma_separated_ints, default=(2, 2, 1)
965
+ )
966
+ parser.add_argument(
967
+ "--ab_dtype",
968
+ type=cutlass.dtype,
969
+ choices=[cutlass.Float16],
970
+ default=cutlass.Float16,
971
+ )
972
+ parser.add_argument(
973
+ "--acc_dtype",
974
+ type=cutlass.dtype,
975
+ choices=[cutlass.Float32],
976
+ default=cutlass.Float32,
977
+ )
978
+ parser.add_argument(
979
+ "--c_dtype",
980
+ type=cutlass.dtype,
981
+ choices=[cutlass.Float16],
982
+ default=cutlass.Float16,
983
+ )
984
+ parser.add_argument("--a_major", choices=["k", "m"], default="m")
985
+ parser.add_argument("--b_major", choices=["k", "n"], default="n")
986
+ parser.add_argument("--c_major", choices=["n", "m"], default="n")
987
+ parser.add_argument("--warmup_iterations", default=2, type=int)
988
+ parser.add_argument("--iterations", default=100, type=int)
989
+ parser.add_argument("--skip_ref_check", action="store_true")
990
+ parser.add_argument(
991
+ "--use_cold_l2",
992
+ action="store_true",
993
+ default=False,
994
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
995
+ )
996
+
997
+ args = parser.parse_args()
998
+ run(
999
+ args.a_major,
1000
+ args.b_major,
1001
+ args.c_major,
1002
+ args.ab_dtype,
1003
+ args.c_dtype,
1004
+ args.acc_dtype,
1005
+ args.mnkl,
1006
+ args.atom_layout_mnk,
1007
+ args.warmup_iterations,
1008
+ args.iterations,
1009
+ args.skip_ref_check,
1010
+ args.use_cold_l2,
1011
+ )
1012
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_blockscaled_gemm_persistent.py ADDED
@@ -0,0 +1,2466 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ from typing import Optional, Type, Tuple, Union
31
+
32
+ import cuda.bindings.driver as cuda
33
+ import torch
34
+
35
+ import cutlass
36
+ import cutlass.cute as cute
37
+ from cutlass.cute.nvgpu import cpasync, tcgen05
38
+ import cutlass.torch as cutlass_torch
39
+ import cutlass.utils as utils
40
+ import cutlass.pipeline as pipeline
41
+ import cutlass.utils.blackwell_helpers as sm100_utils
42
+ import cutlass.utils.blockscaled_layout as blockscaled_utils
43
+ from cutlass.cute.runtime import from_dlpack
44
+
45
+ """
46
+ This example provides an experimental implementation of the SM100 batched dense blockscaled GEMM kernel, please note that the APIs and implementation details related to this kernel may change in future releases.
47
+
48
+ A high-performance persistent batched dense blockscaled GEMM example for the NVIDIA Blackwell SM100 architecture
49
+ using CUTE DSL.
50
+ - Matrix A is MxKxL, L is batch dimension, A can be row-major("K") or column-major("M") for MXF8 input type and can only be row-major("K") for MXF4/NVF4 input type
51
+ - Matrix B is NxKxL, L is batch dimension, B can be row-major("N") or column-major("K") for MXF8 input type and can only be row-major("K") for MXF4/NVF4 input type
52
+ - Matrix C is MxNxL, L is batch dimension, C can be row-major("N") or column-major("M")
53
+ - Matrix SFA layout is filled internally according to A shape and BlockScaledBasicChunk, which has M×ceil_div(K, sf_vec_size)×L elements respectively
54
+ - Matrix SFB layout is filled internally according to B shape and BlockScaledBasicChunk, which has N×ceil_div(K, sf_vec_size)×L elements respectively
55
+
56
+ This GEMM kernel supports the following features:
57
+ - Utilizes Tensor Memory Access (TMA) for efficient memory operations
58
+ - Utilizes Blackwell's tcgen05.mma for matrix multiply-accumulate (MMA) operations (including 2cta mma instructions)
59
+ - Implements TMA multicast with cluster to reduce L2 memory traffic
60
+ - Support persistent tile scheduling to better overlap memory load/store with mma between tiles
61
+ - Support warp specialization to avoid explicit pipelining between mainloop load and mma
62
+
63
+ This GEMM works as follows:
64
+ 1. DMA warp: Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.
65
+ 2. MMA warp:
66
+ - Load scale factor A/B from shared memory (SMEM) to tensor memory (TMEM) using tcgen05.cp instruction.
67
+ - Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.
68
+ 3. EPILOGUE warp:
69
+ - Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.
70
+ - Type convert C matrix to output type.
71
+ - Optionally store C matrix from registers (RMEM) to shared memory (SMEM) to global memory (GMEM) with TMA operations,
72
+ or directly store C matrix from registers (RMEM) to global memory (GMEM) without TMA operations.
73
+ - Optionally accept an elementwise lambda function epilogue_op to apply to the output tensor:
74
+ e.g., relu can set epilogue_op = lambda x: cute.where(x > 0, x, cute.full_like(x, 0))
75
+
76
+ SM100 tcgen05.mma.kind.block_scale instructions operate as follows:
77
+ - Read matrix A from SMEM
78
+ - Read matrix B from SMEM
79
+ - Read scalefactor A from TMEM
80
+ - Read scalefactor B from TMEM
81
+ - Write accumulator to TMEM
82
+ The accumulator in TMEM must then be loaded to registers before writing back to GMEM.
83
+
84
+ Input arguments to this example is shown below:
85
+
86
+ .. code-block:: bash
87
+
88
+ python examples/blackwell/dense_blockscaled_gemm_persistent.py \
89
+ --ab_dtype Float4E2M1FN --sf_dtype Float8E8M0FNU --sf_vec_size 16 \
90
+ --c_dtype Float16 \
91
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
92
+ --mnkl 8192,8192,1024,1
93
+
94
+ To collect performance with NCU profiler:
95
+
96
+ .. code-block:: bash
97
+
98
+ ncu python examples/blackwell/dense_blockscaled_gemm_persistent.py \
99
+ --ab_dtype Float4E2M1FN --sf_dtype Float8E8M0FNU --sf_vec_size 16 \
100
+ --c_dtype Float16 \
101
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
102
+ --mnkl 8192,8192,1024,1 \
103
+ --warmup_iterations 1 --iterations 10 --skip_ref_check
104
+
105
+
106
+ Constraints:
107
+ * Supported input data types: mxf8, mxf4, nvf4
108
+ see detailed valid dtype combinations in below Sm100BlockScaledPersistentDenseGemmKernel class documentation
109
+ * A/B tensor must have the same data type, mixed data type is not supported (e.g., mxf8 x mxf4)
110
+ * Mma tiler M must be 128 or 256(use_2cta_instrs)
111
+ * Mma tiler N must be 128 or 256
112
+ * Cluster shape M/N must be positive and power of 2, total cluster size <= 16
113
+ * Cluster shape M must be multiple of 2 if Mma tiler M is 256(use_2cta_instrs)
114
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned,
115
+ i.e, number of elements is a multiple of 16 and 32 for Float8 and Float4, respectively.
116
+ """
117
+
118
+
119
+ class Sm100BlockScaledPersistentDenseGemmKernel:
120
+ """This class implements batched matrix multiplication (C = A x SFA x B x SFB) with support for various data types
121
+ and architectural features specific to Blackwell GPUs with persistent tile scheduling and warp specialization.
122
+
123
+ :param sf_vec_size: Scalefactor vector size.
124
+ :type sf_vec_size: int
125
+ :param mma_tiler_mn: Shape of the Matrix Multiply-Accumulate (MMA) tile (M,N)
126
+ :type mma_tiler_mn: Tuple[int, int]
127
+ :param cluster_shape_mn: Cluster dimensions (M,N) for parallel processing
128
+ :type cluster_shape_mn: Tuple[int, int]
129
+
130
+ :note: In current version, A and B tensor must have the same data type
131
+ - i.e., Float8E4M3FN for A and Float8E5M2 for B is not supported
132
+
133
+ :note: Supported combinations of A/B data types, SF data typs and SF vector size:
134
+ - MXF8: A/B: Float8E5M2/Float8E4M3FN + SF: Float8E8M0FNU + sf_vec_size: 32
135
+ - MXF4: A/B: Float4E2M1FN + SF: Float8E8M0FNU + sf_vec_size: 32
136
+ - NVF4: A/B: Float4E2M1FN + SF: Float8E8M0FNU/Float8E4M3FN + sf_vec_size: 16
137
+
138
+ :note: Supported accumulator data types:
139
+ - Float32
140
+
141
+ :note: Supported C data types:
142
+ - Float32
143
+ - Float16/BFloat16
144
+ - Float8E4M3FN/Float8E5M2
145
+ :note: Constraints:
146
+ - MMA tiler M must be 128 or 256 (use_2cta_instrs)
147
+ - MMA tiler N must be 128/256
148
+ - Cluster shape M must be multiple of 2 if Mma tiler M is 256
149
+ - Cluster shape M/N must be positive and power of 2, total cluster size <= 16
150
+ - Also, Cluster shape M/N must be <= 4 for scale factor multicasts due to limited size of scale factors
151
+
152
+ Example:
153
+ >>> gemm = Sm100BlockScaledPersistentDenseGemmKernel(
154
+ ... sf_vec_size=16,
155
+ ... mma_tiler_mn=(256, 128),
156
+ ... cluster_shape_mn=(2, 1)
157
+ ... )
158
+ >>> gemm(a_tensor, b_tensor, sfa_tensor, sfb_tensor, c_tensor, max_active_clusters, stream)
159
+ """
160
+
161
+ def __init__(
162
+ self,
163
+ sf_vec_size: int,
164
+ mma_tiler_mn: Tuple[int, int],
165
+ cluster_shape_mn: Tuple[int, int],
166
+ ):
167
+ """Initializes the configuration for a Blackwell dense GEMM kernel.
168
+
169
+ This configuration includes several key aspects:
170
+
171
+ 1. MMA Instruction Settings (tcgen05):
172
+ - acc_dtype: Data types for MMA accumulator, always set to Float32
173
+ - sf_vec_size: Scalefactor A/B vector size.
174
+ - mma_tiler_mn: The (M, N) shape of the MMA instruction tiler.
175
+
176
+ 2. Cluster Shape:
177
+ - cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster.
178
+
179
+ :param sf_vec_size: Scalefactor vector size.
180
+ :type sf_vec_size: int
181
+ :param mma_tiler_mn: Tuple (M, N) shape of the MMA instruction.
182
+ :type mma_tiler_mn: Tuple[int, int]
183
+ :param cluster_shape_mn: Tuple (ClusterM, ClusterN) shape of the cluster.
184
+ :type cluster_shape_mn: Tuple[int, int]
185
+ """
186
+
187
+ self.acc_dtype = cutlass.Float32
188
+ self.sf_vec_size = sf_vec_size
189
+ self.use_2cta_instrs = mma_tiler_mn[0] == 256
190
+ self.cluster_shape_mn = cluster_shape_mn
191
+ # K dimension is deferred in _setup_attributes
192
+ self.mma_tiler = (*mma_tiler_mn, 1)
193
+
194
+ self.cta_group = (
195
+ tcgen05.CtaGroup.TWO if self.use_2cta_instrs else tcgen05.CtaGroup.ONE
196
+ )
197
+
198
+ self.occupancy = 1
199
+ # Set specialized warp ids
200
+ self.epilog_warp_id = (
201
+ 0,
202
+ 1,
203
+ 2,
204
+ 3,
205
+ )
206
+ self.mma_warp_id = 4
207
+ self.tma_warp_id = 5
208
+ self.threads_per_cta = 32 * len(
209
+ (self.mma_warp_id, self.tma_warp_id, *self.epilog_warp_id)
210
+ )
211
+ # Set barrier id for cta sync, epilogue sync and tmem ptr sync
212
+ self.cta_sync_bar_id = 0
213
+ self.epilog_sync_bar_id = 1
214
+ self.tmem_ptr_sync_bar_id = 2
215
+ self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
216
+ SM100_TMEM_CAPACITY_COLUMNS = 512
217
+ self.num_tmem_alloc_cols = SM100_TMEM_CAPACITY_COLUMNS
218
+
219
+ def _setup_attributes(self):
220
+ """Set up configurations that are dependent on GEMM inputs
221
+
222
+ This method configures various attributes based on the input tensor properties
223
+ (data types, leading dimensions) and kernel settings:
224
+ - Configuring tiled MMA
225
+ - Computing MMA/cluster/tile shapes
226
+ - Computing cluster layout
227
+ - Computing multicast CTAs for A/B/SFA/SFB
228
+ - Computing epilogue subtile
229
+ - Setting up A/B/SFA/SFB/C stage counts in shared memory
230
+ - Computing A/B/SFA/SFB/C shared memory layout
231
+ - Computing tensor memory allocation columns
232
+ """
233
+ # Compute mma instruction shapes
234
+ mma_inst_bits_k = 256
235
+ # (MMA_Tile_Shape_M, MMA_Tile_Shape_N, MMA_Inst_Shape_K)
236
+ self.mma_inst_shape_mnk = (
237
+ self.mma_tiler[0],
238
+ self.mma_tiler[1],
239
+ mma_inst_bits_k // self.a_dtype.width,
240
+ )
241
+ # (CTA_Tile_Shape_M, Round_Up(MMA_Tile_Shape_N, 128), MMA_Inst_Shape_K)
242
+ self.mma_inst_shape_mnk_sfb = (
243
+ self.mma_inst_shape_mnk[0] // (2 if self.use_2cta_instrs else 1),
244
+ cute.round_up(self.mma_inst_shape_mnk[1], 128),
245
+ self.mma_inst_shape_mnk[2],
246
+ )
247
+
248
+ tiled_mma = sm100_utils.make_blockscaled_trivial_tiled_mma(
249
+ self.a_dtype,
250
+ self.a_major_mode,
251
+ self.b_major_mode,
252
+ self.sf_dtype,
253
+ self.sf_vec_size,
254
+ self.cta_group,
255
+ self.mma_inst_shape_mnk[:2],
256
+ )
257
+
258
+ tiled_mma_sfb = sm100_utils.make_blockscaled_trivial_tiled_mma(
259
+ self.a_dtype,
260
+ self.a_major_mode,
261
+ self.b_major_mode,
262
+ self.sf_dtype,
263
+ self.sf_vec_size,
264
+ cute.nvgpu.tcgen05.CtaGroup.ONE,
265
+ self.mma_inst_shape_mnk_sfb[:2],
266
+ )
267
+
268
+ # Compute mma/cluster/tile shapes
269
+ mma_inst_tile_k = 4
270
+ self.mma_tiler = (
271
+ self.mma_inst_shape_mnk[0],
272
+ self.mma_inst_shape_mnk[1],
273
+ self.mma_inst_shape_mnk[2] * mma_inst_tile_k,
274
+ )
275
+ self.mma_tiler_sfb = (
276
+ self.mma_inst_shape_mnk_sfb[0],
277
+ self.mma_inst_shape_mnk_sfb[1],
278
+ self.mma_inst_shape_mnk_sfb[2] * mma_inst_tile_k,
279
+ )
280
+ self.cta_tile_shape_mnk = (
281
+ self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
282
+ self.mma_tiler[1],
283
+ self.mma_tiler[2],
284
+ )
285
+
286
+ # Compute cluster layout
287
+ self.cluster_layout_vmnk = cute.tiled_divide(
288
+ cute.make_layout((*self.cluster_shape_mn, 1)),
289
+ (tiled_mma.thr_id.shape,),
290
+ )
291
+ self.cluster_layout_sfb_vmnk = cute.tiled_divide(
292
+ cute.make_layout((*self.cluster_shape_mn, 1)),
293
+ (tiled_mma_sfb.thr_id.shape,),
294
+ )
295
+
296
+ # Compute number of multicast CTAs for A/B
297
+ self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
298
+ self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
299
+ self.num_mcast_ctas_sfb = cute.size(self.cluster_layout_sfb_vmnk.shape[1])
300
+ self.is_a_mcast = self.num_mcast_ctas_a > 1
301
+ self.is_b_mcast = self.num_mcast_ctas_b > 1
302
+ self.is_sfb_mcast = self.num_mcast_ctas_sfb > 1
303
+
304
+ # Compute epilogue subtile
305
+ self.epi_tile = sm100_utils.compute_epilogue_tile_shape(
306
+ self.cta_tile_shape_mnk,
307
+ self.use_2cta_instrs,
308
+ self.c_layout,
309
+ self.c_dtype,
310
+ )
311
+
312
+ # Setup A/B/C stage count in shared memory and ACC stage count in tensor memory
313
+ self.num_acc_stage, self.num_ab_stage, self.num_c_stage = self._compute_stages(
314
+ tiled_mma,
315
+ self.mma_tiler,
316
+ self.a_dtype,
317
+ self.a_major_mode,
318
+ self.b_dtype,
319
+ self.b_major_mode,
320
+ self.epi_tile,
321
+ self.c_dtype,
322
+ self.c_layout,
323
+ self.sf_dtype,
324
+ self.sf_vec_size,
325
+ self.smem_capacity,
326
+ self.occupancy,
327
+ )
328
+
329
+ # Compute A/B/SFA/SFB/C shared memory layout
330
+ self.a_smem_layout_staged = sm100_utils.make_smem_layout_a(
331
+ tiled_mma,
332
+ self.mma_tiler,
333
+ self.a_dtype,
334
+ self.num_ab_stage,
335
+ )
336
+ self.b_smem_layout_staged = sm100_utils.make_smem_layout_b(
337
+ tiled_mma,
338
+ self.mma_tiler,
339
+ self.b_dtype,
340
+ self.num_ab_stage,
341
+ )
342
+ self.sfa_smem_layout_staged = blockscaled_utils.make_smem_layout_sfa(
343
+ tiled_mma,
344
+ self.mma_tiler,
345
+ self.sf_vec_size,
346
+ self.num_ab_stage,
347
+ )
348
+ self.sfb_smem_layout_staged = blockscaled_utils.make_smem_layout_sfb(
349
+ tiled_mma,
350
+ self.mma_tiler,
351
+ self.sf_vec_size,
352
+ self.num_ab_stage,
353
+ )
354
+ self.c_smem_layout_staged = sm100_utils.make_smem_layout_epi(
355
+ self.c_dtype,
356
+ self.c_layout,
357
+ self.epi_tile,
358
+ self.num_c_stage,
359
+ )
360
+
361
+ @cute.jit
362
+ def __call__(
363
+ self,
364
+ a_tensor: cute.Tensor,
365
+ b_tensor: cute.Tensor,
366
+ sfa_tensor: cute.Tensor,
367
+ sfb_tensor: cute.Tensor,
368
+ c_tensor: cute.Tensor,
369
+ max_active_clusters: cutlass.Constexpr,
370
+ stream: cuda.CUstream,
371
+ epilogue_op: cutlass.Constexpr = lambda x: x,
372
+ ):
373
+ """Execute the GEMM operation in steps:
374
+ - Setup static attributes before smem/grid/tma computation
375
+ - Setup TMA load/store atoms and tensors
376
+ - Compute grid size with regard to hardware constraints
377
+ - Define shared storage for kernel
378
+ - Launch the kernel synchronously
379
+
380
+ :param a_tensor: Input tensor A
381
+ :type a_tensor: cute.Tensor
382
+ :param b_tensor: Input tensor B
383
+ :type b_tensor: cute.Tensor
384
+ :param sfa_tensor: Scale factor tensor A
385
+ :type sfa_tensor: cute.Tensor
386
+ :param sfb_tensor: Scale factor tensor B
387
+ :type sfb_tensor: cute.Tensor
388
+ :param c_tensor: Output tensor C
389
+ :type c_tensor: cute.Tensor
390
+ :param max_active_clusters: Maximum number of active clusters
391
+ :type max_active_clusters: cutlass.Constexpr
392
+ :param stream: CUDA stream for asynchronous execution
393
+ :type stream: cuda.CUstream
394
+ :param epilogue_op: Optional elementwise lambda function to apply to the output tensor
395
+ :type epilogue_op: cutlass.Constexpr
396
+ :raises TypeError: If input data types are incompatible with the MMA instruction.
397
+ """
398
+ # Setup static attributes before smem/grid/tma computation
399
+ self.a_dtype: Type[cutlass.Numeric] = a_tensor.element_type
400
+ self.b_dtype: Type[cutlass.Numeric] = b_tensor.element_type
401
+ self.sf_dtype: Type[cutlass.Numeric] = sfa_tensor.element_type
402
+ self.c_dtype: Type[cutlass.Numeric] = c_tensor.element_type
403
+ self.a_major_mode = utils.LayoutEnum.from_tensor(a_tensor).mma_major_mode()
404
+ self.b_major_mode = utils.LayoutEnum.from_tensor(b_tensor).mma_major_mode()
405
+ self.c_layout = utils.LayoutEnum.from_tensor(c_tensor)
406
+
407
+ # Check if input data types are compatible with MMA instruction
408
+ if cutlass.const_expr(self.a_dtype != self.b_dtype):
409
+ raise TypeError(f"Type must match: {self.a_dtype} != {self.b_dtype}")
410
+
411
+ # Setup attributes that dependent on gemm inputs
412
+ self._setup_attributes()
413
+
414
+ # Setup sfa/sfb tensor by filling A/B tensor to scale factor atom layout
415
+ # ((Atom_M, Rest_M),(Atom_K, Rest_K),RestL)
416
+ sfa_layout = blockscaled_utils.tile_atom_to_shape_SF(
417
+ a_tensor.shape, self.sf_vec_size
418
+ )
419
+ sfa_tensor = cute.make_tensor(sfa_tensor.iterator, sfa_layout)
420
+
421
+ # ((Atom_N, Rest_N),(Atom_K, Rest_K),RestL)
422
+ sfb_layout = blockscaled_utils.tile_atom_to_shape_SF(
423
+ b_tensor.shape, self.sf_vec_size
424
+ )
425
+ sfb_tensor = cute.make_tensor(sfb_tensor.iterator, sfb_layout)
426
+
427
+ tiled_mma = sm100_utils.make_blockscaled_trivial_tiled_mma(
428
+ self.a_dtype,
429
+ self.a_major_mode,
430
+ self.b_major_mode,
431
+ self.sf_dtype,
432
+ self.sf_vec_size,
433
+ self.cta_group,
434
+ self.mma_inst_shape_mnk[:2],
435
+ )
436
+
437
+ tiled_mma_sfb = sm100_utils.make_blockscaled_trivial_tiled_mma(
438
+ self.a_dtype,
439
+ self.a_major_mode,
440
+ self.b_major_mode,
441
+ self.sf_dtype,
442
+ self.sf_vec_size,
443
+ cute.nvgpu.tcgen05.CtaGroup.ONE,
444
+ self.mma_inst_shape_mnk_sfb[:2],
445
+ )
446
+ atom_thr_size = cute.size(tiled_mma.thr_id.shape)
447
+
448
+ # Setup TMA load for A
449
+ a_op = sm100_utils.cluster_shape_to_tma_atom_A(
450
+ self.cluster_shape_mn, tiled_mma.thr_id
451
+ )
452
+ a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
453
+ tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
454
+ a_op,
455
+ a_tensor,
456
+ a_smem_layout,
457
+ self.mma_tiler,
458
+ tiled_mma,
459
+ self.cluster_layout_vmnk.shape,
460
+ )
461
+
462
+ # Setup TMA load for B
463
+ b_op = sm100_utils.cluster_shape_to_tma_atom_B(
464
+ self.cluster_shape_mn, tiled_mma.thr_id
465
+ )
466
+ b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
467
+ tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
468
+ b_op,
469
+ b_tensor,
470
+ b_smem_layout,
471
+ self.mma_tiler,
472
+ tiled_mma,
473
+ self.cluster_layout_vmnk.shape,
474
+ )
475
+
476
+ # Setup TMA load for SFA
477
+ sfa_op = sm100_utils.cluster_shape_to_tma_atom_A(
478
+ self.cluster_shape_mn, tiled_mma.thr_id
479
+ )
480
+ sfa_smem_layout = cute.slice_(
481
+ self.sfa_smem_layout_staged, (None, None, None, 0)
482
+ )
483
+ tma_atom_sfa, tma_tensor_sfa = cute.nvgpu.make_tiled_tma_atom_A(
484
+ sfa_op,
485
+ sfa_tensor,
486
+ sfa_smem_layout,
487
+ self.mma_tiler,
488
+ tiled_mma,
489
+ self.cluster_layout_vmnk.shape,
490
+ internal_type=cutlass.Int16,
491
+ )
492
+
493
+ # Setup TMA load for SFB
494
+ sfb_op = sm100_utils.cluster_shape_to_tma_atom_SFB(
495
+ self.cluster_shape_mn, tiled_mma.thr_id
496
+ )
497
+ sfb_smem_layout = cute.slice_(
498
+ self.sfb_smem_layout_staged, (None, None, None, 0)
499
+ )
500
+ tma_atom_sfb, tma_tensor_sfb = cute.nvgpu.make_tiled_tma_atom_B(
501
+ sfb_op,
502
+ sfb_tensor,
503
+ sfb_smem_layout,
504
+ self.mma_tiler_sfb,
505
+ tiled_mma_sfb,
506
+ self.cluster_layout_sfb_vmnk.shape,
507
+ internal_type=cutlass.Int16,
508
+ )
509
+
510
+ a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
511
+ b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
512
+ sfa_copy_size = cute.size_in_bytes(self.sf_dtype, sfa_smem_layout)
513
+ sfb_copy_size = cute.size_in_bytes(self.sf_dtype, sfb_smem_layout)
514
+ self.num_tma_load_bytes = (
515
+ a_copy_size + b_copy_size + sfa_copy_size + sfb_copy_size
516
+ ) * atom_thr_size
517
+
518
+ # Setup TMA store for C
519
+ epi_smem_layout = cute.slice_(self.c_smem_layout_staged, (None, None, 0))
520
+ tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
521
+ cpasync.CopyBulkTensorTileS2GOp(),
522
+ c_tensor,
523
+ epi_smem_layout,
524
+ self.epi_tile,
525
+ )
526
+
527
+ # Compute grid size
528
+ self.tile_sched_params, grid = self._compute_grid(
529
+ c_tensor,
530
+ self.cta_tile_shape_mnk,
531
+ self.cluster_shape_mn,
532
+ max_active_clusters,
533
+ )
534
+
535
+ self.buffer_align_bytes = 1024
536
+
537
+ # Define shared storage for kernel
538
+ @cute.struct
539
+ class SharedStorage:
540
+ ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
541
+ ab_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
542
+ acc_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
543
+ acc_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
544
+ tmem_dealloc_mbar_ptr: cutlass.Int64
545
+ tmem_holding_buf: cutlass.Int32
546
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
547
+ sC: cute.struct.Align[
548
+ cute.struct.MemRange[
549
+ self.c_dtype,
550
+ cute.cosize(self.c_smem_layout_staged.outer),
551
+ ],
552
+ self.buffer_align_bytes,
553
+ ]
554
+ # (MMA, MMA_M, MMA_K, STAGE)
555
+ sA: cute.struct.Align[
556
+ cute.struct.MemRange[
557
+ self.a_dtype, cute.cosize(self.a_smem_layout_staged.outer)
558
+ ],
559
+ self.buffer_align_bytes,
560
+ ]
561
+ # (MMA, MMA_N, MMA_K, STAGE)
562
+ sB: cute.struct.Align[
563
+ cute.struct.MemRange[
564
+ self.b_dtype, cute.cosize(self.b_smem_layout_staged.outer)
565
+ ],
566
+ self.buffer_align_bytes,
567
+ ]
568
+ # (MMA, MMA_M, MMA_K, STAGE)
569
+ sSFA: cute.struct.Align[
570
+ cute.struct.MemRange[
571
+ self.sf_dtype, cute.cosize(self.sfa_smem_layout_staged)
572
+ ],
573
+ self.buffer_align_bytes,
574
+ ]
575
+ # (MMA, MMA_N, MMA_K, STAGE)
576
+ sSFB: cute.struct.Align[
577
+ cute.struct.MemRange[
578
+ self.sf_dtype, cute.cosize(self.sfb_smem_layout_staged)
579
+ ],
580
+ self.buffer_align_bytes,
581
+ ]
582
+
583
+ self.shared_storage = SharedStorage
584
+
585
+ # Launch the kernel synchronously
586
+ self.kernel(
587
+ tiled_mma,
588
+ tiled_mma_sfb,
589
+ tma_atom_a,
590
+ tma_tensor_a,
591
+ tma_atom_b,
592
+ tma_tensor_b,
593
+ tma_atom_sfa,
594
+ tma_tensor_sfa,
595
+ tma_atom_sfb,
596
+ tma_tensor_sfb,
597
+ tma_atom_c,
598
+ tma_tensor_c,
599
+ self.cluster_layout_vmnk,
600
+ self.cluster_layout_sfb_vmnk,
601
+ self.a_smem_layout_staged,
602
+ self.b_smem_layout_staged,
603
+ self.sfa_smem_layout_staged,
604
+ self.sfb_smem_layout_staged,
605
+ self.c_smem_layout_staged,
606
+ self.epi_tile,
607
+ self.tile_sched_params,
608
+ epilogue_op,
609
+ ).launch(
610
+ grid=grid,
611
+ block=[self.threads_per_cta, 1, 1],
612
+ cluster=(*self.cluster_shape_mn, 1),
613
+ stream=stream,
614
+ )
615
+ return
616
+
617
+ # GPU device kernel
618
+ @cute.kernel
619
+ def kernel(
620
+ self,
621
+ tiled_mma: cute.TiledMma,
622
+ tiled_mma_sfb: cute.TiledMma,
623
+ tma_atom_a: cute.CopyAtom,
624
+ mA_mkl: cute.Tensor,
625
+ tma_atom_b: cute.CopyAtom,
626
+ mB_nkl: cute.Tensor,
627
+ tma_atom_sfa: cute.CopyAtom,
628
+ mSFA_mkl: cute.Tensor,
629
+ tma_atom_sfb: cute.CopyAtom,
630
+ mSFB_nkl: cute.Tensor,
631
+ tma_atom_c: Optional[cute.CopyAtom],
632
+ mC_mnl: cute.Tensor,
633
+ cluster_layout_vmnk: cute.Layout,
634
+ cluster_layout_sfb_vmnk: cute.Layout,
635
+ a_smem_layout_staged: cute.ComposedLayout,
636
+ b_smem_layout_staged: cute.ComposedLayout,
637
+ sfa_smem_layout_staged: cute.Layout,
638
+ sfb_smem_layout_staged: cute.Layout,
639
+ c_smem_layout_staged: Union[cute.Layout, cute.ComposedLayout, None],
640
+ epi_tile: cute.Tile,
641
+ tile_sched_params: utils.PersistentTileSchedulerParams,
642
+ epilogue_op: cutlass.Constexpr,
643
+ ):
644
+ """
645
+ GPU device kernel performing the Persistent batched GEMM computation.
646
+ """
647
+ warp_idx = cute.arch.warp_idx()
648
+ warp_idx = cute.arch.make_warp_uniform(warp_idx)
649
+
650
+ #
651
+ # Prefetch tma desc
652
+ #
653
+ if warp_idx == self.tma_warp_id:
654
+ cpasync.prefetch_descriptor(tma_atom_a)
655
+ cpasync.prefetch_descriptor(tma_atom_b)
656
+ cpasync.prefetch_descriptor(tma_atom_sfa)
657
+ cpasync.prefetch_descriptor(tma_atom_sfb)
658
+ cpasync.prefetch_descriptor(tma_atom_c)
659
+
660
+ use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
661
+
662
+ #
663
+ # Setup cta/thread coordinates
664
+ #
665
+ # Coords inside cluster
666
+ bidx, bidy, bidz = cute.arch.block_idx()
667
+ mma_tile_coord_v = bidx % cute.size(tiled_mma.thr_id.shape)
668
+ is_leader_cta = mma_tile_coord_v == 0
669
+ cta_rank_in_cluster = cute.arch.make_warp_uniform(
670
+ cute.arch.block_idx_in_cluster()
671
+ )
672
+ block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
673
+ cta_rank_in_cluster
674
+ )
675
+ block_in_cluster_coord_sfb_vmnk = cluster_layout_sfb_vmnk.get_flat_coord(
676
+ cta_rank_in_cluster
677
+ )
678
+ # Coord inside cta
679
+ tidx, _, _ = cute.arch.thread_idx()
680
+
681
+ #
682
+ # Alloc and init: a+b full/empty, accumulator full/empty, tensor memory dealloc barrier
683
+ #
684
+ smem = utils.SmemAllocator()
685
+ storage = smem.allocate(self.shared_storage)
686
+
687
+ tmem_dealloc_mbar_ptr = storage.tmem_dealloc_mbar_ptr
688
+ tmem_holding_buf = storage.tmem_holding_buf
689
+
690
+ # Initialize mainloop ab_pipeline (barrier) and states
691
+ ab_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
692
+ num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
693
+ ab_pipeline_consumer_group = pipeline.CooperativeGroup(
694
+ pipeline.Agent.Thread, num_tma_producer
695
+ )
696
+ ab_pipeline = pipeline.PipelineTmaUmma.create(
697
+ barrier_storage=storage.ab_full_mbar_ptr.data_ptr(),
698
+ num_stages=self.num_ab_stage,
699
+ producer_group=ab_pipeline_producer_group,
700
+ consumer_group=ab_pipeline_consumer_group,
701
+ tx_count=self.num_tma_load_bytes,
702
+ cta_layout_vmnk=cluster_layout_vmnk,
703
+ )
704
+
705
+ # Initialize acc_pipeline (barrier) and states
706
+ acc_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
707
+ num_acc_consumer_threads = len(self.epilog_warp_id) * (
708
+ 2 if use_2cta_instrs else 1
709
+ )
710
+ acc_pipeline_consumer_group = pipeline.CooperativeGroup(
711
+ pipeline.Agent.Thread, num_acc_consumer_threads
712
+ )
713
+ acc_pipeline = pipeline.PipelineUmmaAsync.create(
714
+ barrier_storage=storage.acc_full_mbar_ptr.data_ptr(),
715
+ num_stages=self.num_acc_stage,
716
+ producer_group=acc_pipeline_producer_group,
717
+ consumer_group=acc_pipeline_consumer_group,
718
+ cta_layout_vmnk=cluster_layout_vmnk,
719
+ )
720
+
721
+ # Tensor memory dealloc barrier init
722
+ if use_2cta_instrs:
723
+ if warp_idx == self.tma_warp_id:
724
+ num_tmem_dealloc_threads = 32
725
+ with cute.arch.elect_one():
726
+ cute.arch.mbarrier_init(
727
+ tmem_dealloc_mbar_ptr, num_tmem_dealloc_threads
728
+ )
729
+ cute.arch.mbarrier_init_fence()
730
+
731
+ # Cluster arrive after barrier init
732
+ if cute.size(self.cluster_shape_mn) > 1:
733
+ cute.arch.cluster_arrive_relaxed()
734
+
735
+ #
736
+ # Setup smem tensor A/B/SFA/SFB/C
737
+ #
738
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
739
+ sC = storage.sC.get_tensor(
740
+ c_smem_layout_staged.outer, swizzle=c_smem_layout_staged.inner
741
+ )
742
+ # (MMA, MMA_M, MMA_K, STAGE)
743
+ sA = storage.sA.get_tensor(
744
+ a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
745
+ )
746
+ # (MMA, MMA_N, MMA_K, STAGE)
747
+ sB = storage.sB.get_tensor(
748
+ b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
749
+ )
750
+ # (MMA, MMA_M, MMA_K, STAGE)
751
+ sSFA = storage.sSFA.get_tensor(sfa_smem_layout_staged)
752
+ # (MMA, MMA_N, MMA_K, STAGE)
753
+ sSFB = storage.sSFB.get_tensor(sfb_smem_layout_staged)
754
+
755
+ #
756
+ # Compute multicast mask for A/B/SFA/SFB buffer full
757
+ #
758
+ a_full_mcast_mask = None
759
+ b_full_mcast_mask = None
760
+ sfa_full_mcast_mask = None
761
+ sfb_full_mcast_mask = None
762
+ if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
763
+ a_full_mcast_mask = cpasync.create_tma_multicast_mask(
764
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
765
+ )
766
+ b_full_mcast_mask = cpasync.create_tma_multicast_mask(
767
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
768
+ )
769
+ sfa_full_mcast_mask = cpasync.create_tma_multicast_mask(
770
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
771
+ )
772
+ sfb_full_mcast_mask = cpasync.create_tma_multicast_mask(
773
+ cluster_layout_sfb_vmnk, block_in_cluster_coord_sfb_vmnk, mcast_mode=1
774
+ )
775
+
776
+ #
777
+ # Local_tile partition global tensors
778
+ #
779
+ # (bM, bK, RestM, RestK, RestL)
780
+ gA_mkl = cute.local_tile(
781
+ mA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
782
+ )
783
+ # (bN, bK, RestN, RestK, RestL)
784
+ gB_nkl = cute.local_tile(
785
+ mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
786
+ )
787
+ # (bM, bK, RestM, RestK, RestL)
788
+ gSFA_mkl = cute.local_tile(
789
+ mSFA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
790
+ )
791
+ # (bN, bK, RestN, RestK, RestL)
792
+ gSFB_nkl = cute.local_tile(
793
+ mSFB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
794
+ )
795
+ # (bM, bN, RestM, RestN, RestL)
796
+ gC_mnl = cute.local_tile(
797
+ mC_mnl, cute.slice_(self.mma_tiler, (None, None, 0)), (None, None, None)
798
+ )
799
+ k_tile_cnt = cute.size(gA_mkl, mode=[3])
800
+
801
+ #
802
+ # Partition global tensor for TiledMMA_A/B/C
803
+ #
804
+ thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
805
+ thr_mma_sfb = tiled_mma_sfb.get_slice(mma_tile_coord_v)
806
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
807
+ tCgA = thr_mma.partition_A(gA_mkl)
808
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
809
+ tCgB = thr_mma.partition_B(gB_nkl)
810
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
811
+ tCgSFA = thr_mma.partition_A(gSFA_mkl)
812
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
813
+ tCgSFB = thr_mma_sfb.partition_B(gSFB_nkl)
814
+ # (MMA, MMA_M, MMA_N, RestM, RestN, RestL)
815
+ tCgC = thr_mma.partition_C(gC_mnl)
816
+
817
+ #
818
+ # Partition global/shared tensor for TMA load A/B
819
+ #
820
+ # TMA load A partition_S/D
821
+ a_cta_layout = cute.make_layout(
822
+ cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
823
+ )
824
+ # ((atom_v, rest_v), STAGE)
825
+ # ((atom_v, rest_v), RestM, RestK, RestL)
826
+ tAsA, tAgA = cpasync.tma_partition(
827
+ tma_atom_a,
828
+ block_in_cluster_coord_vmnk[2],
829
+ a_cta_layout,
830
+ cute.group_modes(sA, 0, 3),
831
+ cute.group_modes(tCgA, 0, 3),
832
+ )
833
+ # TMA load B partition_S/D
834
+ b_cta_layout = cute.make_layout(
835
+ cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
836
+ )
837
+ # ((atom_v, rest_v), STAGE)
838
+ # ((atom_v, rest_v), RestN, RestK, RestL)
839
+ tBsB, tBgB = cpasync.tma_partition(
840
+ tma_atom_b,
841
+ block_in_cluster_coord_vmnk[1],
842
+ b_cta_layout,
843
+ cute.group_modes(sB, 0, 3),
844
+ cute.group_modes(tCgB, 0, 3),
845
+ )
846
+
847
+ # TMA load SFA partition_S/D
848
+ sfa_cta_layout = a_cta_layout
849
+ # ((atom_v, rest_v), STAGE)
850
+ # ((atom_v, rest_v), RestM, RestK, RestL)
851
+ tAsSFA, tAgSFA = cute.nvgpu.cpasync.tma_partition(
852
+ tma_atom_sfa,
853
+ block_in_cluster_coord_vmnk[2],
854
+ sfa_cta_layout,
855
+ cute.group_modes(sSFA, 0, 3),
856
+ cute.group_modes(tCgSFA, 0, 3),
857
+ )
858
+ tAsSFA = cute.filter_zeros(tAsSFA)
859
+ tAgSFA = cute.filter_zeros(tAgSFA)
860
+
861
+ # TMA load SFB partition_S/D
862
+ sfb_cta_layout = cute.make_layout(
863
+ cute.slice_(cluster_layout_sfb_vmnk, (0, None, 0, 0)).shape
864
+ )
865
+ # ((atom_v, rest_v), STAGE)
866
+ # ((atom_v, rest_v), RestN, RestK, RestL)
867
+ tBsSFB, tBgSFB = cute.nvgpu.cpasync.tma_partition(
868
+ tma_atom_sfb,
869
+ block_in_cluster_coord_sfb_vmnk[1],
870
+ sfb_cta_layout,
871
+ cute.group_modes(sSFB, 0, 3),
872
+ cute.group_modes(tCgSFB, 0, 3),
873
+ )
874
+ tBsSFB = cute.filter_zeros(tBsSFB)
875
+ tBgSFB = cute.filter_zeros(tBgSFB)
876
+
877
+ #
878
+ # Partition shared/tensor memory tensor for TiledMMA_A/B/C
879
+ #
880
+ # (MMA, MMA_M, MMA_K, STAGE)
881
+ tCrA = tiled_mma.make_fragment_A(sA)
882
+ # (MMA, MMA_N, MMA_K, STAGE)
883
+ tCrB = tiled_mma.make_fragment_B(sB)
884
+ # (MMA, MMA_M, MMA_N)
885
+ acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
886
+ # (MMA, MMA_M, MMA_N, STAGE)
887
+ tCtAcc_fake = tiled_mma.make_fragment_C(
888
+ cute.append(acc_shape, self.num_acc_stage)
889
+ )
890
+
891
+ #
892
+ # Cluster wait before tensor memory alloc
893
+ #
894
+ if cute.size(self.cluster_shape_mn) > 1:
895
+ cute.arch.cluster_wait()
896
+ else:
897
+ cute.arch.barrier(
898
+ barrier_id=self.cta_sync_bar_id, number_of_threads=self.threads_per_cta
899
+ )
900
+
901
+ #
902
+ # Specialized TMA load warp
903
+ #
904
+ if warp_idx == self.tma_warp_id:
905
+ #
906
+ # Persistent tile scheduling loop
907
+ #
908
+ tile_sched = utils.StaticPersistentTileScheduler.create(
909
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
910
+ )
911
+ work_tile = tile_sched.initial_work_tile_info()
912
+
913
+ ab_producer_state = pipeline.make_pipeline_state(
914
+ pipeline.PipelineUserType.Producer, self.num_ab_stage
915
+ )
916
+
917
+ while work_tile.is_valid_tile:
918
+
919
+ # Get tile coord from tile scheduler
920
+ cur_tile_coord = work_tile.tile_idx
921
+ mma_tile_coord_mnl = (
922
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
923
+ cur_tile_coord[1],
924
+ cur_tile_coord[2],
925
+ )
926
+
927
+ #
928
+ # Slice to per mma tile index
929
+ #
930
+ # ((atom_v, rest_v), RestK)
931
+ tAgA_slice = tAgA[
932
+ (None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])
933
+ ]
934
+ # ((atom_v, rest_v), RestK)
935
+ tBgB_slice = tBgB[
936
+ (None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])
937
+ ]
938
+
939
+ # ((atom_v, rest_v), RestK)
940
+ tAgSFA_slice = tAgSFA[
941
+ (None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])
942
+ ]
943
+ # ((atom_v, rest_v), RestK)
944
+ tBgSFB_slice = tBgSFB[
945
+ (None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])
946
+ ]
947
+
948
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt
949
+ ab_producer_state.reset_count()
950
+ peek_ab_empty_status = cutlass.Boolean(1)
951
+ if ab_producer_state.count < k_tile_cnt:
952
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
953
+ ab_producer_state
954
+ )
955
+ #
956
+ # Tma load loop
957
+ #
958
+ for k_tile in cutlass.range(0, k_tile_cnt, 1, unroll=1):
959
+ # Conditionally wait for AB buffer empty
960
+ ab_pipeline.producer_acquire(
961
+ ab_producer_state, peek_ab_empty_status
962
+ )
963
+
964
+ # TMA load A/B/SFA/SFB
965
+ cute.copy(
966
+ tma_atom_a,
967
+ tAgA_slice[(None, ab_producer_state.count)],
968
+ tAsA[(None, ab_producer_state.index)],
969
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
970
+ mcast_mask=a_full_mcast_mask,
971
+ )
972
+ cute.copy(
973
+ tma_atom_b,
974
+ tBgB_slice[(None, ab_producer_state.count)],
975
+ tBsB[(None, ab_producer_state.index)],
976
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
977
+ mcast_mask=b_full_mcast_mask,
978
+ )
979
+ cute.copy(
980
+ tma_atom_sfa,
981
+ tAgSFA_slice[(None, ab_producer_state.count)],
982
+ tAsSFA[(None, ab_producer_state.index)],
983
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
984
+ mcast_mask=sfa_full_mcast_mask,
985
+ )
986
+ cute.copy(
987
+ tma_atom_sfb,
988
+ tBgSFB_slice[(None, ab_producer_state.count)],
989
+ tBsSFB[(None, ab_producer_state.index)],
990
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
991
+ mcast_mask=sfb_full_mcast_mask,
992
+ )
993
+
994
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt + k_tile + 1
995
+ ab_producer_state.advance()
996
+ peek_ab_empty_status = cutlass.Boolean(1)
997
+ if ab_producer_state.count < k_tile_cnt:
998
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
999
+ ab_producer_state
1000
+ )
1001
+
1002
+ #
1003
+ # Advance to next tile
1004
+ #
1005
+ tile_sched.advance_to_next_work()
1006
+ work_tile = tile_sched.get_current_work()
1007
+
1008
+ #
1009
+ # Wait A/B buffer empty
1010
+ #
1011
+ ab_pipeline.producer_tail(ab_producer_state)
1012
+
1013
+ #
1014
+ # Specialized MMA warp
1015
+ #
1016
+ if warp_idx == self.mma_warp_id:
1017
+ #
1018
+ # Bar sync for retrieve tensor memory ptr from shared mem
1019
+ #
1020
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
1021
+ cute.arch.barrier(
1022
+ barrier_id=self.tmem_ptr_sync_bar_id,
1023
+ number_of_threads=tmem_ptr_read_threads,
1024
+ )
1025
+
1026
+ #
1027
+ # Retrieving tensor memory ptr and make accumulator/SFA/SFB tensor
1028
+ #
1029
+ # Make accumulator tmem tensor
1030
+ acc_tmem_ptr = cute.arch.retrieve_tmem_ptr(
1031
+ self.acc_dtype,
1032
+ alignment=16,
1033
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
1034
+ )
1035
+ # (MMA, MMA_M, MMA_N, STAGE)
1036
+ tCtAcc_base = cute.make_tensor(acc_tmem_ptr, tCtAcc_fake.layout)
1037
+
1038
+ # Make SFA tmem tensor
1039
+ sfa_tmem_ptr = cute.recast_ptr(
1040
+ acc_tmem_ptr + tcgen05.find_tmem_tensor_col_offset(tCtAcc_base),
1041
+ dtype=self.sf_dtype,
1042
+ )
1043
+ # (MMA, MMA_M, MMA_K)
1044
+ tCtSFA_layout = blockscaled_utils.make_tmem_layout_sfa(
1045
+ tiled_mma,
1046
+ self.mma_tiler,
1047
+ self.sf_vec_size,
1048
+ cute.slice_(sfa_smem_layout_staged, (None, None, None, 0)),
1049
+ )
1050
+ tCtSFA = cute.make_tensor(sfa_tmem_ptr, tCtSFA_layout)
1051
+
1052
+ # Make SFB tmem tensor
1053
+ sfb_tmem_ptr = cute.recast_ptr(
1054
+ acc_tmem_ptr
1055
+ + tcgen05.find_tmem_tensor_col_offset(tCtAcc_base)
1056
+ + tcgen05.find_tmem_tensor_col_offset(tCtSFA),
1057
+ dtype=self.sf_dtype,
1058
+ )
1059
+ # (MMA, MMA_N, MMA_K)
1060
+ tCtSFB_layout = blockscaled_utils.make_tmem_layout_sfb(
1061
+ tiled_mma,
1062
+ self.mma_tiler,
1063
+ self.sf_vec_size,
1064
+ cute.slice_(sfb_smem_layout_staged, (None, None, None, 0)),
1065
+ )
1066
+ tCtSFB = cute.make_tensor(sfb_tmem_ptr, tCtSFB_layout)
1067
+ #
1068
+ # Partition for S2T copy of SFA/SFB
1069
+ #
1070
+ tiled_copy_s2t_sfa, tCsSFA_compact_s2t, tCtSFA_compact_s2t = (
1071
+ self.mainloop_s2t_copy_and_partition(sSFA, tCtSFA)
1072
+ )
1073
+ tiled_copy_s2t_sfb, tCsSFB_compact_s2t, tCtSFB_compact_s2t = (
1074
+ self.mainloop_s2t_copy_and_partition(sSFB, tCtSFB)
1075
+ )
1076
+
1077
+ #
1078
+ # Persistent tile scheduling loop
1079
+ #
1080
+ tile_sched = utils.StaticPersistentTileScheduler.create(
1081
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
1082
+ )
1083
+ work_tile = tile_sched.initial_work_tile_info()
1084
+
1085
+ ab_consumer_state = pipeline.make_pipeline_state(
1086
+ pipeline.PipelineUserType.Consumer, self.num_ab_stage
1087
+ )
1088
+ acc_producer_state = pipeline.make_pipeline_state(
1089
+ pipeline.PipelineUserType.Producer, self.num_acc_stage
1090
+ )
1091
+
1092
+ while work_tile.is_valid_tile:
1093
+ # Get tile coord from tile scheduler
1094
+ cur_tile_coord = work_tile.tile_idx
1095
+ mma_tile_coord_mnl = (
1096
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
1097
+ cur_tile_coord[1],
1098
+ cur_tile_coord[2],
1099
+ )
1100
+
1101
+ # Set tensor memory buffer for current tile
1102
+ # (MMA, MMA_M, MMA_N)
1103
+ tCtAcc = tCtAcc_base[(None, None, None, acc_producer_state.index)]
1104
+
1105
+ # Peek (try_wait) AB buffer full for k_tile = 0
1106
+ ab_consumer_state.reset_count()
1107
+ peek_ab_full_status = cutlass.Boolean(1)
1108
+ if ab_consumer_state.count < k_tile_cnt and is_leader_cta:
1109
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(
1110
+ ab_consumer_state
1111
+ )
1112
+
1113
+ #
1114
+ # Wait for accumulator buffer empty
1115
+ #
1116
+ if is_leader_cta:
1117
+ acc_pipeline.producer_acquire(acc_producer_state)
1118
+
1119
+ #
1120
+ # Reset the ACCUMULATE field for each tile
1121
+ #
1122
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, False)
1123
+
1124
+ #
1125
+ # Mma mainloop
1126
+ #
1127
+ for k_tile in range(k_tile_cnt):
1128
+ if is_leader_cta:
1129
+ # Conditionally wait for AB buffer full
1130
+ ab_pipeline.consumer_wait(
1131
+ ab_consumer_state, peek_ab_full_status
1132
+ )
1133
+
1134
+ # Copy SFA/SFB from smem to tmem
1135
+ s2t_stage_coord = (
1136
+ None,
1137
+ None,
1138
+ None,
1139
+ None,
1140
+ ab_consumer_state.index,
1141
+ )
1142
+ tCsSFA_compact_s2t_staged = tCsSFA_compact_s2t[s2t_stage_coord]
1143
+ tCsSFB_compact_s2t_staged = tCsSFB_compact_s2t[s2t_stage_coord]
1144
+ cute.copy(
1145
+ tiled_copy_s2t_sfa,
1146
+ tCsSFA_compact_s2t_staged,
1147
+ tCtSFA_compact_s2t,
1148
+ )
1149
+ cute.copy(
1150
+ tiled_copy_s2t_sfb,
1151
+ tCsSFB_compact_s2t_staged,
1152
+ tCtSFB_compact_s2t,
1153
+ )
1154
+
1155
+ # tCtAcc += tCrA * tCrSFA * tCrB * tCrSFB
1156
+ num_kblocks = cute.size(tCrA, mode=[2])
1157
+ for kblock_idx in cutlass.range(num_kblocks, unroll_full=True):
1158
+ kblock_coord = (
1159
+ None,
1160
+ None,
1161
+ kblock_idx,
1162
+ ab_consumer_state.index,
1163
+ )
1164
+
1165
+ # Set SFA/SFB tensor to tiled_mma
1166
+ sf_kblock_coord = (None, None, kblock_idx)
1167
+ tiled_mma.set(
1168
+ tcgen05.Field.SFA,
1169
+ tCtSFA[sf_kblock_coord].iterator,
1170
+ )
1171
+ tiled_mma.set(
1172
+ tcgen05.Field.SFB,
1173
+ tCtSFB[sf_kblock_coord].iterator,
1174
+ )
1175
+
1176
+ cute.gemm(
1177
+ tiled_mma,
1178
+ tCtAcc,
1179
+ tCrA[kblock_coord],
1180
+ tCrB[kblock_coord],
1181
+ tCtAcc,
1182
+ )
1183
+
1184
+ # Enable accumulate on tCtAcc after first kblock
1185
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, True)
1186
+
1187
+ # Async arrive AB buffer empty
1188
+ ab_pipeline.consumer_release(ab_consumer_state)
1189
+
1190
+ # Peek (try_wait) AB buffer full for k_tile = k_tile + 1
1191
+ ab_consumer_state.advance()
1192
+ peek_ab_full_status = cutlass.Boolean(1)
1193
+ if ab_consumer_state.count < k_tile_cnt:
1194
+ if is_leader_cta:
1195
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(
1196
+ ab_consumer_state
1197
+ )
1198
+
1199
+ #
1200
+ # Async arrive accumulator buffer full
1201
+ #
1202
+ if is_leader_cta:
1203
+ acc_pipeline.producer_commit(acc_producer_state)
1204
+ acc_producer_state.advance()
1205
+
1206
+ #
1207
+ # Advance to next tile
1208
+ #
1209
+ tile_sched.advance_to_next_work()
1210
+ work_tile = tile_sched.get_current_work()
1211
+
1212
+ #
1213
+ # Wait for accumulator buffer empty
1214
+ #
1215
+ acc_pipeline.producer_tail(acc_producer_state)
1216
+ #
1217
+ # Specialized epilogue warps
1218
+ #
1219
+ if warp_idx < self.mma_warp_id:
1220
+ #
1221
+ # Alloc tensor memory buffer
1222
+ #
1223
+ if warp_idx == self.epilog_warp_id[0]:
1224
+ cute.arch.alloc_tmem(
1225
+ self.num_tmem_alloc_cols,
1226
+ tmem_holding_buf,
1227
+ is_two_cta=use_2cta_instrs,
1228
+ )
1229
+
1230
+ #
1231
+ # Bar sync for retrieve tensor memory ptr from shared memory
1232
+ #
1233
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
1234
+ cute.arch.barrier(
1235
+ barrier_id=self.tmem_ptr_sync_bar_id,
1236
+ number_of_threads=tmem_ptr_read_threads,
1237
+ )
1238
+
1239
+ #
1240
+ # Retrieving tensor memory ptr and make accumulator tensor
1241
+ #
1242
+ acc_tmem_ptr = cute.arch.retrieve_tmem_ptr(
1243
+ self.acc_dtype,
1244
+ alignment=16,
1245
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
1246
+ )
1247
+ # (MMA, MMA_M, MMA_N, STAGE)
1248
+ tCtAcc_base = cute.make_tensor(acc_tmem_ptr, tCtAcc_fake.layout)
1249
+
1250
+ #
1251
+ # Partition for epilogue
1252
+ #
1253
+ epi_tidx = tidx
1254
+ tiled_copy_t2r, tTR_tAcc_base, tTR_rAcc = (
1255
+ self.epilog_tmem_copy_and_partition(
1256
+ epi_tidx, tCtAcc_base, tCgC, epi_tile, use_2cta_instrs
1257
+ )
1258
+ )
1259
+
1260
+ tTR_rC = cute.make_fragment(tTR_rAcc.shape, self.c_dtype)
1261
+ tiled_copy_r2s, tRS_rC, tRS_sC = self.epilog_smem_copy_and_partition(
1262
+ tiled_copy_t2r, tTR_rC, epi_tidx, sC
1263
+ )
1264
+ tma_atom_c, bSG_sC, bSG_gC_partitioned = (
1265
+ self.epilog_gmem_copy_and_partition(
1266
+ epi_tidx, tma_atom_c, tCgC, epi_tile, sC
1267
+ )
1268
+ )
1269
+
1270
+ #
1271
+ # Persistent tile scheduling loop
1272
+ #
1273
+ tile_sched = utils.StaticPersistentTileScheduler.create(
1274
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
1275
+ )
1276
+ work_tile = tile_sched.initial_work_tile_info()
1277
+
1278
+ acc_consumer_state = pipeline.make_pipeline_state(
1279
+ pipeline.PipelineUserType.Consumer, self.num_acc_stage
1280
+ )
1281
+
1282
+ # Threads/warps participating in tma store pipeline
1283
+ c_producer_group = pipeline.CooperativeGroup(
1284
+ pipeline.Agent.Thread,
1285
+ 32 * len(self.epilog_warp_id),
1286
+ 32 * len(self.epilog_warp_id),
1287
+ )
1288
+ c_pipeline = pipeline.PipelineTmaStore.create(
1289
+ num_stages=self.num_c_stage,
1290
+ producer_group=c_producer_group,
1291
+ )
1292
+
1293
+ while work_tile.is_valid_tile:
1294
+
1295
+ # Get tile coord from tile scheduler
1296
+ cur_tile_coord = work_tile.tile_idx
1297
+ mma_tile_coord_mnl = (
1298
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
1299
+ cur_tile_coord[1],
1300
+ cur_tile_coord[2],
1301
+ )
1302
+
1303
+ #
1304
+ # Slice to per mma tile index
1305
+ #
1306
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1307
+ bSG_gC = bSG_gC_partitioned[
1308
+ (
1309
+ None,
1310
+ None,
1311
+ None,
1312
+ *mma_tile_coord_mnl,
1313
+ )
1314
+ ]
1315
+
1316
+ # Set tensor memory buffer for current tile
1317
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M)
1318
+ tTR_tAcc = tTR_tAcc_base[
1319
+ (None, None, None, None, None, acc_consumer_state.index)
1320
+ ]
1321
+
1322
+ #
1323
+ # Wait for accumulator buffer full
1324
+ #
1325
+ acc_pipeline.consumer_wait(acc_consumer_state)
1326
+
1327
+ tTR_tAcc = cute.group_modes(tTR_tAcc, 3, cute.rank(tTR_tAcc))
1328
+ bSG_gC = cute.group_modes(bSG_gC, 1, cute.rank(bSG_gC))
1329
+
1330
+ #
1331
+ # Store accumulator to global memory in subtiles
1332
+ #
1333
+ subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3])
1334
+ num_prev_subtiles = tile_sched.num_tiles_executed * subtile_cnt
1335
+ for subtile_idx in cutlass.range(subtile_cnt):
1336
+ #
1337
+ # Load accumulator from tensor memory buffer to register
1338
+ #
1339
+ tTR_tAcc_mn = tTR_tAcc[(None, None, None, subtile_idx)]
1340
+ cute.copy(tiled_copy_t2r, tTR_tAcc_mn, tTR_rAcc)
1341
+
1342
+ #
1343
+ # Convert to C type
1344
+ #
1345
+ acc_vec = tiled_copy_r2s.retile(tTR_rAcc).load()
1346
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
1347
+ tRS_rC.store(acc_vec)
1348
+
1349
+ #
1350
+ # Store C to shared memory
1351
+ #
1352
+ c_buffer = (num_prev_subtiles + subtile_idx) % self.num_c_stage
1353
+ cute.copy(
1354
+ tiled_copy_r2s,
1355
+ tRS_rC,
1356
+ tRS_sC[(None, None, None, c_buffer)],
1357
+ )
1358
+ # Fence and barrier to make sure shared memory store is visible to TMA store
1359
+ cute.arch.fence_proxy(
1360
+ cute.arch.ProxyKind.async_shared,
1361
+ space=cute.arch.SharedSpace.shared_cta,
1362
+ )
1363
+ epilog_threads = 32 * len(self.epilog_warp_id)
1364
+ cute.arch.barrier(
1365
+ barrier_id=self.epilog_sync_bar_id,
1366
+ number_of_threads=epilog_threads,
1367
+ )
1368
+
1369
+ #
1370
+ # TMA store C to global memory
1371
+ #
1372
+ if warp_idx == self.epilog_warp_id[0]:
1373
+ cute.copy(
1374
+ tma_atom_c,
1375
+ bSG_sC[(None, c_buffer)],
1376
+ bSG_gC[(None, subtile_idx)],
1377
+ )
1378
+ # Fence and barrier to make sure shared memory store is visible to TMA store
1379
+ c_pipeline.producer_commit()
1380
+ c_pipeline.producer_acquire()
1381
+ cute.arch.barrier(
1382
+ barrier_id=self.epilog_sync_bar_id,
1383
+ number_of_threads=epilog_threads,
1384
+ )
1385
+
1386
+ #
1387
+ # Async arrive accumulator buffer empty
1388
+ #
1389
+ with cute.arch.elect_one():
1390
+ acc_pipeline.consumer_release(acc_consumer_state)
1391
+ acc_consumer_state.advance()
1392
+
1393
+ #
1394
+ # Advance to next tile
1395
+ #
1396
+ tile_sched.advance_to_next_work()
1397
+ work_tile = tile_sched.get_current_work()
1398
+
1399
+ #
1400
+ # Dealloc the tensor memory buffer
1401
+ #
1402
+ if warp_idx == self.epilog_warp_id[0]:
1403
+ cute.arch.relinquish_tmem_alloc_permit(is_two_cta=use_2cta_instrs)
1404
+ epilog_threads = 32 * len(self.epilog_warp_id)
1405
+ cute.arch.barrier(
1406
+ barrier_id=self.epilog_sync_bar_id, number_of_threads=epilog_threads
1407
+ )
1408
+ if warp_idx == self.epilog_warp_id[0]:
1409
+ if use_2cta_instrs:
1410
+ cute.arch.mbarrier_arrive(
1411
+ tmem_dealloc_mbar_ptr, cta_rank_in_cluster ^ 1
1412
+ )
1413
+ cute.arch.mbarrier_wait(tmem_dealloc_mbar_ptr, 0)
1414
+ cute.arch.dealloc_tmem(
1415
+ acc_tmem_ptr, self.num_tmem_alloc_cols, is_two_cta=use_2cta_instrs
1416
+ )
1417
+ #
1418
+ # Wait for C store complete
1419
+ #
1420
+ c_pipeline.producer_tail()
1421
+
1422
+ def mainloop_s2t_copy_and_partition(
1423
+ self,
1424
+ sSF: cute.Tensor,
1425
+ tSF: cute.Tensor,
1426
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1427
+ """
1428
+ Make tiledCopy for smem to tmem load for scale factor tensor, then use it to partition smem memory (source) and tensor memory (destination).
1429
+
1430
+ :param sSF: The scale factor tensor in smem
1431
+ :type sSF: cute.Tensor
1432
+ :param tSF: The scale factor tensor in tmem
1433
+ :type tSF: cute.Tensor
1434
+
1435
+ :return: A tuple containing (tiled_copy_s2t, tCsSF_compact_s2t, tCtSF_compact_s2t) where:
1436
+ - tiled_copy_s2t: The tiled copy operation for smem to tmem load for scale factor tensor(s2t)
1437
+ - tCsSF_compact_s2t: The partitioned scale factor tensor in smem
1438
+ - tSF_compact_s2t: The partitioned scale factor tensor in tmem
1439
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1440
+ """
1441
+ # (MMA, MMA_MN, MMA_K, STAGE)
1442
+ tCsSF_compact = cute.filter_zeros(sSF)
1443
+ # (MMA, MMA_MN, MMA_K)
1444
+ tCtSF_compact = cute.filter_zeros(tSF)
1445
+
1446
+ # Make S2T CopyAtom and tiledCopy
1447
+ copy_atom_s2t = cute.make_copy_atom(
1448
+ tcgen05.Cp4x32x128bOp(self.cta_group),
1449
+ self.sf_dtype,
1450
+ )
1451
+ tiled_copy_s2t = tcgen05.make_s2t_copy(copy_atom_s2t, tCtSF_compact)
1452
+ thr_copy_s2t = tiled_copy_s2t.get_slice(0)
1453
+
1454
+ # ((ATOM_V, REST_V), Rest_Tiler, MMA_MN, MMA_K, STAGE)
1455
+ tCsSF_compact_s2t_ = thr_copy_s2t.partition_S(tCsSF_compact)
1456
+ # ((ATOM_V, REST_V), Rest_Tiler, MMA_MN, MMA_K, STAGE)
1457
+ tCsSF_compact_s2t = tcgen05.get_s2t_smem_desc_tensor(
1458
+ tiled_copy_s2t, tCsSF_compact_s2t_
1459
+ )
1460
+ # ((ATOM_V, REST_V), Rest_Tiler, MMA_MN, MMA_K)
1461
+ tCtSF_compact_s2t = thr_copy_s2t.partition_D(tCtSF_compact)
1462
+
1463
+ return tiled_copy_s2t, tCsSF_compact_s2t, tCtSF_compact_s2t
1464
+
1465
+ def epilog_tmem_copy_and_partition(
1466
+ self,
1467
+ tidx: cutlass.Int32,
1468
+ tAcc: cute.Tensor,
1469
+ gC_mnl: cute.Tensor,
1470
+ epi_tile: cute.Tile,
1471
+ use_2cta_instrs: Union[cutlass.Boolean, bool],
1472
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1473
+ """
1474
+ Make tiledCopy for tensor memory load, then use it to partition tensor memory (source) and register array (destination).
1475
+
1476
+ :param tidx: The thread index in epilogue warp groups
1477
+ :type tidx: cutlass.Int32
1478
+ :param tAcc: The accumulator tensor to be copied and partitioned
1479
+ :type tAcc: cute.Tensor
1480
+ :param gC_mnl: The global tensor C
1481
+ :type gC_mnl: cute.Tensor
1482
+ :param epi_tile: The epilogue tiler
1483
+ :type epi_tile: cute.Tile
1484
+ :param use_2cta_instrs: Whether use_2cta_instrs is enabled
1485
+ :type use_2cta_instrs: bool
1486
+
1487
+ :return: A tuple containing (tiled_copy_t2r, tTR_tAcc, tTR_rAcc) where:
1488
+ - tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1489
+ - tTR_tAcc: The partitioned accumulator tensor
1490
+ - tTR_rAcc: The accumulated tensor in register used to hold t2r results
1491
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1492
+ """
1493
+ # Make tiledCopy for tensor memory load
1494
+ copy_atom_t2r = sm100_utils.get_tmem_load_op(
1495
+ self.cta_tile_shape_mnk,
1496
+ self.c_layout,
1497
+ self.c_dtype,
1498
+ self.acc_dtype,
1499
+ epi_tile,
1500
+ use_2cta_instrs,
1501
+ )
1502
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, STAGE)
1503
+ tAcc_epi = cute.flat_divide(
1504
+ tAcc[((None, None), 0, 0, None)],
1505
+ epi_tile,
1506
+ )
1507
+ # (EPI_TILE_M, EPI_TILE_N)
1508
+ tiled_copy_t2r = tcgen05.make_tmem_copy(
1509
+ copy_atom_t2r, tAcc_epi[(None, None, 0, 0, 0)]
1510
+ )
1511
+
1512
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1513
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M, STAGE)
1514
+ tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
1515
+
1516
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1517
+ gC_mnl_epi = cute.flat_divide(
1518
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1519
+ )
1520
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1521
+ tTR_gC = thr_copy_t2r.partition_D(gC_mnl_epi)
1522
+ # (T2R, T2R_M, T2R_N)
1523
+ tTR_rAcc = cute.make_fragment(
1524
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.acc_dtype
1525
+ )
1526
+ return tiled_copy_t2r, tTR_tAcc, tTR_rAcc
1527
+
1528
+ def epilog_smem_copy_and_partition(
1529
+ self,
1530
+ tiled_copy_t2r: cute.TiledCopy,
1531
+ tTR_rC: cute.Tensor,
1532
+ tidx: cutlass.Int32,
1533
+ sC: cute.Tensor,
1534
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1535
+ """
1536
+ Make tiledCopy for shared memory store, then use it to partition register array (source) and shared memory (destination).
1537
+
1538
+ :param tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1539
+ :type tiled_copy_t2r: cute.TiledCopy
1540
+ :param tTR_rC: The partitioned accumulator tensor
1541
+ :type tTR_rC: cute.Tensor
1542
+ :param tidx: The thread index in epilogue warp groups
1543
+ :type tidx: cutlass.Int32
1544
+ :param sC: The shared memory tensor to be copied and partitioned
1545
+ :type sC: cute.Tensor
1546
+ :type sepi: cute.Tensor
1547
+
1548
+ :return: A tuple containing (tiled_copy_r2s, tRS_rC, tRS_sC) where:
1549
+ - tiled_copy_r2s: The tiled copy operation for register to smem copy(r2s)
1550
+ - tRS_rC: The partitioned tensor C (register source)
1551
+ - tRS_sC: The partitioned tensor C (smem destination)
1552
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1553
+ """
1554
+ copy_atom_r2s = sm100_utils.get_smem_store_op(
1555
+ self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
1556
+ )
1557
+ tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
1558
+ # (R2S, R2S_M, R2S_N, PIPE_D)
1559
+ thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
1560
+ tRS_sC = thr_copy_r2s.partition_D(sC)
1561
+ # (R2S, R2S_M, R2S_N)
1562
+ tRS_rC = tiled_copy_r2s.retile(tTR_rC)
1563
+ return tiled_copy_r2s, tRS_rC, tRS_sC
1564
+
1565
+ def epilog_gmem_copy_and_partition(
1566
+ self,
1567
+ tidx: cutlass.Int32,
1568
+ atom: Union[cute.CopyAtom, cute.TiledCopy],
1569
+ gC_mnl: cute.Tensor,
1570
+ epi_tile: cute.Tile,
1571
+ sC: cute.Tensor,
1572
+ ) -> Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]:
1573
+ """Make tiledCopy for global memory store, then use it to:
1574
+ partition shared memory (source) and global memory (destination) for TMA store version.
1575
+
1576
+ :param tidx: The thread index in epilogue warp groups
1577
+ :type tidx: cutlass.Int32
1578
+ :param atom: The copy_atom_c to be used for TMA store version, or tiled_copy_t2r for none TMA store version
1579
+ :type atom: cute.CopyAtom or cute.TiledCopy
1580
+ :param gC_mnl: The global tensor C
1581
+ :type gC_mnl: cute.Tensor
1582
+ :param epi_tile: The epilogue tiler
1583
+ :type epi_tile: cute.Tile
1584
+ :param sC: The shared memory tensor to be copied and partitioned
1585
+ :type sC: cute.Tensor
1586
+
1587
+ :return: A tuple containing (tma_atom_c, bSG_sC, bSG_gC) where:
1588
+ - tma_atom_c: The TMA copy atom
1589
+ - bSG_sC: The partitioned shared memory tensor C
1590
+ - bSG_gC: The partitioned global tensor C
1591
+ :rtype: Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]
1592
+ """
1593
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1594
+ gC_epi = cute.flat_divide(
1595
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1596
+ )
1597
+
1598
+ tma_atom_c = atom
1599
+ sC_for_tma_partition = cute.group_modes(sC, 0, 2)
1600
+ gC_for_tma_partition = cute.group_modes(gC_epi, 0, 2)
1601
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1602
+ # ((ATOM_V, REST_V), EPI_M, EPI_N, RestM, RestN, RestL)
1603
+ bSG_sC, bSG_gC = cpasync.tma_partition(
1604
+ tma_atom_c,
1605
+ 0,
1606
+ cute.make_layout(1),
1607
+ sC_for_tma_partition,
1608
+ gC_for_tma_partition,
1609
+ )
1610
+ return tma_atom_c, bSG_sC, bSG_gC
1611
+
1612
+ @staticmethod
1613
+ def _compute_stages(
1614
+ tiled_mma: cute.TiledMma,
1615
+ mma_tiler_mnk: Tuple[int, int, int],
1616
+ a_dtype: Type[cutlass.Numeric],
1617
+ a_major_mode: tcgen05.OperandMajorMode,
1618
+ b_dtype: Type[cutlass.Numeric],
1619
+ b_major_mode: tcgen05.OperandMajorMode,
1620
+ epi_tile: cute.Tile,
1621
+ c_dtype: Type[cutlass.Numeric],
1622
+ c_layout: utils.LayoutEnum,
1623
+ sf_dtype: Type[cutlass.Numeric],
1624
+ sf_vec_size: int,
1625
+ smem_capacity: int,
1626
+ occupancy: int,
1627
+ ) -> Tuple[int, int, int]:
1628
+ """Computes the number of stages for A/B/C operands based on heuristics.
1629
+
1630
+ :param tiled_mma: The tiled MMA object defining the core computation.
1631
+ :type tiled_mma: cute.TiledMma
1632
+ :param mma_tiler_mnk: The shape (M, N, K) of the MMA tiler.
1633
+ :type mma_tiler_mnk: tuple[int, int, int]
1634
+ :param a_dtype: Data type of operand A.
1635
+ :type a_dtype: type[cutlass.Numeric]
1636
+ :param a_major_mode: Major mode of operand A.
1637
+ :type a_major_mode: tcgen05.OperandMajorMode
1638
+ :param b_dtype: Data type of operand B.
1639
+ :type b_dtype: type[cutlass.Numeric]
1640
+ :param b_major_mode: Major mode of operand B.
1641
+ :type b_major_mode: tcgen05.OperandMajorMode
1642
+ :param epi_tile: The epilogue tile shape.
1643
+ :type epi_tile: cute.Tile
1644
+ :param c_dtype: Data type of operand C (output).
1645
+ :type c_dtype: type[cutlass.Numeric]
1646
+ :param c_layout: Layout enum of operand C.
1647
+ :type c_layout: utils.LayoutEnum
1648
+ :param sf_dtype: Data type of Scale factor.
1649
+ :type sf_dtype: type[cutlass.Numeric]
1650
+ :param sf_vec_size: Scale factor vector size.
1651
+ :type sf_vec_size: int
1652
+ :param smem_capacity: Total available shared memory capacity in bytes.
1653
+ :type smem_capacity: int
1654
+ :param occupancy: Target number of CTAs per SM (occupancy).
1655
+ :type occupancy: int
1656
+
1657
+ :return: A tuple containing the computed number of stages for:
1658
+ (ACC stages, A/B operand stages, C stages)
1659
+ :rtype: tuple[int, int, int]
1660
+ """
1661
+ # ACC stages
1662
+ num_acc_stage = 1 if mma_tiler_mnk[1] == 256 else 2
1663
+
1664
+ # Default C stages
1665
+ num_c_stage = 2
1666
+
1667
+ # Calculate smem layout and size for one stage of A, B, SFA, SFB and C
1668
+ a_smem_layout_stage_one = sm100_utils.make_smem_layout_a(
1669
+ tiled_mma,
1670
+ mma_tiler_mnk,
1671
+ a_dtype,
1672
+ 1, # a tmp 1 stage is provided
1673
+ )
1674
+ b_smem_layout_staged_one = sm100_utils.make_smem_layout_b(
1675
+ tiled_mma,
1676
+ mma_tiler_mnk,
1677
+ b_dtype,
1678
+ 1, # a tmp 1 stage is provided
1679
+ )
1680
+ sfa_smem_layout_staged_one = blockscaled_utils.make_smem_layout_sfa(
1681
+ tiled_mma,
1682
+ mma_tiler_mnk,
1683
+ sf_vec_size,
1684
+ 1, # a tmp 1 stage is provided
1685
+ )
1686
+ sfb_smem_layout_staged_one = blockscaled_utils.make_smem_layout_sfb(
1687
+ tiled_mma,
1688
+ mma_tiler_mnk,
1689
+ sf_vec_size,
1690
+ 1, # a tmp 1 stage is provided
1691
+ )
1692
+
1693
+ c_smem_layout_staged_one = sm100_utils.make_smem_layout_epi(
1694
+ c_dtype,
1695
+ c_layout,
1696
+ epi_tile,
1697
+ 1,
1698
+ )
1699
+
1700
+ ab_bytes_per_stage = (
1701
+ cute.size_in_bytes(a_dtype, a_smem_layout_stage_one)
1702
+ + cute.size_in_bytes(b_dtype, b_smem_layout_staged_one)
1703
+ + cute.size_in_bytes(sf_dtype, sfa_smem_layout_staged_one)
1704
+ + cute.size_in_bytes(sf_dtype, sfb_smem_layout_staged_one)
1705
+ )
1706
+ mbar_helpers_bytes = 1024
1707
+ c_bytes_per_stage = cute.size_in_bytes(c_dtype, c_smem_layout_staged_one)
1708
+ c_bytes = c_bytes_per_stage * num_c_stage
1709
+
1710
+ # Calculate A/B/SFA/SFB stages:
1711
+ # Start with total smem per CTA (capacity / occupancy)
1712
+ # Subtract reserved bytes and initial C stages bytes
1713
+ # Divide remaining by bytes needed per A/B/SFA/SFB stage
1714
+ num_ab_stage = (
1715
+ smem_capacity // occupancy - (mbar_helpers_bytes + c_bytes)
1716
+ ) // ab_bytes_per_stage
1717
+
1718
+ # Refine epilogue stages:
1719
+ # Calculate remaining smem after allocating for A/B/SFA/SFB stages and reserved bytes
1720
+ # Add remaining unused smem to epilogue
1721
+ num_c_stage += (
1722
+ smem_capacity
1723
+ - occupancy * ab_bytes_per_stage * num_ab_stage
1724
+ - occupancy * (mbar_helpers_bytes + c_bytes)
1725
+ ) // (occupancy * c_bytes_per_stage)
1726
+
1727
+ return num_acc_stage, num_ab_stage, num_c_stage
1728
+
1729
+ @staticmethod
1730
+ def _compute_grid(
1731
+ c: cute.Tensor,
1732
+ cta_tile_shape_mnk: Tuple[int, int, int],
1733
+ cluster_shape_mn: Tuple[int, int],
1734
+ max_active_clusters: cutlass.Constexpr,
1735
+ ) -> Tuple[utils.PersistentTileSchedulerParams, Tuple[int, int, int]]:
1736
+ """Use persistent tile scheduler to compute the grid size for the output tensor C.
1737
+
1738
+ :param c: The output tensor C
1739
+ :type c: cute.Tensor
1740
+ :param cta_tile_shape_mnk: The shape (M, N, K) of the CTA tile.
1741
+ :type cta_tile_shape_mnk: tuple[int, int, int]
1742
+ :param cluster_shape_mn: Shape of each cluster in M, N dimensions.
1743
+ :type cluster_shape_mn: tuple[int, int]
1744
+ :param max_active_clusters: Maximum number of active clusters.
1745
+ :type max_active_clusters: cutlass.Constexpr
1746
+
1747
+ :return: A tuple containing:
1748
+ - tile_sched_params: Parameters for the persistent tile scheduler.
1749
+ - grid: Grid shape for kernel launch.
1750
+ :rtype: Tuple[utils.PersistentTileSchedulerParams, tuple[int, int, int]]
1751
+ """
1752
+ c_shape = cute.slice_(cta_tile_shape_mnk, (None, None, 0))
1753
+ gc = cute.zipped_divide(c, tiler=c_shape)
1754
+ num_ctas_mnl = gc[(0, (None, None, None))].shape
1755
+ cluster_shape_mnl = (*cluster_shape_mn, 1)
1756
+
1757
+ tile_sched_params = utils.PersistentTileSchedulerParams(
1758
+ num_ctas_mnl, cluster_shape_mnl
1759
+ )
1760
+ grid = utils.StaticPersistentTileScheduler.get_grid_shape(
1761
+ tile_sched_params, max_active_clusters
1762
+ )
1763
+
1764
+ return tile_sched_params, grid
1765
+
1766
+ @staticmethod
1767
+ def is_valid_dtypes_and_scale_factor_vec_size(
1768
+ ab_dtype: Type[cutlass.Numeric],
1769
+ sf_dtype: Type[cutlass.Numeric],
1770
+ sf_vec_size: int,
1771
+ c_dtype: Type[cutlass.Numeric],
1772
+ ) -> bool:
1773
+ """
1774
+ Check if the dtypes and sf_vec_size are valid combinations
1775
+
1776
+ :param ab_dtype: The data type of the A and B operands
1777
+ :type ab_dtype: Type[cutlass.Numeric]
1778
+ :param sf_dtype: The data type of the scale factor
1779
+ :type sf_dtype: Type[cutlass.Numeric]
1780
+ :param sf_vec_size: The vector size of the scale factor
1781
+ :type sf_vec_size: int
1782
+ :param c_dtype: The data type of the output tensor
1783
+ :type c_dtype: Type[cutlass.Numeric]
1784
+
1785
+ :return: True if the dtypes and sf_vec_size are valid, False otherwise
1786
+ :rtype: bool
1787
+ """
1788
+ is_valid = True
1789
+
1790
+ # Check valid ab_dtype
1791
+ if ab_dtype not in {
1792
+ cutlass.Float4E2M1FN,
1793
+ cutlass.Float8E5M2,
1794
+ cutlass.Float8E4M3FN,
1795
+ }:
1796
+ is_valid = False
1797
+
1798
+ # Check valid sf_vec_size
1799
+ if sf_vec_size not in {16, 32}:
1800
+ is_valid = False
1801
+
1802
+ # Check valid sf_dtype
1803
+ if sf_dtype not in {cutlass.Float8E8M0FNU, cutlass.Float8E4M3FN}:
1804
+ is_valid = False
1805
+
1806
+ # Check valid sf_dtype and sf_vec_size combinations
1807
+ if sf_dtype == cutlass.Float8E4M3FN and sf_vec_size == 32:
1808
+ is_valid = False
1809
+ if ab_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN} and sf_vec_size == 16:
1810
+ is_valid = False
1811
+
1812
+ # Check valid c_dtype
1813
+ if c_dtype not in {
1814
+ cutlass.Float32,
1815
+ cutlass.Float16,
1816
+ cutlass.BFloat16,
1817
+ cutlass.Float8E5M2,
1818
+ cutlass.Float8E4M3FN,
1819
+ }:
1820
+ is_valid = False
1821
+
1822
+ return is_valid
1823
+
1824
+ @staticmethod
1825
+ def is_valid_layouts(
1826
+ ab_dtype: Type[cutlass.Numeric],
1827
+ c_dtype: Type[cutlass.Numeric],
1828
+ a_major: str,
1829
+ b_major: str,
1830
+ c_major: str,
1831
+ ) -> bool:
1832
+ """
1833
+ Check if the dtypes and sf_vec_size are valid combinations
1834
+
1835
+ :param ab_dtype: The data type of the A and B operands
1836
+ :type ab_dtype: Type[cutlass.Numeric]
1837
+ :param c_dtype: The data type of the output tensor
1838
+ :type c_dtype: Type[cutlass.Numeric]
1839
+ :param a_major: The major dimension of the A tensor
1840
+ :type a_major: str
1841
+ :param b_major: The major dimension of the B tensor
1842
+ :type b_major: str
1843
+ :param c_major: The major dimension of the C tensor
1844
+ :type c_major: str
1845
+
1846
+ :return: True if the layouts are valid, False otherwise
1847
+ :rtype: bool
1848
+ """
1849
+ is_valid = True
1850
+
1851
+ if ab_dtype is cutlass.Float4E2M1FN and not (a_major == "k" and b_major == "k"):
1852
+ is_valid = False
1853
+ return is_valid
1854
+
1855
+ @staticmethod
1856
+ def is_valid_mma_tiler_and_cluster_shape(
1857
+ mma_tiler_mn: Tuple[int, int],
1858
+ cluster_shape_mn: Tuple[int, int],
1859
+ ) -> bool:
1860
+ """
1861
+ Check if the mma tiler and cluster shape are valid
1862
+
1863
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1864
+ :type mma_tiler_mn: Tuple[int, int]
1865
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1866
+ :type cluster_shape_mn: Tuple[int, int]
1867
+
1868
+ :return: True if the mma tiler and cluster shape are valid, False otherwise
1869
+ :rtype: bool
1870
+ """
1871
+ is_valid = True
1872
+ # Skip invalid mma tile shape
1873
+ if not mma_tiler_mn[0] in [128, 256]:
1874
+ is_valid = False
1875
+ if not mma_tiler_mn[1] in [128, 256]:
1876
+ is_valid = False
1877
+ # Skip illegal cluster shape
1878
+ if cluster_shape_mn[0] % (2 if mma_tiler_mn[0] == 256 else 1) != 0:
1879
+ is_valid = False
1880
+ # Skip invalid cluster shape
1881
+ is_power_of_2 = lambda x: x > 0 and (x & (x - 1)) == 0
1882
+ if (
1883
+ cluster_shape_mn[0] * cluster_shape_mn[1] > 16
1884
+ or cluster_shape_mn[0] <= 0
1885
+ or cluster_shape_mn[1] <= 0
1886
+ # Special cluster shape check for scale factor multicasts.
1887
+ # Due to limited size of scale factors, we can't multicast among more than 4 CTAs.
1888
+ or cluster_shape_mn[0] > 4
1889
+ or cluster_shape_mn[1] > 4
1890
+ or not is_power_of_2(cluster_shape_mn[0])
1891
+ or not is_power_of_2(cluster_shape_mn[1])
1892
+ ):
1893
+ is_valid = False
1894
+ return is_valid
1895
+
1896
+ @staticmethod
1897
+ def is_valid_tensor_alignment(
1898
+ m: int,
1899
+ n: int,
1900
+ k: int,
1901
+ l: int,
1902
+ ab_dtype: Type[cutlass.Numeric],
1903
+ c_dtype: Type[cutlass.Numeric],
1904
+ a_major: str,
1905
+ b_major: str,
1906
+ c_major: str,
1907
+ ) -> bool:
1908
+ """
1909
+ Check if the tensor alignment is valid
1910
+
1911
+ :param m: The number of rows in the A tensor
1912
+ :type m: int
1913
+ :param n: The number of columns in the B tensor
1914
+ :type n: int
1915
+ :param k: The number of columns in the A tensor
1916
+ :type k: int
1917
+ :param l: The number of columns in the C tensor
1918
+ :type l: int
1919
+ :param ab_dtype: The data type of the A and B operands
1920
+ :type ab_dtype: Type[cutlass.Numeric]
1921
+ :param c_dtype: The data type of the output tensor
1922
+ :type c_dtype: Type[cutlass.Numeric]
1923
+ :param a_major: The major axis of the A tensor
1924
+ :type a_major: str
1925
+ :param b_major: The major axis of the B tensor
1926
+ :type b_major: str
1927
+ :param c_major: The major axis of the C tensor
1928
+ :type c_major: str
1929
+
1930
+ :return: True if the problem shape is valid, False otherwise
1931
+ :rtype: bool
1932
+ """
1933
+ is_valid = True
1934
+
1935
+ def check_contigous_16B_alignment(dtype, is_mode0_major, tensor_shape):
1936
+ major_mode_idx = 0 if is_mode0_major else 1
1937
+ num_major_elements = tensor_shape[major_mode_idx]
1938
+ num_contiguous_elements = 16 * 8 // dtype.width
1939
+ return num_major_elements % num_contiguous_elements == 0
1940
+
1941
+ if (
1942
+ not check_contigous_16B_alignment(ab_dtype, a_major == "m", (m, k, l))
1943
+ or not check_contigous_16B_alignment(ab_dtype, b_major == "n", (n, k, l))
1944
+ or not check_contigous_16B_alignment(c_dtype, c_major == "m", (m, n, l))
1945
+ ):
1946
+ is_valid = False
1947
+ return is_valid
1948
+
1949
+ @staticmethod
1950
+ def can_implement(
1951
+ ab_dtype: Type[cutlass.Numeric],
1952
+ sf_dtype: Type[cutlass.Numeric],
1953
+ sf_vec_size: int,
1954
+ c_dtype: Type[cutlass.Numeric],
1955
+ mma_tiler_mn: Tuple[int, int],
1956
+ cluster_shape_mn: Tuple[int, int],
1957
+ m: int,
1958
+ n: int,
1959
+ k: int,
1960
+ l: int,
1961
+ a_major: str,
1962
+ b_major: str,
1963
+ c_major: str,
1964
+ ) -> bool:
1965
+ """
1966
+ Check if the gemm can be implemented
1967
+
1968
+ :param ab_dtype: The data type of the A and B operands
1969
+ :type ab_dtype: Type[cutlass.Numeric]
1970
+ :param sf_dtype: The data type of the scale factor tensor
1971
+ :type sf_dtype: Type[cutlass.Numeric]
1972
+ :param sf_vec_size: The vector size
1973
+ :type sf_vec_size: int
1974
+ :param c_dtype: The data type of the output tensor
1975
+ :type c_dtype: Type[cutlass.Numeric]
1976
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1977
+ :type mma_tiler_mn: Tuple[int, int]
1978
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1979
+ :type cluster_shape_mn: Tuple[int, int]
1980
+ :param m: The number of rows in the A tensor
1981
+ :type m: int
1982
+ :param n: The number of columns in the B tensor
1983
+ :type n: int
1984
+ :param k: The number of columns in the A tensor
1985
+ :type k: int
1986
+ :param l: The number of columns in the C tensor
1987
+ :type l: int
1988
+ :param a_major: The major axis of the A tensor
1989
+ :type a_major: str
1990
+ :param b_major: The major axis of the B tensor
1991
+ :type b_major: str
1992
+ :param c_major: The major axis of the C tensor
1993
+ :type c_major: str
1994
+
1995
+ :return: True if the gemm can be implemented, False otherwise
1996
+ :rtype: bool
1997
+ """
1998
+ can_implement = True
1999
+ # Skip unsupported types
2000
+ if not Sm100BlockScaledPersistentDenseGemmKernel.is_valid_dtypes_and_scale_factor_vec_size(
2001
+ ab_dtype, sf_dtype, sf_vec_size, c_dtype
2002
+ ):
2003
+ can_implement = False
2004
+ # Skip unsupported layouts
2005
+ if not Sm100BlockScaledPersistentDenseGemmKernel.is_valid_layouts(
2006
+ ab_dtype, c_dtype, a_major, b_major, c_major
2007
+ ):
2008
+ can_implement = False
2009
+ # Skip invalid mma tile shape and cluster shape
2010
+ if not Sm100BlockScaledPersistentDenseGemmKernel.is_valid_mma_tiler_and_cluster_shape(
2011
+ mma_tiler_mn, cluster_shape_mn
2012
+ ):
2013
+ can_implement = False
2014
+ # Skip illegal problem shape for load/store alignment
2015
+ if not Sm100BlockScaledPersistentDenseGemmKernel.is_valid_tensor_alignment(
2016
+ m, n, k, l, ab_dtype, c_dtype, a_major, b_major, c_major
2017
+ ):
2018
+ can_implement = False
2019
+ return can_implement
2020
+
2021
+
2022
+ @cute.jit
2023
+ def cvt_sf_MKL_to_M32x4xrm_K4xrk_L(
2024
+ sf_ref_tensor: cute.Tensor,
2025
+ sf_mma_tensor: cute.Tensor,
2026
+ ):
2027
+ """Convert scale factor tensor from MKL layout to mma specification M(32x4xrest_m)xK(4xrest_k)xL layout"""
2028
+ # sf_mma_tensor has flatten shape (32, 4, rest_m, 4, rest_k, l)
2029
+ # group to ((32, 4, rest_m), (4, rest_k), l)
2030
+ sf_mma_tensor = cute.group_modes(sf_mma_tensor, 0, 3)
2031
+ sf_mma_tensor = cute.group_modes(sf_mma_tensor, 1, 3)
2032
+ for i in cutlass.range(cute.size(sf_ref_tensor)):
2033
+ mkl_coord = sf_ref_tensor.layout.get_hier_coord(i)
2034
+ sf_mma_tensor[mkl_coord] = sf_ref_tensor[mkl_coord]
2035
+
2036
+
2037
+ def run(
2038
+ mnkl: Tuple[int, int, int, int],
2039
+ ab_dtype: Type[cutlass.Numeric],
2040
+ sf_dtype: Type[cutlass.Numeric],
2041
+ sf_vec_size: int,
2042
+ c_dtype: Type[cutlass.Numeric],
2043
+ a_major: str,
2044
+ b_major: str,
2045
+ c_major: str,
2046
+ mma_tiler_mn: Tuple[int, int],
2047
+ cluster_shape_mn: Tuple[int, int],
2048
+ tolerance: float = 1e-01,
2049
+ warmup_iterations: int = 0,
2050
+ iterations: int = 1,
2051
+ skip_ref_check: bool = False,
2052
+ use_cold_l2: bool = False,
2053
+ **kwargs,
2054
+ ):
2055
+ """Execute a persistent batched dense blockscaled GEMM operation on Blackwell architecture with performance benchmarking.
2056
+
2057
+ This function prepares input tensors, configures and launches the persistent GEMM kernel,
2058
+ optionally performs reference validation, and benchmarks the execution performance.
2059
+
2060
+ :param mnkl: Problem size (M, N, K, L)
2061
+ :type mnkl: Tuple[int, int, int, int]
2062
+ :param ab_dtype: Data type for input tensors A and B
2063
+ :type ab_dtype: Type[cutlass.Numeric]
2064
+ :param sf_dtype: Data type for scale factor tensor
2065
+ :type sf_dtype: Type[cutlass.Numeric]
2066
+ :param sf_vec_size: Vector size for scale factor tensor
2067
+ :type sf_vec_size: int
2068
+ :param c_dtype: Data type for output tensor C
2069
+ :type c_dtype: Type[cutlass.Numeric]
2070
+ :param a_major/b_major/c_major: Memory layout of tensor A/B/C
2071
+ :type a_major/b_major/c_major: str
2072
+ :param mma_tiler_mn: MMA tiling size.
2073
+ :type mma_tiler_mn: Tuple[int, int]
2074
+ :param cluster_shape_mn: Cluster shape.
2075
+ :type cluster_shape_mn: Tuple[int, int]
2076
+ :param tolerance: Tolerance value for reference validation comparison, defaults to 1e-01
2077
+ :type tolerance: float, optional
2078
+ :param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 0
2079
+ :type warmup_iterations: int, optional
2080
+ :param iterations: Number of benchmark iterations to run, defaults to 1
2081
+ :type iterations: int, optional
2082
+ :param skip_ref_check: Whether to skip reference result validation, defaults to False
2083
+ :type skip_ref_check: bool, optional
2084
+ :param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
2085
+ :type use_cold_l2: bool, optional
2086
+ :raises RuntimeError: If CUDA GPU is not available
2087
+ :raises ValueError: If the configuration is invalid or unsupported by the kernel
2088
+ :return: Execution time of the GEMM kernel
2089
+ :rtype: float
2090
+ """
2091
+ print(f"Running Sm100 Persistent Dense BlockScaled GEMM test with:")
2092
+ print(f"mnkl: {mnkl}")
2093
+ print(f"AB dtype: {ab_dtype}, SF dtype: {sf_dtype}, SF Vec size: {sf_vec_size}")
2094
+ print(f"C dtype: {c_dtype}")
2095
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
2096
+ print(f"Mma Tiler (M, N): {mma_tiler_mn}, Cluster Shape (M, N): {cluster_shape_mn}")
2097
+ print(f"Tolerance: {tolerance}")
2098
+ print(f"Warmup iterations: {warmup_iterations}")
2099
+ print(f"Iterations: {iterations}")
2100
+ print(f"Skip reference checking: {skip_ref_check}")
2101
+ print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
2102
+
2103
+ # Unpack parameters
2104
+ m, n, k, l = mnkl
2105
+
2106
+ # Skip unsupported testcase
2107
+ if not Sm100BlockScaledPersistentDenseGemmKernel.can_implement(
2108
+ ab_dtype,
2109
+ sf_dtype,
2110
+ sf_vec_size,
2111
+ c_dtype,
2112
+ mma_tiler_mn,
2113
+ cluster_shape_mn,
2114
+ m,
2115
+ n,
2116
+ k,
2117
+ l,
2118
+ a_major,
2119
+ b_major,
2120
+ c_major,
2121
+ ):
2122
+ raise TypeError(
2123
+ f"Unsupported testcase {ab_dtype}, {sf_dtype}, {sf_vec_size}, {c_dtype}, {mma_tiler_mn}, {cluster_shape_mn}, {m}, {n}, {k}, {l}, {a_major}, {b_major}, {c_major}"
2124
+ )
2125
+
2126
+ if not torch.cuda.is_available():
2127
+ raise RuntimeError("GPU is required to run this example!")
2128
+
2129
+ torch.manual_seed(1111)
2130
+
2131
+ # Create tensor A/B/C
2132
+ a_ref = cutlass_torch.matrix(l, m, k, a_major == "m", cutlass.Float32)
2133
+ b_ref = cutlass_torch.matrix(l, n, k, b_major == "n", cutlass.Float32)
2134
+ c_ref = cutlass_torch.matrix(l, m, n, c_major == "m", cutlass.Float32)
2135
+
2136
+ a_tensor, a_torch = cutlass_torch.cute_tensor_like(
2137
+ a_ref, ab_dtype, is_dynamic_layout=True, assumed_align=16
2138
+ )
2139
+ b_tensor, b_torch = cutlass_torch.cute_tensor_like(
2140
+ b_ref, ab_dtype, is_dynamic_layout=True, assumed_align=16
2141
+ )
2142
+ c_tensor, c_torch = cutlass_torch.cute_tensor_like(
2143
+ c_ref, c_dtype, is_dynamic_layout=True, assumed_align=16
2144
+ )
2145
+
2146
+ # Mark tensor to be byte aligned
2147
+ a_tensor.mark_compact_shape_dynamic(
2148
+ mode=1 if a_major == "k" else 0,
2149
+ stride_order=(2, 0, 1) if a_major == "k" else (2, 1, 0),
2150
+ divisibility=2 if ab_dtype == cutlass.Float4E2M1FN else 1,
2151
+ )
2152
+ b_tensor.mark_compact_shape_dynamic(
2153
+ mode=1 if b_major == "k" else 0,
2154
+ stride_order=(2, 0, 1) if b_major == "k" else (2, 1, 0),
2155
+ divisibility=2 if ab_dtype == cutlass.Float4E2M1FN else 1,
2156
+ )
2157
+ c_tensor.mark_compact_shape_dynamic(
2158
+ mode=1 if c_major == "n" else 0,
2159
+ stride_order=(2, 0, 1) if c_major == "n" else (2, 1, 0),
2160
+ divisibility=2 if c_dtype == cutlass.Float4E2M1FN else 1,
2161
+ )
2162
+
2163
+ # Create scale factor tensor SFA/SFB
2164
+ def create_scale_factor_tensor(l, mn, k, sf_vec_size, dtype):
2165
+ def ceil_div(a, b):
2166
+ return (a + b - 1) // b
2167
+
2168
+ sf_k = ceil_div(k, sf_vec_size)
2169
+ ref_shape = (l, mn, sf_k)
2170
+
2171
+ atom_m = (32, 4)
2172
+ atom_k = 4
2173
+ mma_shape = (
2174
+ l,
2175
+ ceil_div(mn, atom_m[0] * atom_m[1]),
2176
+ ceil_div(sf_k, atom_k),
2177
+ atom_m[0],
2178
+ atom_m[1],
2179
+ atom_k,
2180
+ )
2181
+
2182
+ ref_permute_order = (1, 2, 0)
2183
+ mma_permute_order = (3, 4, 1, 5, 2, 0)
2184
+
2185
+ # Create f32 ref torch tensor (cpu)
2186
+ ref_f32_torch_tensor_cpu = cutlass_torch.create_and_permute_torch_tensor(
2187
+ ref_shape,
2188
+ torch.float32,
2189
+ permute_order=ref_permute_order,
2190
+ init_type=cutlass_torch.TensorInitType.RANDOM,
2191
+ init_config=cutlass_torch.RandomInitConfig(
2192
+ min_val=1,
2193
+ max_val=3,
2194
+ ),
2195
+ )
2196
+
2197
+ # Create f32 cute torch tensor (cpu)
2198
+ cute_f32_torch_tensor_cpu = cutlass_torch.create_and_permute_torch_tensor(
2199
+ mma_shape,
2200
+ torch.float32,
2201
+ permute_order=mma_permute_order,
2202
+ init_type=cutlass_torch.TensorInitType.RANDOM,
2203
+ init_config=cutlass_torch.RandomInitConfig(
2204
+ min_val=0,
2205
+ max_val=1,
2206
+ ),
2207
+ )
2208
+
2209
+ # convert ref f32 tensor to cute f32 tensor
2210
+ cvt_sf_MKL_to_M32x4xrm_K4xrk_L(
2211
+ from_dlpack(ref_f32_torch_tensor_cpu),
2212
+ from_dlpack(cute_f32_torch_tensor_cpu),
2213
+ )
2214
+ cute_f32_torch_tensor = cute_f32_torch_tensor_cpu.cuda()
2215
+
2216
+ # reshape makes memory contiguous
2217
+ ref_f32_torch_tensor_cpu = (
2218
+ ref_f32_torch_tensor_cpu.permute(2, 0, 1)
2219
+ .unsqueeze(-1)
2220
+ .expand(l, mn, sf_k, sf_vec_size)
2221
+ .reshape(l, mn, sf_k * sf_vec_size)
2222
+ .permute(*ref_permute_order)
2223
+ )
2224
+ # prune to mkl for reference check.
2225
+ ref_f32_torch_tensor_cpu = ref_f32_torch_tensor_cpu[:, :k, :]
2226
+
2227
+ # Create dtype cute torch tensor (cpu)
2228
+ cute_tensor, cute_torch_tensor = cutlass_torch.cute_tensor_like(
2229
+ cute_f32_torch_tensor_cpu,
2230
+ dtype,
2231
+ is_dynamic_layout=True,
2232
+ assumed_align=16,
2233
+ )
2234
+
2235
+ # Convert f32 cute tensor to dtype cute tensor
2236
+ cute_tensor = cutlass_torch.convert_cute_tensor(
2237
+ cute_f32_torch_tensor,
2238
+ cute_tensor,
2239
+ dtype,
2240
+ is_dynamic_layout=True,
2241
+ )
2242
+ return ref_f32_torch_tensor_cpu, cute_tensor, cute_torch_tensor
2243
+
2244
+ sfa_ref, sfa_tensor, sfa_torch = create_scale_factor_tensor(
2245
+ l, m, k, sf_vec_size, sf_dtype
2246
+ )
2247
+ sfb_ref, sfb_tensor, sfb_torch = create_scale_factor_tensor(
2248
+ l, n, k, sf_vec_size, sf_dtype
2249
+ )
2250
+
2251
+ # Configure gemm kernel
2252
+ gemm = Sm100BlockScaledPersistentDenseGemmKernel(
2253
+ sf_vec_size,
2254
+ mma_tiler_mn,
2255
+ cluster_shape_mn,
2256
+ )
2257
+
2258
+ # Compute max active clusters on current device
2259
+ hardware_info = cutlass.utils.HardwareInfo()
2260
+ max_active_clusters = hardware_info.get_max_active_clusters(
2261
+ cluster_shape_mn[0] * cluster_shape_mn[1]
2262
+ )
2263
+
2264
+ # Initialize Stream
2265
+ current_stream = cutlass_torch.default_stream()
2266
+
2267
+ # Compile gemm kernel
2268
+ compiled_gemm = cute.compile(
2269
+ gemm,
2270
+ a_tensor,
2271
+ b_tensor,
2272
+ sfa_tensor,
2273
+ sfb_tensor,
2274
+ c_tensor,
2275
+ max_active_clusters,
2276
+ current_stream,
2277
+ )
2278
+
2279
+ # Compute reference result
2280
+ if not skip_ref_check:
2281
+ # Execute kernel once for reference checking
2282
+ compiled_gemm(
2283
+ a_tensor, b_tensor, sfa_tensor, sfb_tensor, c_tensor, current_stream
2284
+ )
2285
+ print("Verifying results...")
2286
+ res_a = torch.einsum("mkl,mkl->mkl", a_ref, sfa_ref)
2287
+ res_b = torch.einsum("nkl,nkl->nkl", b_ref, sfb_ref)
2288
+ ref = torch.einsum("mkl,nkl->mnl", res_a, res_b)
2289
+
2290
+ # Convert c back to f32 for comparison.
2291
+ c_ref_device = c_ref.cuda()
2292
+ cute.testing.convert(
2293
+ c_tensor,
2294
+ from_dlpack(c_ref_device, assumed_align=16).mark_layout_dynamic(
2295
+ leading_dim=(1 if c_major == "n" else 0)
2296
+ ),
2297
+ )
2298
+ c_ref = c_ref_device.cpu()
2299
+
2300
+ if c_dtype in (cutlass.Float32, cutlass.Float16, cutlass.BFloat16):
2301
+ torch.testing.assert_close(c_ref, ref, atol=tolerance, rtol=1e-02)
2302
+ elif c_dtype in (cutlass.Float8E5M2, cutlass.Float8E4M3FN):
2303
+ # Convert ref : f32 -> f8 -> f32
2304
+ ref_f8_ = torch.empty(*(l, m, n), dtype=torch.uint8, device="cuda").permute(
2305
+ 1, 2, 0
2306
+ )
2307
+ ref_f8 = from_dlpack(ref_f8_, assumed_align=16).mark_layout_dynamic(
2308
+ leading_dim=1
2309
+ )
2310
+ ref_f8.element_type = c_dtype
2311
+ ref_device = ref.permute(2, 0, 1).contiguous().permute(1, 2, 0).cuda()
2312
+ ref_tensor = from_dlpack(ref_device, assumed_align=16).mark_layout_dynamic(
2313
+ leading_dim=1
2314
+ )
2315
+ cute.testing.convert(ref_tensor, ref_f8)
2316
+ cute.testing.convert(ref_f8, ref_tensor)
2317
+ ref = ref_device.cpu()
2318
+ torch.testing.assert_close(c_ref, ref, atol=tolerance, rtol=1e-02)
2319
+ def generate_tensors():
2320
+ a_tensor, _ = cutlass_torch.cute_tensor_like(
2321
+ a_ref, ab_dtype, is_dynamic_layout=True, assumed_align=16
2322
+ )
2323
+ b_tensor, _ = cutlass_torch.cute_tensor_like(
2324
+ b_ref, ab_dtype, is_dynamic_layout=True, assumed_align=16
2325
+ )
2326
+ c_tensor, _ = cutlass_torch.cute_tensor_like(
2327
+ c_ref, c_dtype, is_dynamic_layout=True, assumed_align=16
2328
+ )
2329
+
2330
+ # Mark tensor to be byte aligned
2331
+ a_tensor.mark_compact_shape_dynamic(
2332
+ mode=1 if a_major == "k" else 0,
2333
+ stride_order=(2, 0, 1) if a_major == "k" else (2, 1, 0),
2334
+ divisibility=2 if ab_dtype == cutlass.Float4E2M1FN else 1,
2335
+ )
2336
+ b_tensor.mark_compact_shape_dynamic(
2337
+ mode=1 if b_major == "k" else 0,
2338
+ stride_order=(2, 0, 1) if b_major == "k" else (2, 1, 0),
2339
+ divisibility=2 if ab_dtype == cutlass.Float4E2M1FN else 1,
2340
+ )
2341
+ c_tensor.mark_compact_shape_dynamic(
2342
+ mode=1 if c_major == "n" else 0,
2343
+ stride_order=(2, 0, 1) if c_major == "n" else (2, 1, 0),
2344
+ divisibility=2 if c_dtype == cutlass.Float4E2M1FN else 1,
2345
+ )
2346
+
2347
+ _, sfa_tensor, _ = create_scale_factor_tensor(l, m, k, sf_vec_size, sf_dtype)
2348
+ _, sfb_tensor, _ = create_scale_factor_tensor(l, n, k, sf_vec_size, sf_dtype)
2349
+ return cute.testing.JitArguments(
2350
+ a_tensor, b_tensor, sfa_tensor, sfb_tensor, c_tensor, current_stream
2351
+ )
2352
+
2353
+ workspace_count = 1
2354
+ if use_cold_l2:
2355
+ one_workspace_bytes = (
2356
+ a_torch.numel() * a_torch.element_size()
2357
+ + b_torch.numel() * b_torch.element_size()
2358
+ + sfa_torch.numel() * sfa_torch.element_size()
2359
+ + sfb_torch.numel() * sfb_torch.element_size()
2360
+ + c_torch.numel() * c_torch.element_size()
2361
+ )
2362
+ workspace_count = cute.testing.get_workspace_count(
2363
+ one_workspace_bytes, warmup_iterations, iterations
2364
+ )
2365
+
2366
+ exec_time = cute.testing.benchmark(
2367
+ compiled_gemm,
2368
+ workspace_generator=generate_tensors,
2369
+ workspace_count=workspace_count,
2370
+ stream=current_stream,
2371
+ warmup_iterations=warmup_iterations,
2372
+ iterations=iterations,
2373
+ )
2374
+
2375
+ return exec_time # Return execution time in microseconds
2376
+
2377
+ if __name__ == "__main__":
2378
+
2379
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
2380
+ try:
2381
+ return tuple(int(x.strip()) for x in s.split(","))
2382
+ except ValueError:
2383
+ raise argparse.ArgumentTypeError(
2384
+ "Invalid format. Expected comma-separated integers."
2385
+ )
2386
+
2387
+ parser = argparse.ArgumentParser(
2388
+ description="Example of Sm100 Dense Persistent BlockScaled GEMM."
2389
+ )
2390
+
2391
+ parser.add_argument(
2392
+ "--mnkl",
2393
+ type=parse_comma_separated_ints,
2394
+ default=(512, 256, 256, 1),
2395
+ help="mnkl dimensions (comma-separated)",
2396
+ )
2397
+ parser.add_argument(
2398
+ "--mma_tiler_mn",
2399
+ type=parse_comma_separated_ints,
2400
+ default=(128, 128),
2401
+ help="Mma tile shape (comma-separated)",
2402
+ )
2403
+ parser.add_argument(
2404
+ "--cluster_shape_mn",
2405
+ type=parse_comma_separated_ints,
2406
+ default=(1, 1),
2407
+ help="Cluster shape (comma-separated)",
2408
+ )
2409
+ parser.add_argument("--ab_dtype", type=cutlass.dtype, default=cutlass.Float4E2M1FN)
2410
+ parser.add_argument("--sf_dtype", type=cutlass.dtype, default=cutlass.Float8E8M0FNU)
2411
+ parser.add_argument("--sf_vec_size", type=int, default=16)
2412
+ parser.add_argument("--c_dtype", type=cutlass.dtype, default=cutlass.Float16)
2413
+ parser.add_argument("--a_major", choices=["k", "m"], type=str, default="k")
2414
+ parser.add_argument("--b_major", choices=["k", "n"], type=str, default="k")
2415
+ parser.add_argument("--c_major", choices=["n", "m"], type=str, default="n")
2416
+ parser.add_argument(
2417
+ "--tolerance", type=float, default=1e-01, help="Tolerance for validation"
2418
+ )
2419
+ parser.add_argument(
2420
+ "--warmup_iterations", type=int, default=0, help="Warmup iterations"
2421
+ )
2422
+ parser.add_argument(
2423
+ "--iterations",
2424
+ type=int,
2425
+ default=1,
2426
+ help="Number of iterations to run the kernel",
2427
+ )
2428
+ parser.add_argument(
2429
+ "--skip_ref_check", action="store_true", help="Skip reference checking"
2430
+ )
2431
+ parser.add_argument(
2432
+ "--use_cold_l2",
2433
+ action="store_true",
2434
+ default=False,
2435
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
2436
+ )
2437
+
2438
+ args = parser.parse_args()
2439
+
2440
+ if len(args.mnkl) != 4:
2441
+ parser.error("--mnkl must contain exactly 4 values")
2442
+
2443
+ if len(args.mma_tiler_mn) != 2:
2444
+ parser.error("--mma_tiler_mn must contain exactly 2 values")
2445
+
2446
+ if len(args.cluster_shape_mn) != 2:
2447
+ parser.error("--cluster_shape_mn must contain exactly 2 values")
2448
+
2449
+ run(
2450
+ args.mnkl,
2451
+ args.ab_dtype,
2452
+ args.sf_dtype,
2453
+ args.sf_vec_size,
2454
+ args.c_dtype,
2455
+ args.a_major,
2456
+ args.b_major,
2457
+ args.c_major,
2458
+ args.mma_tiler_mn,
2459
+ args.cluster_shape_mn,
2460
+ args.tolerance,
2461
+ args.warmup_iterations,
2462
+ args.iterations,
2463
+ args.skip_ref_check,
2464
+ args.use_cold_l2,
2465
+ )
2466
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm.py ADDED
@@ -0,0 +1,1890 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ from typing import Optional, Type, Tuple, Union
31
+ import cuda.bindings.driver as cuda
32
+
33
+ import torch
34
+
35
+ import cutlass
36
+ import cutlass.cute as cute
37
+ import cutlass.utils as utils
38
+ import cutlass.pipeline as pipeline
39
+ from cutlass.cute.nvgpu import cpasync, tcgen05
40
+ import cutlass.torch as cutlass_torch
41
+ import cutlass.utils.blackwell_helpers as sm100_utils
42
+ from cutlass.cute.runtime import from_dlpack
43
+
44
+ """
45
+ A high-performance batched dense GEMM (C = A * B) example for the NVIDIA Blackwell SM100 architecture
46
+ using CUTE DSL.
47
+ - Matrix A is MxKxL, L is batch dimension, A can be row-major("K") or column-major("M")
48
+ - Matrix B is NxKxL, L is batch dimension, B can be row-major("N") or column-major("K")
49
+ - Matrix C is MxNxL, L is batch dimension, C can be row-major("N") or column-major("M")
50
+
51
+ This GEMM kernel supports the following features:
52
+ - Utilizes Tensor Memory Access (TMA) for efficient memory operations
53
+ - Utilizes Blackwell's tcgen05.mma for matrix multiply-accumulate (MMA) operations (including 2cta mma instructions)
54
+ - Implements TMA multicast with cluster to reduce L2 memory traffic
55
+ - Supports multi-stage pipeline to overlap computation and memory access
56
+
57
+ This GEMM works as follows:
58
+ 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.
59
+ 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.
60
+ 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.
61
+ 4. Type convert C matrix to output type.
62
+ 5. Optionally store C matrix from registers (RMEM) to shared memory (SMEM) to global memory (GMEM) with TMA operations,
63
+ or directly store C matrix from registers (RMEM) to global memory (GMEM) without TMA operations.
64
+ 6. Optionally accept an elementwise lambda function epilogue_op to apply to the output tensor:
65
+ e.g., relu can set epilogue_op = lambda x: cute.where(x > 0, x, cute.full_like(x, 0))
66
+
67
+ SM100 tcgen05.mma instructions operate as follows:
68
+ - Read matrix A from SMEM
69
+ - Read matrix B from SMEM
70
+ - Write accumulator to TMEM
71
+ The accumulator in TMEM must then be loaded to registers before writing back to GMEM.
72
+
73
+ To run this example:
74
+
75
+ .. code-block:: bash
76
+
77
+ python examples/blackwell/dense_gemm.py \
78
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
79
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
80
+ --mnkl 8192,8192,8192,1 \
81
+ --use_tma_store --use_2cta_instrs
82
+
83
+ The above example command compute batched gemm with M=8192, N=8192, K=8192,
84
+ batch_count=1. The Blackwell tcgen05 MMA tile shape used 2 cta with 256x128
85
+ MMA tile and the cluster shape is (2,1). The input, mma accumulator and output
86
+ data type are set as fp16, fp32 and fp16, respectively.
87
+
88
+ To collect performance with NCU profiler:
89
+
90
+ .. code-block:: bash
91
+
92
+ ncu python examples/blackwell/dense_gemm.py \
93
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
94
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
95
+ --mnkl 8192,8192,8192,1 \
96
+ --use_tma_store --use_2cta_instrs
97
+
98
+ Constraints:
99
+ * Supported input data types: fp16, bf16, tf32, int8, uint8, fp8 (e4m3fn, e5m2),
100
+ see detailed valid dtype combinations in below DenseGemmKernel class documentation
101
+ * A/B tensor must have the same data type
102
+ * Mma tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
103
+ * Mma tiler N must be 32-256, step 32
104
+ * Cluster shape M/N must be positive and power of 2, total cluster size <= 16
105
+ * Cluster shape M must be multiple of 2 if use_2cta_instrs=True
106
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned,
107
+ i.e, number of elements is a multiple of 4, 8, and 16 for TFloat32,
108
+ Float16/BFloat16, and Int8/Uint8/Float8, respectively.
109
+ * OOB tiles are not allowed when TMA store is disabled
110
+ """
111
+
112
+
113
+ class DenseGemmKernel:
114
+ """
115
+ This class implements batched matrix multiplication (C = A x B) with support for various data types
116
+ and architectural features specific to Blackwell GPUs.
117
+
118
+ :param acc_dtype: Data type for accumulation during computation
119
+ :type acc_dtype: type[cutlass.Numeric]
120
+ :param use_2cta_instrs: Whether to use CTA group 2 for advanced thread cooperation
121
+ :type use_2cta_instrs: bool
122
+ :param mma_tiler_mn: Shape of the Matrix Multiply-Accumulate (MMA) tiler (M,N)
123
+ :type mma_tiler_mn: Tuple[int, int]
124
+ :param cluster_shape_mn: Cluster dimensions (M,N) for parallel processing
125
+ :type cluster_shape_mn: Tuple[int, int]
126
+ :param use_tma_store: Whether to use Tensor Memory Access (TMA) for storing results
127
+ :type use_tma_store: bool
128
+
129
+ :note: In current version, A and B tensor must have the same data type
130
+ - i.e., Float8E4M3FN for A and Float8E5M2 for B is not supported
131
+
132
+ :note: Supported A/B data types:
133
+ - TFloat32
134
+ - Float16/BFloat16
135
+ - Int8/Uint8
136
+ - Float8E4M3FN/Float8E5M2
137
+
138
+ :note: Supported accumulator data types:
139
+ - Float32 (for all floating point A/B data types)
140
+ - Float16 (only for fp16 and fp8 A/B data types)
141
+ - Int32 (only for uint8/int8 A/B data types)
142
+
143
+ :note: Supported C data types:
144
+ - Float32 (for float32 and int32 accumulator data types)
145
+ - Int32 (for float32 and int32 accumulator data types)
146
+ - Float16/BFloat16 (for fp16 and fp8 accumulator data types)
147
+ - Int8/Uint8 (for uint8/int8 accumulator data types)
148
+ - Float8E4M3FN/Float8E5M2 (for float32 accumulator data types)
149
+
150
+ :note: Constraints:
151
+ - MMA tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
152
+ - MMA tiler N must be 32-256, step 32
153
+ - Cluster shape M must be multiple of 2 if use_2cta_instrs=True
154
+ - Cluster shape M/N must be positive and power of 2, total cluster size <= 16
155
+
156
+ Example:
157
+ >>> gemm = DenseGemmKernel(
158
+ ... acc_dtype=cutlass.Float32,
159
+ ... use_2cta_instrs=True,
160
+ ... mma_tiler_mn=(128, 128),
161
+ ... cluster_shape_mn=(2, 2)
162
+ ... )
163
+ >>> gemm(a_tensor, b_tensor, c_tensor, stream)
164
+ """
165
+
166
+ def __init__(
167
+ self,
168
+ acc_dtype: Type[cutlass.Numeric],
169
+ use_2cta_instrs: bool,
170
+ mma_tiler_mn: Tuple[int, int],
171
+ cluster_shape_mn: Tuple[int, int],
172
+ use_tma_store: bool,
173
+ ):
174
+ """Initializes the configuration for a Blackwell dense GEMM kernel.
175
+
176
+ This configuration includes several key aspects:
177
+
178
+ 1. MMA Instruction Settings (tcgen05):
179
+ - acc_dtype: Data types for MMA accumulator.
180
+ - mma_tiler_mn: The (M, N) shape of the MMA instruction tiler.
181
+ - use_2cta_instrs: Boolean indicating if the tcgen05 MMA variant
182
+ with cta_group=2 should be used.
183
+
184
+ 2. Cluster Shape:
185
+ - cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster.
186
+
187
+ 3. Output C tensor store mode:
188
+ - use_tma_store: Boolean indicating whether to use Tensor Memory Access (TMA) for storing results.
189
+
190
+ :param acc_dtype: Data type of the accumulator.
191
+ :type acc_dtype: type[cutlass.Numeric]
192
+ :param mma_tiler_mn: Tuple (M, N) shape of the MMA instruction.
193
+ :type mma_tiler_mn: Tuple[int, int]
194
+ :param use_2cta_instrs: Boolean, True to use cta_group=2 MMA variant.
195
+ :type use_2cta_instrs: bool
196
+ :param cluster_shape_mn: Tuple (ClusterM, ClusterN) shape of the cluster.
197
+ :type cluster_shape_mn: Tuple[int, int]
198
+ :param use_tma_store: Use Tensor Memory Access (TMA) or normal store for output C tensor.
199
+ :type use_tma_store: bool
200
+ """
201
+
202
+ self.acc_dtype: Type[cutlass.Numeric] = acc_dtype
203
+ self.use_2cta_instrs = use_2cta_instrs
204
+ self.cluster_shape_mn = cluster_shape_mn
205
+ # K dimension is deferred in _setup_attributes
206
+ self.mma_tiler = (*mma_tiler_mn, 1)
207
+ self.use_tma_store = use_tma_store
208
+
209
+ self.cta_group = (
210
+ tcgen05.CtaGroup.TWO if self.use_2cta_instrs else tcgen05.CtaGroup.ONE
211
+ )
212
+
213
+ self.occupancy = 1
214
+ self.threads_per_cta = 128
215
+ self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
216
+
217
+ def _setup_attributes(self):
218
+ """Set up configurations that are dependent on GEMM inputs
219
+
220
+ This method configures various attributes based on the input tensor properties
221
+ (data types, leading dimensions) and kernel settings:
222
+ - Configuring tiled MMA
223
+ - Computing MMA/cluster/tile shapes
224
+ - Computing cluster layout
225
+ - Computing multicast CTAs for A/B
226
+ - Computing epilogue subtile
227
+ - Setting up A/B/C stage counts in shared memory
228
+ - Computing A/B/C shared memory layout
229
+ - Computing tensor memory allocation columns
230
+ """
231
+ # Configure tiled mma
232
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
233
+ self.a_dtype,
234
+ self.a_major_mode,
235
+ self.b_major_mode,
236
+ self.acc_dtype,
237
+ self.cta_group,
238
+ self.mma_tiler[:2],
239
+ )
240
+
241
+ # Compute mma/cluster/tile shapes
242
+ mma_inst_shape_k = cute.size(tiled_mma.shape_mnk, mode=[2])
243
+ mma_inst_tile_k = 4
244
+ self.mma_tiler = (
245
+ self.mma_tiler[0],
246
+ self.mma_tiler[1],
247
+ mma_inst_shape_k * mma_inst_tile_k,
248
+ )
249
+ self.cta_tile_shape_mnk = (
250
+ self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
251
+ self.mma_tiler[1],
252
+ self.mma_tiler[2],
253
+ )
254
+
255
+ # Compute cluster layout
256
+ self.cluster_layout_vmnk = cute.tiled_divide(
257
+ cute.make_layout((*self.cluster_shape_mn, 1)),
258
+ (tiled_mma.thr_id.shape,),
259
+ )
260
+
261
+ # Compute number of multicast CTAs for A/B
262
+ self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
263
+ self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
264
+ self.is_a_mcast = self.num_mcast_ctas_a > 1
265
+ self.is_b_mcast = self.num_mcast_ctas_b > 1
266
+
267
+ # Compute epilogue subtile
268
+ if cutlass.const_expr(self.use_tma_store):
269
+ self.epi_tile = sm100_utils.compute_epilogue_tile_shape(
270
+ self.cta_tile_shape_mnk,
271
+ self.use_2cta_instrs,
272
+ self.c_layout,
273
+ self.c_dtype,
274
+ )
275
+ else:
276
+ self.epi_tile = self.cta_tile_shape_mnk[:2]
277
+
278
+ # Setup A/B/C stage count in shared memory
279
+ self.num_acc_stage, self.num_ab_stage, self.num_c_stage = self._compute_stages(
280
+ tiled_mma,
281
+ self.mma_tiler,
282
+ self.a_dtype,
283
+ self.b_dtype,
284
+ self.epi_tile,
285
+ self.c_dtype,
286
+ self.c_layout,
287
+ self.smem_capacity,
288
+ self.occupancy,
289
+ self.use_tma_store,
290
+ )
291
+
292
+ # Compute A/B/C shared memory layout
293
+ self.a_smem_layout_staged = sm100_utils.make_smem_layout_a(
294
+ tiled_mma,
295
+ self.mma_tiler,
296
+ self.a_dtype,
297
+ self.num_ab_stage,
298
+ )
299
+ self.b_smem_layout_staged = sm100_utils.make_smem_layout_b(
300
+ tiled_mma,
301
+ self.mma_tiler,
302
+ self.b_dtype,
303
+ self.num_ab_stage,
304
+ )
305
+ self.c_smem_layout_staged = (
306
+ sm100_utils.make_smem_layout_epi(
307
+ self.c_dtype,
308
+ self.c_layout,
309
+ self.epi_tile,
310
+ self.num_c_stage,
311
+ )
312
+ if self.use_tma_store
313
+ else None
314
+ )
315
+
316
+ # Compute the number of tensor memory allocation columns
317
+ self.num_tmem_alloc_cols = self._compute_num_tmem_alloc_cols(
318
+ tiled_mma, self.mma_tiler
319
+ )
320
+
321
+ @cute.jit
322
+ def __call__(
323
+ self,
324
+ a: cute.Tensor,
325
+ b: cute.Tensor,
326
+ c: cute.Tensor,
327
+ stream: cuda.CUstream,
328
+ epilogue_op: cutlass.Constexpr = lambda x: x,
329
+ ):
330
+ """Execute the GEMM operation in steps:
331
+ - Setup static attributes
332
+ - Setup TMA load/store atoms and tensors
333
+ - Compute grid size
334
+ - Define shared storage for kernel
335
+ - Launch the kernel synchronously
336
+
337
+ :param a: Input tensor A
338
+ :type a: cute.Tensor
339
+ :param b: Input tensor B
340
+ :type b: cute.Tensor
341
+ :param c: Output tensor C
342
+ :type c: cute.Tensor
343
+ :param stream: CUDA stream for asynchronous execution
344
+ :type stream: cuda.CUstream
345
+ :param epilogue_op: Optional elementwise lambda function to apply to the output tensor
346
+ :type epilogue_op: cutlass.Constexpr
347
+ :raises TypeError: If input data types are incompatible with the MMA instruction.
348
+ :raises AssertionError: If OOB (Out-Of-Bounds) tiles are present when TMA store is disabled.
349
+ """
350
+ # Setup static attributes before smem/grid/tma computation
351
+ self.a_dtype: Type[cutlass.Numeric] = a.element_type
352
+ self.b_dtype: Type[cutlass.Numeric] = b.element_type
353
+ self.c_dtype: Type[cutlass.Numeric] = c.element_type
354
+ self.a_major_mode = utils.LayoutEnum.from_tensor(a).mma_major_mode()
355
+ self.b_major_mode = utils.LayoutEnum.from_tensor(b).mma_major_mode()
356
+ self.c_layout = utils.LayoutEnum.from_tensor(c)
357
+
358
+ # Check if input data types are compatible with MMA instruction
359
+ if cutlass.const_expr(self.a_dtype != self.b_dtype):
360
+ raise TypeError(f"Type must match: {self.a_dtype} != {self.b_dtype}")
361
+
362
+ # Setup attributes that dependent on gemm inputs
363
+ self._setup_attributes()
364
+
365
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
366
+ self.a_dtype,
367
+ self.a_major_mode,
368
+ self.b_major_mode,
369
+ self.acc_dtype,
370
+ self.cta_group,
371
+ self.mma_tiler[:2],
372
+ )
373
+ atom_thr_size = cute.size(tiled_mma.thr_id.shape)
374
+
375
+ # Setup TMA load for A
376
+ a_op = sm100_utils.cluster_shape_to_tma_atom_A(
377
+ self.cluster_shape_mn, tiled_mma.thr_id
378
+ )
379
+ a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
380
+ tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
381
+ a_op,
382
+ a,
383
+ a_smem_layout,
384
+ self.mma_tiler,
385
+ tiled_mma,
386
+ self.cluster_layout_vmnk.shape,
387
+ internal_type=(
388
+ cutlass.TFloat32 if a.element_type is cutlass.Float32 else None
389
+ ),
390
+ )
391
+
392
+ # Setup TMA load for B
393
+ b_op = sm100_utils.cluster_shape_to_tma_atom_B(
394
+ self.cluster_shape_mn, tiled_mma.thr_id
395
+ )
396
+ b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
397
+ tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
398
+ b_op,
399
+ b,
400
+ b_smem_layout,
401
+ self.mma_tiler,
402
+ tiled_mma,
403
+ self.cluster_layout_vmnk.shape,
404
+ internal_type=(
405
+ cutlass.TFloat32 if b.element_type is cutlass.Float32 else None
406
+ ),
407
+ )
408
+
409
+ a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
410
+ b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
411
+ self.num_tma_load_bytes = (a_copy_size + b_copy_size) * atom_thr_size
412
+
413
+ # Setup store for C
414
+ tma_atom_c = None
415
+ tma_tensor_c = None
416
+ if cutlass.const_expr(self.use_tma_store):
417
+ c_cta_v_layout = cute.composition(
418
+ cute.make_identity_layout(c.shape), self.epi_tile
419
+ )
420
+ epi_smem_layout = cute.slice_(self.c_smem_layout_staged, (None, None, 0))
421
+ tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
422
+ cpasync.CopyBulkTensorTileS2GOp(),
423
+ c,
424
+ epi_smem_layout,
425
+ c_cta_v_layout,
426
+ )
427
+
428
+ # Compute grid size
429
+ grid = self._compute_grid(c, self.cta_tile_shape_mnk, self.cluster_shape_mn)
430
+
431
+ self.buffer_align_bytes = 1024
432
+
433
+ c_smem_size = (
434
+ cute.cosize(self.c_smem_layout_staged.outer) if self.use_tma_store else 0
435
+ )
436
+
437
+ # Define shared storage for kernel
438
+ @cute.struct
439
+ class SharedStorage:
440
+ ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
441
+ ab_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
442
+ acc_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
443
+ tmem_dealloc_mbar_ptr: cutlass.Int64
444
+ tmem_holding_buf: cutlass.Int32
445
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
446
+ sC: cute.struct.Align[
447
+ cute.struct.MemRange[
448
+ self.c_dtype,
449
+ c_smem_size,
450
+ ],
451
+ self.buffer_align_bytes,
452
+ ]
453
+ # (MMA, MMA_M, MMA_K, STAGE)
454
+ sA: cute.struct.Align[
455
+ cute.struct.MemRange[
456
+ self.a_dtype, cute.cosize(self.a_smem_layout_staged.outer)
457
+ ],
458
+ self.buffer_align_bytes,
459
+ ]
460
+ # (MMA, MMA_N, MMA_K, STAGE)
461
+ sB: cute.struct.Align[
462
+ cute.struct.MemRange[
463
+ self.b_dtype, cute.cosize(self.b_smem_layout_staged.outer)
464
+ ],
465
+ self.buffer_align_bytes,
466
+ ]
467
+
468
+ self.shared_storage = SharedStorage
469
+
470
+ # Launch the kernel synchronously
471
+ self.kernel(
472
+ tiled_mma,
473
+ tma_atom_a,
474
+ tma_tensor_a,
475
+ tma_atom_b,
476
+ tma_tensor_b,
477
+ tma_atom_c,
478
+ tma_tensor_c if self.use_tma_store else c,
479
+ self.cluster_layout_vmnk,
480
+ self.a_smem_layout_staged,
481
+ self.b_smem_layout_staged,
482
+ self.c_smem_layout_staged,
483
+ self.epi_tile,
484
+ epilogue_op,
485
+ ).launch(
486
+ grid=grid,
487
+ block=[self.threads_per_cta, 1, 1],
488
+ cluster=(*self.cluster_shape_mn, 1),
489
+ stream=stream,
490
+ )
491
+ return
492
+
493
+ # GPU device kernel
494
+ @cute.kernel
495
+ def kernel(
496
+ self,
497
+ tiled_mma: cute.TiledMma,
498
+ tma_atom_a: cute.CopyAtom,
499
+ mA_mkl: cute.Tensor,
500
+ tma_atom_b: cute.CopyAtom,
501
+ mB_nkl: cute.Tensor,
502
+ tma_atom_c: Optional[cute.CopyAtom],
503
+ mC_mnl: cute.Tensor,
504
+ cluster_layout_vmnk: cute.Layout,
505
+ a_smem_layout_staged: cute.ComposedLayout,
506
+ b_smem_layout_staged: cute.ComposedLayout,
507
+ c_smem_layout_staged: Union[cute.Layout, cute.ComposedLayout, None],
508
+ epi_tile: cute.Tile,
509
+ epilogue_op: cutlass.Constexpr,
510
+ ):
511
+ """
512
+ GPU device kernel performing the batched GEMM computation.
513
+ """
514
+ warp_idx = cute.arch.warp_idx()
515
+ warp_idx = cute.arch.make_warp_uniform(warp_idx)
516
+
517
+ #
518
+ # Prefetch tma descriptor
519
+ #
520
+ if warp_idx == 0:
521
+ cpasync.prefetch_descriptor(tma_atom_a)
522
+ cpasync.prefetch_descriptor(tma_atom_b)
523
+ if cutlass.const_expr(self.use_tma_store):
524
+ cpasync.prefetch_descriptor(tma_atom_c)
525
+
526
+ use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
527
+
528
+ #
529
+ # Setup cta/thread coordinates
530
+ #
531
+ # Coords inside cluster
532
+ bidx, bidy, bidz = cute.arch.block_idx()
533
+ mma_tile_coord_v = bidx % cute.size(tiled_mma.thr_id.shape)
534
+ is_leader_cta = mma_tile_coord_v == 0
535
+ cta_rank_in_cluster = cute.arch.make_warp_uniform(
536
+ cute.arch.block_idx_in_cluster()
537
+ )
538
+ block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
539
+ cta_rank_in_cluster
540
+ )
541
+ # Coords outside cluster
542
+ cta_coord = (bidx, bidy, bidz)
543
+ mma_tile_coord_mnl = (
544
+ cta_coord[0] // cute.size(tiled_mma.thr_id.shape),
545
+ cta_coord[1],
546
+ cta_coord[2],
547
+ )
548
+ # Coords inside cta
549
+ tidx, _, _ = cute.arch.thread_idx()
550
+
551
+ #
552
+ # Alloc and init: a+b full/empty, accumulator full, tensor memory dealloc barrier
553
+ #
554
+ smem = utils.SmemAllocator()
555
+ storage = smem.allocate(self.shared_storage)
556
+
557
+ tmem_dealloc_mbar_ptr = storage.tmem_dealloc_mbar_ptr
558
+ tmem_holding_buf = storage.tmem_holding_buf
559
+
560
+ # Initialize mainloop ab_pipeline (barrier) and states
561
+ ab_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
562
+ num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
563
+ ab_pipeline_consumer_group = pipeline.CooperativeGroup(
564
+ pipeline.Agent.Thread, num_tma_producer
565
+ )
566
+ ab_pipeline = pipeline.PipelineTmaUmma.create(
567
+ barrier_storage=storage.ab_full_mbar_ptr.data_ptr(),
568
+ num_stages=self.num_ab_stage,
569
+ producer_group=ab_pipeline_producer_group,
570
+ consumer_group=ab_pipeline_consumer_group,
571
+ tx_count=self.num_tma_load_bytes,
572
+ cta_layout_vmnk=cluster_layout_vmnk,
573
+ )
574
+ ab_producer_state = pipeline.make_pipeline_state(
575
+ pipeline.PipelineUserType.Producer, self.num_ab_stage
576
+ )
577
+ ab_consumer_state = pipeline.make_pipeline_state(
578
+ pipeline.PipelineUserType.Consumer, self.num_ab_stage
579
+ )
580
+
581
+ # Initialize acc_pipeline (barrier) and states
582
+ acc_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
583
+ acc_pipeline_consumer_group = pipeline.CooperativeGroup(
584
+ pipeline.Agent.Thread, self.threads_per_cta, self.threads_per_cta
585
+ )
586
+ acc_pipeline = pipeline.PipelineUmmaAsync.create(
587
+ barrier_storage=storage.acc_full_mbar_ptr.data_ptr(),
588
+ num_stages=self.num_acc_stage,
589
+ producer_group=acc_pipeline_producer_group,
590
+ consumer_group=acc_pipeline_consumer_group,
591
+ cta_layout_vmnk=cluster_layout_vmnk,
592
+ )
593
+ acc_producer_state = pipeline.make_pipeline_state(
594
+ pipeline.PipelineUserType.Producer, self.num_acc_stage
595
+ )
596
+ acc_consumer_state = pipeline.make_pipeline_state(
597
+ pipeline.PipelineUserType.Consumer, self.num_acc_stage
598
+ )
599
+
600
+ # Tensor memory dealloc barrier init
601
+ if use_2cta_instrs:
602
+ if warp_idx == 0:
603
+ num_tmem_dealloc_threads = 32
604
+ with cute.arch.elect_one():
605
+ cute.arch.mbarrier_init(
606
+ tmem_dealloc_mbar_ptr, num_tmem_dealloc_threads
607
+ )
608
+ cute.arch.mbarrier_init_fence()
609
+
610
+ # Cluster arrive after barrier init
611
+ if cute.size(self.cluster_shape_mn) > 1:
612
+ cute.arch.cluster_arrive_relaxed()
613
+
614
+ #
615
+ # Setup smem tensor A/B/C
616
+ #
617
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
618
+ sC = (
619
+ storage.sC.get_tensor(
620
+ c_smem_layout_staged.outer, swizzle=c_smem_layout_staged.inner
621
+ )
622
+ if self.use_tma_store
623
+ else None
624
+ )
625
+ # (MMA, MMA_M, MMA_K, STAGE)
626
+ sA = storage.sA.get_tensor(
627
+ a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
628
+ )
629
+ # (MMA, MMA_N, MMA_K, STAGE)
630
+ sB = storage.sB.get_tensor(
631
+ b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
632
+ )
633
+
634
+ #
635
+ # Compute multicast mask for A/B buffer full
636
+ #
637
+ a_full_mcast_mask = None
638
+ b_full_mcast_mask = None
639
+ if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
640
+ a_full_mcast_mask = cpasync.create_tma_multicast_mask(
641
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
642
+ )
643
+ b_full_mcast_mask = cpasync.create_tma_multicast_mask(
644
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
645
+ )
646
+
647
+ #
648
+ # Local_tile partition global tensors
649
+ #
650
+ # (bM, bK, RestM, RestK, RestL)
651
+ gA_mkl = cute.local_tile(
652
+ mA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
653
+ )
654
+ # (bN, bK, RestN, RestK, RestL)
655
+ gB_nkl = cute.local_tile(
656
+ mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
657
+ )
658
+ # (bM, bN, RestM, RestN, RestL)
659
+ gC_mnl = cute.local_tile(
660
+ mC_mnl, cute.slice_(self.mma_tiler, (None, None, 0)), (None, None, None)
661
+ )
662
+ k_tile_cnt = cute.size(gA_mkl, mode=[3])
663
+
664
+ #
665
+ # Partition global tensor for TiledMMA_A/B/C
666
+ #
667
+ thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
668
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
669
+ tCgA = thr_mma.partition_A(gA_mkl)
670
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
671
+ tCgB = thr_mma.partition_B(gB_nkl)
672
+ # (MMA, MMA_M, MMA_N, RestM, RestN, RestL)
673
+ tCgC = thr_mma.partition_C(gC_mnl)
674
+
675
+ #
676
+ # Partition global/shared tensor for TMA load A/B
677
+ #
678
+ # TMA load A partition_S/D
679
+ a_cta_layout = cute.make_layout(
680
+ cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
681
+ )
682
+ # ((atom_v, rest_v), STAGE)
683
+ # ((atom_v, rest_v), RestM, RestK, RestL)
684
+ tAsA, tAgA = cpasync.tma_partition(
685
+ tma_atom_a,
686
+ block_in_cluster_coord_vmnk[2],
687
+ a_cta_layout,
688
+ cute.group_modes(sA, 0, 3),
689
+ cute.group_modes(tCgA, 0, 3),
690
+ )
691
+ # TMA load B partition_S/D
692
+ b_cta_layout = cute.make_layout(
693
+ cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
694
+ )
695
+ # ((atom_v, rest_v), STAGE)
696
+ # ((atom_v, rest_v), RestN, RestK, RestL)
697
+ tBsB, tBgB = cpasync.tma_partition(
698
+ tma_atom_b,
699
+ block_in_cluster_coord_vmnk[1],
700
+ b_cta_layout,
701
+ cute.group_modes(sB, 0, 3),
702
+ cute.group_modes(tCgB, 0, 3),
703
+ )
704
+
705
+ #
706
+ # Partition shared/tensor memory tensor for TiledMMA_A/B/C
707
+ #
708
+ # (MMA, MMA_M, MMA_K, STAGE)
709
+ tCrA = tiled_mma.make_fragment_A(sA)
710
+ # (MMA, MMA_N, MMA_K, STAGE)
711
+ tCrB = tiled_mma.make_fragment_B(sB)
712
+ # (MMA, MMA_M, MMA_N)
713
+ acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
714
+ # (MMA, MMA_M, MMA_N)
715
+ tCtAcc_fake = tiled_mma.make_fragment_C(acc_shape)
716
+
717
+ #
718
+ # Cluster wait before tensor memory alloc
719
+ #
720
+ if cute.size(self.cluster_shape_mn) > 1:
721
+ cute.arch.cluster_wait()
722
+
723
+ #
724
+ # Alloc tensor memory buffer
725
+ #
726
+ if warp_idx == 0:
727
+ cute.arch.alloc_tmem(
728
+ self.num_tmem_alloc_cols, tmem_holding_buf, is_two_cta=use_2cta_instrs
729
+ )
730
+
731
+ #
732
+ # Bar sync for retrieve tensor memory ptr from shared memory
733
+ #
734
+ cute.arch.barrier()
735
+
736
+ #
737
+ # Retrieving tensor memory ptr and make accumulator tensor
738
+ #
739
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
740
+ self.acc_dtype, alignment=16, ptr_to_buffer_holding_addr=tmem_holding_buf
741
+ )
742
+ # (MMA, MMA_M, MMA_N)
743
+ tCtAcc = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
744
+
745
+ #
746
+ # Partition for epilogue
747
+ #
748
+ tiled_copy_t2r, tTR_tAcc, tTR_rAcc = self.epilog_tmem_copy_and_partition(
749
+ tidx, tCtAcc, tCgC, epi_tile, use_2cta_instrs
750
+ )
751
+
752
+ tTR_rC = None
753
+ tiled_copy_r2s = None
754
+ simt_atom = None
755
+ tRS_rC = None
756
+ tRS_sC = None
757
+ bSG_sC = None
758
+ bSG_gC = None
759
+ tTR_gC = None
760
+ if cutlass.const_expr(self.use_tma_store):
761
+ tTR_rC = cute.make_fragment(tTR_rAcc.shape, self.c_dtype)
762
+ tiled_copy_r2s, tRS_rC, tRS_sC = self.epilog_smem_copy_and_partition(
763
+ tiled_copy_t2r, tTR_rC, tidx, sC
764
+ )
765
+ tma_atom_c, bSG_sC, bSG_gC = self.epilog_gmem_copy_and_partition(
766
+ tidx, tma_atom_c, tCgC, epi_tile, sC
767
+ )
768
+ else:
769
+ simt_atom, tTR_rC, tTR_gC = self.epilog_gmem_copy_and_partition(
770
+ tidx, tiled_copy_t2r, tCgC, epi_tile, sC
771
+ )
772
+
773
+ #
774
+ # Slice to per mma tile index
775
+ #
776
+ # ((atom_v, rest_v), RestK)
777
+ tAgA = tAgA[(None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])]
778
+ # ((atom_v, rest_v), RestK)
779
+ tBgB = tBgB[(None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])]
780
+ if cutlass.const_expr(self.use_tma_store):
781
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
782
+ bSG_gC = bSG_gC[(None, None, None, *mma_tile_coord_mnl)]
783
+ else:
784
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N)
785
+ tTR_gC = tTR_gC[(None, None, None, None, None, *mma_tile_coord_mnl)]
786
+
787
+ #
788
+ # Pipelining TMA load A/B and MMA mainloop
789
+ #
790
+ prefetch_k_tile_cnt = cutlass.min(self.num_ab_stage - 2, k_tile_cnt)
791
+
792
+ if warp_idx == 0:
793
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt
794
+ peek_ab_empty_status = cutlass.Boolean(1)
795
+ if ab_producer_state.count < k_tile_cnt:
796
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
797
+ ab_producer_state
798
+ )
799
+ #
800
+ # Prefetch TMA load A/B
801
+ #
802
+ for prefetch_idx in cutlass.range(prefetch_k_tile_cnt, unroll=1):
803
+ # Conditionally wait for AB buffer empty
804
+ ab_pipeline.producer_acquire(ab_producer_state, peek_ab_empty_status)
805
+
806
+ # TMA load A/B
807
+ cute.copy(
808
+ tma_atom_a,
809
+ tAgA[(None, ab_producer_state.count)],
810
+ tAsA[(None, ab_producer_state.index)],
811
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
812
+ mcast_mask=a_full_mcast_mask,
813
+ )
814
+ cute.copy(
815
+ tma_atom_b,
816
+ tBgB[(None, ab_producer_state.count)],
817
+ tBsB[(None, ab_producer_state.index)],
818
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
819
+ mcast_mask=b_full_mcast_mask,
820
+ )
821
+
822
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt + k_tile + 1
823
+ ab_producer_state.advance()
824
+ peek_ab_empty_status = cutlass.Boolean(1)
825
+ if ab_producer_state.count < k_tile_cnt:
826
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
827
+ ab_producer_state
828
+ )
829
+
830
+ # Peek (try_wait) AB buffer full for k_tile = 0
831
+ peek_ab_full_status = cutlass.Boolean(1)
832
+ if ab_consumer_state.count < k_tile_cnt and is_leader_cta:
833
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(ab_consumer_state)
834
+
835
+ #
836
+ # MMA mainloop
837
+ #
838
+ for k_tile in range(k_tile_cnt):
839
+ # Conditionally wait for AB buffer empty
840
+ ab_pipeline.producer_acquire(ab_producer_state, peek_ab_empty_status)
841
+
842
+ if ab_producer_state.count < k_tile_cnt:
843
+ # TMA load A/B
844
+ cute.copy(
845
+ tma_atom_a,
846
+ tAgA[(None, ab_producer_state.count)],
847
+ tAsA[(None, ab_producer_state.index)],
848
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
849
+ mcast_mask=a_full_mcast_mask,
850
+ )
851
+ cute.copy(
852
+ tma_atom_b,
853
+ tBgB[(None, ab_producer_state.count)],
854
+ tBsB[(None, ab_producer_state.index)],
855
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
856
+ mcast_mask=b_full_mcast_mask,
857
+ )
858
+
859
+ if is_leader_cta:
860
+ # Conditionally wait for AB buffer full
861
+ ab_pipeline.consumer_wait(ab_consumer_state, peek_ab_full_status)
862
+
863
+ # tCtAcc += tCrA * tCrB
864
+ num_kblocks = cute.size(tCrA, mode=[2])
865
+ for kblock_idx in cutlass.range(num_kblocks, unroll_full=True):
866
+ kblock_coord = (None, None, kblock_idx, ab_consumer_state.index)
867
+
868
+ cute.gemm(
869
+ tiled_mma,
870
+ tCtAcc,
871
+ tCrA[kblock_coord],
872
+ tCrB[kblock_coord],
873
+ tCtAcc,
874
+ )
875
+ # Enable accumulate on tCtAcc after first kblock
876
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, True)
877
+
878
+ # Async arrive AB buffer empty
879
+ ab_pipeline.consumer_release(ab_consumer_state)
880
+
881
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt + k_tile + 1
882
+ ab_producer_state.advance()
883
+ peek_ab_empty_status = cutlass.Boolean(1)
884
+ if ab_producer_state.count < k_tile_cnt:
885
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
886
+ ab_producer_state
887
+ )
888
+
889
+ # Peek (try_wait) AB buffer full for k_tile = k_tile + 1
890
+ ab_consumer_state.advance()
891
+ peek_ab_full_status = cutlass.Boolean(1)
892
+ if ab_consumer_state.count < k_tile_cnt:
893
+ if is_leader_cta:
894
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(
895
+ ab_consumer_state
896
+ )
897
+
898
+ # Async arrive accumulator buffer full
899
+ if is_leader_cta:
900
+ acc_pipeline.producer_commit(acc_producer_state)
901
+
902
+ #
903
+ # Epilogue
904
+ #
905
+
906
+ # Release tensor memory allocation lock
907
+ if warp_idx == 0:
908
+ cute.arch.relinquish_tmem_alloc_permit(is_two_cta=use_2cta_instrs)
909
+
910
+ # Wait for accumulator buffer full
911
+ acc_pipeline.consumer_wait(acc_consumer_state)
912
+
913
+ tTR_tAcc = cute.group_modes(tTR_tAcc, 3, cute.rank(tTR_tAcc))
914
+ if cutlass.const_expr(self.use_tma_store):
915
+ bSG_gC = cute.group_modes(bSG_gC, 1, cute.rank(bSG_gC))
916
+ else:
917
+ tTR_gC = cute.group_modes(tTR_gC, 3, cute.rank(tTR_gC))
918
+
919
+ c_pipeline = None
920
+ if cutlass.const_expr(self.use_tma_store):
921
+ # Initialize tma store c_pipeline
922
+ c_producer_group = pipeline.CooperativeGroup(
923
+ pipeline.Agent.Thread, self.threads_per_cta, self.threads_per_cta
924
+ )
925
+ c_pipeline = pipeline.PipelineTmaStore.create(
926
+ num_stages=self.num_c_stage,
927
+ producer_group=c_producer_group,
928
+ )
929
+
930
+ #
931
+ # Store accumulator to global memory in subtiles
932
+ #
933
+ subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3])
934
+ for subtile_idx in range(subtile_cnt):
935
+ #
936
+ # Load accumulator from tensor memory buffer to register
937
+ #
938
+ tTR_tAcc_mn = tTR_tAcc[(None, None, None, subtile_idx)]
939
+ cute.copy(tiled_copy_t2r, tTR_tAcc_mn, tTR_rAcc)
940
+
941
+ if cutlass.const_expr(self.use_tma_store):
942
+ #
943
+ # Perform epilogue op on accumulator and convert to C type
944
+ #
945
+ acc_vec = tiled_copy_r2s.retile(tTR_rAcc).load()
946
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
947
+ tRS_rC.store(acc_vec)
948
+
949
+ #
950
+ # Store C to shared memory
951
+ #
952
+ c_buffer = subtile_idx % self.num_c_stage
953
+ cute.copy(tiled_copy_r2s, tRS_rC, tRS_sC[(None, None, None, c_buffer)])
954
+ # Fence and barrier to make sure shared memory store is visible to TMA store
955
+ cute.arch.fence_proxy(
956
+ cute.arch.ProxyKind.async_shared,
957
+ space=cute.arch.SharedSpace.shared_cta,
958
+ )
959
+ cute.arch.barrier()
960
+
961
+ #
962
+ # TMA store C to global memory
963
+ #
964
+ if warp_idx == 0:
965
+ cute.copy(
966
+ tma_atom_c,
967
+ bSG_sC[(None, c_buffer)],
968
+ bSG_gC[(None, subtile_idx)],
969
+ )
970
+ # Fence and barrier to make sure TMA store is completed to recollect C buffer
971
+ c_pipeline.producer_commit()
972
+ c_pipeline.producer_acquire()
973
+ cute.arch.barrier()
974
+ else:
975
+ #
976
+ # Perform epilogue op on accumulator and convert to C type
977
+ #
978
+ acc_vec = tTR_rAcc.load()
979
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
980
+ tTR_rC.store(acc_vec)
981
+
982
+ #
983
+ # Store C to global memory
984
+ #
985
+ cute.copy(simt_atom, tTR_rC, tTR_gC[(None, None, None, subtile_idx)])
986
+
987
+ #
988
+ # Dealloc the tensor memory buffer
989
+ #
990
+ cute.arch.barrier()
991
+ if warp_idx == 0:
992
+ if use_2cta_instrs:
993
+ cute.arch.mbarrier_arrive(
994
+ tmem_dealloc_mbar_ptr, cta_rank_in_cluster ^ 1
995
+ )
996
+ cute.arch.mbarrier_wait(tmem_dealloc_mbar_ptr, 0)
997
+ cute.arch.dealloc_tmem(
998
+ tmem_ptr, self.num_tmem_alloc_cols, is_two_cta=use_2cta_instrs
999
+ )
1000
+
1001
+ #
1002
+ # Wait for C store complete
1003
+ #
1004
+ if cutlass.const_expr(self.use_tma_store):
1005
+ c_pipeline.producer_tail()
1006
+
1007
+ #
1008
+ # Wait A/B buffer empty
1009
+ #
1010
+ if warp_idx == 0:
1011
+ # Reverse prefetch_k_tile_cnt times to next available buffer
1012
+ for i in range(prefetch_k_tile_cnt):
1013
+ ab_producer_state.reverse()
1014
+ ab_pipeline.producer_tail(ab_producer_state)
1015
+ return
1016
+
1017
+ def epilog_tmem_copy_and_partition(
1018
+ self,
1019
+ tidx: cutlass.Int32,
1020
+ tAcc: cute.Tensor,
1021
+ gC_mnl: cute.Tensor,
1022
+ epi_tile: cute.Tile,
1023
+ use_2cta_instrs: Union[cutlass.Boolean, bool],
1024
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1025
+ """
1026
+ Make tiledCopy for tensor memory load, then use it to partition tensor memory (source) and register array (destination).
1027
+
1028
+ :param tidx: The thread index in epilogue warp groups
1029
+ :type tidx: cutlass.Int32
1030
+ :param tAcc: The accumulator tensor to be copied and partitioned
1031
+ :type tAcc: cute.Tensor
1032
+ :param gC_mnl: The global tensor C
1033
+ :type gC_mnl: cute.Tensor
1034
+ :param epi_tile: The epilogue tiler
1035
+ :type epi_tile: cute.Tile
1036
+ :param use_2cta_instrs: Whether use_2cta_instrs is enabled
1037
+ :type use_2cta_instrs: bool
1038
+
1039
+ :return: A tuple containing (tiled_copy_t2r, tTR_tAcc, tTR_rAcc) where:
1040
+ - tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1041
+ - tTR_tAcc: The partitioned accumulator tensor
1042
+ - tTR_rAcc: The accumulated tensor in register used to hold t2r results
1043
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1044
+ """
1045
+ # Make tiledCopy for tensor memory load
1046
+ copy_atom_t2r = sm100_utils.get_tmem_load_op(
1047
+ self.cta_tile_shape_mnk,
1048
+ self.c_layout,
1049
+ self.c_dtype,
1050
+ self.acc_dtype,
1051
+ epi_tile,
1052
+ use_2cta_instrs,
1053
+ )
1054
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N)
1055
+ tAcc_epi = cute.flat_divide(
1056
+ tAcc[((None, None), 0, 0)],
1057
+ epi_tile,
1058
+ )
1059
+ # (EPI_TILE_M, EPI_TILE_N)
1060
+ tiled_copy_t2r = tcgen05.make_tmem_copy(
1061
+ copy_atom_t2r, tAcc_epi[(None, None, 0, 0)]
1062
+ )
1063
+
1064
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1065
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M)
1066
+ tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
1067
+
1068
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1069
+ gC_mnl_epi = cute.flat_divide(
1070
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1071
+ )
1072
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1073
+ tTR_gC = thr_copy_t2r.partition_D(gC_mnl_epi)
1074
+ # (T2R, T2R_M, T2R_N)
1075
+ tTR_rAcc = cute.make_fragment(
1076
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.acc_dtype
1077
+ )
1078
+ return tiled_copy_t2r, tTR_tAcc, tTR_rAcc
1079
+
1080
+ def epilog_smem_copy_and_partition(
1081
+ self,
1082
+ tiled_copy_t2r: cute.TiledCopy,
1083
+ tTR_rC: cute.Tensor,
1084
+ tidx: cutlass.Int32,
1085
+ sC: cute.Tensor,
1086
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1087
+ """
1088
+ Make tiledCopy for shared memory store, then use it to partition register array (source) and shared memory (destination).
1089
+
1090
+ :param tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1091
+ :type tiled_copy_t2r: cute.TiledCopy
1092
+ :param tTR_rC: The partitioned accumulator tensor
1093
+ :type tTR_rC: cute.Tensor
1094
+ :param tidx: The thread index in epilogue warp groups
1095
+ :type tidx: cutlass.Int32
1096
+ :param sC: The shared memory tensor to be copied and partitioned
1097
+ :type sC: cute.Tensor
1098
+
1099
+ :return: A tuple containing (tiled_copy_r2s, tRS_rC, tRS_sC) where:
1100
+ - tiled_copy_r2s: The tiled copy operation for register to smem copy(r2s)
1101
+ - tRS_rC: The partitioned tensor C (register source)
1102
+ - tRS_sC: The partitioned tensor C (smem destination)
1103
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1104
+ """
1105
+ copy_atom_r2s = sm100_utils.get_smem_store_op(
1106
+ self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
1107
+ )
1108
+ tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
1109
+ # (R2S, R2S_M, R2S_N, PIPE_D)
1110
+ thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
1111
+ tRS_sC = thr_copy_r2s.partition_D(sC)
1112
+ # (R2S, R2S_M, R2S_N)
1113
+ tRS_rC = tiled_copy_r2s.retile(tTR_rC)
1114
+ return tiled_copy_r2s, tRS_rC, tRS_sC
1115
+
1116
+ def epilog_gmem_copy_and_partition(
1117
+ self,
1118
+ tidx: cutlass.Int32,
1119
+ atom: Union[cute.CopyAtom, cute.TiledCopy],
1120
+ gC_mnl: cute.Tensor,
1121
+ epi_tile: cute.Tile,
1122
+ sC: cute.Tensor,
1123
+ ) -> Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]:
1124
+ """Make tiledCopy for global memory store, then use it to:
1125
+ - partition register array (source) and global memory (destination) for none TMA store version;
1126
+ - partition shared memory (source) and global memory (destination) for TMA store version.
1127
+
1128
+ :param tidx: The thread index in epilogue warp groups
1129
+ :type tidx: cutlass.Int32
1130
+ :param atom: The copy_atom_c to be used for TMA store version, or tiled_copy_t2r for none TMA store version
1131
+ :type atom: cute.CopyAtom or cute.TiledCopy
1132
+ :param gC_mnl: The global tensor C
1133
+ :type gC_mnl: cute.Tensor
1134
+ :param epi_tile: The epilogue tiler
1135
+ :type epi_tile: cute.Tile
1136
+ :param sC: The shared memory tensor to be copied and partitioned
1137
+ :type sC: cute.Tensor
1138
+
1139
+ :return: A tuple containing either:
1140
+ - For TMA store: (tma_atom_c, bSG_sC, bSG_gC) where:
1141
+ - tma_atom_c: The TMA copy atom
1142
+ - bSG_sC: The partitioned shared memory tensor C
1143
+ - bSG_gC: The partitioned global tensor C
1144
+ - For non-TMA store: (simt_atom, tTR_rC, tTR_gC) where:
1145
+ - simt_atom: The SIMT copy atom
1146
+ - tTR_rC: The register tensor C
1147
+ - tTR_gC: The partitioned global tensor C
1148
+ :rtype: Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]
1149
+ """
1150
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1151
+ gC_epi = cute.flat_divide(
1152
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1153
+ )
1154
+ if cutlass.const_expr(self.use_tma_store):
1155
+ tma_atom_c = atom
1156
+ sC_for_tma_partition = cute.group_modes(sC, 0, 2)
1157
+ gC_for_tma_partition = cute.group_modes(gC_epi, 0, 2)
1158
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1159
+ # ((ATOM_V, REST_V), EPI_M, EPI_N, RestM, RestN, RestL)
1160
+ bSG_sC, bSG_gC = cpasync.tma_partition(
1161
+ tma_atom_c,
1162
+ 0,
1163
+ cute.make_layout(1),
1164
+ sC_for_tma_partition,
1165
+ gC_for_tma_partition,
1166
+ )
1167
+ return tma_atom_c, bSG_sC, bSG_gC
1168
+ else:
1169
+ tiled_copy_t2r = atom
1170
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1171
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1172
+ tTR_gC = thr_copy_t2r.partition_D(gC_epi)
1173
+ # (T2R, T2R_M, T2R_N)
1174
+ tTR_rC = cute.make_fragment(
1175
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.c_dtype
1176
+ )
1177
+ simt_atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), self.c_dtype)
1178
+ return simt_atom, tTR_rC, tTR_gC
1179
+
1180
+ @staticmethod
1181
+ def _compute_stages(
1182
+ tiled_mma: cute.TiledMma,
1183
+ mma_tiler_mnk: Tuple[int, int, int],
1184
+ a_dtype: Type[cutlass.Numeric],
1185
+ b_dtype: Type[cutlass.Numeric],
1186
+ epi_tile: cute.Tile,
1187
+ c_dtype: Type[cutlass.Numeric],
1188
+ c_layout: utils.LayoutEnum,
1189
+ smem_capacity: int,
1190
+ occupancy: int,
1191
+ use_tma_store: bool,
1192
+ ) -> Tuple[int, int, int]:
1193
+ """Computes the number of stages for A/B/C operands based on heuristics.
1194
+
1195
+ :param tiled_mma: The tiled MMA object defining the core computation.
1196
+ :type tiled_mma: cute.TiledMma
1197
+ :param mma_tiler_mnk: The shape (M, N, K) of the MMA tile.
1198
+ :type mma_tiler_mnk: tuple[int, int, int]
1199
+ :param a_dtype: Data type of operand A.
1200
+ :type a_dtype: type[cutlass.Numeric]
1201
+ :param b_dtype: Data type of operand B.
1202
+ :type b_dtype: type[cutlass.Numeric]
1203
+ :param epi_tile: The epilogue tile shape.
1204
+ :type epi_tile: cute.Tile
1205
+ :param c_dtype: Data type of operand C (output).
1206
+ :type c_dtype: type[cutlass.Numeric]
1207
+ :param c_layout: Layout enum of operand C in global memory.
1208
+ :type c_layout: utils.LayoutEnum
1209
+ :param smem_capacity: Total available shared memory capacity in bytes.
1210
+ :type smem_capacity: int
1211
+ :param occupancy: Target number of CTAs per SM (occupancy).
1212
+ :type occupancy: int
1213
+ :param use_tma_store: Whether TMA store is enabled.
1214
+ :type use_tma_store: bool
1215
+
1216
+ :return: A tuple containing the computed number of stages for:
1217
+ (ACC stages, A/B operand stages, epilogue stages)
1218
+ :rtype: tuple[int, int, int]
1219
+ """
1220
+ # Default ACC stages
1221
+ num_acc_stage = 1
1222
+ # Default C stages
1223
+ num_c_stage = 2 if use_tma_store else 0
1224
+
1225
+ # Calculate smem layout and size for one stage of A, B, and C
1226
+ a_smem_layout_stage_one = sm100_utils.make_smem_layout_a(
1227
+ tiled_mma,
1228
+ mma_tiler_mnk,
1229
+ a_dtype,
1230
+ 1, # a tmp 1 stage is provided
1231
+ )
1232
+ b_smem_layout_staged_one = sm100_utils.make_smem_layout_b(
1233
+ tiled_mma,
1234
+ mma_tiler_mnk,
1235
+ b_dtype,
1236
+ 1, # a tmp 1 stage is provided
1237
+ )
1238
+ c_smem_layout_staged_one = (
1239
+ sm100_utils.make_smem_layout_epi(
1240
+ c_dtype,
1241
+ c_layout,
1242
+ epi_tile,
1243
+ 1,
1244
+ )
1245
+ if use_tma_store
1246
+ else None
1247
+ )
1248
+ ab_bytes_per_stage = cute.size_in_bytes(
1249
+ a_dtype, a_smem_layout_stage_one
1250
+ ) + cute.size_in_bytes(b_dtype, b_smem_layout_staged_one)
1251
+ mbar_helpers_bytes = 1024
1252
+ c_bytes_per_stage = (
1253
+ cute.size_in_bytes(c_dtype, c_smem_layout_staged_one)
1254
+ if use_tma_store
1255
+ else 0
1256
+ )
1257
+ c_bytes = c_bytes_per_stage * num_c_stage
1258
+
1259
+ # Calculate A/B stages:
1260
+ # Start with total smem per CTA (capacity / occupancy)
1261
+ # Subtract reserved bytes and initial C stages bytes
1262
+ # Divide remaining by bytes needed per A/B stage
1263
+ num_ab_stage = (
1264
+ smem_capacity - (occupancy + 1) * (mbar_helpers_bytes + c_bytes)
1265
+ ) // ab_bytes_per_stage
1266
+
1267
+ # Refine epilogue stages:
1268
+ # Calculate remaining smem after allocating for A/B stages and reserved bytes
1269
+ # Add remaining unused smem to epilogue
1270
+ if use_tma_store:
1271
+ num_c_stage += (
1272
+ smem_capacity
1273
+ - ab_bytes_per_stage * num_ab_stage
1274
+ - (occupancy + 1) * (mbar_helpers_bytes + c_bytes)
1275
+ ) // ((occupancy + 1) * c_bytes_per_stage)
1276
+ return num_acc_stage, num_ab_stage, num_c_stage
1277
+
1278
+ @staticmethod
1279
+ def _compute_grid(
1280
+ c: cute.Tensor,
1281
+ cta_tile_shape_mnk: Tuple[int, int, int],
1282
+ cluster_shape_mn: Tuple[int, int],
1283
+ ) -> Tuple[int, int, int]:
1284
+ """Compute grid shape for the output tensor C.
1285
+
1286
+ :param c: The output tensor C
1287
+ :type c: cute.Tensor
1288
+ :param cta_tile_shape_mnk: The shape (M, N, K) of the CTA tile.
1289
+ :type cta_tile_shape_mnk: tuple[int, int, int]
1290
+ :param cluster_shape_mn: Shape of each cluster in M, N dimensions.
1291
+ :type cluster_shape_mn: tuple[int, int]
1292
+
1293
+ :return: Grid shape for kernel launch.
1294
+ :rtype: tuple[int, int, int]
1295
+ """
1296
+
1297
+ cluster_shape_mnl = (*cluster_shape_mn, 1)
1298
+
1299
+ grid = cute.round_up(
1300
+ (
1301
+ cute.ceil_div(c.layout.shape[0], cta_tile_shape_mnk[0]),
1302
+ cute.ceil_div(c.layout.shape[1], cta_tile_shape_mnk[1]),
1303
+ c.layout.shape[2],
1304
+ ),
1305
+ cluster_shape_mnl,
1306
+ )
1307
+
1308
+ return grid
1309
+
1310
+ @staticmethod
1311
+ def _compute_num_tmem_alloc_cols(
1312
+ tiled_mma: cute.TiledMma, mma_tiler: Tuple[int, int, int]
1313
+ ) -> int:
1314
+ """
1315
+ Compute the number of tensor memory allocation columns.
1316
+
1317
+ :param tiled_mma: The tiled MMA object defining the core computation.
1318
+ :type tiled_mma: cute.TiledMma
1319
+ :param mma_tiler: The shape (M, N, K) of the MMA tile.
1320
+ :type mma_tiler: tuple[int, int, int]
1321
+
1322
+ :return: The number of tensor memory allocation columns.
1323
+ :rtype: int
1324
+ """
1325
+ acc_shape = tiled_mma.partition_shape_C(mma_tiler[:2])
1326
+ tCtAcc_fake = tiled_mma.make_fragment_C(acc_shape)
1327
+ return sm100_utils.get_num_tmem_alloc_cols(tCtAcc_fake)
1328
+
1329
+ @staticmethod
1330
+ def is_valid_dtypes(
1331
+ ab_dtype: Type[cutlass.Numeric],
1332
+ acc_dtype: Type[cutlass.Numeric],
1333
+ c_dtype: Type[cutlass.Numeric],
1334
+ ) -> bool:
1335
+ """
1336
+ Check if the dtypes are valid
1337
+
1338
+ :param ab_dtype: The data type of the A and B operands
1339
+ :type ab_dtype: Type[cutlass.Numeric]
1340
+ :param acc_dtype: The data type of the accumulator
1341
+ :type acc_dtype: Type[cutlass.Numeric]
1342
+ :param c_dtype: The data type of the output tensor
1343
+ :type c_dtype: Type[cutlass.Numeric]
1344
+
1345
+ :return: True if the dtypes are valid, False otherwise
1346
+ :rtype: bool
1347
+ """
1348
+ is_valid = True
1349
+ if ab_dtype not in {
1350
+ cutlass.Float16,
1351
+ cutlass.BFloat16,
1352
+ cutlass.TFloat32,
1353
+ cutlass.Uint8,
1354
+ cutlass.Int8,
1355
+ cutlass.Float8E4M3FN,
1356
+ cutlass.Float8E5M2,
1357
+ }:
1358
+ is_valid = False
1359
+ if (
1360
+ acc_dtype not in {cutlass.Float32, cutlass.Float16, cutlass.Int32}
1361
+ or acc_dtype == cutlass.Float16
1362
+ and ab_dtype
1363
+ not in {cutlass.Float16, cutlass.Float8E4M3FN, cutlass.Float8E5M2}
1364
+ or acc_dtype == cutlass.Int32
1365
+ and ab_dtype not in {cutlass.Uint8, cutlass.Int8}
1366
+ ):
1367
+ is_valid = False
1368
+ if (
1369
+ acc_dtype == cutlass.Float32
1370
+ and c_dtype
1371
+ not in {
1372
+ cutlass.Float32,
1373
+ cutlass.Float16,
1374
+ cutlass.BFloat16,
1375
+ cutlass.Float8E4M3FN,
1376
+ cutlass.Float8E5M2,
1377
+ cutlass.Int32,
1378
+ cutlass.Int8,
1379
+ cutlass.Uint8,
1380
+ }
1381
+ or acc_dtype == cutlass.Float16
1382
+ and c_dtype
1383
+ not in {
1384
+ cutlass.BFloat16,
1385
+ cutlass.Float16,
1386
+ }
1387
+ or acc_dtype == cutlass.Int32
1388
+ and c_dtype
1389
+ not in {
1390
+ cutlass.BFloat16,
1391
+ cutlass.Float16,
1392
+ cutlass.Float32,
1393
+ cutlass.Int32,
1394
+ cutlass.Int8,
1395
+ cutlass.Uint8,
1396
+ }
1397
+ ):
1398
+ is_valid = False
1399
+ return is_valid
1400
+
1401
+ @staticmethod
1402
+ def is_valid_mma_tiler_and_cluster_shape(
1403
+ use_2cta_instrs: bool,
1404
+ mma_tiler_mn: Tuple[int, int],
1405
+ cluster_shape_mn: Tuple[int, int],
1406
+ ) -> bool:
1407
+ """
1408
+ Check if the mma tiler and cluster shape are valid
1409
+
1410
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1411
+ :type use_2cta_instrs: bool
1412
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1413
+ :type mma_tiler_mn: Tuple[int, int]
1414
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1415
+ :type cluster_shape_mn: Tuple[int, int]
1416
+
1417
+ :return: True if the mma tiler and cluster shape are valid, False otherwise
1418
+ :rtype: bool
1419
+ """
1420
+ is_valid = True
1421
+ # Skip invalid mma tile shape
1422
+ if not (
1423
+ (not use_2cta_instrs and mma_tiler_mn[0] in [64, 128])
1424
+ or (use_2cta_instrs and mma_tiler_mn[0] in [128, 256])
1425
+ ):
1426
+ is_valid = False
1427
+ if mma_tiler_mn[1] not in range(32, 257, 32):
1428
+ is_valid = False
1429
+ # Skip illegal cluster shape
1430
+ if cluster_shape_mn[0] % (2 if use_2cta_instrs else 1) != 0:
1431
+ is_valid = False
1432
+ # Skip invalid cluster shape
1433
+ is_power_of_2 = lambda x: x > 0 and (x & (x - 1)) == 0
1434
+ if (
1435
+ cluster_shape_mn[0] * cluster_shape_mn[1] > 16
1436
+ or cluster_shape_mn[0] <= 0
1437
+ or cluster_shape_mn[1] <= 0
1438
+ or not is_power_of_2(cluster_shape_mn[0])
1439
+ or not is_power_of_2(cluster_shape_mn[1])
1440
+ ):
1441
+ is_valid = False
1442
+ return is_valid
1443
+
1444
+ @staticmethod
1445
+ def is_valid_tensor_alignment(
1446
+ m: int,
1447
+ n: int,
1448
+ k: int,
1449
+ l: int,
1450
+ ab_dtype: Type[cutlass.Numeric],
1451
+ c_dtype: Type[cutlass.Numeric],
1452
+ a_major: str,
1453
+ b_major: str,
1454
+ c_major: str,
1455
+ ) -> bool:
1456
+ """
1457
+ Check if the tensor alignment is valid
1458
+
1459
+ :param m: The number of rows in the A tensor
1460
+ :type m: int
1461
+ :param n: The number of columns in the B tensor
1462
+ :type n: int
1463
+ :param k: The number of columns in the A tensor
1464
+ :type k: int
1465
+ :param l: The number of columns in the C tensor
1466
+ :type l: int
1467
+ :param ab_dtype: The data type of the A and B operands
1468
+ :type ab_dtype: Type[cutlass.Numeric]
1469
+ :param c_dtype: The data type of the output tensor
1470
+ :type c_dtype: Type[cutlass.Numeric]
1471
+ :param a_major: The major axis of the A tensor
1472
+ :type a_major: str
1473
+ :param b_major: The major axis of the B tensor
1474
+ :type b_major: str
1475
+ :param c_major: The major axis of the C tensor
1476
+ :type c_major: str
1477
+
1478
+ :return: True if the problem shape is valid, False otherwise
1479
+ :rtype: bool
1480
+ """
1481
+ is_valid = True
1482
+
1483
+ def check_contigous_16B_alignment(dtype, is_mode0_major, tensor_shape):
1484
+ major_mode_idx = 0 if is_mode0_major else 1
1485
+ num_major_elements = tensor_shape[major_mode_idx]
1486
+ num_contiguous_elements = 16 * 8 // dtype.width
1487
+ return num_major_elements % num_contiguous_elements == 0
1488
+
1489
+ if (
1490
+ not check_contigous_16B_alignment(ab_dtype, a_major == "m", (m, k, l))
1491
+ or not check_contigous_16B_alignment(ab_dtype, b_major == "n", (n, k, l))
1492
+ or not check_contigous_16B_alignment(c_dtype, c_major == "m", (m, n, l))
1493
+ ):
1494
+ is_valid = False
1495
+ return is_valid
1496
+
1497
+ @staticmethod
1498
+ def is_valid_epilog_store_option(
1499
+ use_2cta_instrs: bool,
1500
+ use_tma_store: bool,
1501
+ m: int,
1502
+ n: int,
1503
+ mma_tiler_mn: Tuple[int, int],
1504
+ ) -> bool:
1505
+ """
1506
+ Check if the epilogue store option is valid
1507
+
1508
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1509
+ :type use_2cta_instrs: bool
1510
+ :param use_tma_store: Whether to use TMA store
1511
+ :type use_tma_store: bool
1512
+ :param m: The number of rows in the A tensor
1513
+ :type m: int
1514
+ :param n: The number of columns in the B tensor
1515
+ :type n: int
1516
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1517
+ :type mma_tiler_mn: Tuple[int, int]
1518
+
1519
+ :return: True if the epilogue store option is valid, False otherwise
1520
+ :rtype: bool
1521
+ """
1522
+
1523
+ is_valid = True
1524
+ # None TMA store version does not have predication, can not support OOB tiles
1525
+ cta_tile_shape_mn = (
1526
+ mma_tiler_mn[0] // (2 if use_2cta_instrs else 1),
1527
+ mma_tiler_mn[1],
1528
+ )
1529
+ if not use_tma_store:
1530
+ if not (m % cta_tile_shape_mn[0] == 0 and n % cta_tile_shape_mn[1] == 0):
1531
+ is_valid = False
1532
+ return is_valid
1533
+
1534
+ @staticmethod
1535
+ def can_implement(
1536
+ ab_dtype: Type[cutlass.Numeric],
1537
+ acc_dtype: Type[cutlass.Numeric],
1538
+ c_dtype: Type[cutlass.Numeric],
1539
+ use_2cta_instrs: bool,
1540
+ mma_tiler_mn: Tuple[int, int],
1541
+ cluster_shape_mn: Tuple[int, int],
1542
+ use_tma_store: bool,
1543
+ m: int,
1544
+ n: int,
1545
+ k: int,
1546
+ l: int,
1547
+ a_major: str,
1548
+ b_major: str,
1549
+ c_major: str,
1550
+ ) -> bool:
1551
+ """
1552
+ Check if the gemm can be implemented
1553
+
1554
+ :param ab_dtype: The data type of the A and B operands
1555
+ :type ab_dtype: Type[cutlass.Numeric]
1556
+ :param acc_dtype: The data type of the accumulator
1557
+ :type acc_dtype: Type[cutlass.Numeric]
1558
+ :param c_dtype: The data type of the output tensor
1559
+ :type c_dtype: Type[cutlass.Numeric]
1560
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1561
+ :type use_2cta_instrs: bool
1562
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1563
+ :type mma_tiler_mn: Tuple[int, int]
1564
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1565
+ :type cluster_shape_mn: Tuple[int, int]
1566
+ :param use_tma_store: Whether to use TMA store
1567
+ :type use_tma_store: bool
1568
+ :param m: The number of rows in the A tensor
1569
+ :type m: int
1570
+ :param n: The number of columns in the B tensor
1571
+ :type n: int
1572
+ :param k: The number of columns in the A tensor
1573
+ :type k: int
1574
+ :param l: The number of columns in the C tensor
1575
+ :type l: int
1576
+ :param a_major: The major axis of the A tensor
1577
+ :type a_major: str
1578
+ :param b_major: The major axis of the B tensor
1579
+ :type b_major: str
1580
+ :param c_major: The major axis of the C tensor
1581
+ :type c_major: str
1582
+
1583
+ :return: True if the gemm can be implemented, False otherwise
1584
+ :rtype: bool
1585
+ """
1586
+ can_implement = True
1587
+ # Skip unsupported types
1588
+ if not DenseGemmKernel.is_valid_dtypes(ab_dtype, acc_dtype, c_dtype):
1589
+ can_implement = False
1590
+ # Skip invalid mma tile shape and cluster shape
1591
+ if not DenseGemmKernel.is_valid_mma_tiler_and_cluster_shape(
1592
+ use_2cta_instrs, mma_tiler_mn, cluster_shape_mn
1593
+ ):
1594
+ can_implement = False
1595
+ # Skip illegal problem shape for load/store alignment
1596
+ if not DenseGemmKernel.is_valid_tensor_alignment(
1597
+ m, n, k, l, ab_dtype, c_dtype, a_major, b_major, c_major
1598
+ ):
1599
+ can_implement = False
1600
+ # Skip invalid epilogue store option
1601
+ if not DenseGemmKernel.is_valid_epilog_store_option(
1602
+ use_2cta_instrs, use_tma_store, m, n, mma_tiler_mn
1603
+ ):
1604
+ can_implement = False
1605
+ return can_implement
1606
+
1607
+
1608
+ def run_dense_gemm(
1609
+ mnkl: Tuple[int, int, int, int],
1610
+ ab_dtype: Type[cutlass.Numeric],
1611
+ c_dtype: Type[cutlass.Numeric],
1612
+ acc_dtype: Type[cutlass.Numeric],
1613
+ a_major: str,
1614
+ b_major: str,
1615
+ c_major: str,
1616
+ mma_tiler_mn: Tuple[int, int],
1617
+ cluster_shape_mn: Tuple[int, int],
1618
+ use_2cta_instrs: bool,
1619
+ use_tma_store: bool,
1620
+ tolerance: float,
1621
+ warmup_iterations: int = 0,
1622
+ iterations: int = 1,
1623
+ skip_ref_check: bool = False,
1624
+ measure_launch_overhead=False,
1625
+ ):
1626
+ """
1627
+ Prepare A/B/C tensors, launch GPU kernel, and reference checking.
1628
+ """
1629
+ print(f"Running B100 Dense GEMM test with:")
1630
+ print(f"mnkl: {mnkl}")
1631
+ print(f"AB dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}")
1632
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
1633
+ print(f"Mma Tiler (M, N): {mma_tiler_mn}, Cluster Shape (M, N): {cluster_shape_mn}")
1634
+ print(f"2CTA MMA instructions: {'True' if use_2cta_instrs else 'False'}")
1635
+ print(f"Use TMA Store: {'True' if use_tma_store else 'False'}")
1636
+ print(f"Tolerance: {tolerance}")
1637
+ print(f"Warmup iterations: {warmup_iterations}")
1638
+ print(f"Iterations: {iterations}")
1639
+ print(f"Skip reference checking: {skip_ref_check}")
1640
+
1641
+ # Unpack parameters
1642
+ m, n, k, l = mnkl
1643
+
1644
+ # Skip unsupported testcase
1645
+ if not DenseGemmKernel.can_implement(
1646
+ ab_dtype,
1647
+ acc_dtype,
1648
+ c_dtype,
1649
+ use_2cta_instrs,
1650
+ mma_tiler_mn,
1651
+ cluster_shape_mn,
1652
+ use_tma_store,
1653
+ m,
1654
+ n,
1655
+ k,
1656
+ l,
1657
+ a_major,
1658
+ b_major,
1659
+ c_major,
1660
+ ):
1661
+ raise TypeError(
1662
+ f"Unsupported testcase {ab_dtype}, {acc_dtype}, {c_dtype}, {use_2cta_instrs}, {mma_tiler_mn}, {cluster_shape_mn}, {use_tma_store}, {m}, {n}, {k}, {l}, {a_major}, {b_major}, {c_major}"
1663
+ )
1664
+
1665
+ if not torch.cuda.is_available():
1666
+ raise RuntimeError("GPU is required to run this example!")
1667
+
1668
+ torch.manual_seed(1111)
1669
+
1670
+ # Create and permute tensor A/B/C
1671
+ def create_and_permute_tensor(
1672
+ l, mode0, mode1, is_mode0_major, dtype, is_dynamic_layout=True
1673
+ ):
1674
+ # is_mode0_major: (l, mode1, mode0) -> (mode0, mode1, l)
1675
+ # else: (l, mode0, mode1) -> (mode0, mode1, l)
1676
+ shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
1677
+ permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
1678
+ is_unsigned = dtype in {cutlass.Uint8}
1679
+ # Temporarily use uint8 as torch does not support fp8 type
1680
+ torch_dtype = (
1681
+ cutlass_torch.dtype(dtype)
1682
+ if dtype not in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}
1683
+ else torch.uint8
1684
+ )
1685
+
1686
+ # Create dtype torch tensor (cpu)
1687
+ torch_tensor_cpu = cutlass_torch.create_and_permute_torch_tensor(
1688
+ shape,
1689
+ torch_dtype,
1690
+ permute_order=permute_order,
1691
+ init_type=cutlass_torch.TensorInitType.RANDOM,
1692
+ init_config=cutlass_torch.RandomInitConfig(
1693
+ min_val=0 if is_unsigned else -2, max_val=4 if is_unsigned else 2
1694
+ ),
1695
+ )
1696
+ # Create dtype torch tensor (gpu)
1697
+ torch_tensor = torch_tensor_cpu.cuda()
1698
+
1699
+ # Create f32 torch tensor (cpu)
1700
+ f32_torch_tensor = torch_tensor_cpu.to(dtype=torch.float32)
1701
+
1702
+ # Create dtype cute tensor (gpu)
1703
+ cute_tensor = from_dlpack(torch_tensor, assumed_align=16)
1704
+ cute_tensor.element_type = dtype
1705
+ if is_dynamic_layout:
1706
+ cute_tensor = cute_tensor.mark_layout_dynamic(
1707
+ leading_dim=(0 if is_mode0_major else 1)
1708
+ )
1709
+ cute_tensor = cutlass_torch.convert_cute_tensor(
1710
+ f32_torch_tensor,
1711
+ cute_tensor,
1712
+ dtype,
1713
+ is_dynamic_layout=is_dynamic_layout,
1714
+ )
1715
+
1716
+ return f32_torch_tensor, cute_tensor, torch_tensor
1717
+
1718
+ a_ref, a_tensor, a_torch = create_and_permute_tensor(
1719
+ l, m, k, a_major == "m", ab_dtype, is_dynamic_layout=True
1720
+ )
1721
+ b_ref, b_tensor, b_torch = create_and_permute_tensor(
1722
+ l, n, k, b_major == "n", ab_dtype, is_dynamic_layout=True
1723
+ )
1724
+ c_ref, c_tensor, c_torch = create_and_permute_tensor(
1725
+ l, m, n, c_major == "m", c_dtype, is_dynamic_layout=True
1726
+ )
1727
+
1728
+ # Configure gemm kernel
1729
+ gemm = DenseGemmKernel(
1730
+ acc_dtype,
1731
+ use_2cta_instrs,
1732
+ mma_tiler_mn,
1733
+ cluster_shape_mn,
1734
+ use_tma_store,
1735
+ )
1736
+
1737
+ torch_stream = torch.cuda.Stream()
1738
+ stream = cuda.CUstream(torch_stream.cuda_stream)
1739
+ # Compile gemm kernel
1740
+ compiled_gemm = cute.compile(gemm, a_tensor, b_tensor, c_tensor, stream)
1741
+
1742
+ # Launch GPU kernel
1743
+ # Warm up
1744
+ for i in range(warmup_iterations):
1745
+ compiled_gemm(a_tensor, b_tensor, c_tensor, stream)
1746
+ # Execution
1747
+ for i in range(iterations):
1748
+ compiled_gemm(a_tensor, b_tensor, c_tensor, stream)
1749
+
1750
+ # Compute reference result
1751
+ if not skip_ref_check:
1752
+ if ab_dtype in {
1753
+ cutlass.Int8,
1754
+ cutlass.Uint8,
1755
+ cutlass.Float8E4M3FN,
1756
+ cutlass.Float8E5M2,
1757
+ }:
1758
+ ref = torch.einsum("mkl,nkl->mnl", a_ref.cpu(), b_ref.cpu())
1759
+ else:
1760
+ ref = (torch.einsum("mkl,nkl->mnl", a_ref, b_ref)).cpu()
1761
+
1762
+ # Copy gpu result back
1763
+ gpu_c = c_torch.cpu()
1764
+
1765
+ # Convert ref to c_type
1766
+ if c_dtype == cutlass.Float32:
1767
+ ref_c = ref
1768
+ elif c_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}:
1769
+ # m major: (l, n, m) -> (m, n, l)
1770
+ # n major: (l, m, n) -> (m, n, l)
1771
+ permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
1772
+ shape = (l, m, n) if c_major == "n" else (l, n, m)
1773
+ f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
1774
+ shape,
1775
+ torch.uint8,
1776
+ permute_order=permute_order,
1777
+ init_type=cutlass_torch.TensorInitType.SKIP,
1778
+ ).cuda()
1779
+ # Create dtype cute tensor (gpu)
1780
+ ref_c_tensor = from_dlpack(
1781
+ f8_torch_tensor, assumed_align=16
1782
+ ).mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
1783
+ ref_c_tensor.element_type = c_dtype
1784
+ ref_c_tensor = cutlass_torch.convert_cute_tensor(
1785
+ ref,
1786
+ ref_c_tensor,
1787
+ c_dtype,
1788
+ is_dynamic_layout=True,
1789
+ )
1790
+
1791
+ ref_c = f8_torch_tensor.cpu()
1792
+ else:
1793
+ ref_c = ref.to(cutlass_torch.dtype(c_dtype))
1794
+
1795
+ # Reference checking ref_c and gpu_c
1796
+ torch.testing.assert_close(
1797
+ gpu_c,
1798
+ ref_c,
1799
+ atol=tolerance,
1800
+ rtol=1e-05,
1801
+ )
1802
+
1803
+
1804
+ if __name__ == "__main__":
1805
+
1806
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
1807
+ try:
1808
+ return tuple(int(x.strip()) for x in s.split(","))
1809
+ # or: return tuple([int(x.strip()) for x in s.split(",")])
1810
+ except ValueError:
1811
+ raise argparse.ArgumentTypeError(
1812
+ "Invalid format. Expected comma-separated integers."
1813
+ )
1814
+
1815
+ parser = argparse.ArgumentParser(
1816
+ description="Example of MxNxKxL GEMM on Blackwell."
1817
+ )
1818
+
1819
+ parser.add_argument(
1820
+ "--mnkl",
1821
+ type=parse_comma_separated_ints,
1822
+ default=(256, 256, 512, 1),
1823
+ help="mnkl dimensions (comma-separated)",
1824
+ )
1825
+ parser.add_argument(
1826
+ "--mma_tiler_mn",
1827
+ type=parse_comma_separated_ints,
1828
+ default=(128, 128),
1829
+ help="Mma tiler (comma-separated)",
1830
+ )
1831
+ parser.add_argument(
1832
+ "--cluster_shape_mn",
1833
+ type=parse_comma_separated_ints,
1834
+ default=(1, 1),
1835
+ help="Cluster shape (comma-separated)",
1836
+ )
1837
+ parser.add_argument("--ab_dtype", type=cutlass.dtype, default=cutlass.TFloat32)
1838
+ parser.add_argument("--c_dtype", type=cutlass.dtype, default=cutlass.Float32)
1839
+ parser.add_argument("--acc_dtype", type=cutlass.dtype, default=cutlass.Float32)
1840
+ parser.add_argument(
1841
+ "--use_2cta_instrs",
1842
+ action="store_true",
1843
+ help="Enable 2CTA MMA instructions feature",
1844
+ )
1845
+ parser.add_argument("--a_major", choices=["k", "m"], type=str, default="k")
1846
+ parser.add_argument("--b_major", choices=["k", "n"], type=str, default="k")
1847
+ parser.add_argument("--c_major", choices=["n", "m"], type=str, default="n")
1848
+ parser.add_argument(
1849
+ "--use_tma_store", action="store_true", help="Use tma store or not"
1850
+ )
1851
+ parser.add_argument(
1852
+ "--tolerance", type=float, default=1e-01, help="Tolerance for validation"
1853
+ )
1854
+ parser.add_argument(
1855
+ "--warmup_iterations", type=int, default=0, help="Warmup iterations"
1856
+ )
1857
+ parser.add_argument("--iterations", type=int, default=1, help="Iterations")
1858
+ parser.add_argument(
1859
+ "--skip_ref_check", action="store_true", help="Skip reference checking"
1860
+ )
1861
+
1862
+ args = parser.parse_args()
1863
+
1864
+ if len(args.mnkl) != 4:
1865
+ parser.error("--mnkl must contain exactly 4 values")
1866
+
1867
+ if len(args.mma_tiler_mn) != 2:
1868
+ parser.error("--mma_tiler_mn must contain exactly 2 values")
1869
+
1870
+ if len(args.cluster_shape_mn) != 2:
1871
+ parser.error("--cluster_shape_mn must contain exactly 2 values")
1872
+
1873
+ run_dense_gemm(
1874
+ args.mnkl,
1875
+ args.ab_dtype,
1876
+ args.c_dtype,
1877
+ args.acc_dtype,
1878
+ args.a_major,
1879
+ args.b_major,
1880
+ args.c_major,
1881
+ args.mma_tiler_mn,
1882
+ args.cluster_shape_mn,
1883
+ args.use_2cta_instrs,
1884
+ args.use_tma_store,
1885
+ args.tolerance,
1886
+ args.warmup_iterations,
1887
+ args.iterations,
1888
+ args.skip_ref_check,
1889
+ )
1890
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm_persistent.py ADDED
@@ -0,0 +1,2193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ from typing import Optional, Type, Tuple, Union
31
+
32
+ import cuda.bindings.driver as cuda
33
+ import torch
34
+
35
+ import cutlass
36
+ import cutlass.cute as cute
37
+ from cutlass.cute.nvgpu import cpasync, tcgen05
38
+ import cutlass.torch as cutlass_torch
39
+ import cutlass.utils as utils
40
+ import cutlass.pipeline as pipeline
41
+ import cutlass.cute.testing as testing
42
+ import cutlass.utils.blackwell_helpers as sm100_utils
43
+ from cutlass.cute.runtime import from_dlpack
44
+
45
+
46
+ """
47
+ A high-performance persistent batched dense GEMM example for the NVIDIA Blackwell SM100 architecture
48
+ using CUTE DSL.
49
+ - Matrix A is MxKxL, L is batch dimension, A can be row-major("K") or column-major("M")
50
+ - Matrix B is NxKxL, L is batch dimension, B can be row-major("N") or column-major("K")
51
+ - Matrix C is MxNxL, L is batch dimension, C can be row-major("N") or column-major("M")
52
+
53
+ This GEMM kernel supports the following features:
54
+ - Utilizes Tensor Memory Access (TMA) for efficient memory operations
55
+ - Utilizes Blackwell's tcgen05.mma for matrix multiply-accumulate (MMA) operations (including 2cta mma instructions)
56
+ - Implements TMA multicast with cluster to reduce L2 memory traffic
57
+ - Support persistent tile scheduling to better overlap memory load/store with mma between tiles
58
+ - Support warp specialization to avoid explicit pipelining between mainloop load and mma
59
+
60
+ This GEMM works as follows:
61
+ 1. DMA warp: Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.
62
+ 2. MMA warp: Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.
63
+ 3. EPILOGUE warp:
64
+ - Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.
65
+ - Type convert C matrix to output type.
66
+ - Optionally store C matrix from registers (RMEM) to shared memory (SMEM) to global memory (GMEM) with TMA operations,
67
+ or directly store C matrix from registers (RMEM) to global memory (GMEM) without TMA operations.
68
+ - Optionally accept an elementwise lambda function epilogue_op to apply to the output tensor:
69
+ e.g., relu can set epilogue_op = lambda x: cute.where(x > 0, x, cute.full_like(x, 0))
70
+
71
+ SM100 tcgen05.mma instructions operate as follows:
72
+ - Read matrix A from SMEM
73
+ - Read matrix B from SMEM
74
+ - Write accumulator to TMEM
75
+ The accumulator in TMEM must then be loaded to registers before writing back to GMEM.
76
+
77
+ Input arguments to this example is same as dense_gemm.py.
78
+
79
+ .. code-block:: bash
80
+
81
+ python examples/blackwell/dense_gemm_persistent.py \
82
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
83
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
84
+ --mnkl 8192,8192,8192,1 \
85
+ --use_tma_store --use_2cta_instrs
86
+
87
+ To collect performance with NCU profiler:
88
+
89
+ .. code-block:: bash
90
+
91
+ ncu python examples/blackwell/dense_gemm_persistent.py \
92
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
93
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
94
+ --mnkl 8192,8192,8192,1 \
95
+ --use_tma_store --use_2cta_instrs \
96
+ --warmup_iterations 1 --iterations 10 --skip_ref_check
97
+
98
+
99
+ Constraints are same as dense_gemm.py:
100
+ * Supported input data types: fp16, bf16, tf32, int8, uint8, fp8 (e4m3fn, e5m2),
101
+ see detailed valid dtype combinations in below PersistentDenseGemmKernel class documentation
102
+ * A/B tensor must have the same data type
103
+ * Mma tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
104
+ * Mma tiler N must be 32-256, step 32
105
+ * Cluster shape M/N must be positive and power of 2, total cluster size <= 16
106
+ * Cluster shape M must be multiple of 2 if use_2cta_instrs=True
107
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned,
108
+ i.e, number of elements is a multiple of 4, 8, and 16 for TFloat32,
109
+ Float16/BFloat16, and Int8/Uint8/Float8, respectively.
110
+ * OOB tiles are not allowed when TMA store is disabled
111
+ """
112
+
113
+
114
+ class PersistentDenseGemmKernel:
115
+ """This class implements batched matrix multiplication (C = A x B) with support for various data types
116
+ and architectural features specific to Blackwell GPUs with persistent tile scheduling and warp specialization.
117
+
118
+ :param acc_dtype: Data type for accumulation during computation
119
+ :type acc_dtype: type[cutlass.Numeric]
120
+ :param use_2cta_instrs: Whether to use CTA group 2 for advanced thread cooperation
121
+ :type use_2cta_instrs: bool
122
+ :param mma_tiler_mn: Shape of the Matrix Multiply-Accumulate (MMA) tile (M,N)
123
+ :type mma_tiler_mn: Tuple[int, int]
124
+ :param cluster_shape_mn: Cluster dimensions (M,N) for parallel processing
125
+ :type cluster_shape_mn: Tuple[int, int]
126
+ :param use_tma_store: Whether to use Tensor Memory Access (TMA) for storing results
127
+ :type use_tma_store: bool
128
+
129
+ :note: In current version, A and B tensor must have the same data type
130
+ - i.e., Float8E4M3FN for A and Float8E5M2 for B is not supported
131
+
132
+ :note: Supported A/B data types:
133
+ - TFloat32
134
+ - Float16/BFloat16
135
+ - Int8/Uint8
136
+ - Float8E4M3FN/Float8E5M2
137
+
138
+ :note: Supported accumulator data types:
139
+ - Float32 (for all floating point A/B data types)
140
+ - Float16 (only for fp16 and fp8 A/B data types)
141
+ - Int32 (only for uint8/int8 A/B data types)
142
+
143
+ :note: Supported C data types:
144
+ - Float32 (for float32 and int32 accumulator data types)
145
+ - Int32 (for float32 and int32 accumulator data types)
146
+ - Float16/BFloat16 (for fp16 and fp8 accumulator data types)
147
+ - Int8/Uint8 (for uint8/int8 accumulator data types)
148
+ - Float8E4M3FN/Float8E5M2 (for float32 accumulator data types)
149
+
150
+ :note: Constraints:
151
+ - MMA tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
152
+ - MMA tiler N must be 32-256, step 32
153
+ - Cluster shape M must be multiple of 2 if use_2cta_instrs=True
154
+ - Cluster shape M/N must be positive and power of 2, total cluster size <= 16
155
+
156
+ Example:
157
+ >>> gemm = PersistentDenseGemmKernel(
158
+ ... acc_dtype=cutlass.Float32,
159
+ ... use_2cta_instrs=True,
160
+ ... mma_tiler_mn=(128, 128),
161
+ ... cluster_shape_mn=(2, 2)
162
+ ... )
163
+ >>> gemm(a_tensor, b_tensor, c_tensor, max_active_clusters, stream)
164
+ """
165
+
166
+ def __init__(
167
+ self,
168
+ acc_dtype: Type[cutlass.Numeric],
169
+ use_2cta_instrs: bool,
170
+ mma_tiler_mn: Tuple[int, int],
171
+ cluster_shape_mn: Tuple[int, int],
172
+ use_tma_store: bool,
173
+ ):
174
+ """Initializes the configuration for a Blackwell dense GEMM kernel.
175
+
176
+ This configuration includes several key aspects:
177
+
178
+ 1. MMA Instruction Settings (tcgen05):
179
+ - acc_dtype: Data types for MMA accumulator.
180
+ - mma_tiler_mn: The (M, N) shape of the MMA instruction tiler.
181
+ - use_2cta_instrs: Boolean indicating if the tcgen05 MMA variant
182
+ with cta_group=2 should be used.
183
+
184
+ 2. Cluster Shape:
185
+ - cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster.
186
+
187
+ 3. Output C tensor store mode:
188
+ - use_tma_store: Boolean indicating whether to use Tensor Memory Access (TMA) for storing results.
189
+
190
+ :param acc_dtype: Data type of the accumulator.
191
+ :type acc_dtype: type[cutlass.Numeric]
192
+ :param mma_tiler_mn: Tuple (M, N) shape of the MMA instruction.
193
+ :type mma_tiler_mn: Tuple[int, int]
194
+ :param use_2cta_instrs: Boolean, True to use cta_group=2 MMA variant.
195
+ :type use_2cta_instrs: bool
196
+ :param cluster_shape_mn: Tuple (ClusterM, ClusterN) shape of the cluster.
197
+ :type cluster_shape_mn: Tuple[int, int]
198
+ :param use_tma_store: Use Tensor Memory Access (TMA) or normal store for output C tensor.
199
+ :type use_tma_store: bool
200
+ """
201
+
202
+ self.acc_dtype: Type[cutlass.Numeric] = acc_dtype
203
+ self.use_2cta_instrs = use_2cta_instrs
204
+ self.cluster_shape_mn = cluster_shape_mn
205
+ # K dimension is deferred in _setup_attributes
206
+ self.mma_tiler = (*mma_tiler_mn, 1)
207
+ self.use_tma_store = use_tma_store
208
+
209
+ self.cta_group = (
210
+ tcgen05.CtaGroup.TWO if use_2cta_instrs else tcgen05.CtaGroup.ONE
211
+ )
212
+
213
+ self.occupancy = 1
214
+ # Set specialized warp ids
215
+ self.epilog_warp_id = (
216
+ 0,
217
+ 1,
218
+ 2,
219
+ 3,
220
+ )
221
+ self.mma_warp_id = 4
222
+ self.tma_warp_id = 5
223
+ self.threads_per_cta = 32 * len(
224
+ (self.mma_warp_id, self.tma_warp_id, *self.epilog_warp_id)
225
+ )
226
+ # Set barrier id for cta sync, epilogue sync and tmem ptr sync
227
+ self.cta_sync_bar_id = 0
228
+ self.epilog_sync_bar_id = 1
229
+ self.tmem_ptr_sync_bar_id = 2
230
+ self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
231
+
232
+ def _setup_attributes(self):
233
+ """Set up configurations that are dependent on GEMM inputs
234
+
235
+ This method configures various attributes based on the input tensor properties
236
+ (data types, leading dimensions) and kernel settings:
237
+ - Configuring tiled MMA
238
+ - Computing MMA/cluster/tile shapes
239
+ - Computing cluster layout
240
+ - Computing multicast CTAs for A/B
241
+ - Computing epilogue subtile
242
+ - Setting up A/B/C stage counts in shared memory
243
+ - Computing A/B/C shared memory layout
244
+ - Computing tensor memory allocation columns
245
+ """
246
+ # Configure tiled mma
247
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
248
+ self.a_dtype,
249
+ self.a_major_mode,
250
+ self.b_major_mode,
251
+ self.acc_dtype,
252
+ self.cta_group,
253
+ self.mma_tiler[:2],
254
+ )
255
+
256
+ # Compute mma/cluster/tile shapes
257
+ mma_inst_shape_k = cute.size(tiled_mma.shape_mnk, mode=[2])
258
+ mma_inst_tile_k = 4
259
+ self.mma_tiler = (
260
+ self.mma_tiler[0],
261
+ self.mma_tiler[1],
262
+ mma_inst_shape_k * mma_inst_tile_k,
263
+ )
264
+ self.cta_tile_shape_mnk = (
265
+ self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
266
+ self.mma_tiler[1],
267
+ self.mma_tiler[2],
268
+ )
269
+
270
+ # Compute cluster layout
271
+ self.cluster_layout_vmnk = cute.tiled_divide(
272
+ cute.make_layout((*self.cluster_shape_mn, 1)),
273
+ (tiled_mma.thr_id.shape,),
274
+ )
275
+
276
+ # Compute number of multicast CTAs for A/B
277
+ self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
278
+ self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
279
+ self.is_a_mcast = self.num_mcast_ctas_a > 1
280
+ self.is_b_mcast = self.num_mcast_ctas_b > 1
281
+
282
+ # Compute epilogue subtile
283
+ if cutlass.const_expr(self.use_tma_store):
284
+ self.epi_tile = sm100_utils.compute_epilogue_tile_shape(
285
+ self.cta_tile_shape_mnk,
286
+ self.use_2cta_instrs,
287
+ self.c_layout,
288
+ self.c_dtype,
289
+ )
290
+ else:
291
+ self.epi_tile = self.cta_tile_shape_mnk[:2]
292
+
293
+ # Setup A/B/C stage count in shared memory and ACC stage count in tensor memory
294
+ self.num_acc_stage, self.num_ab_stage, self.num_c_stage = self._compute_stages(
295
+ tiled_mma,
296
+ self.mma_tiler,
297
+ self.a_dtype,
298
+ self.b_dtype,
299
+ self.epi_tile,
300
+ self.c_dtype,
301
+ self.c_layout,
302
+ self.smem_capacity,
303
+ self.occupancy,
304
+ self.use_tma_store,
305
+ )
306
+
307
+ # Compute A/B/C shared memory layout
308
+ self.a_smem_layout_staged = sm100_utils.make_smem_layout_a(
309
+ tiled_mma,
310
+ self.mma_tiler,
311
+ self.a_dtype,
312
+ self.num_ab_stage,
313
+ )
314
+ self.b_smem_layout_staged = sm100_utils.make_smem_layout_b(
315
+ tiled_mma,
316
+ self.mma_tiler,
317
+ self.b_dtype,
318
+ self.num_ab_stage,
319
+ )
320
+ self.c_smem_layout_staged = (
321
+ sm100_utils.make_smem_layout_epi(
322
+ self.c_dtype,
323
+ self.c_layout,
324
+ self.epi_tile,
325
+ self.num_c_stage,
326
+ )
327
+ if cutlass.const_expr(self.use_tma_store)
328
+ else None
329
+ )
330
+
331
+ # Compute the number of tensor memory allocation columns
332
+ self.num_tmem_alloc_cols = self._compute_num_tmem_alloc_cols(
333
+ tiled_mma, self.mma_tiler, self.num_acc_stage
334
+ )
335
+
336
+ @cute.jit
337
+ def __call__(
338
+ self,
339
+ a: cute.Tensor,
340
+ b: cute.Tensor,
341
+ c: cute.Tensor,
342
+ max_active_clusters: cutlass.Constexpr,
343
+ stream: cuda.CUstream,
344
+ epilogue_op: cutlass.Constexpr = lambda x: x,
345
+ ):
346
+ """Execute the GEMM operation in steps:
347
+ - Setup static attributes before smem/grid/tma computation
348
+ - Setup TMA load/store atoms and tensors
349
+ - Compute grid size with regard to hardware constraints
350
+ - Define shared storage for kernel
351
+ - Launch the kernel synchronously
352
+
353
+ :param a: Input tensor A
354
+ :type a: cute.Tensor
355
+ :param b: Input tensor B
356
+ :type b: cute.Tensor
357
+ :param c: Output tensor C
358
+ :type c: cute.Tensor
359
+ :param max_active_clusters: Maximum number of active clusters
360
+ :type max_active_clusters: cutlass.Constexpr
361
+ :param stream: CUDA stream for asynchronous execution
362
+ :type stream: cuda.CUstream
363
+ :param epilogue_op: Optional elementwise lambda function to apply to the output tensor
364
+ :type epilogue_op: cutlass.Constexpr
365
+ :raises TypeError: If input data types are incompatible with the MMA instruction.
366
+ :raises AssertionError: If OOB (Out-Of-Bounds) tiles are present when TMA store is disabled.
367
+ """
368
+ # Setup static attributes before smem/grid/tma computation
369
+ self.a_dtype: Type[cutlass.Numeric] = a.element_type
370
+ self.b_dtype: Type[cutlass.Numeric] = b.element_type
371
+ self.c_dtype: Type[cutlass.Numeric] = c.element_type
372
+ self.a_major_mode = utils.LayoutEnum.from_tensor(a).mma_major_mode()
373
+ self.b_major_mode = utils.LayoutEnum.from_tensor(b).mma_major_mode()
374
+ self.c_layout = utils.LayoutEnum.from_tensor(c)
375
+
376
+ # Check if input data types are compatible with MMA instruction
377
+ if cutlass.const_expr(self.a_dtype != self.b_dtype):
378
+ raise TypeError(f"Type must match: {self.a_dtype} != {self.b_dtype}")
379
+
380
+ # Setup attributes that dependent on gemm inputs
381
+ self._setup_attributes()
382
+
383
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
384
+ self.a_dtype,
385
+ self.a_major_mode,
386
+ self.b_major_mode,
387
+ self.acc_dtype,
388
+ self.cta_group,
389
+ self.mma_tiler[:2],
390
+ )
391
+ atom_thr_size = cute.size(tiled_mma.thr_id.shape)
392
+
393
+ # Setup TMA load for A
394
+ a_op = sm100_utils.cluster_shape_to_tma_atom_A(
395
+ self.cluster_shape_mn, tiled_mma.thr_id
396
+ )
397
+ a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
398
+ tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
399
+ a_op,
400
+ a,
401
+ a_smem_layout,
402
+ self.mma_tiler,
403
+ tiled_mma,
404
+ self.cluster_layout_vmnk.shape,
405
+ internal_type=(
406
+ cutlass.TFloat32 if a.element_type is cutlass.Float32 else None
407
+ ),
408
+ )
409
+
410
+ # Setup TMA load for B
411
+ b_op = sm100_utils.cluster_shape_to_tma_atom_B(
412
+ self.cluster_shape_mn, tiled_mma.thr_id
413
+ )
414
+ b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
415
+ tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
416
+ b_op,
417
+ b,
418
+ b_smem_layout,
419
+ self.mma_tiler,
420
+ tiled_mma,
421
+ self.cluster_layout_vmnk.shape,
422
+ internal_type=(
423
+ cutlass.TFloat32 if b.element_type is cutlass.Float32 else None
424
+ ),
425
+ )
426
+
427
+ a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
428
+ b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
429
+ self.num_tma_load_bytes = (a_copy_size + b_copy_size) * atom_thr_size
430
+
431
+ # Setup TMA store for C
432
+ tma_atom_c = None
433
+ tma_tensor_c = None
434
+ if cutlass.const_expr(self.use_tma_store):
435
+ c_cta_v_layout = cute.composition(
436
+ cute.make_identity_layout(c.shape), self.epi_tile
437
+ )
438
+ epi_smem_layout = cute.slice_(self.c_smem_layout_staged, (None, None, 0))
439
+ tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
440
+ cpasync.CopyBulkTensorTileS2GOp(),
441
+ c,
442
+ epi_smem_layout,
443
+ c_cta_v_layout,
444
+ )
445
+
446
+ # Compute grid size
447
+ self.tile_sched_params, grid = self._compute_grid(
448
+ c, self.cta_tile_shape_mnk, self.cluster_shape_mn, max_active_clusters
449
+ )
450
+
451
+ self.buffer_align_bytes = 1024
452
+
453
+ c_smem_size = (
454
+ cute.cosize(self.c_smem_layout_staged.outer)
455
+ if cutlass.const_expr(self.use_tma_store)
456
+ else 0
457
+ )
458
+
459
+ # Define shared storage for kernel
460
+ @cute.struct
461
+ class SharedStorage:
462
+ ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
463
+ ab_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
464
+ acc_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
465
+ acc_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
466
+ tmem_dealloc_mbar_ptr: cutlass.Int64
467
+ tmem_holding_buf: cutlass.Int32
468
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
469
+ sC: cute.struct.Align[
470
+ cute.struct.MemRange[
471
+ self.c_dtype,
472
+ c_smem_size,
473
+ ],
474
+ self.buffer_align_bytes,
475
+ ]
476
+ # (MMA, MMA_M, MMA_K, STAGE)
477
+ sA: cute.struct.Align[
478
+ cute.struct.MemRange[
479
+ self.a_dtype, cute.cosize(self.a_smem_layout_staged.outer)
480
+ ],
481
+ self.buffer_align_bytes,
482
+ ]
483
+ # (MMA, MMA_N, MMA_K, STAGE)
484
+ sB: cute.struct.Align[
485
+ cute.struct.MemRange[
486
+ self.b_dtype, cute.cosize(self.b_smem_layout_staged.outer)
487
+ ],
488
+ self.buffer_align_bytes,
489
+ ]
490
+
491
+ self.shared_storage = SharedStorage
492
+
493
+ # Launch the kernel synchronously
494
+ self.kernel(
495
+ tiled_mma,
496
+ tma_atom_a,
497
+ tma_tensor_a,
498
+ tma_atom_b,
499
+ tma_tensor_b,
500
+ tma_atom_c,
501
+ tma_tensor_c if cutlass.const_expr(self.use_tma_store) else c,
502
+ self.cluster_layout_vmnk,
503
+ self.a_smem_layout_staged,
504
+ self.b_smem_layout_staged,
505
+ self.c_smem_layout_staged,
506
+ self.epi_tile,
507
+ self.tile_sched_params,
508
+ epilogue_op,
509
+ ).launch(
510
+ grid=grid,
511
+ block=[self.threads_per_cta, 1, 1],
512
+ cluster=(*self.cluster_shape_mn, 1),
513
+ stream=stream,
514
+ )
515
+ return
516
+
517
+ # GPU device kernel
518
+ @cute.kernel
519
+ def kernel(
520
+ self,
521
+ tiled_mma: cute.TiledMma,
522
+ tma_atom_a: cute.CopyAtom,
523
+ mA_mkl: cute.Tensor,
524
+ tma_atom_b: cute.CopyAtom,
525
+ mB_nkl: cute.Tensor,
526
+ tma_atom_c: Optional[cute.CopyAtom],
527
+ mC_mnl: cute.Tensor,
528
+ cluster_layout_vmnk: cute.Layout,
529
+ a_smem_layout_staged: cute.ComposedLayout,
530
+ b_smem_layout_staged: cute.ComposedLayout,
531
+ c_smem_layout_staged: Union[cute.Layout, cute.ComposedLayout, None],
532
+ epi_tile: cute.Tile,
533
+ tile_sched_params: utils.PersistentTileSchedulerParams,
534
+ epilogue_op: cutlass.Constexpr,
535
+ ):
536
+ """
537
+ GPU device kernel performing the Persistent batched GEMM computation.
538
+ """
539
+ warp_idx = cute.arch.warp_idx()
540
+ warp_idx = cute.arch.make_warp_uniform(warp_idx)
541
+
542
+ #
543
+ # Prefetch tma desc
544
+ #
545
+ if warp_idx == self.tma_warp_id:
546
+ cpasync.prefetch_descriptor(tma_atom_a)
547
+ cpasync.prefetch_descriptor(tma_atom_b)
548
+ if cutlass.const_expr(self.use_tma_store):
549
+ cpasync.prefetch_descriptor(tma_atom_c)
550
+
551
+ use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
552
+
553
+ #
554
+ # Setup cta/thread coordinates
555
+ #
556
+ # Coords inside cluster
557
+ bidx, bidy, bidz = cute.arch.block_idx()
558
+ mma_tile_coord_v = bidx % cute.size(tiled_mma.thr_id.shape)
559
+ is_leader_cta = mma_tile_coord_v == 0
560
+ cta_rank_in_cluster = cute.arch.make_warp_uniform(
561
+ cute.arch.block_idx_in_cluster()
562
+ )
563
+ block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
564
+ cta_rank_in_cluster
565
+ )
566
+ # Coord inside cta
567
+ tidx, _, _ = cute.arch.thread_idx()
568
+
569
+ #
570
+ # Alloc and init: a+b full/empty, accumulator full/empty, tensor memory dealloc barrier
571
+ #
572
+ smem = utils.SmemAllocator()
573
+ storage = smem.allocate(self.shared_storage)
574
+
575
+ tmem_dealloc_mbar_ptr = storage.tmem_dealloc_mbar_ptr
576
+ tmem_holding_buf = storage.tmem_holding_buf
577
+
578
+ # Initialize mainloop ab_pipeline (barrier) and states
579
+ ab_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
580
+ num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
581
+ ab_pipeline_consumer_group = pipeline.CooperativeGroup(
582
+ pipeline.Agent.Thread, num_tma_producer
583
+ )
584
+ ab_pipeline = pipeline.PipelineTmaUmma.create(
585
+ barrier_storage=storage.ab_full_mbar_ptr.data_ptr(),
586
+ num_stages=self.num_ab_stage,
587
+ producer_group=ab_pipeline_producer_group,
588
+ consumer_group=ab_pipeline_consumer_group,
589
+ tx_count=self.num_tma_load_bytes,
590
+ cta_layout_vmnk=cluster_layout_vmnk,
591
+ )
592
+
593
+ # Initialize acc_pipeline (barrier) and states
594
+ acc_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
595
+ num_acc_consumer_threads = len(self.epilog_warp_id) * (
596
+ 2 if use_2cta_instrs else 1
597
+ )
598
+ acc_pipeline_consumer_group = pipeline.CooperativeGroup(
599
+ pipeline.Agent.Thread, num_acc_consumer_threads
600
+ )
601
+ acc_pipeline = pipeline.PipelineUmmaAsync.create(
602
+ barrier_storage=storage.acc_full_mbar_ptr.data_ptr(),
603
+ num_stages=self.num_acc_stage,
604
+ producer_group=acc_pipeline_producer_group,
605
+ consumer_group=acc_pipeline_consumer_group,
606
+ cta_layout_vmnk=cluster_layout_vmnk,
607
+ )
608
+
609
+ # Tensor memory dealloc barrier init
610
+ if use_2cta_instrs:
611
+ if warp_idx == self.tma_warp_id:
612
+ num_tmem_dealloc_threads = 32
613
+ with cute.arch.elect_one():
614
+ cute.arch.mbarrier_init(
615
+ tmem_dealloc_mbar_ptr, num_tmem_dealloc_threads
616
+ )
617
+ cute.arch.mbarrier_init_fence()
618
+
619
+ # Cluster arrive after barrier init
620
+ if cute.size(self.cluster_shape_mn) > 1:
621
+ cute.arch.cluster_arrive_relaxed()
622
+
623
+ #
624
+ # Setup smem tensor A/B/C
625
+ #
626
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
627
+ sC = (
628
+ storage.sC.get_tensor(
629
+ c_smem_layout_staged.outer, swizzle=c_smem_layout_staged.inner
630
+ )
631
+ if cutlass.const_expr(self.use_tma_store)
632
+ else None
633
+ )
634
+ # (MMA, MMA_M, MMA_K, STAGE)
635
+ sA = storage.sA.get_tensor(
636
+ a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
637
+ )
638
+ # (MMA, MMA_N, MMA_K, STAGE)
639
+ sB = storage.sB.get_tensor(
640
+ b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
641
+ )
642
+
643
+ #
644
+ # Compute multicast mask for A/B buffer full
645
+ #
646
+ a_full_mcast_mask = None
647
+ b_full_mcast_mask = None
648
+ if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
649
+ a_full_mcast_mask = cpasync.create_tma_multicast_mask(
650
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
651
+ )
652
+ b_full_mcast_mask = cpasync.create_tma_multicast_mask(
653
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
654
+ )
655
+
656
+ #
657
+ # Local_tile partition global tensors
658
+ #
659
+ # (bM, bK, RestM, RestK, RestL)
660
+ gA_mkl = cute.local_tile(
661
+ mA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
662
+ )
663
+ # (bN, bK, RestN, RestK, RestL)
664
+ gB_nkl = cute.local_tile(
665
+ mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
666
+ )
667
+ # (bM, bN, RestM, RestN, RestL)
668
+ gC_mnl = cute.local_tile(
669
+ mC_mnl, cute.slice_(self.mma_tiler, (None, None, 0)), (None, None, None)
670
+ )
671
+ k_tile_cnt = cute.size(gA_mkl, mode=[3])
672
+
673
+ #
674
+ # Partition global tensor for TiledMMA_A/B/C
675
+ #
676
+ thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
677
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
678
+ tCgA = thr_mma.partition_A(gA_mkl)
679
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
680
+ tCgB = thr_mma.partition_B(gB_nkl)
681
+ # (MMA, MMA_M, MMA_N, RestM, RestN, RestL)
682
+ tCgC = thr_mma.partition_C(gC_mnl)
683
+
684
+ #
685
+ # Partition global/shared tensor for TMA load A/B
686
+ #
687
+ # TMA load A partition_S/D
688
+ a_cta_layout = cute.make_layout(
689
+ cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
690
+ )
691
+ # ((atom_v, rest_v), STAGE)
692
+ # ((atom_v, rest_v), RestM, RestK, RestL)
693
+ tAsA, tAgA = cpasync.tma_partition(
694
+ tma_atom_a,
695
+ block_in_cluster_coord_vmnk[2],
696
+ a_cta_layout,
697
+ cute.group_modes(sA, 0, 3),
698
+ cute.group_modes(tCgA, 0, 3),
699
+ )
700
+ # TMA load B partition_S/D
701
+ b_cta_layout = cute.make_layout(
702
+ cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
703
+ )
704
+ # ((atom_v, rest_v), STAGE)
705
+ # ((atom_v, rest_v), RestM, RestK, RestL)
706
+ tBsB, tBgB = cpasync.tma_partition(
707
+ tma_atom_b,
708
+ block_in_cluster_coord_vmnk[1],
709
+ b_cta_layout,
710
+ cute.group_modes(sB, 0, 3),
711
+ cute.group_modes(tCgB, 0, 3),
712
+ )
713
+
714
+ #
715
+ # Partition shared/tensor memory tensor for TiledMMA_A/B/C
716
+ #
717
+ # (MMA, MMA_M, MMA_K, STAGE)
718
+ tCrA = tiled_mma.make_fragment_A(sA)
719
+ # (MMA, MMA_N, MMA_K, STAGE)
720
+ tCrB = tiled_mma.make_fragment_B(sB)
721
+ # (MMA, MMA_M, MMA_N)
722
+ acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
723
+ # (MMA, MMA_M, MMA_N, STAGE)
724
+ tCtAcc_fake = tiled_mma.make_fragment_C(
725
+ cute.append(acc_shape, self.num_acc_stage)
726
+ )
727
+
728
+ #
729
+ # Cluster wait before tensor memory alloc
730
+ #
731
+ if cute.size(self.cluster_shape_mn) > 1:
732
+ cute.arch.cluster_wait()
733
+ else:
734
+ cute.arch.barrier(
735
+ barrier_id=self.cta_sync_bar_id, number_of_threads=self.threads_per_cta
736
+ )
737
+
738
+ #
739
+ # Specialized TMA load warp
740
+ #
741
+
742
+ if warp_idx == self.tma_warp_id:
743
+ #
744
+ # Persistent tile scheduling loop
745
+ #
746
+ tile_sched = utils.StaticPersistentTileScheduler.create(
747
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
748
+ )
749
+ work_tile = tile_sched.initial_work_tile_info()
750
+
751
+ ab_producer_state = pipeline.make_pipeline_state(
752
+ pipeline.PipelineUserType.Producer, self.num_ab_stage
753
+ )
754
+
755
+ while work_tile.is_valid_tile:
756
+ # Get tile coord from tile scheduler
757
+ cur_tile_coord = work_tile.tile_idx
758
+ mma_tile_coord_mnl = (
759
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
760
+ cur_tile_coord[1],
761
+ cur_tile_coord[2],
762
+ )
763
+
764
+ #
765
+ # Slice to per mma tile index
766
+ #
767
+ # ((atom_v, rest_v), RestK)
768
+ tAgA_slice = tAgA[
769
+ (None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])
770
+ ]
771
+ # ((atom_v, rest_v), RestK)
772
+ tBgB_slice = tBgB[
773
+ (None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])
774
+ ]
775
+
776
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt
777
+ ab_producer_state.reset_count()
778
+ peek_ab_empty_status = cutlass.Boolean(1)
779
+ if ab_producer_state.count < k_tile_cnt:
780
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
781
+ ab_producer_state
782
+ )
783
+ #
784
+ # Tma load loop
785
+ #
786
+ for k_tile in cutlass.range(0, k_tile_cnt, 1, unroll=1):
787
+ # Conditionally wait for AB buffer empty
788
+ ab_pipeline.producer_acquire(
789
+ ab_producer_state, peek_ab_empty_status
790
+ )
791
+
792
+ # TMA load A/B
793
+ cute.copy(
794
+ tma_atom_a,
795
+ tAgA_slice[(None, ab_producer_state.count)],
796
+ tAsA[(None, ab_producer_state.index)],
797
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
798
+ mcast_mask=a_full_mcast_mask,
799
+ )
800
+ cute.copy(
801
+ tma_atom_b,
802
+ tBgB_slice[(None, ab_producer_state.count)],
803
+ tBsB[(None, ab_producer_state.index)],
804
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
805
+ mcast_mask=b_full_mcast_mask,
806
+ )
807
+
808
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt + k_tile + 1
809
+ ab_producer_state.advance()
810
+ peek_ab_empty_status = cutlass.Boolean(1)
811
+ if ab_producer_state.count < k_tile_cnt:
812
+ peek_ab_empty_status = ab_pipeline.producer_try_acquire(
813
+ ab_producer_state
814
+ )
815
+
816
+ #
817
+ # Advance to next tile
818
+ #
819
+ tile_sched.advance_to_next_work()
820
+ work_tile = tile_sched.get_current_work()
821
+
822
+ #
823
+ # Wait A/B buffer empty
824
+ #
825
+ ab_pipeline.producer_tail(ab_producer_state)
826
+
827
+ #
828
+ # Specialized MMA warp
829
+ #
830
+ if warp_idx == self.mma_warp_id:
831
+ #
832
+ # Bar sync for retrieve tensor memory ptr from shared mem
833
+ #
834
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
835
+ cute.arch.barrier(
836
+ barrier_id=self.tmem_ptr_sync_bar_id,
837
+ number_of_threads=tmem_ptr_read_threads,
838
+ )
839
+
840
+ #
841
+ # Retrieving tensor memory ptr and make accumulator tensor
842
+ #
843
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
844
+ self.acc_dtype,
845
+ alignment=16,
846
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
847
+ )
848
+ # (MMA, MMA_M, MMA_N, STAGE)
849
+ tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
850
+
851
+ #
852
+ # Persistent tile scheduling loop
853
+ #
854
+ tile_sched = utils.StaticPersistentTileScheduler.create(
855
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
856
+ )
857
+ work_tile = tile_sched.initial_work_tile_info()
858
+
859
+ ab_consumer_state = pipeline.make_pipeline_state(
860
+ pipeline.PipelineUserType.Consumer, self.num_ab_stage
861
+ )
862
+ acc_producer_state = pipeline.make_pipeline_state(
863
+ pipeline.PipelineUserType.Producer, self.num_acc_stage
864
+ )
865
+
866
+ while work_tile.is_valid_tile:
867
+ # Get tile coord from tile scheduler
868
+ cur_tile_coord = work_tile.tile_idx
869
+ mma_tile_coord_mnl = (
870
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
871
+ cur_tile_coord[1],
872
+ cur_tile_coord[2],
873
+ )
874
+
875
+ # Set tensor memory buffer for current tile
876
+ # (MMA, MMA_M, MMA_N)
877
+ tCtAcc = tCtAcc_base[(None, None, None, acc_producer_state.index)]
878
+
879
+ # Peek (try_wait) AB buffer full for k_tile = 0
880
+ ab_consumer_state.reset_count()
881
+ peek_ab_full_status = cutlass.Boolean(1)
882
+ if ab_consumer_state.count < k_tile_cnt and is_leader_cta:
883
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(
884
+ ab_consumer_state
885
+ )
886
+
887
+ #
888
+ # Wait for accumulator buffer empty
889
+ #
890
+ if is_leader_cta:
891
+ acc_pipeline.producer_acquire(acc_producer_state)
892
+
893
+ #
894
+ # Reset the ACCUMULATE field for each tile
895
+ #
896
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, False)
897
+
898
+ #
899
+ # Mma mainloop
900
+ #
901
+ for k_tile in range(k_tile_cnt):
902
+ if is_leader_cta:
903
+ # Conditionally wait for AB buffer full
904
+ ab_pipeline.consumer_wait(
905
+ ab_consumer_state, peek_ab_full_status
906
+ )
907
+
908
+ # tCtAcc += tCrA * tCrB
909
+ num_kblocks = cute.size(tCrA, mode=[2])
910
+ for kblock_idx in cutlass.range(num_kblocks, unroll_full=True):
911
+ kblock_coord = (
912
+ None,
913
+ None,
914
+ kblock_idx,
915
+ ab_consumer_state.index,
916
+ )
917
+
918
+ cute.gemm(
919
+ tiled_mma,
920
+ tCtAcc,
921
+ tCrA[kblock_coord],
922
+ tCrB[kblock_coord],
923
+ tCtAcc,
924
+ )
925
+ # Enable accumulate on tCtAcc after first kblock
926
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, True)
927
+
928
+ # Async arrive AB buffer empty
929
+ ab_pipeline.consumer_release(ab_consumer_state)
930
+
931
+ # Peek (try_wait) AB buffer full for k_tile = k_tile + 1
932
+ ab_consumer_state.advance()
933
+ peek_ab_full_status = cutlass.Boolean(1)
934
+ if ab_consumer_state.count < k_tile_cnt:
935
+ if is_leader_cta:
936
+ peek_ab_full_status = ab_pipeline.consumer_try_wait(
937
+ ab_consumer_state
938
+ )
939
+
940
+ #
941
+ # Async arrive accumulator buffer full
942
+ #
943
+ if is_leader_cta:
944
+ acc_pipeline.producer_commit(acc_producer_state)
945
+ acc_producer_state.advance()
946
+
947
+ #
948
+ # Advance to next tile
949
+ #
950
+ tile_sched.advance_to_next_work()
951
+ work_tile = tile_sched.get_current_work()
952
+
953
+ #
954
+ # Wait for accumulator buffer empty
955
+ #
956
+ acc_pipeline.producer_tail(acc_producer_state)
957
+ #
958
+ # Specialized epilogue warps
959
+ #
960
+ if warp_idx < self.mma_warp_id:
961
+ #
962
+ # Alloc tensor memory buffer
963
+ #
964
+ if warp_idx == self.epilog_warp_id[0]:
965
+ cute.arch.alloc_tmem(
966
+ self.num_tmem_alloc_cols,
967
+ tmem_holding_buf,
968
+ is_two_cta=use_2cta_instrs,
969
+ )
970
+
971
+ #
972
+ # Bar sync for retrieve tensor memory ptr from shared memory
973
+ #
974
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
975
+ cute.arch.barrier(
976
+ barrier_id=self.tmem_ptr_sync_bar_id,
977
+ number_of_threads=tmem_ptr_read_threads,
978
+ )
979
+
980
+ #
981
+ # Retrieving tensor memory ptr and make accumulator tensor
982
+ #
983
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
984
+ self.acc_dtype,
985
+ alignment=16,
986
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
987
+ )
988
+ # (MMA, MMA_M, MMA_N, STAGE)
989
+ tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
990
+
991
+ #
992
+ # Partition for epilogue
993
+ #
994
+ epi_tidx = tidx
995
+ (
996
+ tiled_copy_t2r,
997
+ tTR_tAcc_base,
998
+ tTR_rAcc,
999
+ ) = self.epilog_tmem_copy_and_partition(
1000
+ epi_tidx, tCtAcc_base, tCgC, epi_tile, use_2cta_instrs
1001
+ )
1002
+
1003
+ tTR_rC = None
1004
+ tiled_copy_r2s = None
1005
+ simt_atom = None
1006
+ tRS_rC = None
1007
+ tRS_sC = None
1008
+ bSG_sC = None
1009
+ bSG_gC_partitioned = None
1010
+ tTR_gC_partitioned = None
1011
+ if cutlass.const_expr(self.use_tma_store):
1012
+ tTR_rC = cute.make_fragment(tTR_rAcc.shape, self.c_dtype)
1013
+ tiled_copy_r2s, tRS_rC, tRS_sC = self.epilog_smem_copy_and_partition(
1014
+ tiled_copy_t2r, tTR_rC, epi_tidx, sC
1015
+ )
1016
+ (
1017
+ tma_atom_c,
1018
+ bSG_sC,
1019
+ bSG_gC_partitioned,
1020
+ ) = self.epilog_gmem_copy_and_partition(
1021
+ epi_tidx, tma_atom_c, tCgC, epi_tile, sC
1022
+ )
1023
+ else:
1024
+ (
1025
+ simt_atom,
1026
+ tTR_rC,
1027
+ tTR_gC_partitioned,
1028
+ ) = self.epilog_gmem_copy_and_partition(
1029
+ epi_tidx, tiled_copy_t2r, tCgC, epi_tile, sC
1030
+ )
1031
+
1032
+ #
1033
+ # Persistent tile scheduling loop
1034
+ #
1035
+ tile_sched = utils.StaticPersistentTileScheduler.create(
1036
+ tile_sched_params, cute.arch.block_idx(), cute.arch.grid_dim()
1037
+ )
1038
+ work_tile = tile_sched.initial_work_tile_info()
1039
+
1040
+ acc_consumer_state = pipeline.make_pipeline_state(
1041
+ pipeline.PipelineUserType.Consumer, self.num_acc_stage
1042
+ )
1043
+
1044
+ c_pipeline = None
1045
+ if cutlass.const_expr(self.use_tma_store):
1046
+ # Threads/warps participating in tma store pipeline
1047
+ c_producer_group = pipeline.CooperativeGroup(
1048
+ pipeline.Agent.Thread,
1049
+ 32 * len(self.epilog_warp_id),
1050
+ 32 * len(self.epilog_warp_id),
1051
+ )
1052
+ c_pipeline = pipeline.PipelineTmaStore.create(
1053
+ num_stages=self.num_c_stage,
1054
+ producer_group=c_producer_group,
1055
+ )
1056
+
1057
+ while work_tile.is_valid_tile:
1058
+ # Get tile coord from tile scheduler
1059
+ cur_tile_coord = work_tile.tile_idx
1060
+ mma_tile_coord_mnl = (
1061
+ cur_tile_coord[0] // cute.size(tiled_mma.thr_id.shape),
1062
+ cur_tile_coord[1],
1063
+ cur_tile_coord[2],
1064
+ )
1065
+
1066
+ #
1067
+ # Slice to per mma tile index
1068
+ #
1069
+ bSG_gC = None
1070
+ tTR_gC = None
1071
+ if cutlass.const_expr(self.use_tma_store):
1072
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1073
+ bSG_gC = bSG_gC_partitioned[
1074
+ (
1075
+ None,
1076
+ None,
1077
+ None,
1078
+ *mma_tile_coord_mnl,
1079
+ )
1080
+ ]
1081
+ else:
1082
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N)
1083
+ tTR_gC = tTR_gC_partitioned[
1084
+ (
1085
+ None,
1086
+ None,
1087
+ None,
1088
+ None,
1089
+ None,
1090
+ *mma_tile_coord_mnl,
1091
+ )
1092
+ ]
1093
+
1094
+ # Set tensor memory buffer for current tile
1095
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M)
1096
+ tTR_tAcc = tTR_tAcc_base[
1097
+ (None, None, None, None, None, acc_consumer_state.index)
1098
+ ]
1099
+
1100
+ #
1101
+ # Wait for accumulator buffer full
1102
+ #
1103
+ acc_pipeline.consumer_wait(acc_consumer_state)
1104
+
1105
+ tTR_tAcc = cute.group_modes(tTR_tAcc, 3, cute.rank(tTR_tAcc))
1106
+ if cutlass.const_expr(self.use_tma_store):
1107
+ bSG_gC = cute.group_modes(bSG_gC, 1, cute.rank(bSG_gC))
1108
+ else:
1109
+ tTR_gC = cute.group_modes(tTR_gC, 3, cute.rank(tTR_gC))
1110
+
1111
+ #
1112
+ # Store accumulator to global memory in subtiles
1113
+ #
1114
+ subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3])
1115
+ num_prev_subtiles = tile_sched.num_tiles_executed * subtile_cnt
1116
+ for subtile_idx in cutlass.range(subtile_cnt):
1117
+ #
1118
+ # Load accumulator from tensor memory buffer to register
1119
+ #
1120
+ tTR_tAcc_mn = tTR_tAcc[(None, None, None, subtile_idx)]
1121
+ cute.copy(tiled_copy_t2r, tTR_tAcc_mn, tTR_rAcc)
1122
+
1123
+ if cutlass.const_expr(self.use_tma_store):
1124
+ #
1125
+ # Convert to C type
1126
+ #
1127
+ acc_vec = tiled_copy_r2s.retile(tTR_rAcc).load()
1128
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
1129
+ tRS_rC.store(acc_vec)
1130
+
1131
+ #
1132
+ # Store C to shared memory
1133
+ #
1134
+ c_buffer = (num_prev_subtiles + subtile_idx) % self.num_c_stage
1135
+ cute.copy(
1136
+ tiled_copy_r2s,
1137
+ tRS_rC,
1138
+ tRS_sC[(None, None, None, c_buffer)],
1139
+ )
1140
+ # Fence and barrier to make sure shared memory store is visible to TMA store
1141
+ cute.arch.fence_proxy(
1142
+ cute.arch.ProxyKind.async_shared,
1143
+ space=cute.arch.SharedSpace.shared_cta,
1144
+ )
1145
+ epilog_threads = 32 * len(self.epilog_warp_id)
1146
+ cute.arch.barrier(
1147
+ barrier_id=self.epilog_sync_bar_id,
1148
+ number_of_threads=epilog_threads,
1149
+ )
1150
+
1151
+ #
1152
+ # TMA store C to global memory
1153
+ #
1154
+ if warp_idx == self.epilog_warp_id[0]:
1155
+ cute.copy(
1156
+ tma_atom_c,
1157
+ bSG_sC[(None, c_buffer)],
1158
+ bSG_gC[(None, subtile_idx)],
1159
+ )
1160
+ # Fence and barrier to make sure shared memory store is visible to TMA store
1161
+ c_pipeline.producer_commit()
1162
+ c_pipeline.producer_acquire()
1163
+ cute.arch.barrier(
1164
+ barrier_id=self.epilog_sync_bar_id,
1165
+ number_of_threads=epilog_threads,
1166
+ )
1167
+ else:
1168
+ #
1169
+ # Convert to C type
1170
+ #
1171
+ acc_vec = tTR_rAcc.load()
1172
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
1173
+ tTR_rC.store(acc_vec)
1174
+
1175
+ #
1176
+ # Store C to global memory
1177
+ #
1178
+ cute.copy(
1179
+ simt_atom, tTR_rC, tTR_gC[(None, None, None, subtile_idx)]
1180
+ )
1181
+
1182
+ #
1183
+ # Async arrive accumulator buffer empty
1184
+ #
1185
+ with cute.arch.elect_one():
1186
+ acc_pipeline.consumer_release(acc_consumer_state)
1187
+ acc_consumer_state.advance()
1188
+
1189
+ #
1190
+ # Advance to next tile
1191
+ #
1192
+ tile_sched.advance_to_next_work()
1193
+ work_tile = tile_sched.get_current_work()
1194
+
1195
+ #
1196
+ # Dealloc the tensor memory buffer
1197
+ #
1198
+ if warp_idx == self.epilog_warp_id[0]:
1199
+ cute.arch.relinquish_tmem_alloc_permit(is_two_cta=use_2cta_instrs)
1200
+ epilog_threads = 32 * len(self.epilog_warp_id)
1201
+ cute.arch.barrier(
1202
+ barrier_id=self.epilog_sync_bar_id, number_of_threads=epilog_threads
1203
+ )
1204
+ if warp_idx == self.epilog_warp_id[0]:
1205
+ if use_2cta_instrs:
1206
+ cute.arch.mbarrier_arrive(
1207
+ tmem_dealloc_mbar_ptr, cta_rank_in_cluster ^ 1
1208
+ )
1209
+ cute.arch.mbarrier_wait(tmem_dealloc_mbar_ptr, 0)
1210
+ cute.arch.dealloc_tmem(
1211
+ tmem_ptr, self.num_tmem_alloc_cols, is_two_cta=use_2cta_instrs
1212
+ )
1213
+ #
1214
+ # Wait for C store complete
1215
+ #
1216
+ if cutlass.const_expr(self.use_tma_store):
1217
+ c_pipeline.producer_tail()
1218
+
1219
+ def epilog_tmem_copy_and_partition(
1220
+ self,
1221
+ tidx: cutlass.Int32,
1222
+ tAcc: cute.Tensor,
1223
+ gC_mnl: cute.Tensor,
1224
+ epi_tile: cute.Tile,
1225
+ use_2cta_instrs: Union[cutlass.Boolean, bool],
1226
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1227
+ """
1228
+ Make tiledCopy for tensor memory load, then use it to partition tensor memory (source) and register array (destination).
1229
+
1230
+ :param tidx: The thread index in epilogue warp groups
1231
+ :type tidx: cutlass.Int32
1232
+ :param tAcc: The accumulator tensor to be copied and partitioned
1233
+ :type tAcc: cute.Tensor
1234
+ :param gC_mnl: The global tensor C
1235
+ :type gC_mnl: cute.Tensor
1236
+ :param epi_tile: The epilogue tiler
1237
+ :type epi_tile: cute.Tile
1238
+ :param use_2cta_instrs: Whether use_2cta_instrs is enabled
1239
+ :type use_2cta_instrs: bool
1240
+
1241
+ :return: A tuple containing (tiled_copy_t2r, tTR_tAcc, tTR_rAcc) where:
1242
+ - tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1243
+ - tTR_tAcc: The partitioned accumulator tensor
1244
+ - tTR_rAcc: The accumulated tensor in register used to hold t2r results
1245
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1246
+ """
1247
+ # Make tiledCopy for tensor memory load
1248
+ copy_atom_t2r = sm100_utils.get_tmem_load_op(
1249
+ self.cta_tile_shape_mnk,
1250
+ self.c_layout,
1251
+ self.c_dtype,
1252
+ self.acc_dtype,
1253
+ epi_tile,
1254
+ use_2cta_instrs,
1255
+ )
1256
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, STAGE)
1257
+ tAcc_epi = cute.flat_divide(
1258
+ tAcc[((None, None), 0, 0, None)],
1259
+ epi_tile,
1260
+ )
1261
+ # (EPI_TILE_M, EPI_TILE_N)
1262
+ tiled_copy_t2r = tcgen05.make_tmem_copy(
1263
+ copy_atom_t2r, tAcc_epi[(None, None, 0, 0, 0)]
1264
+ )
1265
+
1266
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1267
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M, STAGE)
1268
+ tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
1269
+
1270
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1271
+ gC_mnl_epi = cute.flat_divide(
1272
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1273
+ )
1274
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1275
+ tTR_gC = thr_copy_t2r.partition_D(gC_mnl_epi)
1276
+ # (T2R, T2R_M, T2R_N)
1277
+ tTR_rAcc = cute.make_fragment(
1278
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.acc_dtype
1279
+ )
1280
+ return tiled_copy_t2r, tTR_tAcc, tTR_rAcc
1281
+
1282
+ def epilog_smem_copy_and_partition(
1283
+ self,
1284
+ tiled_copy_t2r: cute.TiledCopy,
1285
+ tTR_rC: cute.Tensor,
1286
+ tidx: cutlass.Int32,
1287
+ sC: cute.Tensor,
1288
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1289
+ """
1290
+ Make tiledCopy for shared memory store, then use it to partition register array (source) and shared memory (destination).
1291
+
1292
+ :param tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1293
+ :type tiled_copy_t2r: cute.TiledCopy
1294
+ :param tTR_rC: The partitioned accumulator tensor
1295
+ :type tTR_rC: cute.Tensor
1296
+ :param tidx: The thread index in epilogue warp groups
1297
+ :type tidx: cutlass.Int32
1298
+ :param sC: The shared memory tensor to be copied and partitioned
1299
+ :type sC: cute.Tensor
1300
+ :type sepi: cute.Tensor
1301
+
1302
+ :return: A tuple containing (tiled_copy_r2s, tRS_rC, tRS_sC) where:
1303
+ - tiled_copy_r2s: The tiled copy operation for register to smem copy(r2s)
1304
+ - tRS_rC: The partitioned tensor C (register source)
1305
+ - tRS_sC: The partitioned tensor C (smem destination)
1306
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1307
+ """
1308
+ copy_atom_r2s = sm100_utils.get_smem_store_op(
1309
+ self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
1310
+ )
1311
+ tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
1312
+ # (R2S, R2S_M, R2S_N, PIPE_D)
1313
+ thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
1314
+ tRS_sC = thr_copy_r2s.partition_D(sC)
1315
+ # (R2S, R2S_M, R2S_N)
1316
+ tRS_rC = tiled_copy_r2s.retile(tTR_rC)
1317
+ return tiled_copy_r2s, tRS_rC, tRS_sC
1318
+
1319
+ def epilog_gmem_copy_and_partition(
1320
+ self,
1321
+ tidx: cutlass.Int32,
1322
+ atom: Union[cute.CopyAtom, cute.TiledCopy],
1323
+ gC_mnl: cute.Tensor,
1324
+ epi_tile: cute.Tile,
1325
+ sC: cute.Tensor,
1326
+ ) -> Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]:
1327
+ """Make tiledCopy for global memory store, then use it to:
1328
+ - partition register array (source) and global memory (destination) for none TMA store version;
1329
+ - partition shared memory (source) and global memory (destination) for TMA store version.
1330
+
1331
+ :param tidx: The thread index in epilogue warp groups
1332
+ :type tidx: cutlass.Int32
1333
+ :param atom: The copy_atom_c to be used for TMA store version, or tiled_copy_t2r for none TMA store version
1334
+ :type atom: cute.CopyAtom or cute.TiledCopy
1335
+ :param gC_mnl: The global tensor C
1336
+ :type gC_mnl: cute.Tensor
1337
+ :param epi_tile: The epilogue tiler
1338
+ :type epi_tile: cute.Tile
1339
+ :param sC: The shared memory tensor to be copied and partitioned
1340
+ :type sC: cute.Tensor
1341
+
1342
+ :return: A tuple containing either:
1343
+ - For TMA store: (tma_atom_c, bSG_sC, bSG_gC) where:
1344
+ - tma_atom_c: The TMA copy atom
1345
+ - bSG_sC: The partitioned shared memory tensor C
1346
+ - bSG_gC: The partitioned global tensor C
1347
+ - For non-TMA store: (simt_atom, tTR_rC, tTR_gC) where:
1348
+ - simt_atom: The SIMT copy atom
1349
+ - tTR_rC: The register tensor C
1350
+ - tTR_gC: The partitioned global tensor C
1351
+ :rtype: Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]
1352
+ """
1353
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1354
+ gC_epi = cute.flat_divide(
1355
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1356
+ )
1357
+ if cutlass.const_expr(self.use_tma_store):
1358
+ tma_atom_c = atom
1359
+ sC_for_tma_partition = cute.group_modes(sC, 0, 2)
1360
+ gC_for_tma_partition = cute.group_modes(gC_epi, 0, 2)
1361
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1362
+ # ((ATOM_V, REST_V), EPI_M, EPI_N, RestM, RestN, RestL)
1363
+ bSG_sC, bSG_gC = cpasync.tma_partition(
1364
+ tma_atom_c,
1365
+ 0,
1366
+ cute.make_layout(1),
1367
+ sC_for_tma_partition,
1368
+ gC_for_tma_partition,
1369
+ )
1370
+ return tma_atom_c, bSG_sC, bSG_gC
1371
+ else:
1372
+ tiled_copy_t2r = atom
1373
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1374
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1375
+ tTR_gC = thr_copy_t2r.partition_D(gC_epi)
1376
+ # (T2R, T2R_M, T2R_N)
1377
+ tTR_rC = cute.make_fragment(
1378
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.c_dtype
1379
+ )
1380
+ simt_atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), self.c_dtype)
1381
+ return simt_atom, tTR_rC, tTR_gC
1382
+
1383
+ @staticmethod
1384
+ def _compute_stages(
1385
+ tiled_mma: cute.TiledMma,
1386
+ mma_tiler_mnk: Tuple[int, int, int],
1387
+ a_dtype: Type[cutlass.Numeric],
1388
+ b_dtype: Type[cutlass.Numeric],
1389
+ epi_tile: cute.Tile,
1390
+ c_dtype: Type[cutlass.Numeric],
1391
+ c_layout: utils.LayoutEnum,
1392
+ smem_capacity: int,
1393
+ occupancy: int,
1394
+ use_tma_store: bool,
1395
+ ) -> Tuple[int, int, int]:
1396
+ """Computes the number of stages for A/B/C operands based on heuristics.
1397
+
1398
+ :param tiled_mma: The tiled MMA object defining the core computation.
1399
+ :type tiled_mma: cute.TiledMma
1400
+ :param mma_tiler_mnk: The shape (M, N, K) of the MMA tiler.
1401
+ :type mma_tiler_mnk: tuple[int, int, int]
1402
+ :param a_dtype: Data type of operand A.
1403
+ :type a_dtype: type[cutlass.Numeric]
1404
+ :param b_dtype: Data type of operand B.
1405
+ :type b_dtype: type[cutlass.Numeric]
1406
+ :param epi_tile: The epilogue tile shape.
1407
+ :type epi_tile: cute.Tile
1408
+ :param c_dtype: Data type of operand C (output).
1409
+ :type c_dtype: type[cutlass.Numeric]
1410
+ :param c_layout: Layout enum of operand C.
1411
+ :type c_layout: utils.LayoutEnum
1412
+ :param smem_capacity: Total available shared memory capacity in bytes.
1413
+ :type smem_capacity: int
1414
+ :param occupancy: Target number of CTAs per SM (occupancy).
1415
+ :type occupancy: int
1416
+ :param use_tma_store: Whether TMA store is enabled.
1417
+ :type use_tma_store: bool
1418
+
1419
+ :return: A tuple containing the computed number of stages for:
1420
+ (ACC stages, A/B operand stages, C stages)
1421
+ :rtype: tuple[int, int, int]
1422
+ """
1423
+ # Default ACC stages
1424
+ num_acc_stage = 2
1425
+
1426
+ # Default C stages
1427
+ num_c_stage = 2 if use_tma_store else 0
1428
+
1429
+ # Calculate smem layout and size for one stage of A, B, and C
1430
+ a_smem_layout_stage_one = sm100_utils.make_smem_layout_a(
1431
+ tiled_mma,
1432
+ mma_tiler_mnk,
1433
+ a_dtype,
1434
+ 1, # a tmp 1 stage is provided
1435
+ )
1436
+ b_smem_layout_staged_one = sm100_utils.make_smem_layout_b(
1437
+ tiled_mma,
1438
+ mma_tiler_mnk,
1439
+ b_dtype,
1440
+ 1, # a tmp 1 stage is provided
1441
+ )
1442
+ c_smem_layout_staged_one = (
1443
+ sm100_utils.make_smem_layout_epi(
1444
+ c_dtype,
1445
+ c_layout,
1446
+ epi_tile,
1447
+ 1,
1448
+ )
1449
+ if use_tma_store
1450
+ else None
1451
+ )
1452
+ ab_bytes_per_stage = cute.size_in_bytes(
1453
+ a_dtype, a_smem_layout_stage_one
1454
+ ) + cute.size_in_bytes(b_dtype, b_smem_layout_staged_one)
1455
+ mbar_helpers_bytes = 1024
1456
+ c_bytes_per_stage = (
1457
+ cute.size_in_bytes(c_dtype, c_smem_layout_staged_one)
1458
+ if use_tma_store
1459
+ else 0
1460
+ )
1461
+ c_bytes = c_bytes_per_stage * num_c_stage
1462
+
1463
+ # Calculate A/B stages:
1464
+ # Start with total smem per CTA (capacity / occupancy)
1465
+ # Subtract reserved bytes and initial C stages bytes
1466
+ # Divide remaining by bytes needed per A/B stage
1467
+ num_ab_stage = (
1468
+ smem_capacity // occupancy - (mbar_helpers_bytes + c_bytes)
1469
+ ) // ab_bytes_per_stage
1470
+
1471
+ # Refine epilogue stages:
1472
+ # Calculate remaining smem after allocating for A/B stages and reserved bytes
1473
+ # Add remaining unused smem to epilogue
1474
+ if use_tma_store:
1475
+ num_c_stage += (
1476
+ smem_capacity
1477
+ - occupancy * ab_bytes_per_stage * num_ab_stage
1478
+ - occupancy * (mbar_helpers_bytes + c_bytes)
1479
+ ) // (occupancy * c_bytes_per_stage)
1480
+ return num_acc_stage, num_ab_stage, num_c_stage
1481
+
1482
+ @staticmethod
1483
+ def _compute_grid(
1484
+ c: cute.Tensor,
1485
+ cta_tile_shape_mnk: Tuple[int, int, int],
1486
+ cluster_shape_mn: Tuple[int, int],
1487
+ max_active_clusters: cutlass.Constexpr,
1488
+ ) -> Tuple[utils.PersistentTileSchedulerParams, Tuple[int, int, int]]:
1489
+ """Use persistent tile scheduler to compute the grid size for the output tensor C.
1490
+
1491
+ :param c: The output tensor C
1492
+ :type c: cute.Tensor
1493
+ :param cta_tile_shape_mnk: The shape (M, N, K) of the CTA tile.
1494
+ :type cta_tile_shape_mnk: tuple[int, int, int]
1495
+ :param cluster_shape_mn: Shape of each cluster in M, N dimensions.
1496
+ :type cluster_shape_mn: tuple[int, int]
1497
+ :param max_active_clusters: Maximum number of active clusters.
1498
+ :type max_active_clusters: cutlass.Constexpr
1499
+
1500
+ :return: A tuple containing:
1501
+ - tile_sched_params: Parameters for the persistent tile scheduler.
1502
+ - grid: Grid shape for kernel launch.
1503
+ :rtype: Tuple[utils.PersistentTileSchedulerParams, tuple[int, int, int]]
1504
+ """
1505
+ c_shape = cute.slice_(cta_tile_shape_mnk, (None, None, 0))
1506
+ gc = cute.zipped_divide(c, tiler=c_shape)
1507
+ num_ctas_mnl = gc[(0, (None, None, None))].shape
1508
+ cluster_shape_mnl = (*cluster_shape_mn, 1)
1509
+
1510
+ tile_sched_params = utils.PersistentTileSchedulerParams(
1511
+ num_ctas_mnl, cluster_shape_mnl
1512
+ )
1513
+ grid = utils.StaticPersistentTileScheduler.get_grid_shape(
1514
+ tile_sched_params, max_active_clusters
1515
+ )
1516
+
1517
+ return tile_sched_params, grid
1518
+
1519
+ @staticmethod
1520
+ def _compute_num_tmem_alloc_cols(
1521
+ tiled_mma: cute.TiledMma,
1522
+ mma_tiler: Tuple[int, int, int],
1523
+ num_acc_stage: int,
1524
+ ) -> int:
1525
+ """
1526
+ Compute the number of tensor memory allocation columns.
1527
+
1528
+ :param tiled_mma: The tiled MMA object defining the core computation.
1529
+ :type tiled_mma: cute.TiledMma
1530
+ :param mma_tiler: The shape (M, N, K) of the MMA tile.
1531
+ :type mma_tiler: tuple[int, int, int]
1532
+ :param num_acc_stage: The stage of the accumulator tensor.
1533
+ :type num_acc_stage: int
1534
+
1535
+ :return: The number of tensor memory allocation columns.
1536
+ :rtype: int
1537
+ """
1538
+ acc_shape = tiled_mma.partition_shape_C(mma_tiler[:2])
1539
+ tCtAcc_fake = tiled_mma.make_fragment_C(cute.append(acc_shape, num_acc_stage))
1540
+ num_tmem_alloc_cols = utils.get_num_tmem_alloc_cols(tCtAcc_fake)
1541
+
1542
+ return num_tmem_alloc_cols
1543
+
1544
+ @staticmethod
1545
+ def is_valid_dtypes(
1546
+ ab_dtype: Type[cutlass.Numeric],
1547
+ acc_dtype: Type[cutlass.Numeric],
1548
+ c_dtype: Type[cutlass.Numeric],
1549
+ ) -> bool:
1550
+ """
1551
+ Check if the dtypes are valid
1552
+
1553
+ :param ab_dtype: The data type of the A and B operands
1554
+ :type ab_dtype: Type[cutlass.Numeric]
1555
+ :param acc_dtype: The data type of the accumulator
1556
+ :type acc_dtype: Type[cutlass.Numeric]
1557
+ :param c_dtype: The data type of the output tensor
1558
+ :type c_dtype: Type[cutlass.Numeric]
1559
+
1560
+ :return: True if the dtypes are valid, False otherwise
1561
+ :rtype: bool
1562
+ """
1563
+ is_valid = True
1564
+ if ab_dtype not in {
1565
+ cutlass.Float16,
1566
+ cutlass.BFloat16,
1567
+ cutlass.TFloat32,
1568
+ cutlass.Uint8,
1569
+ cutlass.Int8,
1570
+ cutlass.Float8E4M3FN,
1571
+ cutlass.Float8E5M2,
1572
+ }:
1573
+ is_valid = False
1574
+ if (
1575
+ acc_dtype not in {cutlass.Float32, cutlass.Float16, cutlass.Int32}
1576
+ or acc_dtype == cutlass.Float16
1577
+ and ab_dtype
1578
+ not in {cutlass.Float16, cutlass.Float8E4M3FN, cutlass.Float8E5M2}
1579
+ or acc_dtype == cutlass.Int32
1580
+ and ab_dtype not in {cutlass.Uint8, cutlass.Int8}
1581
+ ):
1582
+ is_valid = False
1583
+ if (
1584
+ acc_dtype == cutlass.Float32
1585
+ and c_dtype
1586
+ not in {
1587
+ cutlass.Float32,
1588
+ cutlass.Float16,
1589
+ cutlass.BFloat16,
1590
+ cutlass.Float8E4M3FN,
1591
+ cutlass.Float8E5M2,
1592
+ cutlass.Int32,
1593
+ cutlass.Int8,
1594
+ cutlass.Uint8,
1595
+ }
1596
+ or acc_dtype == cutlass.Float16
1597
+ and c_dtype
1598
+ not in {
1599
+ cutlass.BFloat16,
1600
+ cutlass.Float16,
1601
+ }
1602
+ or acc_dtype == cutlass.Int32
1603
+ and c_dtype
1604
+ not in {
1605
+ cutlass.BFloat16,
1606
+ cutlass.Float16,
1607
+ cutlass.Float32,
1608
+ cutlass.Int32,
1609
+ cutlass.Int8,
1610
+ cutlass.Uint8,
1611
+ }
1612
+ ):
1613
+ is_valid = False
1614
+ return is_valid
1615
+
1616
+ @staticmethod
1617
+ def is_valid_mma_tiler_and_cluster_shape(
1618
+ use_2cta_instrs: bool,
1619
+ mma_tiler_mn: Tuple[int, int],
1620
+ cluster_shape_mn: Tuple[int, int],
1621
+ ) -> bool:
1622
+ """
1623
+ Check if the mma tiler and cluster shape are valid
1624
+
1625
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1626
+ :type use_2cta_instrs: bool
1627
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1628
+ :type mma_tiler_mn: Tuple[int, int]
1629
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1630
+ :type cluster_shape_mn: Tuple[int, int]
1631
+
1632
+ :return: True if the mma tiler and cluster shape are valid, False otherwise
1633
+ :rtype: bool
1634
+ """
1635
+ is_valid = True
1636
+ # Skip invalid mma tile shape
1637
+ if not (
1638
+ (not use_2cta_instrs and mma_tiler_mn[0] in [64, 128])
1639
+ or (use_2cta_instrs and mma_tiler_mn[0] in [128, 256])
1640
+ ):
1641
+ is_valid = False
1642
+ if mma_tiler_mn[1] not in range(32, 257, 32):
1643
+ is_valid = False
1644
+ # Skip illegal cluster shape
1645
+ if cluster_shape_mn[0] % (2 if use_2cta_instrs else 1) != 0:
1646
+ is_valid = False
1647
+ # Skip invalid cluster shape
1648
+ is_power_of_2 = lambda x: x > 0 and (x & (x - 1)) == 0
1649
+ if (
1650
+ cluster_shape_mn[0] * cluster_shape_mn[1] > 16
1651
+ or cluster_shape_mn[0] <= 0
1652
+ or cluster_shape_mn[1] <= 0
1653
+ or not is_power_of_2(cluster_shape_mn[0])
1654
+ or not is_power_of_2(cluster_shape_mn[1])
1655
+ ):
1656
+ is_valid = False
1657
+ return is_valid
1658
+
1659
+ @staticmethod
1660
+ def is_valid_tensor_alignment(
1661
+ m: int,
1662
+ n: int,
1663
+ k: int,
1664
+ l: int,
1665
+ ab_dtype: Type[cutlass.Numeric],
1666
+ c_dtype: Type[cutlass.Numeric],
1667
+ a_major: str,
1668
+ b_major: str,
1669
+ c_major: str,
1670
+ ) -> bool:
1671
+ """
1672
+ Check if the tensor alignment is valid
1673
+
1674
+ :param m: The number of rows in the A tensor
1675
+ :type m: int
1676
+ :param n: The number of columns in the B tensor
1677
+ :type n: int
1678
+ :param k: The number of columns in the A tensor
1679
+ :type k: int
1680
+ :param l: The number of columns in the C tensor
1681
+ :type l: int
1682
+ :param ab_dtype: The data type of the A and B operands
1683
+ :type ab_dtype: Type[cutlass.Numeric]
1684
+ :param c_dtype: The data type of the output tensor
1685
+ :type c_dtype: Type[cutlass.Numeric]
1686
+ :param a_major: The major axis of the A tensor
1687
+ :type a_major: str
1688
+ :param b_major: The major axis of the B tensor
1689
+ :type b_major: str
1690
+ :param c_major: The major axis of the C tensor
1691
+ :type c_major: str
1692
+
1693
+ :return: True if the problem shape is valid, False otherwise
1694
+ :rtype: bool
1695
+ """
1696
+ is_valid = True
1697
+
1698
+ def check_contigous_16B_alignment(dtype, is_mode0_major, tensor_shape):
1699
+ major_mode_idx = 0 if is_mode0_major else 1
1700
+ num_major_elements = tensor_shape[major_mode_idx]
1701
+ num_contiguous_elements = 16 * 8 // dtype.width
1702
+ return num_major_elements % num_contiguous_elements == 0
1703
+
1704
+ if (
1705
+ not check_contigous_16B_alignment(ab_dtype, a_major == "m", (m, k, l))
1706
+ or not check_contigous_16B_alignment(ab_dtype, b_major == "n", (n, k, l))
1707
+ or not check_contigous_16B_alignment(c_dtype, c_major == "m", (m, n, l))
1708
+ ):
1709
+ is_valid = False
1710
+ return is_valid
1711
+
1712
+ @staticmethod
1713
+ def is_valid_epilog_store_option(
1714
+ use_2cta_instrs: bool,
1715
+ use_tma_store: bool,
1716
+ m: int,
1717
+ n: int,
1718
+ mma_tiler_mn: Tuple[int, int],
1719
+ ) -> bool:
1720
+ """
1721
+ Check if the epilogue store option is valid
1722
+
1723
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1724
+ :type use_2cta_instrs: bool
1725
+ :param use_tma_store: Whether to use TMA store
1726
+ :type use_tma_store: bool
1727
+ :param m: The number of rows in the A tensor
1728
+ :type m: int
1729
+ :param n: The number of columns in the B tensor
1730
+ :type n: int
1731
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1732
+ :type mma_tiler_mn: Tuple[int, int]
1733
+
1734
+ :return: True if the epilogue store option is valid, False otherwise
1735
+ :rtype: bool
1736
+ """
1737
+
1738
+ is_valid = True
1739
+ # None TMA store version does not have predication, can not support OOB tiles
1740
+ cta_tile_shape_mn = (
1741
+ mma_tiler_mn[0] // (2 if use_2cta_instrs else 1),
1742
+ mma_tiler_mn[1],
1743
+ )
1744
+ if not use_tma_store:
1745
+ if not (m % cta_tile_shape_mn[0] == 0 and n % cta_tile_shape_mn[1] == 0):
1746
+ is_valid = False
1747
+ return is_valid
1748
+
1749
+ @staticmethod
1750
+ def can_implement(
1751
+ ab_dtype: Type[cutlass.Numeric],
1752
+ acc_dtype: Type[cutlass.Numeric],
1753
+ c_dtype: Type[cutlass.Numeric],
1754
+ use_2cta_instrs: bool,
1755
+ mma_tiler_mn: Tuple[int, int],
1756
+ cluster_shape_mn: Tuple[int, int],
1757
+ use_tma_store: bool,
1758
+ m: int,
1759
+ n: int,
1760
+ k: int,
1761
+ l: int,
1762
+ a_major: str,
1763
+ b_major: str,
1764
+ c_major: str,
1765
+ ) -> bool:
1766
+ """
1767
+ Check if the gemm can be implemented
1768
+
1769
+ :param ab_dtype: The data type of the A and B operands
1770
+ :type ab_dtype: Type[cutlass.Numeric]
1771
+ :param acc_dtype: The data type of the accumulator
1772
+ :type acc_dtype: Type[cutlass.Numeric]
1773
+ :param c_dtype: The data type of the output tensor
1774
+ :type c_dtype: Type[cutlass.Numeric]
1775
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1776
+ :type use_2cta_instrs: bool
1777
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1778
+ :type mma_tiler_mn: Tuple[int, int]
1779
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1780
+ :type cluster_shape_mn: Tuple[int, int]
1781
+ :param use_tma_store: Whether to use TMA store
1782
+ :type use_tma_store: bool
1783
+ :param m: The number of rows in the A tensor
1784
+ :type m: int
1785
+ :param n: The number of columns in the B tensor
1786
+ :type n: int
1787
+ :param k: The number of columns in the A tensor
1788
+ :type k: int
1789
+ :param l: The number of columns in the C tensor
1790
+ :type l: int
1791
+ :param a_major: The major axis of the A tensor
1792
+ :type a_major: str
1793
+ :param b_major: The major axis of the B tensor
1794
+ :type b_major: str
1795
+ :param c_major: The major axis of the C tensor
1796
+ :type c_major: str
1797
+
1798
+ :return: True if the gemm can be implemented, False otherwise
1799
+ :rtype: bool
1800
+ """
1801
+ can_implement = True
1802
+ # Skip unsupported types
1803
+ if not PersistentDenseGemmKernel.is_valid_dtypes(ab_dtype, acc_dtype, c_dtype):
1804
+ can_implement = False
1805
+ # Skip invalid mma tile shape and cluster shape
1806
+ if not PersistentDenseGemmKernel.is_valid_mma_tiler_and_cluster_shape(
1807
+ use_2cta_instrs, mma_tiler_mn, cluster_shape_mn
1808
+ ):
1809
+ can_implement = False
1810
+ # Skip illegal problem shape for load/store alignment
1811
+ if not PersistentDenseGemmKernel.is_valid_tensor_alignment(
1812
+ m, n, k, l, ab_dtype, c_dtype, a_major, b_major, c_major
1813
+ ):
1814
+ can_implement = False
1815
+ # Skip invalid epilogue store option
1816
+ if not PersistentDenseGemmKernel.is_valid_epilog_store_option(
1817
+ use_2cta_instrs, use_tma_store, m, n, mma_tiler_mn
1818
+ ):
1819
+ can_implement = False
1820
+ return can_implement
1821
+
1822
+
1823
+ def run(
1824
+ mnkl: Tuple[int, int, int, int],
1825
+ ab_dtype: Type[cutlass.Numeric],
1826
+ c_dtype: Type[cutlass.Numeric],
1827
+ acc_dtype: Type[cutlass.Numeric],
1828
+ a_major: str,
1829
+ b_major: str,
1830
+ c_major: str,
1831
+ mma_tiler_mn: Tuple[int, int] = (256, 256),
1832
+ cluster_shape_mn: Tuple[int, int] = (2, 1),
1833
+ use_2cta_instrs: bool = True,
1834
+ use_tma_store: bool = True,
1835
+ tolerance: float = 1e-01,
1836
+ warmup_iterations: int = 0,
1837
+ iterations: int = 1,
1838
+ skip_ref_check: bool = False,
1839
+ use_cold_l2: bool = False,
1840
+ **kwargs,
1841
+ ):
1842
+ """Execute a persistent batched dense GEMM operation on Blackwell architecture with performance benchmarking.
1843
+
1844
+ This function prepares input tensors, configures and launches the persistent GEMM kernel,
1845
+ optionally performs reference validation, and benchmarks the execution performance.
1846
+
1847
+ :param mnkl: Problem size (M, N, K, L)
1848
+ :type mnkl: Tuple[int, int, int, int]
1849
+ :param ab_dtype: Data type for input tensors A and B
1850
+ :type ab_dtype: Type[cutlass.Numeric]
1851
+ :param c_dtype: Data type for output tensor C
1852
+ :type c_dtype: Type[cutlass.Numeric]
1853
+ :param acc_dtype: Data type for accumulation during matrix multiplication
1854
+ :type acc_dtype: Type[cutlass.Numeric]
1855
+ :param a_major/b_major/c_major: Memory layout of tensor A/B/C
1856
+ :type a_major/b_major/c_major: str
1857
+ :param mma_tiler_mn: MMA tiling size. If not specified in the decorator parameters, the autotuner will use the
1858
+ default value of (256, 256). Otherwise, the autotuner will use the value specified in the decorator parameters.
1859
+ :type mma_tiler_mn: Tuple[int, int], optional
1860
+ :param cluster_shape_mn: Cluster shape. If not specified in the decorator parameters, the autotuner will use the
1861
+ default value of (2, 1). Otherwise, the autotuner will use the value specified in the decorator parameters.
1862
+ :type cluster_shape_mn: Tuple[int, int], optional
1863
+ :param use_2cta_instrs: Whether to use 2CTA instructions. If not specified in the decorator parameters, the autotuner
1864
+ will use the default value of True. Otherwise, the autotuner will use the value specified in the decorator parameters.
1865
+ :type use_2cta_instrs: bool, optional
1866
+ :param use_tma_store: Whether to use TMA store. If not specified in the decorator parameters, the autotuner will use
1867
+ the default value of True. Otherwise, the autotuner will use the value specified in the decorator parameters.
1868
+ :type use_tma_store: bool, optional
1869
+ :param tolerance: Tolerance value for reference validation comparison, defaults to 1e-01
1870
+ :type tolerance: float, optional
1871
+ :param warmup_iterations: Number of warmup iterations before benchmarking, defaults to 0
1872
+ :type warmup_iterations: int, optional
1873
+ :param iterations: Number of benchmark iterations to run, defaults to 1
1874
+ :type iterations: int, optional
1875
+ :param skip_ref_check: Whether to skip reference result validation, defaults to False
1876
+ :type skip_ref_check: bool, optional
1877
+ :param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
1878
+ :type use_cold_l2: bool, optional
1879
+ :raises RuntimeError: If CUDA GPU is not available
1880
+ :raises ValueError: If the configuration is invalid or unsupported by the kernel
1881
+ :return: Execution time of the GEMM kernel
1882
+ :rtype: float
1883
+ """
1884
+ print(f"Running Blackwell Persistent Dense GEMM test with:")
1885
+ print(f"mnkl: {mnkl}")
1886
+ print(f"AB dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}")
1887
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
1888
+ print(f"Mma Tiler (M, N): {mma_tiler_mn}, Cluster Shape (M, N): {cluster_shape_mn}")
1889
+ print(f"2CTA MMA instructions: {'True' if use_2cta_instrs else 'False'}")
1890
+ print(f"Use TMA Store: {'True' if use_tma_store else 'False'}")
1891
+ print(f"Tolerance: {tolerance}")
1892
+ print(f"Warmup iterations: {warmup_iterations}")
1893
+ print(f"Iterations: {iterations}")
1894
+ print(f"Skip reference checking: {skip_ref_check}")
1895
+ print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
1896
+
1897
+ # Unpack parameters
1898
+ m, n, k, l = mnkl
1899
+
1900
+ # Skip unsupported testcase
1901
+ if not PersistentDenseGemmKernel.can_implement(
1902
+ ab_dtype,
1903
+ acc_dtype,
1904
+ c_dtype,
1905
+ use_2cta_instrs,
1906
+ mma_tiler_mn,
1907
+ cluster_shape_mn,
1908
+ use_tma_store,
1909
+ m,
1910
+ n,
1911
+ k,
1912
+ l,
1913
+ a_major,
1914
+ b_major,
1915
+ c_major,
1916
+ ):
1917
+ raise TypeError(
1918
+ f"Unsupported testcase {ab_dtype}, {acc_dtype}, {c_dtype}, {use_2cta_instrs}, {mma_tiler_mn}, {cluster_shape_mn}, {use_tma_store}, {m}, {n}, {k}, {l}, {a_major}, {b_major}, {c_major}"
1919
+ )
1920
+
1921
+ if not torch.cuda.is_available():
1922
+ raise RuntimeError("GPU is required to run this example!")
1923
+
1924
+ torch.manual_seed(1111)
1925
+
1926
+ # Create and permute tensor A/B/C
1927
+ def create_and_permute_tensor(
1928
+ l, mode0, mode1, is_mode0_major, dtype, is_dynamic_layout=True
1929
+ ):
1930
+ # is_mode0_major: (l, mode1, mode0) -> (mode0, mode1, l)
1931
+ # else: (l, mode0, mode1) -> (mode0, mode1, l)
1932
+ shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
1933
+ permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
1934
+ is_unsigned = dtype in {cutlass.Uint8}
1935
+ # Temporarily use uint8 as torch does not support fp8 type
1936
+ torch_dtype = (
1937
+ cutlass_torch.dtype(dtype)
1938
+ if dtype not in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}
1939
+ else torch.uint8
1940
+ )
1941
+
1942
+ # Create dtype torch tensor (cpu)
1943
+ torch_tensor_cpu = cutlass_torch.create_and_permute_torch_tensor(
1944
+ shape,
1945
+ torch_dtype,
1946
+ permute_order=permute_order,
1947
+ init_type=cutlass_torch.TensorInitType.RANDOM,
1948
+ init_config=cutlass_torch.RandomInitConfig(
1949
+ min_val=0 if is_unsigned else -2, max_val=4 if is_unsigned else 2
1950
+ ),
1951
+ )
1952
+ # Create dtype torch tensor (gpu)
1953
+ torch_tensor = torch_tensor_cpu.cuda()
1954
+
1955
+ # Create f32 torch tensor (cpu)
1956
+ f32_torch_tensor = torch_tensor_cpu.to(dtype=torch.float32)
1957
+
1958
+ # Create dtype cute tensor (gpu)
1959
+ cute_tensor = from_dlpack(torch_tensor, assumed_align=16)
1960
+ cute_tensor.element_type = dtype
1961
+ if is_dynamic_layout:
1962
+ cute_tensor = cute_tensor.mark_layout_dynamic(
1963
+ leading_dim=(0 if is_mode0_major else 1)
1964
+ )
1965
+ cute_tensor = cutlass_torch.convert_cute_tensor(
1966
+ f32_torch_tensor,
1967
+ cute_tensor,
1968
+ dtype,
1969
+ is_dynamic_layout=is_dynamic_layout,
1970
+ )
1971
+
1972
+ return f32_torch_tensor, cute_tensor, torch_tensor, torch_tensor_cpu
1973
+
1974
+ a_ref, a_tensor, a_torch, a_torch_cpu = create_and_permute_tensor(
1975
+ l, m, k, a_major == "m", ab_dtype, is_dynamic_layout=True
1976
+ )
1977
+ b_ref, b_tensor, b_torch, b_torch_cpu = create_and_permute_tensor(
1978
+ l, n, k, b_major == "n", ab_dtype, is_dynamic_layout=True
1979
+ )
1980
+ c_ref, c_tensor, c_torch, c_torch_cpu = create_and_permute_tensor(
1981
+ l, m, n, c_major == "m", c_dtype, is_dynamic_layout=True
1982
+ )
1983
+
1984
+ # Configure gemm kernel
1985
+ gemm = PersistentDenseGemmKernel(
1986
+ acc_dtype,
1987
+ use_2cta_instrs,
1988
+ mma_tiler_mn,
1989
+ cluster_shape_mn,
1990
+ use_tma_store,
1991
+ )
1992
+
1993
+ # Compute max active clusters on current device
1994
+ hardware_info = cutlass.utils.HardwareInfo()
1995
+ max_active_clusters = hardware_info.get_max_active_clusters(
1996
+ cluster_shape_mn[0] * cluster_shape_mn[1]
1997
+ )
1998
+
1999
+ # Get current CUDA stream from PyTorch
2000
+ torch_stream = torch.cuda.current_stream()
2001
+ # Get the raw stream pointer as a CUstream
2002
+ current_stream = cuda.CUstream(torch_stream.cuda_stream)
2003
+ # Compile gemm kernel
2004
+ compiled_gemm = cute.compile(
2005
+ gemm, a_tensor, b_tensor, c_tensor, max_active_clusters, current_stream
2006
+ )
2007
+
2008
+ if not skip_ref_check:
2009
+ compiled_gemm(a_tensor, b_tensor, c_tensor, current_stream)
2010
+ if ab_dtype in {
2011
+ cutlass.Int8,
2012
+ cutlass.Uint8,
2013
+ cutlass.Float8E4M3FN,
2014
+ cutlass.Float8E5M2,
2015
+ }:
2016
+ ref = torch.einsum("mkl,nkl->mnl", a_ref.cpu(), b_ref.cpu())
2017
+ else:
2018
+ ref = (torch.einsum("mkl,nkl->mnl", a_ref, b_ref)).cpu()
2019
+
2020
+ # Copy gpu result back
2021
+ gpu_c = c_torch.cpu()
2022
+
2023
+ # Convert ref to c_type
2024
+ if c_dtype == cutlass.Float32:
2025
+ ref_c = ref
2026
+ elif c_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}:
2027
+ # m major: (l, n, m) -> (m, n, l)
2028
+ # n major: (l, m, n) -> (m, n, l)
2029
+ permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
2030
+ shape = (l, m, n) if c_major == "n" else (l, n, m)
2031
+ f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
2032
+ shape,
2033
+ torch.uint8,
2034
+ permute_order=permute_order,
2035
+ init_type=cutlass_torch.TensorInitType.SKIP,
2036
+ ).cuda()
2037
+ # Create dtype cute tensor (gpu)
2038
+ ref_c_tensor = from_dlpack(
2039
+ f8_torch_tensor, assumed_align=16
2040
+ ).mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
2041
+ ref_c_tensor.element_type = c_dtype
2042
+ ref_c_tensor = cutlass_torch.convert_cute_tensor(
2043
+ ref,
2044
+ ref_c_tensor,
2045
+ c_dtype,
2046
+ is_dynamic_layout=True,
2047
+ )
2048
+
2049
+ ref_c = f8_torch_tensor.cpu()
2050
+ else:
2051
+ ref_c = ref.to(cutlass_torch.dtype(c_dtype))
2052
+
2053
+ # Reference checking ref_c and gpu_c
2054
+ torch.testing.assert_close(
2055
+ gpu_c,
2056
+ ref_c,
2057
+ atol=tolerance,
2058
+ rtol=1e-05,
2059
+ )
2060
+
2061
+ def generate_tensors():
2062
+ a_tensor, _ = cutlass_torch.cute_tensor_like(
2063
+ a_torch_cpu, ab_dtype, is_dynamic_layout=True, assumed_align=16
2064
+ )
2065
+ b_tensor, _ = cutlass_torch.cute_tensor_like(
2066
+ b_torch_cpu, ab_dtype, is_dynamic_layout=True, assumed_align=16
2067
+ )
2068
+ c_tensor, _ = cutlass_torch.cute_tensor_like(
2069
+ c_torch_cpu, c_dtype, is_dynamic_layout=True, assumed_align=16
2070
+ )
2071
+ return testing.JitArguments(a_tensor, b_tensor, c_tensor, current_stream)
2072
+
2073
+ workspace_count = 1
2074
+ if use_cold_l2:
2075
+ one_workspace_bytes = (
2076
+ a_torch_cpu.numel() * a_torch_cpu.element_size()
2077
+ + b_torch_cpu.numel() * b_torch_cpu.element_size()
2078
+ + c_torch_cpu.numel() * c_torch_cpu.element_size()
2079
+ )
2080
+ workspace_count = testing.get_workspace_count(
2081
+ one_workspace_bytes, warmup_iterations, iterations
2082
+ )
2083
+
2084
+ exec_time = testing.benchmark(
2085
+ compiled_gemm,
2086
+ workspace_generator=generate_tensors,
2087
+ workspace_count=workspace_count,
2088
+ stream=current_stream,
2089
+ warmup_iterations=warmup_iterations,
2090
+ iterations=iterations,
2091
+ )
2092
+
2093
+ return exec_time # Return execution time in microseconds
2094
+
2095
+
2096
+ if __name__ == "__main__":
2097
+
2098
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
2099
+ try:
2100
+ return tuple(int(x.strip()) for x in s.split(","))
2101
+ except ValueError:
2102
+ raise argparse.ArgumentTypeError(
2103
+ "Invalid format. Expected comma-separated integers."
2104
+ )
2105
+
2106
+ parser = argparse.ArgumentParser(
2107
+ description="Example of Dense Persistent GEMM on Blackwell."
2108
+ )
2109
+
2110
+ parser.add_argument(
2111
+ "--mnkl",
2112
+ type=parse_comma_separated_ints,
2113
+ default=(256, 256, 512, 1),
2114
+ help="mnkl dimensions (comma-separated)",
2115
+ )
2116
+ parser.add_argument(
2117
+ "--mma_tiler_mn",
2118
+ type=parse_comma_separated_ints,
2119
+ default=(128, 128),
2120
+ help="Mma tile shape (comma-separated)",
2121
+ )
2122
+ parser.add_argument(
2123
+ "--cluster_shape_mn",
2124
+ type=parse_comma_separated_ints,
2125
+ default=(1, 1),
2126
+ help="Cluster shape (comma-separated)",
2127
+ )
2128
+ parser.add_argument("--ab_dtype", type=cutlass.dtype, default=cutlass.TFloat32)
2129
+ parser.add_argument("--c_dtype", type=cutlass.dtype, default=cutlass.Float32)
2130
+ parser.add_argument("--acc_dtype", type=cutlass.dtype, default=cutlass.Float32)
2131
+ parser.add_argument(
2132
+ "--use_2cta_instrs",
2133
+ action="store_true",
2134
+ help="Enable 2CTA MMA instructions feature",
2135
+ )
2136
+ parser.add_argument("--a_major", choices=["k", "m"], type=str, default="k")
2137
+ parser.add_argument("--b_major", choices=["k", "n"], type=str, default="k")
2138
+ parser.add_argument("--c_major", choices=["n", "m"], type=str, default="n")
2139
+ parser.add_argument(
2140
+ "--use_tma_store", action="store_true", help="Use tma store or not"
2141
+ )
2142
+ parser.add_argument(
2143
+ "--tolerance", type=float, default=1e-01, help="Tolerance for validation"
2144
+ )
2145
+ parser.add_argument(
2146
+ "--warmup_iterations", type=int, default=0, help="Warmup iterations"
2147
+ )
2148
+ parser.add_argument(
2149
+ "--iterations",
2150
+ type=int,
2151
+ default=1,
2152
+ help="Number of iterations to run the kernel",
2153
+ )
2154
+ parser.add_argument(
2155
+ "--skip_ref_check", action="store_true", help="Skip reference checking"
2156
+ )
2157
+ parser.add_argument(
2158
+ "--use_cold_l2",
2159
+ action="store_true",
2160
+ default=False,
2161
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
2162
+ )
2163
+
2164
+ args = parser.parse_args()
2165
+
2166
+ if len(args.mnkl) != 4:
2167
+ parser.error("--mnkl must contain exactly 4 values")
2168
+
2169
+ if len(args.mma_tiler_mn) != 2:
2170
+ parser.error("--mma_tiler_mn must contain exactly 2 values")
2171
+
2172
+ if len(args.cluster_shape_mn) != 2:
2173
+ parser.error("--cluster_shape_mn must contain exactly 2 values")
2174
+
2175
+ run(
2176
+ args.mnkl,
2177
+ args.ab_dtype,
2178
+ args.c_dtype,
2179
+ args.acc_dtype,
2180
+ args.a_major,
2181
+ args.b_major,
2182
+ args.c_major,
2183
+ args.mma_tiler_mn,
2184
+ args.cluster_shape_mn,
2185
+ args.use_2cta_instrs,
2186
+ args.use_tma_store,
2187
+ args.tolerance,
2188
+ args.warmup_iterations,
2189
+ args.iterations,
2190
+ args.skip_ref_check,
2191
+ args.use_cold_l2,
2192
+ )
2193
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/dense_gemm_software_pipeline.py ADDED
@@ -0,0 +1,1831 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ from typing import Optional, Type, Tuple, Union
31
+ import cuda.bindings.driver as cuda
32
+
33
+ import torch
34
+
35
+ import cutlass
36
+ import cutlass.cute as cute
37
+ import cutlass.utils as utils
38
+ import cutlass.pipeline as pipeline
39
+ from cutlass.cute.nvgpu import cpasync, tcgen05
40
+ import cutlass.torch as cutlass_torch
41
+ import cutlass.utils.blackwell_helpers as sm100_utils
42
+ from cutlass.cute.runtime import from_dlpack
43
+
44
+ """
45
+ A high-performance batched dense GEMM (C = A * B) example for the NVIDIA Blackwell SM100 architecture
46
+ using CUTE DSL with compiler generated software pipeline.
47
+ - Matrix A is MxKxL, L is batch dimension, A can be row-major("K") or column-major("M")
48
+ - Matrix B is NxKxL, L is batch dimension, B can be row-major("N") or column-major("K")
49
+ - Matrix C is MxNxL, L is batch dimension, C can be row-major("N") or column-major("M")
50
+
51
+ This GEMM kernel supports the following features:
52
+ - Utilizes Tensor Memory Access (TMA) for efficient memory operations
53
+ - Utilizes Blackwell's tcgen05.mma for matrix multiply-accumulate (MMA) operations (including 2cta mma instructions)
54
+ - Implements TMA multicast with cluster to reduce L2 memory traffic
55
+ - Supports multi-stage pipeline to overlap computation and memory access
56
+
57
+ This GEMM works as follows:
58
+ 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.
59
+ 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.
60
+ 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.
61
+ 4. Type convert C matrix to output type.
62
+ 5. Optionally store C matrix from registers (RMEM) to shared memory (SMEM) to global memory (GMEM) with TMA operations,
63
+ or directly store C matrix from registers (RMEM) to global memory (GMEM) without TMA operations.
64
+ 6. Optionally accept an elementwise lambda function epilogue_op to apply to the output tensor:
65
+ e.g., relu can set epilogue_op = lambda x: cute.where(x > 0, x, cute.full_like(x, 0))
66
+
67
+ SM100 tcgen05.mma instructions operate as follows:
68
+ - Read matrix A from SMEM
69
+ - Read matrix B from SMEM
70
+ - Write accumulator to TMEM
71
+ The accumulator in TMEM must then be loaded to registers before writing back to GMEM.
72
+
73
+ To run this example:
74
+
75
+ .. code-block:: bash
76
+
77
+ python examples/blackwell/dense_gemm_software_pipeline.py \
78
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
79
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
80
+ --mnkl 8192,8192,8192,1 \
81
+ --use_tma_store --use_2cta_instrs
82
+
83
+ The above example command compute batched gemm with M=8192, N=8192, K=8192,
84
+ batch_count=1. The Blackwell tcgen05 MMA tile shape used 2 cta with 256x128
85
+ MMA tile and the cluster shape is (2,1). The input, mma accumulator and output
86
+ data type are set as fp16, fp32 and fp16, respectively.
87
+
88
+ To collect performance with NCU profiler:
89
+
90
+ .. code-block:: bash
91
+
92
+ ncu python examples/blackwell/dense_gemm_software_pipeline.py \
93
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
94
+ --mma_tiler_mn 256,128 --cluster_shape_mn 2,1 \
95
+ --mnkl 8192,8192,8192,1 \
96
+ --use_tma_store --use_2cta_instrs
97
+
98
+ Constraints:
99
+ * Supported input data types: fp16, bf16, tf32, int8, uint8, fp8 (e4m3fn, e5m2),
100
+ see detailed valid dtype combinations in below DenseGemmKernel class documentation
101
+ * A/B tensor must have the same data type
102
+ * Mma tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
103
+ * Mma tiler N must be 32-256, step 32
104
+ * Cluster shape M/N must be positive and power of 2, total cluster size <= 16
105
+ * Cluster shape M must be multiple of 2 if use_2cta_instrs=True
106
+ * The contiguous dimension of A/B/C tensors must be at least 16 bytes aligned,
107
+ i.e, number of elements is a multiple of 4, 8, and 16 for TFloat32,
108
+ Float16/BFloat16, and Int8/Uint8/Float8, respectively.
109
+ * OOB tiles are not allowed when TMA store is disabled
110
+ """
111
+
112
+
113
+ class DenseGemmKernel:
114
+ """
115
+ This class implements batched matrix multiplication (C = A x B) with support for various data types
116
+ and architectural features specific to Blackwell GPUs.
117
+
118
+ :param acc_dtype: Data type for accumulation during computation
119
+ :type acc_dtype: type[cutlass.Numeric]
120
+ :param use_2cta_instrs: Whether to use CTA group 2 for advanced thread cooperation
121
+ :type use_2cta_instrs: bool
122
+ :param mma_tiler_mn: Shape of the Matrix Multiply-Accumulate (MMA) tiler (M,N)
123
+ :type mma_tiler_mn: Tuple[int, int]
124
+ :param cluster_shape_mn: Cluster dimensions (M,N) for parallel processing
125
+ :type cluster_shape_mn: Tuple[int, int]
126
+ :param use_tma_store: Whether to use Tensor Memory Access (TMA) for storing results
127
+ :type use_tma_store: bool
128
+
129
+ :note: In current version, A and B tensor must have the same data type
130
+ - i.e., Float8E4M3FN for A and Float8E5M2 for B is not supported
131
+
132
+ :note: Supported A/B data types:
133
+ - TFloat32
134
+ - Float16/BFloat16
135
+ - Int8/Uint8
136
+ - Float8E4M3FN/Float8E5M2
137
+
138
+ :note: Supported accumulator data types:
139
+ - Float32 (for all floating point A/B data types)
140
+ - Float16 (only for fp16 and fp8 A/B data types)
141
+ - Int32 (only for uint8/int8 A/B data types)
142
+
143
+ :note: Supported C data types:
144
+ - Float32 (for float32 and int32 accumulator data types)
145
+ - Int32 (for float32 and int32 accumulator data types)
146
+ - Float16/BFloat16 (for fp16 and fp8 accumulator data types)
147
+ - Int8/Uint8 (for uint8/int8 accumulator data types)
148
+ - Float8E4M3FN/Float8E5M2 (for float32 accumulator data types)
149
+
150
+ :note: Constraints:
151
+ - MMA tiler M must be 64/128 (use_2cta_instrs=False) or 128/256 (use_2cta_instrs=True)
152
+ - MMA tiler N must be 32-256, step 32
153
+ - Cluster shape M must be multiple of 2 if use_2cta_instrs=True
154
+ - Cluster shape M/N must be positive and power of 2, total cluster size <= 16
155
+
156
+ Example:
157
+ >>> gemm = DenseGemmKernel(
158
+ ... acc_dtype=cutlass.Float32,
159
+ ... use_2cta_instrs=True,
160
+ ... mma_tiler_mn=(128, 128),
161
+ ... cluster_shape_mn=(2, 2)
162
+ ... )
163
+ >>> gemm(a_tensor, b_tensor, c_tensor, stream)
164
+ """
165
+
166
+ def __init__(
167
+ self,
168
+ acc_dtype: Type[cutlass.Numeric],
169
+ use_2cta_instrs: bool,
170
+ mma_tiler_mn: Tuple[int, int],
171
+ cluster_shape_mn: Tuple[int, int],
172
+ use_tma_store: bool,
173
+ ):
174
+ """Initializes the configuration for a Blackwell dense GEMM kernel.
175
+
176
+ This configuration includes several key aspects:
177
+
178
+ 1. MMA Instruction Settings (tcgen05):
179
+ - acc_dtype: Data types for MMA accumulator.
180
+ - mma_tiler_mn: The (M, N) shape of the MMA instruction tiler.
181
+ - use_2cta_instrs: Boolean indicating if the tcgen05 MMA variant
182
+ with cta_group=2 should be used.
183
+
184
+ 2. Cluster Shape:
185
+ - cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster.
186
+
187
+ 3. Output C tensor store mode:
188
+ - use_tma_store: Boolean indicating whether to use Tensor Memory Access (TMA) for storing results.
189
+
190
+ :param acc_dtype: Data type of the accumulator.
191
+ :type acc_dtype: type[cutlass.Numeric]
192
+ :param mma_tiler_mn: Tuple (M, N) shape of the MMA instruction.
193
+ :type mma_tiler_mn: Tuple[int, int]
194
+ :param use_2cta_instrs: Boolean, True to use cta_group=2 MMA variant.
195
+ :type use_2cta_instrs: bool
196
+ :param cluster_shape_mn: Tuple (ClusterM, ClusterN) shape of the cluster.
197
+ :type cluster_shape_mn: Tuple[int, int]
198
+ :param use_tma_store: Use Tensor Memory Access (TMA) or normal store for output C tensor.
199
+ :type use_tma_store: bool
200
+ """
201
+
202
+ self.acc_dtype: Type[cutlass.Numeric] = acc_dtype
203
+ self.use_2cta_instrs = use_2cta_instrs
204
+ self.cluster_shape_mn = cluster_shape_mn
205
+ # K dimension is deferred in _setup_attributes
206
+ self.mma_tiler = (*mma_tiler_mn, 1)
207
+ self.use_tma_store = use_tma_store
208
+
209
+ self.cta_group = (
210
+ tcgen05.CtaGroup.TWO if self.use_2cta_instrs else tcgen05.CtaGroup.ONE
211
+ )
212
+
213
+ self.occupancy = 1
214
+ self.threads_per_cta = 128
215
+ self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
216
+
217
+ def _setup_attributes(self):
218
+ """Set up configurations that are dependent on GEMM inputs
219
+
220
+ This method configures various attributes based on the input tensor properties
221
+ (data types, leading dimensions) and kernel settings:
222
+ - Configuring tiled MMA
223
+ - Computing MMA/cluster/tile shapes
224
+ - Computing cluster layout
225
+ - Computing multicast CTAs for A/B
226
+ - Computing epilogue subtile
227
+ - Setting up A/B/C stage counts in shared memory
228
+ - Computing A/B/C shared memory layout
229
+ - Computing tensor memory allocation columns
230
+ """
231
+ # Configure tiled mma
232
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
233
+ self.a_dtype,
234
+ self.a_major_mode,
235
+ self.b_major_mode,
236
+ self.acc_dtype,
237
+ self.cta_group,
238
+ self.mma_tiler[:2],
239
+ )
240
+
241
+ # Compute mma/cluster/tile shapes
242
+ mma_inst_shape_k = cute.size(tiled_mma.shape_mnk, mode=[2])
243
+ mma_inst_tile_k = 4
244
+ self.mma_tiler = (
245
+ self.mma_tiler[0],
246
+ self.mma_tiler[1],
247
+ mma_inst_shape_k * mma_inst_tile_k,
248
+ )
249
+ self.cta_tile_shape_mnk = (
250
+ self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
251
+ self.mma_tiler[1],
252
+ self.mma_tiler[2],
253
+ )
254
+
255
+ # Compute cluster layout
256
+ self.cluster_layout_vmnk = cute.tiled_divide(
257
+ cute.make_layout((*self.cluster_shape_mn, 1)),
258
+ (tiled_mma.thr_id.shape,),
259
+ )
260
+
261
+ # Compute number of multicast CTAs for A/B
262
+ self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
263
+ self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
264
+ self.is_a_mcast = self.num_mcast_ctas_a > 1
265
+ self.is_b_mcast = self.num_mcast_ctas_b > 1
266
+
267
+ # Compute epilogue subtile
268
+ if cutlass.const_expr(self.use_tma_store):
269
+ self.epi_tile = sm100_utils.compute_epilogue_tile_shape(
270
+ self.cta_tile_shape_mnk,
271
+ self.use_2cta_instrs,
272
+ self.c_layout,
273
+ self.c_dtype,
274
+ )
275
+ else:
276
+ self.epi_tile = self.cta_tile_shape_mnk[:2]
277
+
278
+ # Setup A/B/C stage count in shared memory
279
+ self.num_acc_stage, self.num_ab_stage, self.num_c_stage = self._compute_stages(
280
+ tiled_mma,
281
+ self.mma_tiler,
282
+ self.a_dtype,
283
+ self.b_dtype,
284
+ self.epi_tile,
285
+ self.c_dtype,
286
+ self.c_layout,
287
+ self.smem_capacity,
288
+ self.occupancy,
289
+ self.use_tma_store,
290
+ )
291
+
292
+ # Compute A/B/C shared memory layout
293
+ self.a_smem_layout_staged = sm100_utils.make_smem_layout_a(
294
+ tiled_mma,
295
+ self.mma_tiler,
296
+ self.a_dtype,
297
+ self.num_ab_stage,
298
+ )
299
+ self.b_smem_layout_staged = sm100_utils.make_smem_layout_b(
300
+ tiled_mma,
301
+ self.mma_tiler,
302
+ self.b_dtype,
303
+ self.num_ab_stage,
304
+ )
305
+ self.c_smem_layout_staged = (
306
+ sm100_utils.make_smem_layout_epi(
307
+ self.c_dtype,
308
+ self.c_layout,
309
+ self.epi_tile,
310
+ self.num_c_stage,
311
+ )
312
+ if self.use_tma_store
313
+ else None
314
+ )
315
+
316
+ # Compute the number of tensor memory allocation columns
317
+ self.num_tmem_alloc_cols = self._compute_num_tmem_alloc_cols(
318
+ tiled_mma, self.mma_tiler
319
+ )
320
+
321
+ @cute.jit
322
+ def __call__(
323
+ self,
324
+ a: cute.Tensor,
325
+ b: cute.Tensor,
326
+ c: cute.Tensor,
327
+ stream: cuda.CUstream,
328
+ epilogue_op: cutlass.Constexpr = lambda x: x,
329
+ ):
330
+ """Execute the GEMM operation in steps:
331
+ - Setup static attributes
332
+ - Setup TMA load/store atoms and tensors
333
+ - Compute grid size
334
+ - Define shared storage for kernel
335
+ - Launch the kernel synchronously
336
+
337
+ :param a: Input tensor A
338
+ :type a: cute.Tensor
339
+ :param b: Input tensor B
340
+ :type b: cute.Tensor
341
+ :param c: Output tensor C
342
+ :type c: cute.Tensor
343
+ :param stream: CUDA stream for asynchronous execution
344
+ :type stream: cuda.CUstream
345
+ :param epilogue_op: Optional elementwise lambda function to apply to the output tensor
346
+ :type epilogue_op: cutlass.Constexpr
347
+ :raises TypeError: If input data types are incompatible with the MMA instruction.
348
+ :raises AssertionError: If OOB (Out-Of-Bounds) tiles are present when TMA store is disabled.
349
+ """
350
+ # Setup static attributes before smem/grid/tma computation
351
+ self.a_dtype: Type[cutlass.Numeric] = a.element_type
352
+ self.b_dtype: Type[cutlass.Numeric] = b.element_type
353
+ self.c_dtype: Type[cutlass.Numeric] = c.element_type
354
+ self.a_major_mode = utils.LayoutEnum.from_tensor(a).mma_major_mode()
355
+ self.b_major_mode = utils.LayoutEnum.from_tensor(b).mma_major_mode()
356
+ self.c_layout = utils.LayoutEnum.from_tensor(c)
357
+
358
+ # Check if input data types are compatible with MMA instruction
359
+ if cutlass.const_expr(self.a_dtype != self.b_dtype):
360
+ raise TypeError(f"Type must match: {self.a_dtype} != {self.b_dtype}")
361
+
362
+ # Setup attributes that dependent on gemm inputs
363
+ self._setup_attributes()
364
+
365
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
366
+ self.a_dtype,
367
+ self.a_major_mode,
368
+ self.b_major_mode,
369
+ self.acc_dtype,
370
+ self.cta_group,
371
+ self.mma_tiler[:2],
372
+ )
373
+ atom_thr_size = cute.size(tiled_mma.thr_id.shape)
374
+
375
+ # Setup TMA load for A
376
+ a_op = sm100_utils.cluster_shape_to_tma_atom_A(
377
+ self.cluster_shape_mn, tiled_mma.thr_id
378
+ )
379
+ a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
380
+ tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
381
+ a_op,
382
+ a,
383
+ a_smem_layout,
384
+ self.mma_tiler,
385
+ tiled_mma,
386
+ self.cluster_layout_vmnk.shape,
387
+ internal_type=(
388
+ cutlass.TFloat32 if a.element_type is cutlass.Float32 else None
389
+ ),
390
+ )
391
+
392
+ # Setup TMA load for B
393
+ b_op = sm100_utils.cluster_shape_to_tma_atom_B(
394
+ self.cluster_shape_mn, tiled_mma.thr_id
395
+ )
396
+ b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
397
+ tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
398
+ b_op,
399
+ b,
400
+ b_smem_layout,
401
+ self.mma_tiler,
402
+ tiled_mma,
403
+ self.cluster_layout_vmnk.shape,
404
+ internal_type=(
405
+ cutlass.TFloat32 if b.element_type is cutlass.Float32 else None
406
+ ),
407
+ )
408
+
409
+ a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
410
+ b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
411
+ self.num_tma_load_bytes = (a_copy_size + b_copy_size) * atom_thr_size
412
+
413
+ # Setup store for C
414
+ tma_atom_c = None
415
+ tma_tensor_c = None
416
+ if cutlass.const_expr(self.use_tma_store):
417
+ c_cta_v_layout = cute.composition(
418
+ cute.make_identity_layout(c.shape), self.epi_tile
419
+ )
420
+ epi_smem_layout = cute.slice_(self.c_smem_layout_staged, (None, None, 0))
421
+ tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
422
+ cpasync.CopyBulkTensorTileS2GOp(),
423
+ c,
424
+ epi_smem_layout,
425
+ c_cta_v_layout,
426
+ )
427
+
428
+ # Compute grid size
429
+ grid = self._compute_grid(c, self.cta_tile_shape_mnk, self.cluster_shape_mn)
430
+
431
+ self.buffer_align_bytes = 1024
432
+
433
+ c_smem_size = (
434
+ cute.cosize(self.c_smem_layout_staged.outer) if self.use_tma_store else 0
435
+ )
436
+
437
+ # Define shared storage for kernel
438
+ @cute.struct
439
+ class SharedStorage:
440
+ ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
441
+ ab_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
442
+ acc_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
443
+ tmem_dealloc_mbar_ptr: cutlass.Int64
444
+ tmem_holding_buf: cutlass.Int32
445
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
446
+ sC: cute.struct.Align[
447
+ cute.struct.MemRange[
448
+ self.c_dtype,
449
+ c_smem_size,
450
+ ],
451
+ self.buffer_align_bytes,
452
+ ]
453
+ # (MMA, MMA_M, MMA_K, STAGE)
454
+ sA: cute.struct.Align[
455
+ cute.struct.MemRange[
456
+ self.a_dtype, cute.cosize(self.a_smem_layout_staged.outer)
457
+ ],
458
+ self.buffer_align_bytes,
459
+ ]
460
+ # (MMA, MMA_N, MMA_K, STAGE)
461
+ sB: cute.struct.Align[
462
+ cute.struct.MemRange[
463
+ self.b_dtype, cute.cosize(self.b_smem_layout_staged.outer)
464
+ ],
465
+ self.buffer_align_bytes,
466
+ ]
467
+
468
+ self.shared_storage = SharedStorage
469
+
470
+ # Launch the kernel synchronously
471
+ self.kernel(
472
+ tiled_mma,
473
+ tma_atom_a,
474
+ tma_tensor_a,
475
+ tma_atom_b,
476
+ tma_tensor_b,
477
+ tma_atom_c,
478
+ tma_tensor_c if self.use_tma_store else c,
479
+ self.cluster_layout_vmnk,
480
+ self.a_smem_layout_staged,
481
+ self.b_smem_layout_staged,
482
+ self.c_smem_layout_staged,
483
+ self.epi_tile,
484
+ epilogue_op,
485
+ ).launch(
486
+ grid=grid,
487
+ block=[self.threads_per_cta, 1, 1],
488
+ cluster=(*self.cluster_shape_mn, 1),
489
+ stream=stream,
490
+ )
491
+ return
492
+
493
+ # GPU device kernel
494
+ @cute.kernel
495
+ def kernel(
496
+ self,
497
+ tiled_mma: cute.TiledMma,
498
+ tma_atom_a: cute.CopyAtom,
499
+ mA_mkl: cute.Tensor,
500
+ tma_atom_b: cute.CopyAtom,
501
+ mB_nkl: cute.Tensor,
502
+ tma_atom_c: Optional[cute.CopyAtom],
503
+ mC_mnl: cute.Tensor,
504
+ cluster_layout_vmnk: cute.Layout,
505
+ a_smem_layout_staged: cute.ComposedLayout,
506
+ b_smem_layout_staged: cute.ComposedLayout,
507
+ c_smem_layout_staged: Union[cute.Layout, cute.ComposedLayout, None],
508
+ epi_tile: cute.Tile,
509
+ epilogue_op: cutlass.Constexpr,
510
+ ):
511
+ """
512
+ GPU device kernel performing the batched GEMM computation.
513
+ """
514
+ warp_idx = cute.arch.warp_idx()
515
+ warp_idx = cute.arch.make_warp_uniform(warp_idx)
516
+
517
+ #
518
+ # Prefetch tma descriptor
519
+ #
520
+ if warp_idx == 0:
521
+ cpasync.prefetch_descriptor(tma_atom_a)
522
+ cpasync.prefetch_descriptor(tma_atom_b)
523
+ if cutlass.const_expr(self.use_tma_store):
524
+ cpasync.prefetch_descriptor(tma_atom_c)
525
+
526
+ use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
527
+
528
+ #
529
+ # Setup cta/thread coordinates
530
+ #
531
+ # Coords inside cluster
532
+ bidx, bidy, bidz = cute.arch.block_idx()
533
+ mma_tile_coord_v = bidx % cute.size(tiled_mma.thr_id.shape)
534
+ is_leader_cta = mma_tile_coord_v == 0
535
+ cta_rank_in_cluster = cute.arch.make_warp_uniform(
536
+ cute.arch.block_idx_in_cluster()
537
+ )
538
+ block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
539
+ cta_rank_in_cluster
540
+ )
541
+ # Coords outside cluster
542
+ cta_coord = (bidx, bidy, bidz)
543
+ mma_tile_coord_mnl = (
544
+ cta_coord[0] // cute.size(tiled_mma.thr_id.shape),
545
+ cta_coord[1],
546
+ cta_coord[2],
547
+ )
548
+ # Coords inside cta
549
+ tidx, _, _ = cute.arch.thread_idx()
550
+
551
+ #
552
+ # Alloc and init: a+b full/empty, accumulator full, tensor memory dealloc barrier
553
+ #
554
+ smem = utils.SmemAllocator()
555
+ storage = smem.allocate(self.shared_storage)
556
+
557
+ tmem_dealloc_mbar_ptr = storage.tmem_dealloc_mbar_ptr
558
+ tmem_holding_buf = storage.tmem_holding_buf
559
+
560
+ # Initialize mainloop ab_pipeline (barrier) and states
561
+ ab_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
562
+ num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
563
+ ab_pipeline_consumer_group = pipeline.CooperativeGroup(
564
+ pipeline.Agent.Thread, num_tma_producer
565
+ )
566
+ ab_pipeline = pipeline.PipelineTmaUmma.create(
567
+ barrier_storage=storage.ab_full_mbar_ptr.data_ptr(),
568
+ num_stages=self.num_ab_stage,
569
+ producer_group=ab_pipeline_producer_group,
570
+ consumer_group=ab_pipeline_consumer_group,
571
+ tx_count=self.num_tma_load_bytes,
572
+ cta_layout_vmnk=cluster_layout_vmnk,
573
+ )
574
+ ab_producer_state = pipeline.make_pipeline_state(
575
+ pipeline.PipelineUserType.Producer, self.num_ab_stage
576
+ )
577
+ ab_consumer_state = pipeline.make_pipeline_state(
578
+ pipeline.PipelineUserType.Consumer, self.num_ab_stage
579
+ )
580
+
581
+ # Initialize acc_pipeline (barrier) and states
582
+ acc_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
583
+ acc_pipeline_consumer_group = pipeline.CooperativeGroup(
584
+ pipeline.Agent.Thread, self.threads_per_cta, self.threads_per_cta
585
+ )
586
+ acc_pipeline = pipeline.PipelineUmmaAsync.create(
587
+ barrier_storage=storage.acc_full_mbar_ptr.data_ptr(),
588
+ num_stages=self.num_acc_stage,
589
+ producer_group=acc_pipeline_producer_group,
590
+ consumer_group=acc_pipeline_consumer_group,
591
+ cta_layout_vmnk=cluster_layout_vmnk,
592
+ )
593
+ acc_producer_state = pipeline.make_pipeline_state(
594
+ pipeline.PipelineUserType.Producer, self.num_acc_stage
595
+ )
596
+ acc_consumer_state = pipeline.make_pipeline_state(
597
+ pipeline.PipelineUserType.Consumer, self.num_acc_stage
598
+ )
599
+
600
+ # Tensor memory dealloc barrier init
601
+ if use_2cta_instrs:
602
+ if warp_idx == 0:
603
+ num_tmem_dealloc_threads = 32
604
+ with cute.arch.elect_one():
605
+ cute.arch.mbarrier_init(
606
+ tmem_dealloc_mbar_ptr, num_tmem_dealloc_threads
607
+ )
608
+ cute.arch.mbarrier_init_fence()
609
+
610
+ # Cluster arrive after barrier init
611
+ if cute.size(self.cluster_shape_mn) > 1:
612
+ cute.arch.cluster_arrive_relaxed()
613
+
614
+ #
615
+ # Setup smem tensor A/B/C
616
+ #
617
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
618
+ sC = (
619
+ storage.sC.get_tensor(
620
+ c_smem_layout_staged.outer, swizzle=c_smem_layout_staged.inner
621
+ )
622
+ if self.use_tma_store
623
+ else None
624
+ )
625
+ # (MMA, MMA_M, MMA_K, STAGE)
626
+ sA = storage.sA.get_tensor(
627
+ a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
628
+ )
629
+ # (MMA, MMA_N, MMA_K, STAGE)
630
+ sB = storage.sB.get_tensor(
631
+ b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
632
+ )
633
+
634
+ #
635
+ # Compute multicast mask for A/B buffer full
636
+ #
637
+ a_full_mcast_mask = None
638
+ b_full_mcast_mask = None
639
+ if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
640
+ a_full_mcast_mask = cpasync.create_tma_multicast_mask(
641
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
642
+ )
643
+ b_full_mcast_mask = cpasync.create_tma_multicast_mask(
644
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
645
+ )
646
+
647
+ #
648
+ # Local_tile partition global tensors
649
+ #
650
+ # (bM, bK, RestM, RestK, RestL)
651
+ gA_mkl = cute.local_tile(
652
+ mA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
653
+ )
654
+ # (bN, bK, RestN, RestK, RestL)
655
+ gB_nkl = cute.local_tile(
656
+ mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
657
+ )
658
+ # (bM, bN, RestM, RestN, RestL)
659
+ gC_mnl = cute.local_tile(
660
+ mC_mnl, cute.slice_(self.mma_tiler, (None, None, 0)), (None, None, None)
661
+ )
662
+ k_tile_cnt = cute.size(gA_mkl, mode=[3])
663
+
664
+ #
665
+ # Partition global tensor for TiledMMA_A/B/C
666
+ #
667
+ thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
668
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
669
+ tCgA = thr_mma.partition_A(gA_mkl)
670
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
671
+ tCgB = thr_mma.partition_B(gB_nkl)
672
+ # (MMA, MMA_M, MMA_N, RestM, RestN, RestL)
673
+ tCgC = thr_mma.partition_C(gC_mnl)
674
+
675
+ #
676
+ # Partition global/shared tensor for TMA load A/B
677
+ #
678
+ # TMA load A partition_S/D
679
+ a_cta_layout = cute.make_layout(
680
+ cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
681
+ )
682
+ # ((atom_v, rest_v), STAGE)
683
+ # ((atom_v, rest_v), RestM, RestK, RestL)
684
+ tAsA, tAgA = cpasync.tma_partition(
685
+ tma_atom_a,
686
+ block_in_cluster_coord_vmnk[2],
687
+ a_cta_layout,
688
+ cute.group_modes(sA, 0, 3),
689
+ cute.group_modes(tCgA, 0, 3),
690
+ )
691
+ # TMA load B partition_S/D
692
+ b_cta_layout = cute.make_layout(
693
+ cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
694
+ )
695
+ # ((atom_v, rest_v), STAGE)
696
+ # ((atom_v, rest_v), RestN, RestK, RestL)
697
+ tBsB, tBgB = cpasync.tma_partition(
698
+ tma_atom_b,
699
+ block_in_cluster_coord_vmnk[1],
700
+ b_cta_layout,
701
+ cute.group_modes(sB, 0, 3),
702
+ cute.group_modes(tCgB, 0, 3),
703
+ )
704
+
705
+ #
706
+ # Partition shared/tensor memory tensor for TiledMMA_A/B/C
707
+ #
708
+ # (MMA, MMA_M, MMA_K, STAGE)
709
+ tCrA = tiled_mma.make_fragment_A(sA)
710
+ # (MMA, MMA_N, MMA_K, STAGE)
711
+ tCrB = tiled_mma.make_fragment_B(sB)
712
+ # (MMA, MMA_M, MMA_N)
713
+ acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
714
+ # (MMA, MMA_M, MMA_N)
715
+ tCtAcc_fake = tiled_mma.make_fragment_C(acc_shape)
716
+
717
+ #
718
+ # Cluster wait before tensor memory alloc
719
+ #
720
+ if cute.size(self.cluster_shape_mn) > 1:
721
+ cute.arch.cluster_wait()
722
+
723
+ #
724
+ # Alloc tensor memory buffer
725
+ #
726
+ if warp_idx == 0:
727
+ cute.arch.alloc_tmem(
728
+ self.num_tmem_alloc_cols, tmem_holding_buf, is_two_cta=use_2cta_instrs
729
+ )
730
+
731
+ #
732
+ # Bar sync for retrieve tensor memory ptr from shared memory
733
+ #
734
+ cute.arch.barrier()
735
+
736
+ #
737
+ # Retrieving tensor memory ptr and make accumulator tensor
738
+ #
739
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
740
+ self.acc_dtype, alignment=16, ptr_to_buffer_holding_addr=tmem_holding_buf
741
+ )
742
+ # (MMA, MMA_M, MMA_N)
743
+ tCtAcc = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
744
+
745
+ #
746
+ # Partition for epilogue
747
+ #
748
+ tiled_copy_t2r, tTR_tAcc, tTR_rAcc = self.epilog_tmem_copy_and_partition(
749
+ tidx, tCtAcc, tCgC, epi_tile, use_2cta_instrs
750
+ )
751
+
752
+ tTR_rC = None
753
+ tiled_copy_r2s = None
754
+ simt_atom = None
755
+ tRS_rC = None
756
+ tRS_sC = None
757
+ bSG_sC = None
758
+ bSG_gC = None
759
+ tTR_gC = None
760
+ if cutlass.const_expr(self.use_tma_store):
761
+ tTR_rC = cute.make_fragment(tTR_rAcc.shape, self.c_dtype)
762
+ tiled_copy_r2s, tRS_rC, tRS_sC = self.epilog_smem_copy_and_partition(
763
+ tiled_copy_t2r, tTR_rC, tidx, sC
764
+ )
765
+ tma_atom_c, bSG_sC, bSG_gC = self.epilog_gmem_copy_and_partition(
766
+ tidx, tma_atom_c, tCgC, epi_tile, sC
767
+ )
768
+ else:
769
+ simt_atom, tTR_rC, tTR_gC = self.epilog_gmem_copy_and_partition(
770
+ tidx, tiled_copy_t2r, tCgC, epi_tile, sC
771
+ )
772
+
773
+ #
774
+ # Slice to per mma tile index
775
+ #
776
+ # ((atom_v, rest_v), RestK)
777
+ tAgA = tAgA[(None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])]
778
+ # ((atom_v, rest_v), RestK)
779
+ tBgB = tBgB[(None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])]
780
+ if cutlass.const_expr(self.use_tma_store):
781
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
782
+ bSG_gC = bSG_gC[(None, None, None, *mma_tile_coord_mnl)]
783
+ else:
784
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N)
785
+ tTR_gC = tTR_gC[(None, None, None, None, None, *mma_tile_coord_mnl)]
786
+
787
+ # ///////////////////////////////////////////////////////////////////////////////
788
+ # MAINLOOP
789
+ # ///////////////////////////////////////////////////////////////////////////////
790
+ prefetch_k_tile_cnt = cutlass.min(self.num_ab_stage - 2, k_tile_cnt)
791
+ if warp_idx == 0:
792
+ for k_tile in cutlass.range(
793
+ k_tile_cnt,
794
+ prefetch_stages=self.num_ab_stage - 2,
795
+ ):
796
+ # wait for AB buffer empty
797
+ ab_pipeline.producer_acquire(ab_producer_state)
798
+
799
+ # TMA load A/B
800
+ cute.copy(
801
+ tma_atom_a,
802
+ tAgA[(None, ab_producer_state.count)],
803
+ tAsA[(None, ab_producer_state.index)],
804
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
805
+ mcast_mask=a_full_mcast_mask,
806
+ )
807
+ cute.copy(
808
+ tma_atom_b,
809
+ tBgB[(None, ab_producer_state.count)],
810
+ tBsB[(None, ab_producer_state.index)],
811
+ tma_bar_ptr=ab_pipeline.producer_get_barrier(ab_producer_state),
812
+ mcast_mask=b_full_mcast_mask,
813
+ )
814
+
815
+ if is_leader_cta:
816
+ # Wait for AB buffer full
817
+ ab_pipeline.consumer_wait(ab_consumer_state)
818
+
819
+ # tCtAcc += tCrA * tCrB
820
+ num_kblocks = cute.size(tCrA, mode=[2])
821
+ for kblock_idx in cutlass.range(num_kblocks, unroll_full=True):
822
+ kblock_coord = (None, None, kblock_idx, ab_consumer_state.index)
823
+
824
+ cute.gemm(
825
+ tiled_mma,
826
+ tCtAcc,
827
+ tCrA[kblock_coord],
828
+ tCrB[kblock_coord],
829
+ tCtAcc,
830
+ )
831
+ # Enable accumulate on tCtAcc after first kblock
832
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, True)
833
+
834
+ # Async arrive AB buffer empty
835
+ ab_pipeline.consumer_release(ab_consumer_state)
836
+
837
+ ab_producer_state.advance()
838
+ ab_consumer_state.advance()
839
+
840
+ # Async arrive accumulator buffer full
841
+ if is_leader_cta:
842
+ acc_pipeline.producer_commit(acc_producer_state)
843
+
844
+ #
845
+ # Epilogue
846
+ #
847
+
848
+ # Release tensor memory allocation lock
849
+ if warp_idx == 0:
850
+ cute.arch.relinquish_tmem_alloc_permit(is_two_cta=use_2cta_instrs)
851
+
852
+ # Wait for accumulator buffer full
853
+ acc_pipeline.consumer_wait(acc_consumer_state)
854
+
855
+ tTR_tAcc = cute.group_modes(tTR_tAcc, 3, cute.rank(tTR_tAcc))
856
+ if cutlass.const_expr(self.use_tma_store):
857
+ bSG_gC = cute.group_modes(bSG_gC, 1, cute.rank(bSG_gC))
858
+ else:
859
+ tTR_gC = cute.group_modes(tTR_gC, 3, cute.rank(tTR_gC))
860
+
861
+ c_pipeline = None
862
+ if cutlass.const_expr(self.use_tma_store):
863
+ # Initialize tma store c_pipeline
864
+ c_producer_group = pipeline.CooperativeGroup(
865
+ pipeline.Agent.Thread, self.threads_per_cta, self.threads_per_cta
866
+ )
867
+ c_pipeline = pipeline.PipelineTmaStore.create(
868
+ num_stages=self.num_c_stage,
869
+ producer_group=c_producer_group,
870
+ )
871
+
872
+ #
873
+ # Store accumulator to global memory in subtiles
874
+ #
875
+ subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3])
876
+ for subtile_idx in range(subtile_cnt):
877
+ #
878
+ # Load accumulator from tensor memory buffer to register
879
+ #
880
+ tTR_tAcc_mn = tTR_tAcc[(None, None, None, subtile_idx)]
881
+ cute.copy(tiled_copy_t2r, tTR_tAcc_mn, tTR_rAcc)
882
+
883
+ if cutlass.const_expr(self.use_tma_store):
884
+ #
885
+ # Perform epilogue op on accumulator and convert to C type
886
+ #
887
+ acc_vec = tiled_copy_r2s.retile(tTR_rAcc).load()
888
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
889
+ tRS_rC.store(acc_vec)
890
+
891
+ #
892
+ # Store C to shared memory
893
+ #
894
+ c_buffer = subtile_idx % self.num_c_stage
895
+ cute.copy(tiled_copy_r2s, tRS_rC, tRS_sC[(None, None, None, c_buffer)])
896
+ # Fence and barrier to make sure shared memory store is visible to TMA store
897
+ cute.arch.fence_proxy(
898
+ cute.arch.ProxyKind.async_shared,
899
+ space=cute.arch.SharedSpace.shared_cta,
900
+ )
901
+ cute.arch.barrier()
902
+
903
+ #
904
+ # TMA store C to global memory
905
+ #
906
+ if warp_idx == 0:
907
+ cute.copy(
908
+ tma_atom_c,
909
+ bSG_sC[(None, c_buffer)],
910
+ bSG_gC[(None, subtile_idx)],
911
+ )
912
+ # Fence and barrier to make sure TMA store is completed to recollect C buffer
913
+ c_pipeline.producer_commit()
914
+ c_pipeline.producer_acquire()
915
+ cute.arch.barrier()
916
+ else:
917
+ #
918
+ # Perform epilogue op on accumulator and convert to C type
919
+ #
920
+ acc_vec = tTR_rAcc.load()
921
+ acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
922
+ tTR_rC.store(acc_vec)
923
+
924
+ #
925
+ # Store C to global memory
926
+ #
927
+ cute.copy(simt_atom, tTR_rC, tTR_gC[(None, None, None, subtile_idx)])
928
+
929
+ #
930
+ # Dealloc the tensor memory buffer
931
+ #
932
+ cute.arch.barrier()
933
+ if warp_idx == 0:
934
+ if use_2cta_instrs:
935
+ cute.arch.mbarrier_arrive(
936
+ tmem_dealloc_mbar_ptr, cta_rank_in_cluster ^ 1
937
+ )
938
+ cute.arch.mbarrier_wait(tmem_dealloc_mbar_ptr, 0)
939
+ cute.arch.dealloc_tmem(
940
+ tmem_ptr, self.num_tmem_alloc_cols, is_two_cta=use_2cta_instrs
941
+ )
942
+
943
+ #
944
+ # Wait for C store complete
945
+ #
946
+ if cutlass.const_expr(self.use_tma_store):
947
+ c_pipeline.producer_tail()
948
+
949
+ #
950
+ # Wait A/B buffer empty
951
+ #
952
+ if warp_idx == 0:
953
+ # Reverse prefetch_k_tile_cnt times to next available buffer
954
+ for i in range(prefetch_k_tile_cnt):
955
+ ab_producer_state.reverse()
956
+ ab_pipeline.producer_tail(ab_producer_state)
957
+ return
958
+
959
+ def epilog_tmem_copy_and_partition(
960
+ self,
961
+ tidx: cutlass.Int32,
962
+ tAcc: cute.Tensor,
963
+ gC_mnl: cute.Tensor,
964
+ epi_tile: cute.Tile,
965
+ use_2cta_instrs: Union[cutlass.Boolean, bool],
966
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
967
+ """
968
+ Make tiledCopy for tensor memory load, then use it to partition tensor memory (source) and register array (destination).
969
+
970
+ :param tidx: The thread index in epilogue warp groups
971
+ :type tidx: cutlass.Int32
972
+ :param tAcc: The accumulator tensor to be copied and partitioned
973
+ :type tAcc: cute.Tensor
974
+ :param gC_mnl: The global tensor C
975
+ :type gC_mnl: cute.Tensor
976
+ :param epi_tile: The epilogue tiler
977
+ :type epi_tile: cute.Tile
978
+ :param use_2cta_instrs: Whether use_2cta_instrs is enabled
979
+ :type use_2cta_instrs: bool
980
+
981
+ :return: A tuple containing (tiled_copy_t2r, tTR_tAcc, tTR_rAcc) where:
982
+ - tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
983
+ - tTR_tAcc: The partitioned accumulator tensor
984
+ - tTR_rAcc: The accumulated tensor in register used to hold t2r results
985
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
986
+ """
987
+ # Make tiledCopy for tensor memory load
988
+ copy_atom_t2r = sm100_utils.get_tmem_load_op(
989
+ self.cta_tile_shape_mnk,
990
+ self.c_layout,
991
+ self.c_dtype,
992
+ self.acc_dtype,
993
+ epi_tile,
994
+ use_2cta_instrs,
995
+ )
996
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N)
997
+ tAcc_epi = cute.flat_divide(
998
+ tAcc[((None, None), 0, 0)],
999
+ epi_tile,
1000
+ )
1001
+ # (EPI_TILE_M, EPI_TILE_N)
1002
+ tiled_copy_t2r = tcgen05.make_tmem_copy(
1003
+ copy_atom_t2r, tAcc_epi[(None, None, 0, 0)]
1004
+ )
1005
+
1006
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1007
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M)
1008
+ tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
1009
+
1010
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1011
+ gC_mnl_epi = cute.flat_divide(
1012
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1013
+ )
1014
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1015
+ tTR_gC = thr_copy_t2r.partition_D(gC_mnl_epi)
1016
+ # (T2R, T2R_M, T2R_N)
1017
+ tTR_rAcc = cute.make_fragment(
1018
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.acc_dtype
1019
+ )
1020
+ return tiled_copy_t2r, tTR_tAcc, tTR_rAcc
1021
+
1022
+ def epilog_smem_copy_and_partition(
1023
+ self,
1024
+ tiled_copy_t2r: cute.TiledCopy,
1025
+ tTR_rC: cute.Tensor,
1026
+ tidx: cutlass.Int32,
1027
+ sC: cute.Tensor,
1028
+ ) -> Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1029
+ """
1030
+ Make tiledCopy for shared memory store, then use it to partition register array (source) and shared memory (destination).
1031
+
1032
+ :param tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1033
+ :type tiled_copy_t2r: cute.TiledCopy
1034
+ :param tTR_rC: The partitioned accumulator tensor
1035
+ :type tTR_rC: cute.Tensor
1036
+ :param tidx: The thread index in epilogue warp groups
1037
+ :type tidx: cutlass.Int32
1038
+ :param sC: The shared memory tensor to be copied and partitioned
1039
+ :type sC: cute.Tensor
1040
+
1041
+ :return: A tuple containing (tiled_copy_r2s, tRS_rC, tRS_sC) where:
1042
+ - tiled_copy_r2s: The tiled copy operation for register to smem copy(r2s)
1043
+ - tRS_rC: The partitioned tensor C (register source)
1044
+ - tRS_sC: The partitioned tensor C (smem destination)
1045
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1046
+ """
1047
+ copy_atom_r2s = sm100_utils.get_smem_store_op(
1048
+ self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
1049
+ )
1050
+ tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
1051
+ # (R2S, R2S_M, R2S_N, PIPE_D)
1052
+ thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
1053
+ tRS_sC = thr_copy_r2s.partition_D(sC)
1054
+ # (R2S, R2S_M, R2S_N)
1055
+ tRS_rC = tiled_copy_r2s.retile(tTR_rC)
1056
+ return tiled_copy_r2s, tRS_rC, tRS_sC
1057
+
1058
+ def epilog_gmem_copy_and_partition(
1059
+ self,
1060
+ tidx: cutlass.Int32,
1061
+ atom: Union[cute.CopyAtom, cute.TiledCopy],
1062
+ gC_mnl: cute.Tensor,
1063
+ epi_tile: cute.Tile,
1064
+ sC: cute.Tensor,
1065
+ ) -> Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]:
1066
+ """Make tiledCopy for global memory store, then use it to:
1067
+ - partition register array (source) and global memory (destination) for none TMA store version;
1068
+ - partition shared memory (source) and global memory (destination) for TMA store version.
1069
+
1070
+ :param tidx: The thread index in epilogue warp groups
1071
+ :type tidx: cutlass.Int32
1072
+ :param atom: The copy_atom_c to be used for TMA store version, or tiled_copy_t2r for none TMA store version
1073
+ :type atom: cute.CopyAtom or cute.TiledCopy
1074
+ :param gC_mnl: The global tensor C
1075
+ :type gC_mnl: cute.Tensor
1076
+ :param epi_tile: The epilogue tiler
1077
+ :type epi_tile: cute.Tile
1078
+ :param sC: The shared memory tensor to be copied and partitioned
1079
+ :type sC: cute.Tensor
1080
+
1081
+ :return: A tuple containing either:
1082
+ - For TMA store: (tma_atom_c, bSG_sC, bSG_gC) where:
1083
+ - tma_atom_c: The TMA copy atom
1084
+ - bSG_sC: The partitioned shared memory tensor C
1085
+ - bSG_gC: The partitioned global tensor C
1086
+ - For non-TMA store: (simt_atom, tTR_rC, tTR_gC) where:
1087
+ - simt_atom: The SIMT copy atom
1088
+ - tTR_rC: The register tensor C
1089
+ - tTR_gC: The partitioned global tensor C
1090
+ :rtype: Tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]
1091
+ """
1092
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1093
+ gC_epi = cute.flat_divide(
1094
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1095
+ )
1096
+ if cutlass.const_expr(self.use_tma_store):
1097
+ tma_atom_c = atom
1098
+ sC_for_tma_partition = cute.group_modes(sC, 0, 2)
1099
+ gC_for_tma_partition = cute.group_modes(gC_epi, 0, 2)
1100
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1101
+ # ((ATOM_V, REST_V), EPI_M, EPI_N, RestM, RestN, RestL)
1102
+ bSG_sC, bSG_gC = cpasync.tma_partition(
1103
+ tma_atom_c,
1104
+ 0,
1105
+ cute.make_layout(1),
1106
+ sC_for_tma_partition,
1107
+ gC_for_tma_partition,
1108
+ )
1109
+ return tma_atom_c, bSG_sC, bSG_gC
1110
+ else:
1111
+ tiled_copy_t2r = atom
1112
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1113
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1114
+ tTR_gC = thr_copy_t2r.partition_D(gC_epi)
1115
+ # (T2R, T2R_M, T2R_N)
1116
+ tTR_rC = cute.make_fragment(
1117
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.c_dtype
1118
+ )
1119
+ simt_atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), self.c_dtype)
1120
+ return simt_atom, tTR_rC, tTR_gC
1121
+
1122
+ @staticmethod
1123
+ def _compute_stages(
1124
+ tiled_mma: cute.TiledMma,
1125
+ mma_tiler_mnk: Tuple[int, int, int],
1126
+ a_dtype: Type[cutlass.Numeric],
1127
+ b_dtype: Type[cutlass.Numeric],
1128
+ epi_tile: cute.Tile,
1129
+ c_dtype: Type[cutlass.Numeric],
1130
+ c_layout: utils.LayoutEnum,
1131
+ smem_capacity: int,
1132
+ occupancy: int,
1133
+ use_tma_store: bool,
1134
+ ) -> Tuple[int, int, int]:
1135
+ """Computes the number of stages for A/B/C operands based on heuristics.
1136
+
1137
+ :param tiled_mma: The tiled MMA object defining the core computation.
1138
+ :type tiled_mma: cute.TiledMma
1139
+ :param mma_tiler_mnk: The shape (M, N, K) of the MMA tile.
1140
+ :type mma_tiler_mnk: tuple[int, int, int]
1141
+ :param a_dtype: Data type of operand A.
1142
+ :type a_dtype: type[cutlass.Numeric]
1143
+ :param b_dtype: Data type of operand B.
1144
+ :type b_dtype: type[cutlass.Numeric]
1145
+ :param epi_tile: The epilogue tile shape.
1146
+ :type epi_tile: cute.Tile
1147
+ :param c_dtype: Data type of operand C (output).
1148
+ :type c_dtype: type[cutlass.Numeric]
1149
+ :param c_layout: Layout enum of operand C in global memory.
1150
+ :type c_layout: utils.LayoutEnum
1151
+ :param smem_capacity: Total available shared memory capacity in bytes.
1152
+ :type smem_capacity: int
1153
+ :param occupancy: Target number of CTAs per SM (occupancy).
1154
+ :type occupancy: int
1155
+ :param use_tma_store: Whether TMA store is enabled.
1156
+ :type use_tma_store: bool
1157
+
1158
+ :return: A tuple containing the computed number of stages for:
1159
+ (ACC stages, A/B operand stages, epilogue stages)
1160
+ :rtype: tuple[int, int, int]
1161
+ """
1162
+ # Default ACC stages
1163
+ num_acc_stage = 1
1164
+ # Default C stages
1165
+ num_c_stage = 2 if use_tma_store else 0
1166
+
1167
+ # Calculate smem layout and size for one stage of A, B, and C
1168
+ a_smem_layout_stage_one = sm100_utils.make_smem_layout_a(
1169
+ tiled_mma,
1170
+ mma_tiler_mnk,
1171
+ a_dtype,
1172
+ 1, # a tmp 1 stage is provided
1173
+ )
1174
+ b_smem_layout_staged_one = sm100_utils.make_smem_layout_b(
1175
+ tiled_mma,
1176
+ mma_tiler_mnk,
1177
+ b_dtype,
1178
+ 1, # a tmp 1 stage is provided
1179
+ )
1180
+ c_smem_layout_staged_one = (
1181
+ sm100_utils.make_smem_layout_epi(
1182
+ c_dtype,
1183
+ c_layout,
1184
+ epi_tile,
1185
+ 1,
1186
+ )
1187
+ if use_tma_store
1188
+ else None
1189
+ )
1190
+ ab_bytes_per_stage = cute.size_in_bytes(
1191
+ a_dtype, a_smem_layout_stage_one
1192
+ ) + cute.size_in_bytes(b_dtype, b_smem_layout_staged_one)
1193
+ mbar_helpers_bytes = 1024
1194
+ c_bytes_per_stage = (
1195
+ cute.size_in_bytes(c_dtype, c_smem_layout_staged_one)
1196
+ if use_tma_store
1197
+ else 0
1198
+ )
1199
+ c_bytes = c_bytes_per_stage * num_c_stage
1200
+
1201
+ # Calculate A/B stages:
1202
+ # Start with total smem per CTA (capacity / occupancy)
1203
+ # Subtract reserved bytes and initial C stages bytes
1204
+ # Divide remaining by bytes needed per A/B stage
1205
+ num_ab_stage = (
1206
+ smem_capacity - (occupancy + 1) * (mbar_helpers_bytes + c_bytes)
1207
+ ) // ab_bytes_per_stage
1208
+
1209
+ # Refine epilogue stages:
1210
+ # Calculate remaining smem after allocating for A/B stages and reserved bytes
1211
+ # Add remaining unused smem to epilogue
1212
+ if use_tma_store:
1213
+ num_c_stage += (
1214
+ smem_capacity
1215
+ - ab_bytes_per_stage * num_ab_stage
1216
+ - (occupancy + 1) * (mbar_helpers_bytes + c_bytes)
1217
+ ) // ((occupancy + 1) * c_bytes_per_stage)
1218
+ return num_acc_stage, num_ab_stage, num_c_stage
1219
+
1220
+ @staticmethod
1221
+ def _compute_grid(
1222
+ c: cute.Tensor,
1223
+ cta_tile_shape_mnk: Tuple[int, int, int],
1224
+ cluster_shape_mn: Tuple[int, int],
1225
+ ) -> Tuple[int, int, int]:
1226
+ """Compute grid shape for the output tensor C.
1227
+
1228
+ :param c: The output tensor C
1229
+ :type c: cute.Tensor
1230
+ :param cta_tile_shape_mnk: The shape (M, N, K) of the CTA tile.
1231
+ :type cta_tile_shape_mnk: tuple[int, int, int]
1232
+ :param cluster_shape_mn: Shape of each cluster in M, N dimensions.
1233
+ :type cluster_shape_mn: tuple[int, int]
1234
+
1235
+ :return: Grid shape for kernel launch.
1236
+ :rtype: tuple[int, int, int]
1237
+ """
1238
+
1239
+ cluster_shape_mnl = (*cluster_shape_mn, 1)
1240
+
1241
+ grid = cute.round_up(
1242
+ (
1243
+ cute.ceil_div(c.layout.shape[0], cta_tile_shape_mnk[0]),
1244
+ cute.ceil_div(c.layout.shape[1], cta_tile_shape_mnk[1]),
1245
+ c.layout.shape[2],
1246
+ ),
1247
+ cluster_shape_mnl,
1248
+ )
1249
+
1250
+ return grid
1251
+
1252
+ @staticmethod
1253
+ def _compute_num_tmem_alloc_cols(
1254
+ tiled_mma: cute.TiledMma, mma_tiler: Tuple[int, int, int]
1255
+ ) -> int:
1256
+ """
1257
+ Compute the number of tensor memory allocation columns.
1258
+
1259
+ :param tiled_mma: The tiled MMA object defining the core computation.
1260
+ :type tiled_mma: cute.TiledMma
1261
+ :param mma_tiler: The shape (M, N, K) of the MMA tile.
1262
+ :type mma_tiler: tuple[int, int, int]
1263
+
1264
+ :return: The number of tensor memory allocation columns.
1265
+ :rtype: int
1266
+ """
1267
+ acc_shape = tiled_mma.partition_shape_C(mma_tiler[:2])
1268
+ tCtAcc_fake = tiled_mma.make_fragment_C(acc_shape)
1269
+ return sm100_utils.get_num_tmem_alloc_cols(tCtAcc_fake)
1270
+
1271
+ @staticmethod
1272
+ def is_valid_dtypes(
1273
+ ab_dtype: Type[cutlass.Numeric],
1274
+ acc_dtype: Type[cutlass.Numeric],
1275
+ c_dtype: Type[cutlass.Numeric],
1276
+ ) -> bool:
1277
+ """
1278
+ Check if the dtypes are valid
1279
+
1280
+ :param ab_dtype: The data type of the A and B operands
1281
+ :type ab_dtype: Type[cutlass.Numeric]
1282
+ :param acc_dtype: The data type of the accumulator
1283
+ :type acc_dtype: Type[cutlass.Numeric]
1284
+ :param c_dtype: The data type of the output tensor
1285
+ :type c_dtype: Type[cutlass.Numeric]
1286
+
1287
+ :return: True if the dtypes are valid, False otherwise
1288
+ :rtype: bool
1289
+ """
1290
+ is_valid = True
1291
+ if ab_dtype not in {
1292
+ cutlass.Float16,
1293
+ cutlass.BFloat16,
1294
+ cutlass.TFloat32,
1295
+ cutlass.Uint8,
1296
+ cutlass.Int8,
1297
+ cutlass.Float8E4M3FN,
1298
+ cutlass.Float8E5M2,
1299
+ }:
1300
+ is_valid = False
1301
+ if (
1302
+ acc_dtype not in {cutlass.Float32, cutlass.Float16, cutlass.Int32}
1303
+ or acc_dtype == cutlass.Float16
1304
+ and ab_dtype
1305
+ not in {cutlass.Float16, cutlass.Float8E4M3FN, cutlass.Float8E5M2}
1306
+ or acc_dtype == cutlass.Int32
1307
+ and ab_dtype not in {cutlass.Uint8, cutlass.Int8}
1308
+ ):
1309
+ is_valid = False
1310
+ if (
1311
+ acc_dtype == cutlass.Float32
1312
+ and c_dtype
1313
+ not in {
1314
+ cutlass.Float32,
1315
+ cutlass.Float16,
1316
+ cutlass.BFloat16,
1317
+ cutlass.Float8E4M3FN,
1318
+ cutlass.Float8E5M2,
1319
+ cutlass.Int32,
1320
+ cutlass.Int8,
1321
+ cutlass.Uint8,
1322
+ }
1323
+ or acc_dtype == cutlass.Float16
1324
+ and c_dtype
1325
+ not in {
1326
+ cutlass.BFloat16,
1327
+ cutlass.Float16,
1328
+ }
1329
+ or acc_dtype == cutlass.Int32
1330
+ and c_dtype
1331
+ not in {
1332
+ cutlass.BFloat16,
1333
+ cutlass.Float16,
1334
+ cutlass.Float32,
1335
+ cutlass.Int32,
1336
+ cutlass.Int8,
1337
+ cutlass.Uint8,
1338
+ }
1339
+ ):
1340
+ is_valid = False
1341
+ return is_valid
1342
+
1343
+ @staticmethod
1344
+ def is_valid_mma_tiler_and_cluster_shape(
1345
+ use_2cta_instrs: bool,
1346
+ mma_tiler_mn: Tuple[int, int],
1347
+ cluster_shape_mn: Tuple[int, int],
1348
+ ) -> bool:
1349
+ """
1350
+ Check if the mma tiler and cluster shape are valid
1351
+
1352
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1353
+ :type use_2cta_instrs: bool
1354
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1355
+ :type mma_tiler_mn: Tuple[int, int]
1356
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1357
+ :type cluster_shape_mn: Tuple[int, int]
1358
+
1359
+ :return: True if the mma tiler and cluster shape are valid, False otherwise
1360
+ :rtype: bool
1361
+ """
1362
+ is_valid = True
1363
+ # Skip invalid mma tile shape
1364
+ if not (
1365
+ (not use_2cta_instrs and mma_tiler_mn[0] in [64, 128])
1366
+ or (use_2cta_instrs and mma_tiler_mn[0] in [128, 256])
1367
+ ):
1368
+ is_valid = False
1369
+ if mma_tiler_mn[1] not in range(32, 257, 32):
1370
+ is_valid = False
1371
+ # Skip illegal cluster shape
1372
+ if cluster_shape_mn[0] % (2 if use_2cta_instrs else 1) != 0:
1373
+ is_valid = False
1374
+ # Skip invalid cluster shape
1375
+ is_power_of_2 = lambda x: x > 0 and (x & (x - 1)) == 0
1376
+ if (
1377
+ cluster_shape_mn[0] * cluster_shape_mn[1] > 16
1378
+ or cluster_shape_mn[0] <= 0
1379
+ or cluster_shape_mn[1] <= 0
1380
+ or not is_power_of_2(cluster_shape_mn[0])
1381
+ or not is_power_of_2(cluster_shape_mn[1])
1382
+ ):
1383
+ is_valid = False
1384
+ return is_valid
1385
+
1386
+ @staticmethod
1387
+ def is_valid_tensor_alignment(
1388
+ m: int,
1389
+ n: int,
1390
+ k: int,
1391
+ l: int,
1392
+ ab_dtype: Type[cutlass.Numeric],
1393
+ c_dtype: Type[cutlass.Numeric],
1394
+ a_major: str,
1395
+ b_major: str,
1396
+ c_major: str,
1397
+ ) -> bool:
1398
+ """
1399
+ Check if the tensor alignment is valid
1400
+
1401
+ :param m: The number of rows in the A tensor
1402
+ :type m: int
1403
+ :param n: The number of columns in the B tensor
1404
+ :type n: int
1405
+ :param k: The number of columns in the A tensor
1406
+ :type k: int
1407
+ :param l: The number of columns in the C tensor
1408
+ :type l: int
1409
+ :param ab_dtype: The data type of the A and B operands
1410
+ :type ab_dtype: Type[cutlass.Numeric]
1411
+ :param c_dtype: The data type of the output tensor
1412
+ :type c_dtype: Type[cutlass.Numeric]
1413
+ :param a_major: The major axis of the A tensor
1414
+ :type a_major: str
1415
+ :param b_major: The major axis of the B tensor
1416
+ :type b_major: str
1417
+ :param c_major: The major axis of the C tensor
1418
+ :type c_major: str
1419
+
1420
+ :return: True if the problem shape is valid, False otherwise
1421
+ :rtype: bool
1422
+ """
1423
+ is_valid = True
1424
+
1425
+ def check_contigous_16B_alignment(dtype, is_mode0_major, tensor_shape):
1426
+ major_mode_idx = 0 if is_mode0_major else 1
1427
+ num_major_elements = tensor_shape[major_mode_idx]
1428
+ num_contiguous_elements = 16 * 8 // dtype.width
1429
+ return num_major_elements % num_contiguous_elements == 0
1430
+
1431
+ if (
1432
+ not check_contigous_16B_alignment(ab_dtype, a_major == "m", (m, k, l))
1433
+ or not check_contigous_16B_alignment(ab_dtype, b_major == "n", (n, k, l))
1434
+ or not check_contigous_16B_alignment(c_dtype, c_major == "m", (m, n, l))
1435
+ ):
1436
+ is_valid = False
1437
+ return is_valid
1438
+
1439
+ @staticmethod
1440
+ def is_valid_epilog_store_option(
1441
+ use_2cta_instrs: bool,
1442
+ use_tma_store: bool,
1443
+ m: int,
1444
+ n: int,
1445
+ mma_tiler_mn: Tuple[int, int],
1446
+ ) -> bool:
1447
+ """
1448
+ Check if the epilogue store option is valid
1449
+
1450
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1451
+ :type use_2cta_instrs: bool
1452
+ :param use_tma_store: Whether to use TMA store
1453
+ :type use_tma_store: bool
1454
+ :param m: The number of rows in the A tensor
1455
+ :type m: int
1456
+ :param n: The number of columns in the B tensor
1457
+ :type n: int
1458
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1459
+ :type mma_tiler_mn: Tuple[int, int]
1460
+
1461
+ :return: True if the epilogue store option is valid, False otherwise
1462
+ :rtype: bool
1463
+ """
1464
+
1465
+ is_valid = True
1466
+ # None TMA store version does not have predication, can not support OOB tiles
1467
+ cta_tile_shape_mn = (
1468
+ mma_tiler_mn[0] // (2 if use_2cta_instrs else 1),
1469
+ mma_tiler_mn[1],
1470
+ )
1471
+ if not use_tma_store:
1472
+ if not (m % cta_tile_shape_mn[0] == 0 and n % cta_tile_shape_mn[1] == 0):
1473
+ is_valid = False
1474
+ return is_valid
1475
+
1476
+ @staticmethod
1477
+ def can_implement(
1478
+ ab_dtype: Type[cutlass.Numeric],
1479
+ acc_dtype: Type[cutlass.Numeric],
1480
+ c_dtype: Type[cutlass.Numeric],
1481
+ use_2cta_instrs: bool,
1482
+ mma_tiler_mn: Tuple[int, int],
1483
+ cluster_shape_mn: Tuple[int, int],
1484
+ use_tma_store: bool,
1485
+ m: int,
1486
+ n: int,
1487
+ k: int,
1488
+ l: int,
1489
+ a_major: str,
1490
+ b_major: str,
1491
+ c_major: str,
1492
+ ) -> bool:
1493
+ """
1494
+ Check if the gemm can be implemented
1495
+
1496
+ :param ab_dtype: The data type of the A and B operands
1497
+ :type ab_dtype: Type[cutlass.Numeric]
1498
+ :param acc_dtype: The data type of the accumulator
1499
+ :type acc_dtype: Type[cutlass.Numeric]
1500
+ :param c_dtype: The data type of the output tensor
1501
+ :type c_dtype: Type[cutlass.Numeric]
1502
+ :param use_2cta_instrs: Whether to use 2 CTA groups
1503
+ :type use_2cta_instrs: bool
1504
+ :param mma_tiler_mn: The (M, N) shape of the MMA instruction tiler
1505
+ :type mma_tiler_mn: Tuple[int, int]
1506
+ :param cluster_shape_mn: The (ClusterM, ClusterN) shape of the CTA cluster
1507
+ :type cluster_shape_mn: Tuple[int, int]
1508
+ :param use_tma_store: Whether to use TMA store
1509
+ :type use_tma_store: bool
1510
+ :param m: The number of rows in the A tensor
1511
+ :type m: int
1512
+ :param n: The number of columns in the B tensor
1513
+ :type n: int
1514
+ :param k: The number of columns in the A tensor
1515
+ :type k: int
1516
+ :param l: The number of columns in the C tensor
1517
+ :type l: int
1518
+ :param a_major: The major axis of the A tensor
1519
+ :type a_major: str
1520
+ :param b_major: The major axis of the B tensor
1521
+ :type b_major: str
1522
+ :param c_major: The major axis of the C tensor
1523
+ :type c_major: str
1524
+
1525
+ :return: True if the gemm can be implemented, False otherwise
1526
+ :rtype: bool
1527
+ """
1528
+ can_implement = True
1529
+ # Skip unsupported types
1530
+ if not DenseGemmKernel.is_valid_dtypes(ab_dtype, acc_dtype, c_dtype):
1531
+ can_implement = False
1532
+ # Skip invalid mma tile shape and cluster shape
1533
+ if not DenseGemmKernel.is_valid_mma_tiler_and_cluster_shape(
1534
+ use_2cta_instrs, mma_tiler_mn, cluster_shape_mn
1535
+ ):
1536
+ can_implement = False
1537
+ # Skip illegal problem shape for load/store alignment
1538
+ if not DenseGemmKernel.is_valid_tensor_alignment(
1539
+ m, n, k, l, ab_dtype, c_dtype, a_major, b_major, c_major
1540
+ ):
1541
+ can_implement = False
1542
+ # Skip invalid epilogue store option
1543
+ if not DenseGemmKernel.is_valid_epilog_store_option(
1544
+ use_2cta_instrs, use_tma_store, m, n, mma_tiler_mn
1545
+ ):
1546
+ can_implement = False
1547
+ return can_implement
1548
+
1549
+
1550
+ def run_dense_gemm(
1551
+ mnkl: Tuple[int, int, int, int],
1552
+ ab_dtype: Type[cutlass.Numeric],
1553
+ c_dtype: Type[cutlass.Numeric],
1554
+ acc_dtype: Type[cutlass.Numeric],
1555
+ a_major: str,
1556
+ b_major: str,
1557
+ c_major: str,
1558
+ mma_tiler_mn: Tuple[int, int],
1559
+ cluster_shape_mn: Tuple[int, int],
1560
+ use_2cta_instrs: bool,
1561
+ use_tma_store: bool,
1562
+ tolerance: float,
1563
+ warmup_iterations: int = 0,
1564
+ iterations: int = 1,
1565
+ skip_ref_check: bool = False,
1566
+ ):
1567
+ """
1568
+ Prepare A/B/C tensors, launch GPU kernel, and reference checking.
1569
+ """
1570
+ print(f"Running B100 software pipeline Dense GEMM test with:")
1571
+ print(f"mnkl: {mnkl}")
1572
+ print(f"AB dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}")
1573
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
1574
+ print(f"Mma Tiler (M, N): {mma_tiler_mn}, Cluster Shape (M, N): {cluster_shape_mn}")
1575
+ print(f"2CTA MMA instructions: {'True' if use_2cta_instrs else 'False'}")
1576
+ print(f"Use TMA Store: {'True' if use_tma_store else 'False'}")
1577
+ print(f"Tolerance: {tolerance}")
1578
+ print(f"Warmup iterations: {warmup_iterations}")
1579
+ print(f"Iterations: {iterations}")
1580
+ print(f"Skip reference checking: {skip_ref_check}")
1581
+
1582
+ # Unpack parameters
1583
+ m, n, k, l = mnkl
1584
+
1585
+ # Skip unsupported testcase
1586
+ if not DenseGemmKernel.can_implement(
1587
+ ab_dtype,
1588
+ acc_dtype,
1589
+ c_dtype,
1590
+ use_2cta_instrs,
1591
+ mma_tiler_mn,
1592
+ cluster_shape_mn,
1593
+ use_tma_store,
1594
+ m,
1595
+ n,
1596
+ k,
1597
+ l,
1598
+ a_major,
1599
+ b_major,
1600
+ c_major,
1601
+ ):
1602
+ raise TypeError(
1603
+ f"Unsupported testcase {ab_dtype}, {acc_dtype}, {c_dtype}, {use_2cta_instrs}, {mma_tiler_mn}, {cluster_shape_mn}, {use_tma_store}, {m}, {n}, {k}, {l}, {a_major}, {b_major}, {c_major}"
1604
+ )
1605
+
1606
+ if not torch.cuda.is_available():
1607
+ raise RuntimeError("GPU is required to run this example!")
1608
+
1609
+ torch.manual_seed(1111)
1610
+
1611
+ # Create and permute tensor A/B/C
1612
+ def create_and_permute_tensor(
1613
+ l, mode0, mode1, is_mode0_major, dtype, is_dynamic_layout=True
1614
+ ):
1615
+ # is_mode0_major: (l, mode1, mode0) -> (mode0, mode1, l)
1616
+ # else: (l, mode0, mode1) -> (mode0, mode1, l)
1617
+ shape = (l, mode1, mode0) if is_mode0_major else (l, mode0, mode1)
1618
+ permute_order = (2, 1, 0) if is_mode0_major else (1, 2, 0)
1619
+ is_unsigned = dtype in {cutlass.Uint8}
1620
+ # Temporarily use uint8 as torch does not support fp8 type
1621
+ torch_dtype = (
1622
+ cutlass_torch.dtype(dtype)
1623
+ if dtype not in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}
1624
+ else torch.uint8
1625
+ )
1626
+
1627
+ # Create dtype torch tensor (cpu)
1628
+ torch_tensor_cpu = cutlass_torch.create_and_permute_torch_tensor(
1629
+ shape,
1630
+ torch_dtype,
1631
+ permute_order=permute_order,
1632
+ init_type=cutlass_torch.TensorInitType.RANDOM,
1633
+ init_config=cutlass_torch.RandomInitConfig(
1634
+ min_val=0 if is_unsigned else -2, max_val=4 if is_unsigned else 2
1635
+ ),
1636
+ )
1637
+ # Create dtype torch tensor (gpu)
1638
+ torch_tensor = torch_tensor_cpu.cuda()
1639
+
1640
+ # Create f32 torch tensor (cpu)
1641
+ f32_torch_tensor = torch_tensor_cpu.to(dtype=torch.float32)
1642
+
1643
+ # Create dtype cute tensor (gpu)
1644
+ cute_tensor = from_dlpack(torch_tensor, assumed_align=16)
1645
+ cute_tensor.element_type = dtype
1646
+ if is_dynamic_layout:
1647
+ cute_tensor = cute_tensor.mark_layout_dynamic(
1648
+ leading_dim=(0 if is_mode0_major else 1)
1649
+ )
1650
+ cute_tensor = cutlass_torch.convert_cute_tensor(
1651
+ f32_torch_tensor,
1652
+ cute_tensor,
1653
+ dtype,
1654
+ is_dynamic_layout=is_dynamic_layout,
1655
+ )
1656
+
1657
+ return f32_torch_tensor, cute_tensor, torch_tensor
1658
+
1659
+ a_ref, a_tensor, a_torch = create_and_permute_tensor(
1660
+ l, m, k, a_major == "m", ab_dtype, is_dynamic_layout=True
1661
+ )
1662
+ b_ref, b_tensor, b_torch = create_and_permute_tensor(
1663
+ l, n, k, b_major == "n", ab_dtype, is_dynamic_layout=True
1664
+ )
1665
+ c_ref, c_tensor, c_torch = create_and_permute_tensor(
1666
+ l, m, n, c_major == "m", c_dtype, is_dynamic_layout=True
1667
+ )
1668
+
1669
+ # Configure gemm kernel
1670
+ gemm = DenseGemmKernel(
1671
+ acc_dtype,
1672
+ use_2cta_instrs,
1673
+ mma_tiler_mn,
1674
+ cluster_shape_mn,
1675
+ use_tma_store,
1676
+ )
1677
+
1678
+ torch_stream = torch.cuda.Stream()
1679
+ stream = cuda.CUstream(torch_stream.cuda_stream)
1680
+ # Compile gemm kernel
1681
+ compiled_gemm = cute.compile(gemm, a_tensor, b_tensor, c_tensor, stream)
1682
+
1683
+ # Launch GPU kernel
1684
+ # Warm up
1685
+ for i in range(warmup_iterations):
1686
+ compiled_gemm(a_tensor, b_tensor, c_tensor, stream)
1687
+ # Execution
1688
+ for i in range(iterations):
1689
+ compiled_gemm(a_tensor, b_tensor, c_tensor, stream)
1690
+
1691
+ # Compute reference result
1692
+ if not skip_ref_check:
1693
+ if ab_dtype in {
1694
+ cutlass.Int8,
1695
+ cutlass.Uint8,
1696
+ cutlass.Float8E4M3FN,
1697
+ cutlass.Float8E5M2,
1698
+ }:
1699
+ ref = torch.einsum("mkl,nkl->mnl", a_ref.cpu(), b_ref.cpu())
1700
+ else:
1701
+ ref = (torch.einsum("mkl,nkl->mnl", a_ref, b_ref)).cpu()
1702
+
1703
+ # Copy gpu result back
1704
+ gpu_c = c_torch.cpu()
1705
+
1706
+ # Convert ref to c_type
1707
+ if c_dtype == cutlass.Float32:
1708
+ ref_c = ref
1709
+ elif c_dtype in {cutlass.Float8E5M2, cutlass.Float8E4M3FN}:
1710
+ # m major: (l, n, m) -> (m, n, l)
1711
+ # n major: (l, m, n) -> (m, n, l)
1712
+ permute_order = (1, 2, 0) if c_major == "n" else (2, 1, 0)
1713
+ shape = (l, m, n) if c_major == "n" else (l, n, m)
1714
+ f8_torch_tensor = cutlass_torch.create_and_permute_torch_tensor(
1715
+ shape,
1716
+ torch.uint8,
1717
+ permute_order=permute_order,
1718
+ init_type=cutlass_torch.TensorInitType.SKIP,
1719
+ ).cuda()
1720
+ # Create dtype cute tensor (gpu)
1721
+ ref_c_tensor = from_dlpack(
1722
+ f8_torch_tensor, assumed_align=16
1723
+ ).mark_layout_dynamic(leading_dim=(1 if c_major == "n" else 0))
1724
+ ref_c_tensor.element_type = c_dtype
1725
+ ref_c_tensor = cutlass_torch.convert_cute_tensor(
1726
+ ref,
1727
+ ref_c_tensor,
1728
+ c_dtype,
1729
+ is_dynamic_layout=True,
1730
+ )
1731
+
1732
+ ref_c = f8_torch_tensor.cpu()
1733
+ else:
1734
+ ref_c = ref.to(cutlass_torch.dtype(c_dtype))
1735
+
1736
+ # Reference checking ref_c and gpu_c
1737
+ torch.testing.assert_close(
1738
+ gpu_c,
1739
+ ref_c,
1740
+ atol=tolerance,
1741
+ rtol=1e-05,
1742
+ )
1743
+
1744
+
1745
+ if __name__ == "__main__":
1746
+
1747
+ def parse_comma_separated_ints(s: str) -> Tuple[int, ...]:
1748
+ try:
1749
+ return tuple(int(x.strip()) for x in s.split(","))
1750
+ # or: return tuple([int(x.strip()) for x in s.split(",")])
1751
+ except ValueError:
1752
+ raise argparse.ArgumentTypeError(
1753
+ "Invalid format. Expected comma-separated integers."
1754
+ )
1755
+
1756
+ parser = argparse.ArgumentParser(
1757
+ description="Example of MxNxKxL GEMM on Blackwell."
1758
+ )
1759
+
1760
+ parser.add_argument(
1761
+ "--mnkl",
1762
+ type=parse_comma_separated_ints,
1763
+ default=(256, 256, 512, 1),
1764
+ help="mnkl dimensions (comma-separated)",
1765
+ )
1766
+ parser.add_argument(
1767
+ "--mma_tiler_mn",
1768
+ type=parse_comma_separated_ints,
1769
+ default=(128, 128),
1770
+ help="Mma tiler (comma-separated)",
1771
+ )
1772
+ parser.add_argument(
1773
+ "--cluster_shape_mn",
1774
+ type=parse_comma_separated_ints,
1775
+ default=(1, 1),
1776
+ help="Cluster shape (comma-separated)",
1777
+ )
1778
+ parser.add_argument("--ab_dtype", type=cutlass.dtype, default=cutlass.TFloat32)
1779
+ parser.add_argument("--c_dtype", type=cutlass.dtype, default=cutlass.Float32)
1780
+ parser.add_argument("--acc_dtype", type=cutlass.dtype, default=cutlass.Float32)
1781
+ parser.add_argument(
1782
+ "--use_2cta_instrs",
1783
+ action="store_true",
1784
+ help="Enable 2CTA MMA instructions feature",
1785
+ )
1786
+ parser.add_argument("--a_major", choices=["k", "m"], type=str, default="k")
1787
+ parser.add_argument("--b_major", choices=["k", "n"], type=str, default="k")
1788
+ parser.add_argument("--c_major", choices=["n", "m"], type=str, default="n")
1789
+ parser.add_argument(
1790
+ "--use_tma_store", action="store_true", help="Use tma store or not"
1791
+ )
1792
+ parser.add_argument(
1793
+ "--tolerance", type=float, default=1e-01, help="Tolerance for validation"
1794
+ )
1795
+ parser.add_argument(
1796
+ "--warmup_iterations", type=int, default=0, help="Warmup iterations"
1797
+ )
1798
+ parser.add_argument("--iterations", type=int, default=1, help="Iterations")
1799
+ parser.add_argument(
1800
+ "--skip_ref_check", action="store_true", help="Skip reference checking"
1801
+ )
1802
+
1803
+ args = parser.parse_args()
1804
+
1805
+ if len(args.mnkl) != 4:
1806
+ parser.error("--mnkl must contain exactly 4 values")
1807
+
1808
+ if len(args.mma_tiler_mn) != 2:
1809
+ parser.error("--mma_tiler_mn must contain exactly 2 values")
1810
+
1811
+ if len(args.cluster_shape_mn) != 2:
1812
+ parser.error("--cluster_shape_mn must contain exactly 2 values")
1813
+
1814
+ run_dense_gemm(
1815
+ args.mnkl,
1816
+ args.ab_dtype,
1817
+ args.c_dtype,
1818
+ args.acc_dtype,
1819
+ args.a_major,
1820
+ args.b_major,
1821
+ args.c_major,
1822
+ args.mma_tiler_mn,
1823
+ args.cluster_shape_mn,
1824
+ args.use_2cta_instrs,
1825
+ args.use_tma_store,
1826
+ args.tolerance,
1827
+ args.warmup_iterations,
1828
+ args.iterations,
1829
+ args.skip_ref_check,
1830
+ )
1831
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/fmha.py ADDED
The diff for this file is too large to render. See raw diff
 
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/grouped_gemm.py ADDED
@@ -0,0 +1,2445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import argparse
30
+ import functools
31
+ from typing import List, Type, Union
32
+ from inspect import isclass
33
+
34
+ import torch
35
+ import cuda.bindings.driver as cuda
36
+
37
+ import cutlass
38
+ import cutlass.cute as cute
39
+ import cutlass.cute.testing as testing
40
+ import cutlass.utils as utils
41
+ from cutlass.cute.nvgpu import cpasync, tcgen05
42
+ import cutlass.utils.blackwell_helpers as sm100_utils
43
+ import cutlass.torch as cutlass_torch
44
+
45
+ """
46
+ A grouped GEMM example for the NVIDIA Blackwell SM100 architecture using CUTE DSL
47
+
48
+ This example demonstrates an implementation of grouped GEMM using a TMA plus Blackwell SM100 TensorCore
49
+ warp-specialized persistent kernel.
50
+ The grouped GEMM workload computes a batch of GEMM operations with distinct problem sizes. Pointers to matrices
51
+ in global memory are passed to the kernel in an array (also held in global memory). Similarly, problem shapes and
52
+ strides are also stored in arrays in GMEM.
53
+
54
+ This differs from "Batched Array" GEMM since the size of each GEMM problem in the grouped GEMM concept may be distinct.
55
+
56
+ To run this example:
57
+
58
+ .. code-block:: bash
59
+
60
+ python examples/blackwell/grouped_gemm.py \
61
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
62
+ --mma_tiler_mn 128,64 --cluster_shape_mn 1,1 \
63
+ --problem_sizes_mnkl "(8192,1280,32,1),(16,384,1536,1),(640,1280,16,1),(640,160,16,1)" \
64
+ --num_groups 4 --tensormap_update_mode SMEM
65
+
66
+ The above example command makes 4 groups of different m, n, k sizes. The Blackwell tcgen05 MMA tile shape
67
+ is specified as (128, 64) and the cluster shape is (1,1). The input, mma accumulator and output data type
68
+ are set as fp16, fp32 and fp16, respectively.
69
+
70
+ To collect performance with NCU profiler:
71
+
72
+ .. code-block:: bash
73
+
74
+ ncu python examples/blackwell/grouped_gemm.py \
75
+ --ab_dtype Float16 --c_dtype Float16 --acc_dtype Float32 \
76
+ --mma_tiler_mn 128,64 --cluster_shape_mn 1,1 \
77
+ --problem_sizes_mnkl "(8192,1280,32,1),(16,384,1536,1),(640,1280,16,1),(640,160,16,1)" \
78
+ --num_groups 4 --tensormap_update_mode SMEM \
79
+ --warmup_iterations 1 --iterations 10 --skip_ref_check
80
+
81
+ There are some constrains for this example. Besides the constrains from the Balckwell dense GEMM persistent example,
82
+ there are also the following constrains:
83
+ * Only fp16 and bf16 data types are supported as inputs.
84
+ * Output data types could be fp16, bf16 or fp32.
85
+ * The contiguous dimension of each tensor must be at least 16 bytes aligned.
86
+ * The l mode(aka, batch size) for each group must be 1.
87
+ * The majorness for A, B and C must be the same across all groups.
88
+ """
89
+
90
+
91
+ class GroupedGemmKernel:
92
+ def __init__(
93
+ self,
94
+ acc_dtype: type[cutlass.Numeric],
95
+ use_2cta_instrs: bool,
96
+ mma_tiler_mn: tuple[int, int],
97
+ cluster_shape_mn: tuple[int, int],
98
+ tensormap_update_mode: utils.TensorMapUpdateMode = utils.TensorMapUpdateMode.SMEM,
99
+ ):
100
+ """Initializes the configuration for a Blackwell grouped GEMM kernel.
101
+
102
+ Besides configurations for dense persistent GEMM, there is an extra config specific to grouped GEMM:
103
+
104
+ Tensormap Update Mode:
105
+ - tensormap_update_mode: Specifies whether the tensormap is
106
+ updated in global memory(GMEM) or shared memory(SMEM).
107
+ The 2 modes are functionally equivalent and the difference are:
108
+ - We buffer 3 tensormaps in SMEM for A, B, and C tensors (each TMA descriptor takes 128B) when TMA updates performed on SMEM.
109
+ - Performance varies between modes depending on problem size; optimal choice differs across workloads.
110
+
111
+ :param acc_dtype: Data type of the accumulator.
112
+ :type acc_dtype: type[cutlass.Numeric]
113
+ :param use_2cta_instrs: Boolean, True to use cta_group=2 MMA variant.
114
+ :type use_2cta_instrs: bool
115
+ :param mma_tiler_mn: tuple (M, N) shape of the MMA instruction.
116
+ :type mma_tiler_mn: tuple[int, int]
117
+ :param cluster_shape_mn: tuple (ClusterM, ClusterN) shape of the cluster.
118
+ :type cluster_shape_mn: tuple[int, int]
119
+ :param tensormap_update_mode: Mode for updating the tensormap (GMEM or SMEM), defaults to SMEM.
120
+ :type tensormap_update_mode: utils.TensorMapUpdateMode, optional
121
+ """
122
+ self.acc_dtype: Type[cutlass.Numeric] = acc_dtype
123
+ self.use_2cta_instrs = use_2cta_instrs
124
+ self.cluster_shape_mn = cluster_shape_mn
125
+ # K dimension is deferred in _setup_attributes
126
+ self.mma_tiler = (*mma_tiler_mn, 1)
127
+ self.cta_group = (
128
+ tcgen05.CtaGroup.TWO if use_2cta_instrs else tcgen05.CtaGroup.ONE
129
+ )
130
+
131
+ self.tensormap_update_mode = tensormap_update_mode
132
+ # Delegate tensormap ab initialization to MMA warp when SMEM mode is used for better latency hiding
133
+ self.delegate_tensormap_ab_init = (
134
+ tensormap_update_mode == utils.TensorMapUpdateMode.SMEM
135
+ )
136
+
137
+ self.num_mcast_ctas_a = 1
138
+ self.num_mcast_ctas_b = 1
139
+ self.is_a_mcast = False
140
+ self.is_b_mcast = False
141
+
142
+ self.occupancy = 1
143
+ # Set specialized warp ids
144
+ self.epilog_warp_id = (
145
+ 0,
146
+ 1,
147
+ 2,
148
+ 3,
149
+ )
150
+ self.mma_warp_id = 4
151
+ self.tma_warp_id = 5
152
+ self.threads_per_cta = 32 * len(
153
+ (self.mma_warp_id, self.tma_warp_id, *self.epilog_warp_id)
154
+ )
155
+ # Set barrier id for cta sync, epilog sync, tmem ptr sync and tensormap update sync
156
+ self.cta_sync_bar_id = 0
157
+ self.epilog_sync_bar_id = 1
158
+ self.tmem_ptr_sync_bar_id = 2
159
+ # Barrier ID used by MMA/TMA warps to signal A/B tensormap initialization completion
160
+ self.tensormap_ab_init_bar_id = 4
161
+ self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
162
+ self.num_tma_load_bytes = 0
163
+
164
+ def _setup_attributes(self):
165
+ """Set up configurations that are dependent on GEMM inputs
166
+
167
+ Most of the implementation follows standard dense GEMM patterns,
168
+ with the key difference being additional consideration for SMEM
169
+ buffer needed for tensormap updates.
170
+ """
171
+ # Configure tiled mma
172
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
173
+ self.a_dtype,
174
+ self.a_major_mode,
175
+ self.b_major_mode,
176
+ self.acc_dtype,
177
+ self.cta_group,
178
+ self.mma_tiler[:2],
179
+ )
180
+
181
+ # Compute mma/cluster/tile shapes
182
+ mma_inst_shape_k = cute.size(tiled_mma.shape_mnk, mode=[2])
183
+ mma_inst_tile_k = 4
184
+ self.mma_tiler = (
185
+ self.mma_tiler[0],
186
+ self.mma_tiler[1],
187
+ mma_inst_shape_k * mma_inst_tile_k,
188
+ )
189
+ self.cta_tile_shape_mnk = (
190
+ self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
191
+ self.mma_tiler[1],
192
+ self.mma_tiler[2],
193
+ )
194
+ self.cluster_tile_shape_mnk = tuple(
195
+ x * y for x, y in zip(self.cta_tile_shape_mnk, (*self.cluster_shape_mn, 1))
196
+ )
197
+
198
+ # Compute cluster layout
199
+ self.cluster_layout_vmnk = cute.tiled_divide(
200
+ cute.make_layout((*self.cluster_shape_mn, 1)),
201
+ (tiled_mma.thr_id.shape,),
202
+ )
203
+
204
+ # Compute number of multicast CTAs for A/B
205
+ self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
206
+ self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
207
+ self.is_a_mcast = self.num_mcast_ctas_a > 1
208
+ self.is_b_mcast = self.num_mcast_ctas_b > 1
209
+
210
+ # Compute epilogue subtile
211
+ self.epi_tile = utils.compute_epilogue_tile_shape(
212
+ self.cta_tile_shape_mnk,
213
+ self.use_2cta_instrs,
214
+ self.c_layout,
215
+ self.c_dtype,
216
+ )
217
+
218
+ # Setup A/B/C stage count in shared memory and ACC stage count in tensor memory
219
+ (
220
+ self.num_acc_stage,
221
+ self.num_ab_stage,
222
+ self.num_epi_stage,
223
+ ) = self._compute_stages(
224
+ tiled_mma,
225
+ self.mma_tiler,
226
+ self.a_dtype,
227
+ self.b_dtype,
228
+ self.epi_tile,
229
+ self.c_dtype,
230
+ self.c_layout,
231
+ self.smem_capacity,
232
+ self.occupancy,
233
+ )
234
+
235
+ self.a_smem_layout_staged = sm100_utils.make_smem_layout_a(
236
+ tiled_mma,
237
+ self.mma_tiler,
238
+ self.a_dtype,
239
+ self.num_ab_stage,
240
+ )
241
+ self.b_smem_layout_staged = sm100_utils.make_smem_layout_b(
242
+ tiled_mma,
243
+ self.mma_tiler,
244
+ self.b_dtype,
245
+ self.num_ab_stage,
246
+ )
247
+ self.epi_smem_layout_staged = sm100_utils.make_smem_layout_epi(
248
+ self.c_dtype,
249
+ self.c_layout,
250
+ self.epi_tile,
251
+ self.num_epi_stage,
252
+ )
253
+
254
+ tensor_smem_bytes = self._get_tensor_smem_bytes(
255
+ self.a_smem_layout_staged,
256
+ self.a_dtype,
257
+ self.b_smem_layout_staged,
258
+ self.b_dtype,
259
+ self.epi_smem_layout_staged,
260
+ self.c_dtype,
261
+ )
262
+ mbar_smem_bytes = self._get_mbar_smem_bytes(
263
+ num_acc_stage=self.num_acc_stage,
264
+ num_ab_stage=self.num_ab_stage,
265
+ num_epi_stage=self.num_epi_stage,
266
+ )
267
+ tensormap_smem_bytes = self._get_tensormap_smem_bytes(
268
+ self.tensormap_update_mode
269
+ )
270
+ if (
271
+ mbar_smem_bytes
272
+ + tensormap_smem_bytes
273
+ + GroupedGemmKernel.tensor_memory_management_bytes
274
+ > self.reserved_smem_bytes
275
+ ):
276
+ raise ValueError(
277
+ f"smem consumption for mbar and tensormap {mbar_smem_bytes + tensormap_smem_bytes} exceeds the "
278
+ f"reserved smem bytes {self.reserved_smem_bytes}"
279
+ )
280
+
281
+ # Compute the number of tensor memory allocation columns
282
+ self.num_tmem_alloc_cols = self._compute_num_tmem_alloc_cols(
283
+ tiled_mma, self.mma_tiler, self.num_acc_stage
284
+ )
285
+
286
+ @cute.jit
287
+ def __call__(
288
+ self,
289
+ initial_a: cute.Tensor,
290
+ initial_b: cute.Tensor,
291
+ initial_c: cute.Tensor,
292
+ group_count: cutlass.Constexpr[int],
293
+ problem_shape_mnkl: cute.Tensor,
294
+ strides_abc: cute.Tensor,
295
+ tensor_address_abc: cute.Tensor,
296
+ total_num_clusters: cutlass.Constexpr[int],
297
+ tensormap_cute_tensor: cute.Tensor,
298
+ max_active_clusters: cutlass.Constexpr[int],
299
+ stream: cuda.CUstream,
300
+ ):
301
+ """Execute the GEMM operation in steps:
302
+ - Setup static attributes before smem/grid/tma computation
303
+ - Setup TMA load/store atoms and tensors
304
+ - Compute grid size with regard to hardware constraints
305
+ - Define shared storage for kernel
306
+ - Launch the kernel synchronously
307
+
308
+ For grouped GEMM, tensor shapes, tensor strides, and tensor address are all provided
309
+ by different tensors in global memory. The "initial" tensors only carry data type and
310
+ majorness information.
311
+
312
+ :param initial_a: Initial tensor A, used for data type and majorness information.
313
+ :type initial_a: cute.Tensor
314
+ :param initial_b: Initial tensor B, used for data type and majorness information.
315
+ :type initial_b: cute.Tensor
316
+ :param initial_c: Initial tensor C, used for data type and majorness information.
317
+ :type initial_c: cute.Tensor
318
+ :param group_count: The number of GEMM groups.
319
+ :type group_count: cutlass.Constexpr[int]
320
+ :param problem_shape_mnkl: Tensor containing the (M, N, K, L) shape for each group.
321
+ :type problem_shape_mnkl: cute.Tensor
322
+ :param strides_abc: Tensor containing the strides for A, B, and C for each group.
323
+ :type strides_abc: cute.Tensor
324
+ :param tensor_address_abc: Tensor containing the base addresses for A, B, and C for each group.
325
+ :type tensor_address_abc: cute.Tensor
326
+ :param total_num_clusters: Total number of clusters needed for all groups.
327
+ :type total_num_clusters: cutlass.Constexpr[int]
328
+ :param tensormap_cute_tensor: Tensor for storing tensormaps.
329
+ :type tensormap_cute_tensor: cute.Tensor
330
+ :param max_active_clusters: Maximum number of active clusters.
331
+ :type max_active_clusters: cutlass.Constexpr[int]
332
+ :param stream: CUDA stream for asynchronous execution.
333
+ :type stream: cuda.CUstream
334
+ :raises TypeError: If A and B data types do not match.
335
+ """
336
+ self.a_dtype = initial_a.element_type
337
+ self.b_dtype = initial_b.element_type
338
+ self.c_dtype = initial_c.element_type
339
+ self.a_major_mode = utils.LayoutEnum.from_tensor(initial_a).mma_major_mode()
340
+ self.b_major_mode = utils.LayoutEnum.from_tensor(initial_b).mma_major_mode()
341
+ self.c_layout = utils.LayoutEnum.from_tensor(initial_c)
342
+ if cutlass.const_expr(self.a_dtype != self.b_dtype):
343
+ raise TypeError(f"Type mismatch: {self.a_dtype} != {self.b_dtype}")
344
+
345
+ # Setup attributes that dependent on gemm inputs
346
+ self._setup_attributes()
347
+
348
+ tiled_mma = sm100_utils.make_trivial_tiled_mma(
349
+ self.a_dtype,
350
+ self.a_major_mode,
351
+ self.b_major_mode,
352
+ self.acc_dtype,
353
+ self.cta_group,
354
+ self.mma_tiler[:2],
355
+ )
356
+ atom_thr_size = cute.size(tiled_mma.thr_id.shape)
357
+
358
+ # Setup TMA load for A
359
+ a_op = sm100_utils.cluster_shape_to_tma_atom_A(
360
+ self.cluster_shape_mn, tiled_mma.thr_id
361
+ )
362
+ a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
363
+ tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
364
+ a_op,
365
+ initial_a,
366
+ a_smem_layout,
367
+ self.mma_tiler,
368
+ tiled_mma,
369
+ self.cluster_layout_vmnk.shape,
370
+ )
371
+
372
+ # Setup TMA load for B
373
+ b_op = sm100_utils.cluster_shape_to_tma_atom_B(
374
+ self.cluster_shape_mn, tiled_mma.thr_id
375
+ )
376
+ b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
377
+ tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
378
+ b_op,
379
+ initial_b,
380
+ b_smem_layout,
381
+ self.mma_tiler,
382
+ tiled_mma,
383
+ self.cluster_layout_vmnk.shape,
384
+ )
385
+
386
+ a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
387
+ b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
388
+ self.num_tma_load_bytes = (a_copy_size + b_copy_size) * atom_thr_size
389
+
390
+ # Setup TMA store for C
391
+ tma_atom_c = None
392
+ tma_tensor_c = None
393
+ c_cta_v_layout = cute.composition(
394
+ cute.make_identity_layout(initial_c.shape), self.epi_tile
395
+ )
396
+ epi_smem_layout = cute.slice_(self.epi_smem_layout_staged, (None, None, 0))
397
+ tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
398
+ cpasync.CopyBulkTensorTileS2GOp(),
399
+ initial_c,
400
+ epi_smem_layout,
401
+ c_cta_v_layout,
402
+ )
403
+
404
+ self.tile_sched_params, grid = self._compute_grid(
405
+ total_num_clusters, self.cluster_shape_mn, max_active_clusters
406
+ )
407
+
408
+ self.buffer_align_bytes = 1024
409
+ self.size_tensormap_in_i64 = (
410
+ 0
411
+ if self.tensormap_update_mode == utils.TensorMapUpdateMode.GMEM
412
+ else GroupedGemmKernel.num_tensormaps
413
+ * GroupedGemmKernel.bytes_per_tensormap
414
+ // 8
415
+ )
416
+
417
+ # Define shared storage for kernel
418
+ @cute.struct
419
+ class SharedStorage:
420
+ tensormap_buffer: cute.struct.MemRange[
421
+ cutlass.Int64, self.size_tensormap_in_i64
422
+ ]
423
+ ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
424
+ ab_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage]
425
+ acc_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
426
+ acc_empty_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_acc_stage]
427
+ tmem_dealloc_mbar_ptr: cutlass.Int64
428
+ tmem_holding_buf: cutlass.Int32
429
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
430
+ sC: cute.struct.Align[
431
+ cute.struct.MemRange[
432
+ self.c_dtype,
433
+ cute.cosize(self.epi_smem_layout_staged.outer),
434
+ ],
435
+ self.buffer_align_bytes,
436
+ ]
437
+ # (MMA, MMA_M, MMA_K, STAGE)
438
+ sA: cute.struct.Align[
439
+ cute.struct.MemRange[
440
+ self.a_dtype, cute.cosize(self.a_smem_layout_staged.outer)
441
+ ],
442
+ self.buffer_align_bytes,
443
+ ]
444
+ # (MMA, MMA_N, MMA_K, STAGE)
445
+ sB: cute.struct.Align[
446
+ cute.struct.MemRange[
447
+ self.b_dtype, cute.cosize(self.b_smem_layout_staged.outer)
448
+ ],
449
+ self.buffer_align_bytes,
450
+ ]
451
+
452
+ self.shared_storage = SharedStorage
453
+
454
+ # Launch the kernel synchronously
455
+ self.kernel(
456
+ tiled_mma,
457
+ tma_atom_a,
458
+ tma_tensor_a,
459
+ tma_atom_b,
460
+ tma_tensor_b,
461
+ tma_atom_c,
462
+ tma_tensor_c,
463
+ self.cluster_layout_vmnk,
464
+ self.a_smem_layout_staged,
465
+ self.b_smem_layout_staged,
466
+ self.epi_smem_layout_staged,
467
+ self.epi_tile,
468
+ self.tile_sched_params,
469
+ group_count,
470
+ problem_shape_mnkl,
471
+ strides_abc,
472
+ tensor_address_abc,
473
+ tensormap_cute_tensor,
474
+ ).launch(
475
+ grid=grid,
476
+ block=[self.threads_per_cta, 1, 1],
477
+ cluster=(*self.cluster_shape_mn, 1),
478
+ stream=stream,
479
+ )
480
+ return
481
+
482
+ # GPU device kernel
483
+ @cute.kernel
484
+ def kernel(
485
+ self,
486
+ tiled_mma: cute.TiledMma,
487
+ tma_atom_a: cute.CopyAtom,
488
+ mA_mkl: cute.Tensor,
489
+ tma_atom_b: cute.CopyAtom,
490
+ mB_nkl: cute.Tensor,
491
+ tma_atom_c: cute.CopyAtom,
492
+ mC_mnl: cute.Tensor,
493
+ cluster_layout_vmnk: cute.Layout,
494
+ a_smem_layout_staged: cute.ComposedLayout,
495
+ b_smem_layout_staged: cute.ComposedLayout,
496
+ epi_smem_layout_staged: Union[cute.Layout, cute.ComposedLayout],
497
+ epi_tile: cute.Tile,
498
+ tile_sched_params: utils.PersistentTileSchedulerParams,
499
+ group_count: cutlass.Constexpr[int],
500
+ problem_sizes_mnkl: cute.Tensor,
501
+ strides_abc: cute.Tensor,
502
+ ptrs_abc: cute.Tensor,
503
+ tensormaps: cute.Tensor,
504
+ ):
505
+ """
506
+ GPU device kernel performing the grouped GEMM computation.
507
+ """
508
+ warp_idx = cute.arch.warp_idx()
509
+ warp_idx = cute.arch.make_warp_uniform(warp_idx)
510
+
511
+ #
512
+ # Prefetch tma desc
513
+ #
514
+ if warp_idx == self.tma_warp_id:
515
+ cpasync.prefetch_descriptor(tma_atom_a)
516
+ cpasync.prefetch_descriptor(tma_atom_b)
517
+ cpasync.prefetch_descriptor(tma_atom_c)
518
+
519
+ use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
520
+
521
+ #
522
+ # Setup cta/thread coordinates
523
+ #
524
+ # Coord inside cluster
525
+ bid = cute.arch.block_idx()
526
+ mma_tile_coord_v = bid[0] % cute.size(tiled_mma.thr_id.shape)
527
+ is_leader_cta = mma_tile_coord_v == 0
528
+ cta_rank_in_cluster = cute.arch.make_warp_uniform(
529
+ cute.arch.block_idx_in_cluster()
530
+ )
531
+ block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
532
+ cta_rank_in_cluster
533
+ )
534
+ # Coord inside cta
535
+ tidx, _, _ = cute.arch.thread_idx()
536
+
537
+ #
538
+ # Alloc and init: tensormap buffer, a+b full/empty, accumulator full/empty, tensor memory dealloc barrier
539
+ #
540
+ smem = utils.SmemAllocator()
541
+ storage = smem.allocate(self.shared_storage)
542
+
543
+ tensormap_a_smem_ptr = None
544
+ tensormap_b_smem_ptr = None
545
+ tensormap_c_smem_ptr = None
546
+ if cutlass.const_expr(
547
+ self.tensormap_update_mode == utils.TensorMapUpdateMode.SMEM
548
+ ):
549
+ tensormap_smem_ptr = storage.tensormap_buffer.data_ptr()
550
+ tensormap_a_smem_ptr = tensormap_smem_ptr
551
+ tensormap_b_smem_ptr = (
552
+ tensormap_a_smem_ptr + GroupedGemmKernel.bytes_per_tensormap // 8
553
+ )
554
+ tensormap_c_smem_ptr = (
555
+ tensormap_b_smem_ptr + GroupedGemmKernel.bytes_per_tensormap // 8
556
+ )
557
+ ab_full_mbar_ptr = storage.ab_full_mbar_ptr.data_ptr()
558
+ ab_empty_mbar_ptr = storage.ab_empty_mbar_ptr.data_ptr()
559
+ acc_full_mbar_ptr = storage.acc_full_mbar_ptr.data_ptr()
560
+ acc_empty_mbar_ptr = storage.acc_empty_mbar_ptr.data_ptr()
561
+ tmem_dealloc_mbar_ptr = storage.tmem_dealloc_mbar_ptr
562
+ tmem_holding_buf = storage.tmem_holding_buf
563
+
564
+ # init barrier for loading A, B with TMA
565
+ if warp_idx == self.epilog_warp_id[0]:
566
+ for k_stage in range(self.num_ab_stage):
567
+ num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
568
+ with cute.arch.elect_one():
569
+ cute.arch.mbarrier_init(ab_full_mbar_ptr + k_stage, 1)
570
+ cute.arch.mbarrier_init(
571
+ ab_empty_mbar_ptr + k_stage, num_tma_producer
572
+ )
573
+ # Accumulator barrier init
574
+ if warp_idx == self.mma_warp_id:
575
+ for acc_stage in range(self.num_acc_stage):
576
+ with cute.arch.elect_one():
577
+ cute.arch.mbarrier_init(acc_full_mbar_ptr + acc_stage, 1)
578
+ cute.arch.mbarrier_init(
579
+ acc_empty_mbar_ptr + acc_stage, 8 if use_2cta_instrs else 4
580
+ )
581
+ # Tensor memory dealloc barrier init
582
+ if use_2cta_instrs:
583
+ if warp_idx == self.tma_warp_id:
584
+ num_tmem_dealloc_threads = 32
585
+ with cute.arch.elect_one():
586
+ cute.arch.mbarrier_init(
587
+ tmem_dealloc_mbar_ptr, num_tmem_dealloc_threads
588
+ )
589
+ cute.arch.mbarrier_init_fence()
590
+
591
+ # Cluster arrive after barrier init
592
+ if cute.size(self.cluster_shape_mn) > 1:
593
+ cute.arch.cluster_arrive_relaxed()
594
+
595
+ #
596
+ # Setup smem tensor A/B/C
597
+ #
598
+ # (EPI_TILE_M, EPI_TILE_N, STAGE)
599
+ sC = storage.sC.get_tensor(
600
+ epi_smem_layout_staged.outer, swizzle=epi_smem_layout_staged.inner
601
+ )
602
+ # (MMA, MMA_M, MMA_K, STAGE)
603
+ sA = storage.sA.get_tensor(
604
+ a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
605
+ )
606
+ # (MMA, MMA_N, MMA_K, STAGE)
607
+ sB = storage.sB.get_tensor(
608
+ b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
609
+ )
610
+
611
+ #
612
+ # Compute multicast mask for A/B buffer full and empty
613
+ #
614
+ a_full_mcast_mask = None
615
+ b_full_mcast_mask = None
616
+ ab_empty_mcast_mask = None
617
+ if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
618
+ a_full_mcast_mask = cpasync.create_tma_multicast_mask(
619
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
620
+ )
621
+ b_full_mcast_mask = cpasync.create_tma_multicast_mask(
622
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
623
+ )
624
+ ab_empty_mcast_mask = a_full_mcast_mask | b_full_mcast_mask
625
+ acc_full_mcast_mask = None
626
+ if cutlass.const_expr(use_2cta_instrs):
627
+ acc_full_mcast_mask = cute.make_layout_image_mask(
628
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk, mode=0
629
+ )
630
+ block_in_cluster_coord_vmnk_peer = (
631
+ block_in_cluster_coord_vmnk[0] ^ 1,
632
+ *block_in_cluster_coord_vmnk[1:],
633
+ )
634
+ a_full_mcast_mask_peer = cpasync.create_tma_multicast_mask(
635
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk_peer, mcast_mode=2
636
+ )
637
+ b_full_mcast_mask_peer = cpasync.create_tma_multicast_mask(
638
+ cluster_layout_vmnk, block_in_cluster_coord_vmnk_peer, mcast_mode=1
639
+ )
640
+ ab_empty_mcast_mask = (
641
+ a_full_mcast_mask_peer
642
+ | b_full_mcast_mask_peer
643
+ | cutlass.Int16(
644
+ 0 if ab_empty_mcast_mask is None else ab_empty_mcast_mask
645
+ )
646
+ )
647
+
648
+ #
649
+ # Local_tile partition global tensors
650
+ #
651
+ # (bM, bK, RestM, RestK, RestL)
652
+ gA_mkl = cute.local_tile(
653
+ mA_mkl, cute.slice_(self.mma_tiler, (None, 0, None)), (None, None, None)
654
+ )
655
+ # (bN, bK, RestN, RestK, RestL)
656
+ gB_nkl = cute.local_tile(
657
+ mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
658
+ )
659
+ # (bM, bN, RestM, RestN, RestL)
660
+ gC_mnl = cute.local_tile(
661
+ mC_mnl, cute.slice_(self.mma_tiler, (None, None, 0)), (None, None, None)
662
+ )
663
+
664
+ #
665
+ # Partition global tensor for TiledMMA_A/B/C
666
+ #
667
+ thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
668
+ # (MMA, MMA_M, MMA_K, RestM, RestK, RestL)
669
+ tCgA = thr_mma.partition_A(gA_mkl)
670
+ # (MMA, MMA_N, MMA_K, RestN, RestK, RestL)
671
+ tCgB = thr_mma.partition_B(gB_nkl)
672
+ # (MMA, MMA_M, MMA_N, RestM, RestN, RestL)
673
+ tCgC = thr_mma.partition_C(gC_mnl)
674
+
675
+ #
676
+ # Partition global/shared tensor for load A, B with TMA
677
+ #
678
+ a_cta_layout = cute.make_layout(
679
+ cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
680
+ )
681
+ # ((atom_v, rest_v), STAGE)
682
+ # ((atom_v, rest_v), RestM, RestK, RestL)
683
+ tAsA, tAgA = cpasync.tma_partition(
684
+ tma_atom_a,
685
+ block_in_cluster_coord_vmnk[2],
686
+ a_cta_layout,
687
+ cute.group_modes(sA, 0, 3),
688
+ cute.group_modes(tCgA, 0, 3),
689
+ )
690
+ # TMA load B partition_S/D
691
+ b_cta_layout = cute.make_layout(
692
+ cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
693
+ )
694
+ # ((atom_v, rest_v), STAGE)
695
+ # ((atom_v, rest_v), RestM, RestK, RestL)
696
+ tBsB, tBgB = cpasync.tma_partition(
697
+ tma_atom_b,
698
+ block_in_cluster_coord_vmnk[1],
699
+ b_cta_layout,
700
+ cute.group_modes(sB, 0, 3),
701
+ cute.group_modes(tCgB, 0, 3),
702
+ )
703
+
704
+ #
705
+ # Partition shared/tensor memory tensor for TiledMMA_A/B/C
706
+ #
707
+ # (MMA, MMA_M, MMA_K, STAGE)
708
+ tCrA = tiled_mma.make_fragment_A(sA)
709
+ # (MMA, MMA_N, MMA_K, STAGE)
710
+ tCrB = tiled_mma.make_fragment_B(sB)
711
+ # (MMA, MMA_M, MMA_N)
712
+ acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
713
+ # (MMA, MMA_M, MMA_N, STAGE)
714
+ tCtAcc_fake = tiled_mma.make_fragment_C(
715
+ cute.append(acc_shape, self.num_acc_stage)
716
+ )
717
+
718
+ #
719
+ # Cluster wait before tensor memory alloc
720
+ #
721
+ if cute.size(self.cluster_shape_mn) > 1:
722
+ cute.arch.cluster_wait()
723
+ else:
724
+ cute.arch.barrier(
725
+ barrier_id=self.cta_sync_bar_id, number_of_threads=self.threads_per_cta
726
+ )
727
+
728
+ #
729
+ # Get tensormap buffer address
730
+ #
731
+ grid_dim = cute.arch.grid_dim()
732
+ tensormap_workspace_idx = (
733
+ bid[2] * grid_dim[1] * grid_dim[0] + bid[1] * grid_dim[0] + bid[0]
734
+ )
735
+
736
+ tensormap_manager = utils.TensorMapManager(
737
+ self.tensormap_update_mode, GroupedGemmKernel.bytes_per_tensormap
738
+ )
739
+ tensormap_a_ptr = tensormap_manager.get_tensormap_ptr(
740
+ tensormaps[(tensormap_workspace_idx, 0, None)].iterator
741
+ )
742
+ tensormap_b_ptr = tensormap_manager.get_tensormap_ptr(
743
+ tensormaps[(tensormap_workspace_idx, 1, None)].iterator
744
+ )
745
+ tensormap_c_ptr = tensormap_manager.get_tensormap_ptr(
746
+ tensormaps[(tensormap_workspace_idx, 2, None)].iterator
747
+ )
748
+ # Setup tensormap initialization pointer based on the mode
749
+ if cutlass.const_expr(
750
+ self.tensormap_update_mode == utils.TensorMapUpdateMode.SMEM
751
+ ):
752
+ tensormap_a_init_ptr = tensormap_a_smem_ptr
753
+ tensormap_b_init_ptr = tensormap_b_smem_ptr
754
+ tensormap_c_init_ptr = tensormap_c_smem_ptr
755
+ else:
756
+ tensormap_a_init_ptr = tensormap_a_ptr
757
+ tensormap_b_init_ptr = tensormap_b_ptr
758
+ tensormap_c_init_ptr = tensormap_c_ptr
759
+
760
+ #
761
+ # Specialized TMA load warp
762
+ #
763
+ if warp_idx == self.tma_warp_id:
764
+ # Initialize tensormaps for A, B
765
+ if cutlass.const_expr(self.delegate_tensormap_ab_init == False):
766
+ tensormap_manager.init_tensormap_from_atom(
767
+ tma_atom_a, tensormap_a_init_ptr, self.tma_warp_id
768
+ )
769
+ tensormap_manager.init_tensormap_from_atom(
770
+ tma_atom_b, tensormap_b_init_ptr, self.tma_warp_id
771
+ )
772
+ #
773
+ # Persistent tile scheduling loop
774
+ #
775
+ tile_sched = utils.StaticPersistentTileScheduler.create(
776
+ tile_sched_params, bid, grid_dim
777
+ )
778
+ # grouped gemm tile scheduler helper will compute the group index for the tile we're working on
779
+ group_gemm_ts_helper = utils.GroupedGemmTileSchedulerHelper(
780
+ group_count,
781
+ tile_sched_params,
782
+ self.cluster_tile_shape_mnk,
783
+ utils.create_initial_search_state(),
784
+ )
785
+ tensormap_init_done = cutlass.Boolean(False)
786
+ # tile count we have searched
787
+ total_k_tile_cnt = cutlass.Int32(0)
788
+ # group index of last tile
789
+ last_group_idx = cutlass.Int32(-1)
790
+ work_tile = tile_sched.initial_work_tile_info()
791
+ while work_tile.is_valid_tile:
792
+ cur_tile_coord = work_tile.tile_idx
793
+ grouped_gemm_cta_tile_info = group_gemm_ts_helper.delinearize_z(
794
+ cur_tile_coord,
795
+ problem_sizes_mnkl,
796
+ )
797
+ cur_k_tile_cnt = grouped_gemm_cta_tile_info.cta_tile_count_k
798
+ cur_group_idx = grouped_gemm_cta_tile_info.group_idx
799
+ is_group_changed = cur_group_idx != last_group_idx
800
+ # skip tensormap update if we're working on the same group
801
+ if is_group_changed:
802
+ real_tensor_a = self.make_tensor_for_tensormap_update(
803
+ cur_group_idx,
804
+ self.a_dtype,
805
+ (
806
+ grouped_gemm_cta_tile_info.problem_shape_m,
807
+ grouped_gemm_cta_tile_info.problem_shape_n,
808
+ grouped_gemm_cta_tile_info.problem_shape_k,
809
+ ),
810
+ strides_abc,
811
+ ptrs_abc,
812
+ 0, # 0 for tensor A
813
+ )
814
+ real_tensor_b = self.make_tensor_for_tensormap_update(
815
+ cur_group_idx,
816
+ self.b_dtype,
817
+ (
818
+ grouped_gemm_cta_tile_info.problem_shape_m,
819
+ grouped_gemm_cta_tile_info.problem_shape_n,
820
+ grouped_gemm_cta_tile_info.problem_shape_k,
821
+ ),
822
+ strides_abc,
823
+ ptrs_abc,
824
+ 1, # 1 for tensor B
825
+ )
826
+ # wait tensormap initialization complete before update
827
+ if tensormap_init_done == False:
828
+ if cutlass.const_expr(self.delegate_tensormap_ab_init):
829
+ cute.arch.barrier(
830
+ barrier_id=self.tensormap_ab_init_bar_id,
831
+ number_of_threads=64,
832
+ )
833
+ tensormap_manager.fence_tensormap_initialization()
834
+ tensormap_init_done = True
835
+
836
+ tensormap_manager.update_tensormap(
837
+ (real_tensor_a, real_tensor_b),
838
+ (tma_atom_a, tma_atom_b),
839
+ (tensormap_a_ptr, tensormap_b_ptr),
840
+ self.tma_warp_id,
841
+ (tensormap_a_smem_ptr, tensormap_b_smem_ptr),
842
+ )
843
+
844
+ mma_tile_coord_mnl = (
845
+ grouped_gemm_cta_tile_info.cta_tile_idx_m
846
+ // cute.size(tiled_mma.thr_id.shape),
847
+ grouped_gemm_cta_tile_info.cta_tile_idx_n,
848
+ 0,
849
+ )
850
+
851
+ #
852
+ # Slice to per mma tile index
853
+ #
854
+ # ((atom_v, rest_v), RestK)
855
+ tAgA_slice = tAgA[
856
+ (None, mma_tile_coord_mnl[0], None, mma_tile_coord_mnl[2])
857
+ ]
858
+ # ((atom_v, rest_v), RestK)
859
+ tBgB_slice = tBgB[
860
+ (None, mma_tile_coord_mnl[1], None, mma_tile_coord_mnl[2])
861
+ ]
862
+
863
+ num_prev_k_blk = total_k_tile_cnt
864
+ total_k_tile_cnt += cur_k_tile_cnt
865
+
866
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt
867
+ tma_wr_k_tile = cutlass.Int32(0)
868
+ smem_wr_buffer = (num_prev_k_blk + tma_wr_k_tile) % self.num_ab_stage
869
+ tma_wr_ab_empty_phase = (
870
+ num_prev_k_blk + tma_wr_k_tile
871
+ ) // self.num_ab_stage % 2 ^ 1
872
+ peek_ab_empty_status = cute.arch.mbarrier_conditional_try_wait(
873
+ tma_wr_k_tile < cur_k_tile_cnt,
874
+ ab_empty_mbar_ptr + smem_wr_buffer,
875
+ tma_wr_ab_empty_phase,
876
+ )
877
+ # ensure the update to tensormap has completed before using it
878
+ if is_group_changed:
879
+ tensormap_manager.fence_tensormap_update(tensormap_a_ptr)
880
+ tensormap_manager.fence_tensormap_update(tensormap_b_ptr)
881
+ #
882
+ # Tma load loop
883
+ #
884
+ for k_tile in cutlass.range(0, cur_k_tile_cnt, 1, unroll=1):
885
+ tma_wr_k_tile_next = tma_wr_k_tile + 1
886
+ smem_wr_buffer_next = (
887
+ num_prev_k_blk + tma_wr_k_tile_next
888
+ ) % self.num_ab_stage
889
+ tma_wr_ab_empty_phase_next = (
890
+ tma_wr_ab_empty_phase ^ 1
891
+ if smem_wr_buffer_next == 0
892
+ else tma_wr_ab_empty_phase
893
+ )
894
+
895
+ smem_full_mbar_ptr = ab_full_mbar_ptr + smem_wr_buffer
896
+
897
+ # Wait for AB buffer empty
898
+ if peek_ab_empty_status == 0:
899
+ cute.arch.mbarrier_wait(
900
+ ab_empty_mbar_ptr + smem_wr_buffer, tma_wr_ab_empty_phase
901
+ )
902
+
903
+ # Arrive AB buffer and expect full transaction bytes
904
+ if is_leader_cta:
905
+ with cute.arch.elect_one():
906
+ cute.arch.mbarrier_arrive_and_expect_tx(
907
+ smem_full_mbar_ptr, self.num_tma_load_bytes
908
+ )
909
+
910
+ # Load A/B with TMA
911
+ cute.copy(
912
+ tma_atom_a,
913
+ tAgA_slice[(None, tma_wr_k_tile)],
914
+ tAsA[(None, smem_wr_buffer)],
915
+ tma_bar_ptr=smem_full_mbar_ptr,
916
+ mcast_mask=a_full_mcast_mask,
917
+ tma_desc_ptr=tensormap_manager.get_tensormap_ptr(
918
+ tensormap_a_ptr,
919
+ cute.AddressSpace.generic,
920
+ ),
921
+ )
922
+ cute.copy(
923
+ tma_atom_b,
924
+ tBgB_slice[(None, tma_wr_k_tile)],
925
+ tBsB[(None, smem_wr_buffer)],
926
+ tma_bar_ptr=smem_full_mbar_ptr,
927
+ mcast_mask=b_full_mcast_mask,
928
+ tma_desc_ptr=tensormap_manager.get_tensormap_ptr(
929
+ tensormap_b_ptr,
930
+ cute.AddressSpace.generic,
931
+ ),
932
+ )
933
+
934
+ # Peek (try_wait) AB buffer empty for k_tile = prefetch_k_tile_cnt + k_tile + 1
935
+ peek_ab_empty_status = cute.arch.mbarrier_conditional_try_wait(
936
+ tma_wr_k_tile_next < cur_k_tile_cnt,
937
+ ab_empty_mbar_ptr + smem_wr_buffer_next,
938
+ tma_wr_ab_empty_phase_next,
939
+ )
940
+
941
+ tma_wr_k_tile = tma_wr_k_tile_next
942
+ smem_wr_buffer = smem_wr_buffer_next
943
+ tma_wr_ab_empty_phase = tma_wr_ab_empty_phase_next
944
+
945
+ # Advance to next tile
946
+ tile_sched.advance_to_next_work()
947
+ work_tile = tile_sched.get_current_work()
948
+ last_group_idx = cur_group_idx
949
+
950
+ #
951
+ # Specialized MMA warp
952
+ #
953
+ if warp_idx == self.mma_warp_id:
954
+ # initialize tensormap A, B for TMA warp
955
+ if cutlass.const_expr(self.delegate_tensormap_ab_init):
956
+ tensormap_manager.init_tensormap_from_atom(
957
+ tma_atom_a, tensormap_a_init_ptr, self.mma_warp_id
958
+ )
959
+ tensormap_manager.init_tensormap_from_atom(
960
+ tma_atom_b, tensormap_b_init_ptr, self.mma_warp_id
961
+ )
962
+ # signal tensormap initialization has finished
963
+ cute.arch.barrier(
964
+ barrier_id=self.tensormap_ab_init_bar_id, number_of_threads=64
965
+ )
966
+ # Bar sync for retrieve tmem ptr from shared mem
967
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
968
+ cute.arch.barrier(
969
+ barrier_id=self.tmem_ptr_sync_bar_id,
970
+ number_of_threads=tmem_ptr_read_threads,
971
+ )
972
+
973
+ #
974
+ # Retrieving tensor memory ptr and make accumulator tensor
975
+ #
976
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
977
+ self.acc_dtype,
978
+ alignment=16,
979
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
980
+ )
981
+ # (MMA, MMA_M, MMA_N, STAGE)
982
+ tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
983
+
984
+ #
985
+ # Persistent tile scheduling loop
986
+ #
987
+ tile_sched = utils.StaticPersistentTileScheduler.create(
988
+ tile_sched_params, bid, grid_dim
989
+ )
990
+ # grouped gemm tile scheduler helper will compute the group index for the tile we're working on
991
+ group_gemm_ts_helper = utils.GroupedGemmTileSchedulerHelper(
992
+ group_count,
993
+ tile_sched_params,
994
+ self.cluster_tile_shape_mnk,
995
+ utils.create_initial_search_state(),
996
+ )
997
+
998
+ work_tile = tile_sched.initial_work_tile_info()
999
+ # tile count we have searched
1000
+ total_k_tile_cnt = cutlass.Int32(0)
1001
+ while work_tile.is_valid_tile:
1002
+ cur_tile_coord = work_tile.tile_idx
1003
+ # MMA warp is only interested in number of tiles along K dimension
1004
+ (
1005
+ cur_k_tile_cnt,
1006
+ cur_group_idx,
1007
+ ) = group_gemm_ts_helper.search_cluster_tile_count_k(
1008
+ cur_tile_coord,
1009
+ problem_sizes_mnkl,
1010
+ )
1011
+ # Set tensor memory buffer for current tile
1012
+ acc_buf_idx = tile_sched.num_tiles_executed % self.num_acc_stage
1013
+ # (MMA, MMA_M, MMA_N)
1014
+ tCtAcc = tCtAcc_base[(None, None, None, acc_buf_idx)]
1015
+
1016
+ num_prev_k_blk = total_k_tile_cnt
1017
+ total_k_tile_cnt += cur_k_tile_cnt
1018
+
1019
+ # Peek (try_wait) AB buffer full for k_tile = 0
1020
+ mma_rd_k_tile = cutlass.Int32(0)
1021
+ smem_rd_buffer = (num_prev_k_blk + mma_rd_k_tile) % self.num_ab_stage
1022
+ need_check_rd_buffer_full = (
1023
+ mma_rd_k_tile < cur_k_tile_cnt and is_leader_cta
1024
+ )
1025
+ mma_rd_ab_full_phase = (
1026
+ (num_prev_k_blk + mma_rd_k_tile) // self.num_ab_stage % 2
1027
+ )
1028
+ peek_ab_full_status = cute.arch.mbarrier_conditional_try_wait(
1029
+ need_check_rd_buffer_full,
1030
+ ab_full_mbar_ptr + smem_rd_buffer,
1031
+ mma_rd_ab_full_phase,
1032
+ )
1033
+
1034
+ #
1035
+ # Wait for accumulator buffer empty
1036
+ #
1037
+ if is_leader_cta:
1038
+ acc_empty_phase = (
1039
+ tile_sched.num_tiles_executed // self.num_acc_stage % 2 ^ 1
1040
+ )
1041
+ cute.arch.mbarrier_wait(
1042
+ acc_empty_mbar_ptr + acc_buf_idx, acc_empty_phase
1043
+ )
1044
+
1045
+ #
1046
+ # Reset the ACCUMULATE field for each tile
1047
+ #
1048
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, False)
1049
+
1050
+ #
1051
+ # Mma mainloop
1052
+ #
1053
+ for k_tile in range(cur_k_tile_cnt):
1054
+ mma_rd_k_tile_next = cutlass.Int32(k_tile + 1)
1055
+ smem_rd_buffer_next = (
1056
+ num_prev_k_blk + mma_rd_k_tile_next
1057
+ ) % self.num_ab_stage
1058
+ mma_rd_ab_full_phase_next = (
1059
+ mma_rd_ab_full_phase ^ 1
1060
+ if smem_rd_buffer_next == 0
1061
+ else mma_rd_ab_full_phase
1062
+ )
1063
+ if is_leader_cta:
1064
+ # Wait for AB buffer full
1065
+ if peek_ab_full_status == 0:
1066
+ cute.arch.mbarrier_wait(
1067
+ ab_full_mbar_ptr + smem_rd_buffer, mma_rd_ab_full_phase
1068
+ )
1069
+
1070
+ # tCtAcc += tCrA * tCrB
1071
+ num_kblocks = cute.size(tCrA, mode=[2])
1072
+ for kblock_idx in cutlass.range(num_kblocks, unroll_full=True):
1073
+ kblock_coord = (None, None, kblock_idx, smem_rd_buffer)
1074
+
1075
+ cute.gemm(
1076
+ tiled_mma,
1077
+ tCtAcc,
1078
+ tCrA[kblock_coord],
1079
+ tCrB[kblock_coord],
1080
+ tCtAcc,
1081
+ )
1082
+ # Enable accumulate on tCtAcc after first kblock
1083
+ tiled_mma.set(tcgen05.Field.ACCUMULATE, True)
1084
+
1085
+ # Async arrive AB buffer empty
1086
+ with cute.arch.elect_one():
1087
+ tcgen05.commit(
1088
+ ab_empty_mbar_ptr + smem_rd_buffer,
1089
+ ab_empty_mcast_mask,
1090
+ self.cta_group,
1091
+ )
1092
+
1093
+ # Peek (try_wait) AB buffer full for k_tile = k_tile + 1
1094
+ need_check_rd_buffer_full = (
1095
+ mma_rd_k_tile_next < cur_k_tile_cnt and is_leader_cta
1096
+ )
1097
+
1098
+ peek_ab_full_status = cute.arch.mbarrier_conditional_try_wait(
1099
+ need_check_rd_buffer_full,
1100
+ ab_full_mbar_ptr + smem_rd_buffer_next,
1101
+ mma_rd_ab_full_phase_next,
1102
+ )
1103
+
1104
+ mma_rd_k_tile = mma_rd_k_tile_next
1105
+ smem_rd_buffer = smem_rd_buffer_next
1106
+ mma_rd_ab_full_phase = mma_rd_ab_full_phase_next
1107
+
1108
+ #
1109
+ # Async arrive accumulator buffer full
1110
+ #
1111
+ if is_leader_cta:
1112
+ with cute.arch.elect_one():
1113
+ tcgen05.commit(
1114
+ acc_full_mbar_ptr + acc_buf_idx,
1115
+ acc_full_mcast_mask,
1116
+ self.cta_group,
1117
+ )
1118
+
1119
+ #
1120
+ # Advance to next tile
1121
+ #
1122
+ tile_sched.advance_to_next_work()
1123
+ work_tile = tile_sched.get_current_work()
1124
+
1125
+ #
1126
+ # Specialized epilogue warps
1127
+ #
1128
+ if warp_idx < self.mma_warp_id:
1129
+ # initialize tensorap for C
1130
+ tensormap_manager.init_tensormap_from_atom(
1131
+ tma_atom_c,
1132
+ tensormap_c_init_ptr,
1133
+ self.epilog_warp_id[0],
1134
+ )
1135
+ # Alloc tensor memory buffer
1136
+ if warp_idx == self.epilog_warp_id[0]:
1137
+ cute.arch.alloc_tmem(
1138
+ self.num_tmem_alloc_cols,
1139
+ tmem_holding_buf,
1140
+ is_two_cta=use_2cta_instrs,
1141
+ )
1142
+
1143
+ #
1144
+ # Bar sync for retrieve tensor memory ptr from shared memory
1145
+ #
1146
+ tmem_ptr_read_threads = 32 * len((self.mma_warp_id, *self.epilog_warp_id))
1147
+ cute.arch.barrier(
1148
+ barrier_id=self.tmem_ptr_sync_bar_id,
1149
+ number_of_threads=tmem_ptr_read_threads,
1150
+ )
1151
+
1152
+ #
1153
+ # Retrieving tensor memory ptr and make accumulator tensor
1154
+ #
1155
+ tmem_ptr = cute.arch.retrieve_tmem_ptr(
1156
+ self.acc_dtype,
1157
+ alignment=16,
1158
+ ptr_to_buffer_holding_addr=tmem_holding_buf,
1159
+ )
1160
+ # (MMA, MMA_M, MMA_N, STAGE)
1161
+ tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
1162
+
1163
+ epi_tidx = tidx
1164
+ #
1165
+ # Partition for epilogue
1166
+ #
1167
+ (
1168
+ tiled_copy_t2r,
1169
+ tTR_tAcc_base,
1170
+ tTR_rAcc,
1171
+ ) = self.epilog_tmem_copy_and_partition(
1172
+ epi_tidx, tCtAcc_base, tCgC, epi_tile, use_2cta_instrs
1173
+ )
1174
+
1175
+ tTR_rC = cute.make_fragment(tTR_rAcc.shape, self.c_dtype)
1176
+ tiled_copy_r2s, tRS_rC, tRS_sC = self.epilog_smem_copy_and_partition(
1177
+ tiled_copy_t2r, tTR_rC, epi_tidx, sC
1178
+ )
1179
+ (
1180
+ tma_atom_c,
1181
+ bSG_sC,
1182
+ bSG_gC_partitioned,
1183
+ ) = self.epilog_gmem_copy_and_partition(tma_atom_c, tCgC, epi_tile, sC)
1184
+
1185
+ #
1186
+ # Persistent tile scheduling loop
1187
+ #
1188
+ tile_sched = utils.StaticPersistentTileScheduler.create(
1189
+ tile_sched_params, bid, grid_dim
1190
+ )
1191
+ # grouped gemm tile scheduler helper will compute the group index for the tile we're working on
1192
+ group_gemm_ts_helper = utils.GroupedGemmTileSchedulerHelper(
1193
+ group_count,
1194
+ tile_sched_params,
1195
+ self.cluster_tile_shape_mnk,
1196
+ utils.create_initial_search_state(),
1197
+ )
1198
+
1199
+ work_tile = tile_sched.initial_work_tile_info()
1200
+ # wait tensormap initialization complete before update
1201
+ tensormap_manager.fence_tensormap_initialization()
1202
+ # tile count we have searched
1203
+ total_k_tile_cnt = cutlass.Int32(0)
1204
+ # group index of last tile
1205
+ last_group_idx = cutlass.Int32(-1)
1206
+ while work_tile.is_valid_tile:
1207
+ cur_tile_coord = work_tile.tile_idx
1208
+ grouped_gemm_cta_tile_info = group_gemm_ts_helper.delinearize_z(
1209
+ cur_tile_coord,
1210
+ problem_sizes_mnkl,
1211
+ )
1212
+ cur_group_idx = grouped_gemm_cta_tile_info.group_idx
1213
+ is_group_changed = cur_group_idx != last_group_idx
1214
+ if is_group_changed:
1215
+ # construct tensor C based on real address, shape and stride information
1216
+ real_tensor_c = self.make_tensor_for_tensormap_update(
1217
+ cur_group_idx,
1218
+ self.c_dtype,
1219
+ (
1220
+ grouped_gemm_cta_tile_info.problem_shape_m,
1221
+ grouped_gemm_cta_tile_info.problem_shape_n,
1222
+ grouped_gemm_cta_tile_info.problem_shape_k,
1223
+ ),
1224
+ strides_abc,
1225
+ ptrs_abc,
1226
+ 2, # 2 for tensor C
1227
+ )
1228
+ tensormap_manager.update_tensormap(
1229
+ ((real_tensor_c),),
1230
+ ((tma_atom_c),),
1231
+ ((tensormap_c_ptr),),
1232
+ self.epilog_warp_id[0],
1233
+ (tensormap_c_smem_ptr,),
1234
+ )
1235
+
1236
+ mma_tile_coord_mnl = (
1237
+ grouped_gemm_cta_tile_info.cta_tile_idx_m
1238
+ // cute.size(tiled_mma.thr_id.shape),
1239
+ grouped_gemm_cta_tile_info.cta_tile_idx_n,
1240
+ 0,
1241
+ )
1242
+ cur_k_tile_cnt = grouped_gemm_cta_tile_info.cta_tile_count_k
1243
+ total_k_tile_cnt += cur_k_tile_cnt
1244
+
1245
+ #
1246
+ # Slice to per mma tile index
1247
+ #
1248
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1249
+ bSG_gC = bSG_gC_partitioned[
1250
+ (
1251
+ None,
1252
+ None,
1253
+ None,
1254
+ *mma_tile_coord_mnl,
1255
+ )
1256
+ ]
1257
+
1258
+ # Set tensor memory buffer for current tile
1259
+ acc_buf_idx = tile_sched.num_tiles_executed % self.num_acc_stage
1260
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M)
1261
+ tTR_tAcc = tTR_tAcc_base[(None, None, None, None, None, acc_buf_idx)]
1262
+
1263
+ #
1264
+ # Wait for accumulator buffer full
1265
+ #
1266
+ acc_full_phase = tile_sched.num_tiles_executed // self.num_acc_stage % 2
1267
+ cute.arch.mbarrier_wait(acc_full_mbar_ptr + acc_buf_idx, acc_full_phase)
1268
+
1269
+ tTR_tAcc = cute.group_modes(tTR_tAcc, 3, cute.rank(tTR_tAcc))
1270
+ bSG_gC = cute.group_modes(bSG_gC, 1, cute.rank(bSG_gC))
1271
+ # ensure the update to tensormap has completed before using it
1272
+ if is_group_changed:
1273
+ if warp_idx == self.epilog_warp_id[0]:
1274
+ tensormap_manager.fence_tensormap_update(tensormap_c_ptr)
1275
+ #
1276
+ # Store accumulator to global memory in subtiles
1277
+ #
1278
+ subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3])
1279
+ num_prev_subtiles = tile_sched.num_tiles_executed * subtile_cnt
1280
+ for subtile_idx in range(subtile_cnt):
1281
+ #
1282
+ # Load accumulator from tensor memory buffer to register
1283
+ #
1284
+ tTR_tAcc_mn = tTR_tAcc[(None, None, None, subtile_idx)]
1285
+ cute.copy(tiled_copy_t2r, tTR_tAcc_mn, tTR_rAcc)
1286
+
1287
+ #
1288
+ # Convert to output type
1289
+ #
1290
+ acc_vec = tiled_copy_r2s.retile(tTR_rAcc).load()
1291
+ tRS_rC.store(acc_vec.to(self.c_dtype))
1292
+ #
1293
+ # Store C to shared memory
1294
+ #
1295
+ epi_buffer = (num_prev_subtiles + subtile_idx) % self.num_epi_stage
1296
+ cute.copy(
1297
+ tiled_copy_r2s,
1298
+ tRS_rC,
1299
+ tRS_sC[(None, None, None, epi_buffer)],
1300
+ )
1301
+ # Fence and barrier to make sure shared memory store is visible to TMA store
1302
+ cute.arch.fence_proxy(
1303
+ cute.arch.ProxyKind.async_shared,
1304
+ space=cute.arch.SharedSpace.shared_cta,
1305
+ )
1306
+ epilog_threads = 32 * len(self.epilog_warp_id)
1307
+ cute.arch.barrier(
1308
+ barrier_id=self.epilog_sync_bar_id,
1309
+ number_of_threads=epilog_threads,
1310
+ )
1311
+ #
1312
+ # store C to global memory with TMA
1313
+ #
1314
+ if warp_idx == self.epilog_warp_id[0]:
1315
+ cute.copy(
1316
+ tma_atom_c,
1317
+ bSG_sC[(None, epi_buffer)],
1318
+ bSG_gC[(None, subtile_idx)],
1319
+ tma_desc_ptr=tensormap_manager.get_tensormap_ptr(
1320
+ tensormap_c_ptr,
1321
+ cute.AddressSpace.generic,
1322
+ ),
1323
+ )
1324
+ cute.arch.cp_async_bulk_commit_group()
1325
+ cute.arch.cp_async_bulk_wait_group(
1326
+ self.num_epi_stage - 1, read=True
1327
+ )
1328
+ cute.arch.barrier(
1329
+ barrier_id=self.epilog_sync_bar_id,
1330
+ number_of_threads=epilog_threads,
1331
+ )
1332
+ #
1333
+ # Async arrive accumulator buffer empty
1334
+ #
1335
+ with cute.arch.elect_one():
1336
+ cute.arch.mbarrier_arrive(
1337
+ acc_empty_mbar_ptr + acc_buf_idx,
1338
+ cta_rank_in_cluster // 2 * 2 if use_2cta_instrs else None,
1339
+ )
1340
+
1341
+ #
1342
+ # Advance to next tile
1343
+ #
1344
+ tile_sched.advance_to_next_work()
1345
+ work_tile = tile_sched.get_current_work()
1346
+ last_group_idx = cur_group_idx
1347
+
1348
+ #
1349
+ # Dealloc the tensor memory buffer
1350
+ #
1351
+ if warp_idx == self.epilog_warp_id[0]:
1352
+ cute.arch.relinquish_tmem_alloc_permit(is_two_cta=use_2cta_instrs)
1353
+ epilog_threads = 32 * len(self.epilog_warp_id)
1354
+ cute.arch.barrier(
1355
+ barrier_id=self.epilog_sync_bar_id, number_of_threads=epilog_threads
1356
+ )
1357
+ if warp_idx == self.epilog_warp_id[0]:
1358
+ if use_2cta_instrs:
1359
+ cute.arch.mbarrier_arrive(
1360
+ tmem_dealloc_mbar_ptr, cta_rank_in_cluster ^ 1
1361
+ )
1362
+ cute.arch.mbarrier_wait(tmem_dealloc_mbar_ptr, 0)
1363
+ cute.arch.dealloc_tmem(
1364
+ tmem_ptr, self.num_tmem_alloc_cols, is_two_cta=use_2cta_instrs
1365
+ )
1366
+
1367
+ #
1368
+ # Wait a/b buffer empty
1369
+ #
1370
+ if warp_idx == self.epilog_warp_id[0]:
1371
+ cute.arch.mbarrier_wait(
1372
+ (ab_empty_mbar_ptr + ((total_k_tile_cnt - 1) % self.num_ab_stage)),
1373
+ (((total_k_tile_cnt - 1) // self.num_ab_stage) % 2),
1374
+ )
1375
+
1376
+ @cute.jit
1377
+ def make_tensor_for_tensormap_update(
1378
+ self,
1379
+ group_idx: cutlass.Int32,
1380
+ dtype: Type[cutlass.Numeric],
1381
+ problem_shape_mnk: tuple[cutlass.Int32, cutlass.Int32, cutlass.Int32],
1382
+ strides_abc: cute.Tensor,
1383
+ tensor_address_abc: cute.Tensor,
1384
+ tensor_index: int,
1385
+ ):
1386
+ """Extract stride and tensor address for a given group and construct a global tensor.
1387
+
1388
+ This function is used within the kernel to dynamically create a CUTE tensor
1389
+ representing A, B, or C for the current group being processed, using the
1390
+ group-specific address, shape, and stride information.
1391
+
1392
+ :param group_idx: The index of the current group within the grouped GEMM.
1393
+ :type group_idx: cutlass.Int32
1394
+ :param dtype: The data type of the tensor elements (e.g., cutlass.Float16).
1395
+ :type dtype: Type[cutlass.Numeric]
1396
+ :param problem_shape_mnk: The (M, N, K) problem shape for the current group.
1397
+ :type problem_shape_mnk: tuple[cutlass.Int32, cutlass.Int32, cutlass.Int32]
1398
+ :param strides_abc: Tensor containing strides for A, B, C for all groups. Layout: (group_count, 3, 2).
1399
+ :type strides_abc: cute.Tensor
1400
+ :param tensor_address_abc: Tensor containing global memory addresses for A, B, C for all groups. Layout: (group_count, 3).
1401
+ :type tensor_address_abc: cute.Tensor
1402
+ :param tensor_index: Specifies which tensor to create: 0 for A, 1 for B, 2 for C.
1403
+ :type tensor_index: int
1404
+ :return: A CUTE tensor representing the requested global memory tensor (A, B, or C) for the specified group.
1405
+ :rtype: cute.Tensor
1406
+ :raises TypeError: If the provided dtype is not a subclass of cutlass.Numeric.
1407
+ """
1408
+ ptr_i64 = tensor_address_abc[(group_idx, tensor_index)]
1409
+ if cutlass.const_expr(
1410
+ not isclass(dtype) or not issubclass(dtype, cutlass.Numeric)
1411
+ ):
1412
+ raise TypeError(
1413
+ f"dtype must be a type of cutlass.Numeric, got {type(dtype)}"
1414
+ )
1415
+ tensor_gmem_ptr = cute.make_ptr(
1416
+ dtype, ptr_i64, cute.AddressSpace.gmem, assumed_align=16
1417
+ )
1418
+
1419
+ strides_tensor_gmem = strides_abc[(group_idx, tensor_index, None)]
1420
+ strides_tensor_reg = cute.make_fragment(
1421
+ cute.make_layout(2),
1422
+ strides_abc.element_type,
1423
+ )
1424
+ cute.autovec_copy(strides_tensor_gmem, strides_tensor_reg)
1425
+ stride_mn = strides_tensor_reg[0]
1426
+ stride_k = strides_tensor_reg[1]
1427
+ c1 = cutlass.Int32(1)
1428
+ c0 = cutlass.Int32(0)
1429
+
1430
+ if cutlass.const_expr(tensor_index == 0): # tensor A
1431
+ m = problem_shape_mnk[0]
1432
+ k = problem_shape_mnk[2]
1433
+ return cute.make_tensor(
1434
+ tensor_gmem_ptr,
1435
+ cute.make_layout((m, k, c1), stride=(stride_mn, stride_k, c0)),
1436
+ )
1437
+ elif cutlass.const_expr(tensor_index == 1): # tensor B
1438
+ n = problem_shape_mnk[1]
1439
+ k = problem_shape_mnk[2]
1440
+ return cute.make_tensor(
1441
+ tensor_gmem_ptr,
1442
+ cute.make_layout((n, k, c1), stride=(stride_mn, stride_k, c0)),
1443
+ )
1444
+ else: # tensor C
1445
+ m = problem_shape_mnk[0]
1446
+ n = problem_shape_mnk[1]
1447
+ return cute.make_tensor(
1448
+ tensor_gmem_ptr,
1449
+ cute.make_layout((m, n, c1), stride=(stride_mn, stride_k, c0)),
1450
+ )
1451
+
1452
+ def epilog_tmem_copy_and_partition(
1453
+ self,
1454
+ tidx: cutlass.Int32,
1455
+ tAcc: cute.Tensor,
1456
+ gC_mnl: cute.Tensor,
1457
+ epi_tile: cute.Tile,
1458
+ use_2cta_instrs: Union[cutlass.Boolean, bool],
1459
+ ) -> tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1460
+ """
1461
+ Make tiledCopy for tensor memory load, then use it to partition tensor memory (source) and register array (destination).
1462
+
1463
+ :param tidx: The thread index in epilogue warp groups
1464
+ :type tidx: cutlass.Int32
1465
+ :param tAcc: The accumulator tensor to be copied and partitioned
1466
+ :type tAcc: cute.Tensor
1467
+ :param gC_mnl: The global tensor C
1468
+ :type gC_mnl: cute.Tensor
1469
+ :param epi_tile: The epilogue tiler
1470
+ :type epi_tile: cute.Tile
1471
+ :param use_2cta_instrs: Whether use_2cta_instrs is enabled
1472
+ :type use_2cta_instrs: bool
1473
+
1474
+ :return: A tuple containing (tiled_copy_t2r, tTR_tAcc, tTR_rAcc) where:
1475
+ - tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1476
+ - tTR_tAcc: The partitioned accumulator tensor
1477
+ - tTR_rAcc: The accumulated tensor in register used to hold t2r results
1478
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1479
+ """
1480
+ # Make tiledCopy for tensor memory load(t2r)
1481
+ copy_atom_t2r = sm100_utils.get_tmem_load_op(
1482
+ self.cta_tile_shape_mnk,
1483
+ self.c_layout,
1484
+ self.c_dtype,
1485
+ self.acc_dtype,
1486
+ epi_tile,
1487
+ use_2cta_instrs,
1488
+ )
1489
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, STAGE)
1490
+ tAcc_epi = cute.flat_divide(
1491
+ tAcc[((None, None), 0, 0, None)],
1492
+ epi_tile,
1493
+ )
1494
+ # (EPI_TILE_M, EPI_TILE_N)
1495
+ tiled_copy_t2r = tcgen05.make_tmem_copy(
1496
+ copy_atom_t2r, tAcc_epi[(None, None, 0, 0, 0)]
1497
+ )
1498
+
1499
+ thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
1500
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_M, STAGE)
1501
+ tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
1502
+
1503
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1504
+ gC_mnl_epi = cute.flat_divide(
1505
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1506
+ )
1507
+ # (T2R, T2R_M, T2R_N, EPI_M, EPI_N, RestM, RestN, RestL)
1508
+ tTR_gC = thr_copy_t2r.partition_D(gC_mnl_epi)
1509
+ # (T2R, T2R_M, T2R_N)
1510
+ tTR_rAcc = cute.make_fragment(
1511
+ tTR_gC[(None, None, None, 0, 0, 0, 0, 0)].shape, self.acc_dtype
1512
+ )
1513
+ return tiled_copy_t2r, tTR_tAcc, tTR_rAcc
1514
+
1515
+ def epilog_smem_copy_and_partition(
1516
+ self,
1517
+ tiled_copy_t2r: cute.TiledCopy,
1518
+ tTR_rC: cute.Tensor,
1519
+ tidx: cutlass.Int32,
1520
+ sC: cute.Tensor,
1521
+ ) -> tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]:
1522
+ """
1523
+ Make tiledCopy for shared memory store, then use it to partition register array (source) and shared memory (destination).
1524
+
1525
+ :param tiled_copy_t2r: The tiled copy operation for tmem to register copy(t2r)
1526
+ :type tiled_copy_t2r: cute.TiledCopy
1527
+ :param tTR_rC: The partitioned accumulator tensor
1528
+ :type tTR_rC: cute.Tensor
1529
+ :param tidx: The thread index in epilogue warp groups
1530
+ :type tidx: cutlass.Int32
1531
+ :param sC: The shared memory tensor to be copied and partitioned
1532
+ :type sC: cute.Tensor
1533
+
1534
+ :return: A tuple containing (tiled_copy_r2s, tRS_rC, tRS_sC) where:
1535
+ - tiled_copy_r2s: The tiled copy operation for register to smem copy(r2s)
1536
+ - tRS_rC: The partitioned tensor C (register source)
1537
+ - tRS_sC: The partitioned tensor C (smem destination)
1538
+ :rtype: Tuple[cute.TiledCopy, cute.Tensor, cute.Tensor]
1539
+ """
1540
+ copy_atom_r2s = sm100_utils.get_smem_store_op(
1541
+ self.c_layout, self.c_dtype, self.acc_dtype, tiled_copy_t2r
1542
+ )
1543
+ tiled_copy_r2s = cute.make_tiled_copy_D(copy_atom_r2s, tiled_copy_t2r)
1544
+ # (R2S, R2S_M, R2S_N, PIPE_D)
1545
+ thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
1546
+ tRS_sC = thr_copy_r2s.partition_D(sC)
1547
+ # (R2S, R2S_M, R2S_N)
1548
+ tRS_rC = tiled_copy_r2s.retile(tTR_rC)
1549
+ return tiled_copy_r2s, tRS_rC, tRS_sC
1550
+
1551
+ def epilog_gmem_copy_and_partition(
1552
+ self,
1553
+ tma_atom_c: cute.CopyAtom,
1554
+ gC_mnl: cute.Tensor,
1555
+ epi_tile: cute.Tile,
1556
+ sC: cute.Tensor,
1557
+ ) -> tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]:
1558
+ """Make tiledCopy for global memory store, then use it to partition
1559
+ shared memory (source) and global memory (destination) for TMA store version.
1560
+
1561
+ :param tma_atom_c: The TMA copy atom configured for storing tensor C.
1562
+ :type tma_atom_c: cute.CopyAtom
1563
+ :param gC_mnl: The global memory tensor C.
1564
+ :type gC_mnl: cute.Tensor
1565
+ :param epi_tile: The epilogue tiler defining the granularity of the operation.
1566
+ :type epi_tile: cute.Tile
1567
+ :param sC: The shared memory epilogue buffer tensor.
1568
+ :type sC: cute.Tensor
1569
+ :return: A tuple containing:
1570
+ - tma_atom_c: The input TMA copy atom (passed through).
1571
+ - bSG_sC: The source shared memory tensor partitioned for the TMA operation.
1572
+ - tCgC: The destination global memory tensor partitioned for the TMA operation.
1573
+ :rtype: tuple[cute.CopyAtom, cute.Tensor, cute.Tensor]
1574
+ """
1575
+ # (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, RestM, RestN, RestL)
1576
+ gC_epi = cute.flat_divide(
1577
+ gC_mnl[((None, None), 0, 0, None, None, None)], epi_tile
1578
+ )
1579
+ sC_for_tma_partition = cute.group_modes(sC, 0, 2)
1580
+ gC_for_tma_partition = cute.group_modes(gC_epi, 0, 2)
1581
+ # ((ATOM_V, REST_V), EPI_M, EPI_N)
1582
+ # ((ATOM_V, REST_V), EPI_M, EPI_N, RestM, RestN, RestL)
1583
+ bSG_sC, bSG_gC = cpasync.tma_partition(
1584
+ tma_atom_c,
1585
+ 0,
1586
+ cute.make_layout(1),
1587
+ sC_for_tma_partition,
1588
+ gC_for_tma_partition,
1589
+ )
1590
+ return tma_atom_c, bSG_sC, bSG_gC
1591
+
1592
+ @staticmethod
1593
+ def _compute_stages(
1594
+ tiled_mma: cute.TiledMma,
1595
+ mma_tiler_mnk: tuple[int, int, int],
1596
+ a_dtype: type[cutlass.Numeric],
1597
+ b_dtype: type[cutlass.Numeric],
1598
+ epi_tile: cute.Tile,
1599
+ c_dtype: type[cutlass.Numeric],
1600
+ c_layout: utils.LayoutEnum,
1601
+ smem_capacity: int,
1602
+ occupancy: int,
1603
+ ) -> tuple[int, int, int]:
1604
+ """Computes the number of stages for accumulator, A/B operands, and epilogue based on heuristics.
1605
+
1606
+ :param tiled_mma: The tiled MMA object defining the core computation.
1607
+ :type tiled_mma: cute.TiledMma
1608
+ :param mma_tiler_mnk: The shape (M, N, K) of the MMA tiler.
1609
+ :type mma_tiler_mnk: tuple[int, int, int]
1610
+ :param a_dtype: Data type of operand A.
1611
+ :type a_dtype: type[cutlass.Numeric]
1612
+ :param b_dtype: Data type of operand B.
1613
+ :type b_dtype: type[cutlass.Numeric]
1614
+ :param epi_tile: The epilogue tile shape.
1615
+ :type epi_tile: cute.Tile
1616
+ :param c_dtype: Data type of operand C (output).
1617
+ :type c_dtype: type[cutlass.Numeric]
1618
+ :param c_layout: Layout enum of operand C in global memory.
1619
+ :type c_layout: utils.LayoutEnum
1620
+ :param smem_capacity: Total available shared memory capacity in bytes.
1621
+ :type smem_capacity: int
1622
+ :param occupancy: Target number of CTAs per SM (occupancy).
1623
+ :type occupancy: int
1624
+
1625
+ :return: A tuple containing the computed number of stages for:
1626
+ (accumulator stages, A/B operand stages, epilogue stages)
1627
+ :rtype: tuple[int, int, int]
1628
+ """
1629
+ # Default accumulator and epilogue stages
1630
+ num_acc_stage = 2
1631
+ num_epi_stage = 2
1632
+
1633
+ # Calculate smem layout and size for one stage of A, B, and Epilogue
1634
+ a_smem_layout_stage_one = sm100_utils.make_smem_layout_a(
1635
+ tiled_mma,
1636
+ mma_tiler_mnk,
1637
+ a_dtype,
1638
+ 1, # stage=1
1639
+ )
1640
+ b_smem_layout_staged_one = sm100_utils.make_smem_layout_b(
1641
+ tiled_mma,
1642
+ mma_tiler_mnk,
1643
+ b_dtype,
1644
+ 1, # stage=1
1645
+ )
1646
+ epi_smem_layout_staged_one = sm100_utils.make_smem_layout_epi(
1647
+ c_dtype,
1648
+ c_layout,
1649
+ epi_tile,
1650
+ 1, # stage=1
1651
+ )
1652
+ ab_bytes_per_stage = cute.size_in_bytes(
1653
+ a_dtype, a_smem_layout_stage_one
1654
+ ) + cute.size_in_bytes(b_dtype, b_smem_layout_staged_one)
1655
+
1656
+ epi_bytes_per_stage = cute.size_in_bytes(c_dtype, epi_smem_layout_staged_one)
1657
+ epi_bytes = epi_bytes_per_stage * num_epi_stage
1658
+
1659
+ # Calculate A/B stages:
1660
+ # Start with total smem per CTA (capacity / occupancy)
1661
+ # Subtract reserved bytes and initial epilogue bytes
1662
+ # Divide remaining by bytes needed per A/B stage
1663
+ num_ab_stage = (
1664
+ smem_capacity // occupancy
1665
+ - GroupedGemmKernel.reserved_smem_bytes
1666
+ - epi_bytes
1667
+ ) // ab_bytes_per_stage
1668
+
1669
+ # Refine epilogue stages:
1670
+ # Calculate remaining smem after allocating for A/B stages and reserved bytes
1671
+ # Add remaining unused smem to epilogue
1672
+ remaining_smem = (
1673
+ smem_capacity
1674
+ - occupancy * ab_bytes_per_stage * num_ab_stage
1675
+ - occupancy * (GroupedGemmKernel.reserved_smem_bytes + epi_bytes)
1676
+ )
1677
+ num_epi_stage += remaining_smem // (occupancy * epi_bytes_per_stage)
1678
+ return num_acc_stage, num_ab_stage, num_epi_stage
1679
+
1680
+ @staticmethod
1681
+ def _compute_grid(
1682
+ total_num_clusters: int,
1683
+ cluster_shape_mn: tuple[int, int],
1684
+ max_active_clusters: cutlass.Constexpr[int],
1685
+ ) -> tuple[utils.PersistentTileSchedulerParams, tuple[int, int, int]]:
1686
+ """Compute tile scheduler parameters and grid shape for grouped GEMM operations.
1687
+
1688
+ :param total_num_clusters: Total number of clusters to process across all groups.
1689
+ :type total_num_clusters: int
1690
+ :param cluster_shape_mn: Shape of each cluster in M, N dimensions.
1691
+ :type cluster_shape_mn: tuple[int, int]
1692
+ :param max_active_clusters: Maximum number of active clusters.
1693
+ :type max_active_clusters: cutlass.Constexpr[int]
1694
+
1695
+ :return: A tuple containing:
1696
+ - tile_sched_params: Parameters for the persistent tile scheduler.
1697
+ - grid: Grid shape for kernel launch.
1698
+ :rtype: tuple[utils.PersistentTileSchedulerParams, tuple[int, ...]]
1699
+ """
1700
+ # Create problem shape with M, N dimensions from cluster shape
1701
+ # and L dimension representing the total number of clusters.
1702
+ problem_shape_ntile_mnl = (
1703
+ cluster_shape_mn[0],
1704
+ cluster_shape_mn[1],
1705
+ cutlass.Int32(total_num_clusters),
1706
+ )
1707
+
1708
+ tile_sched_params = utils.PersistentTileSchedulerParams(
1709
+ problem_shape_ntile_mnl, (*cluster_shape_mn, 1)
1710
+ )
1711
+
1712
+ grid = utils.StaticPersistentTileScheduler.get_grid_shape(
1713
+ tile_sched_params, max_active_clusters
1714
+ )
1715
+
1716
+ return tile_sched_params, grid
1717
+
1718
+ @staticmethod
1719
+ def _get_mbar_smem_bytes(**kwargs_stages: int) -> int:
1720
+ """Calculate shared memory consumption for memory barriers based on provided stages.
1721
+
1722
+ Each stage requires 2 barriers, and each barrier consumes 8 bytes of shared memory.
1723
+ The total consumption is the sum across all provided stages. This function calculates the total
1724
+ shared memory needed for these barriers.
1725
+
1726
+ :param kwargs_stages: Variable keyword arguments where each key is a stage name
1727
+ (e.g., num_acc_stage, num_ab_stage) and each value is the
1728
+ number of stages of that type.
1729
+ :type kwargs_stages: int
1730
+ :return: Total shared memory bytes required for all memory barriers.
1731
+ :rtype: int
1732
+ """
1733
+ num_barriers_per_stage = 2
1734
+ num_bytes_per_barrier = 8
1735
+ mbar_smem_consumption = sum(
1736
+ [
1737
+ num_barriers_per_stage * num_bytes_per_barrier * stage
1738
+ for stage in kwargs_stages.values()
1739
+ ]
1740
+ )
1741
+ return mbar_smem_consumption
1742
+
1743
+ @staticmethod
1744
+ def _get_tensormap_smem_bytes(
1745
+ tensormap_update_mode: utils.TensorMapUpdateMode,
1746
+ ) -> int:
1747
+ """Get the SMEM consumption for the tensormap buffer based on the update mode.
1748
+
1749
+ :param tensormap_update_mode: Specifies whether tensormaps are updated in GMEM or SMEM.
1750
+ :type tensormap_update_mode: utils.TensorMapUpdateMode
1751
+ :return: The shared memory bytes required for the tensormap buffer. Returns 0 if mode is GMEM.
1752
+ :rtype: int
1753
+ :raises ValueError: If an invalid tensormap update mode is provided.
1754
+ """
1755
+ if tensormap_update_mode == utils.TensorMapUpdateMode.GMEM:
1756
+ return 0
1757
+ elif tensormap_update_mode == utils.TensorMapUpdateMode.SMEM:
1758
+ return (
1759
+ GroupedGemmKernel.bytes_per_tensormap * GroupedGemmKernel.num_tensormaps
1760
+ )
1761
+ else:
1762
+ raise ValueError(f"Invalid tensormap update mode: {tensormap_update_mode}")
1763
+
1764
+ @staticmethod
1765
+ def _get_tensor_smem_bytes(
1766
+ a_smem_layout_staged: cute.Layout,
1767
+ a_dtype: Type[cutlass.Numeric],
1768
+ b_smem_layout_staged: cute.Layout,
1769
+ b_dtype: Type[cutlass.Numeric],
1770
+ epi_smem_layout_staged: cute.Layout,
1771
+ c_dtype: Type[cutlass.Numeric],
1772
+ ) -> int:
1773
+ """Compute the total SMEM consumption for tensor A, B and C."""
1774
+ ab_bytes = cute.size_in_bytes(
1775
+ a_dtype, a_smem_layout_staged
1776
+ ) + cute.size_in_bytes(b_dtype, b_smem_layout_staged)
1777
+
1778
+ epi_bytes = cute.size_in_bytes(c_dtype, epi_smem_layout_staged)
1779
+ return ab_bytes + epi_bytes
1780
+
1781
+ @staticmethod
1782
+ def _compute_num_tmem_alloc_cols(
1783
+ tiled_mma: cute.TiledMma,
1784
+ mma_tiler: tuple[int, int, int],
1785
+ num_acc_stage: int,
1786
+ ) -> int:
1787
+ """
1788
+ Compute the number of tensor memory allocation columns.
1789
+
1790
+ :param tiled_mma: The tiled MMA object defining the core computation.
1791
+ :type tiled_mma: cute.TiledMma
1792
+ :param mma_tiler: The shape (M, N, K) of the MMA tile.
1793
+ :type mma_tiler: tuple[int, int, int]
1794
+ :param acc_stage: The stage of the accumulator tensor.
1795
+ :type acc_stage: int
1796
+
1797
+ :return: The number of tensor memory allocation columns.
1798
+ :rtype: int
1799
+ """
1800
+ acc_shape = tiled_mma.partition_shape_C(mma_tiler[:2])
1801
+ tCtAcc_fake = tiled_mma.make_fragment_C(cute.append(acc_shape, num_acc_stage))
1802
+ num_tmem_alloc_cols = utils.get_num_tmem_alloc_cols(tCtAcc_fake)
1803
+
1804
+ return num_tmem_alloc_cols
1805
+
1806
+ # Size of smem we reserved for mbarrier, tensor memory management and tensormap update
1807
+ reserved_smem_bytes = 1024
1808
+ bytes_per_tensormap = 128
1809
+ num_tensormaps = 3
1810
+ # size of smem used for tensor memory management
1811
+ tensor_memory_management_bytes = 12
1812
+
1813
+
1814
+ # Create tensor and return the pointer, tensor, and stride
1815
+ def create_tensor_and_stride(
1816
+ l: int,
1817
+ mode0: int,
1818
+ mode1: int,
1819
+ is_mode0_major: bool,
1820
+ dtype: type[cutlass.Numeric],
1821
+ is_dynamic_layout: bool = True,
1822
+ torch_tensor_cpu: torch.Tensor = None,
1823
+ ) -> tuple[int, torch.Tensor, cute.Tensor, torch.Tensor, tuple[int, int]]:
1824
+ """Create a GPU tensor from scratch or based on an existing CPU tensor.
1825
+
1826
+ :param torch_tensor_cpu: Optional existing CPU tensor to reuse. If None, creates a new one.
1827
+ :type torch_tensor_cpu: torch.Tensor, optional
1828
+ """
1829
+ if torch_tensor_cpu is None:
1830
+ # Create new CPU tensor
1831
+ torch_tensor_cpu = cutlass_torch.matrix(l, mode0, mode1, is_mode0_major, dtype)
1832
+
1833
+ # Create GPU tensor from CPU tensor (new or existing)
1834
+ cute_tensor, torch_tensor = cutlass_torch.cute_tensor_like(
1835
+ torch_tensor_cpu, dtype, is_dynamic_layout, assumed_align=16
1836
+ )
1837
+ return (
1838
+ torch_tensor.data_ptr(),
1839
+ torch_tensor,
1840
+ cute_tensor,
1841
+ torch_tensor_cpu,
1842
+ torch_tensor.stride()[:-1],
1843
+ )
1844
+
1845
+
1846
+ def create_tensors_for_all_groups(
1847
+ problem_sizes_mnkl: List[tuple[int, int, int, int]],
1848
+ ab_dtype: Type[cutlass.Numeric],
1849
+ c_dtype: Type[cutlass.Numeric],
1850
+ a_major: str,
1851
+ b_major: str,
1852
+ c_major: str,
1853
+ torch_fp32_tensors_abc: List[List[torch.Tensor]] = None,
1854
+ ) -> tuple[
1855
+ List[List[int]],
1856
+ List[List[torch.Tensor]],
1857
+ List[tuple],
1858
+ List[List[tuple]],
1859
+ List[List[torch.Tensor]],
1860
+ ]:
1861
+ if torch_fp32_tensors_abc is not None and len(torch_fp32_tensors_abc) != len(
1862
+ problem_sizes_mnkl
1863
+ ):
1864
+ raise ValueError("torch_fp32_tensors_abc must have one entry per group")
1865
+
1866
+ # Initialize lists to store tensors for all groups
1867
+ new_torch_fp32_tensors_abc = (
1868
+ [] if torch_fp32_tensors_abc is None else torch_fp32_tensors_abc
1869
+ )
1870
+ torch_tensors_abc = []
1871
+ cute_tensors_abc = []
1872
+ strides_abc = []
1873
+ ptrs_abc = []
1874
+
1875
+ # Iterate through all groups and create tensors for each group
1876
+ for group_idx, (m, n, k, l) in enumerate(problem_sizes_mnkl):
1877
+ # Get existing CPU tensors if available, otherwise None
1878
+ existing_cpu_a = (
1879
+ torch_fp32_tensors_abc[group_idx][0] if torch_fp32_tensors_abc else None
1880
+ )
1881
+ existing_cpu_b = (
1882
+ torch_fp32_tensors_abc[group_idx][1] if torch_fp32_tensors_abc else None
1883
+ )
1884
+ existing_cpu_c = (
1885
+ torch_fp32_tensors_abc[group_idx][2] if torch_fp32_tensors_abc else None
1886
+ )
1887
+
1888
+ # Create tensors (reusing CPU tensors if provided)
1889
+ (
1890
+ ptr_a,
1891
+ torch_tensor_a,
1892
+ cute_tensor_a,
1893
+ tensor_fp32_a,
1894
+ stride_mk_a,
1895
+ ) = create_tensor_and_stride(
1896
+ l, m, k, a_major == "m", ab_dtype, torch_tensor_cpu=existing_cpu_a
1897
+ )
1898
+ (
1899
+ ptr_b,
1900
+ torch_tensor_b,
1901
+ cute_tensor_b,
1902
+ tensor_fp32_b,
1903
+ stride_nk_b,
1904
+ ) = create_tensor_and_stride(
1905
+ l, n, k, b_major == "n", ab_dtype, torch_tensor_cpu=existing_cpu_b
1906
+ )
1907
+ (
1908
+ ptr_c,
1909
+ torch_tensor_c,
1910
+ cute_tensor_c,
1911
+ tensor_fp32_c,
1912
+ stride_mn_c,
1913
+ ) = create_tensor_and_stride(
1914
+ l, m, n, c_major == "m", c_dtype, torch_tensor_cpu=existing_cpu_c
1915
+ )
1916
+
1917
+ # Only append to new_torch_fp32_tensors_abc if we created new CPU tensors
1918
+ if torch_fp32_tensors_abc is None:
1919
+ new_torch_fp32_tensors_abc.append(
1920
+ [tensor_fp32_a, tensor_fp32_b, tensor_fp32_c]
1921
+ )
1922
+
1923
+ ptrs_abc.append([ptr_a, ptr_b, ptr_c])
1924
+ torch_tensors_abc.append([torch_tensor_a, torch_tensor_b, torch_tensor_c])
1925
+ strides_abc.append([stride_mk_a, stride_nk_b, stride_mn_c])
1926
+ cute_tensors_abc.append(
1927
+ (
1928
+ cute_tensor_a,
1929
+ cute_tensor_b,
1930
+ cute_tensor_c,
1931
+ )
1932
+ )
1933
+
1934
+ return (
1935
+ ptrs_abc,
1936
+ torch_tensors_abc,
1937
+ cute_tensors_abc,
1938
+ strides_abc,
1939
+ new_torch_fp32_tensors_abc,
1940
+ )
1941
+
1942
+
1943
+ def run(
1944
+ num_groups: int,
1945
+ problem_sizes_mnkl: tuple[int, int, int, int],
1946
+ ab_dtype: Type[cutlass.Numeric],
1947
+ c_dtype: Type[cutlass.Numeric],
1948
+ acc_dtype: Type[cutlass.Numeric],
1949
+ a_major: str,
1950
+ b_major: str,
1951
+ c_major: str,
1952
+ mma_tiler_mn: tuple[int, int],
1953
+ cluster_shape_mn: tuple[int, int],
1954
+ use_2cta_instrs: bool,
1955
+ tensormap_update_mode: utils.TensorMapUpdateMode,
1956
+ tolerance: float,
1957
+ warmup_iterations: int,
1958
+ iterations: int,
1959
+ skip_ref_check: bool,
1960
+ use_cold_l2: bool = False,
1961
+ **kwargs,
1962
+ ):
1963
+ """Run grouped GEMM example with specified configurations.
1964
+
1965
+ :param use_cold_l2: Whether to use circular buffer strategy to ensure cold L2 cache, defaults to False
1966
+ :type use_cold_l2: bool, optional
1967
+ :return: Execution time of the GEMM kernel in microseconds
1968
+ :rtype: float
1969
+ """
1970
+ print(f"Running Blackwell Grouped GEMM test with:")
1971
+ print(f"{num_groups} groups")
1972
+ for i, (m, n, k, l) in enumerate(problem_sizes_mnkl):
1973
+ print(f"Group {i}: {m}x{n}x{k}x{l}")
1974
+ print(f"AB dtype: {ab_dtype}, C dtype: {c_dtype}, Acc dtype: {acc_dtype}")
1975
+ print(f"Matrix majors - A: {a_major}, B: {b_major}, C: {c_major}")
1976
+ print(f"Mma Tiler (M, N): {mma_tiler_mn}, Cluster Shape (M, N): {cluster_shape_mn}")
1977
+ print(f"2CTA MMA instructions: {'True' if use_2cta_instrs else 'False'}")
1978
+ print(f"Tensor map update mode: {tensormap_update_mode}")
1979
+ print(f"Tolerance: {tolerance}")
1980
+ print(f"Warmup iterations: {warmup_iterations}")
1981
+ print(f"Iterations: {iterations}")
1982
+ print(f"Skip reference checking: {skip_ref_check}")
1983
+ print(f"Use cold L2: {'True' if use_cold_l2 else 'False'}")
1984
+
1985
+ # Skip unsupported types
1986
+ if ab_dtype not in {
1987
+ cutlass.Float16,
1988
+ cutlass.BFloat16,
1989
+ }:
1990
+ raise ValueError(f"Skip unsupported ab_dtype {ab_dtype}")
1991
+ if c_dtype not in {cutlass.Float16, cutlass.BFloat16, cutlass.Float32}:
1992
+ raise ValueError(f"Skip unsupported c_dtype {c_dtype}")
1993
+ # Skip unsupported acc dtype
1994
+ if acc_dtype not in {cutlass.Float32, cutlass.Float16}:
1995
+ raise ValueError(f"Skip unsupported acc_dtype {acc_dtype}")
1996
+ # Skip invalid ab_dtype and acc_dtype combination
1997
+ if ab_dtype == cutlass.BFloat16 and acc_dtype == cutlass.Float16:
1998
+ raise ValueError("Skip invalid ab_dtype and acc_dtype combination")
1999
+ # Skip invalid mma tile shape
2000
+ if not (
2001
+ (not use_2cta_instrs and mma_tiler_mn[0] in [64, 128])
2002
+ or (use_2cta_instrs and mma_tiler_mn[0] in [128, 256])
2003
+ ):
2004
+ raise ValueError(f"Skip invalid mma tiler M {mma_tiler_mn[0]}")
2005
+ if mma_tiler_mn[1] not in range(32, 257, 32):
2006
+ raise ValueError(f"Skip invalid mma tiler N {mma_tiler_mn[1]}")
2007
+ # Skip illegal cluster shape
2008
+ if cluster_shape_mn[0] % (2 if use_2cta_instrs else 1) != 0:
2009
+ raise ValueError(
2010
+ f"cluster_shape_m need align with use_2cta_instrs config {cluster_shape_mn}"
2011
+ )
2012
+ # Skip invalid cluster shape
2013
+ is_power_of_2 = lambda x: x > 0 and (x & (x - 1)) == 0
2014
+ if (
2015
+ cluster_shape_mn[0] * cluster_shape_mn[1] > 16
2016
+ or cluster_shape_mn[0] <= 0
2017
+ or cluster_shape_mn[1] <= 0
2018
+ or not is_power_of_2(cluster_shape_mn[0])
2019
+ or not is_power_of_2(cluster_shape_mn[1])
2020
+ ):
2021
+ raise ValueError(f"Skip invalid cluster shape {cluster_shape_mn}")
2022
+
2023
+ # Skip illegal problem shape for load/store alignment
2024
+ def check_contigous_16B_alignment(dtype, is_mode0_major, tensor_shape):
2025
+ major_mode_idx = 0 if is_mode0_major else 1
2026
+ num_major_elements = tensor_shape[major_mode_idx]
2027
+ num_contiguous_elements = 16 * 8 // dtype.width
2028
+ return num_major_elements % num_contiguous_elements == 0
2029
+
2030
+ if (
2031
+ not check_contigous_16B_alignment(ab_dtype, a_major == "m", (m, k, l))
2032
+ or not check_contigous_16B_alignment(ab_dtype, b_major == "n", (n, k, l))
2033
+ or not check_contigous_16B_alignment(c_dtype, c_major == "m", (m, n, l))
2034
+ ):
2035
+ raise ValueError("Skip invalid problem alignment")
2036
+ if not torch.cuda.is_available():
2037
+ raise RuntimeError("GPU is required to run this example!")
2038
+
2039
+ # Create tensors for all groups using the new function
2040
+ (
2041
+ ptrs_abc,
2042
+ torch_tensors_abc,
2043
+ cute_tensors_abc,
2044
+ strides_abc,
2045
+ torch_fp32_tensors_abc,
2046
+ ) = create_tensors_for_all_groups(
2047
+ problem_sizes_mnkl,
2048
+ ab_dtype,
2049
+ c_dtype,
2050
+ a_major,
2051
+ b_major,
2052
+ c_major,
2053
+ )
2054
+
2055
+ # Choose A, B, C with the smallest size to create initial tensormaps
2056
+ key_size_a = lambda item: item[1][0] * item[1][2]
2057
+ key_size_b = lambda item: item[1][1] * item[1][2]
2058
+ key_size_c = lambda item: item[1][0] * item[1][1]
2059
+ # Find the indices of the groups with the smallest tensor sizes
2060
+ min_a_idx, _ = min(enumerate(problem_sizes_mnkl), key=key_size_a)
2061
+ min_b_idx, _ = min(enumerate(problem_sizes_mnkl), key=key_size_b)
2062
+ min_c_idx, _ = min(enumerate(problem_sizes_mnkl), key=key_size_c)
2063
+ initial_cute_tensors_abc = [
2064
+ cute_tensors_abc[min_a_idx][0], # A with smallest (m, k)
2065
+ cute_tensors_abc[min_b_idx][1], # B with smallest (n, k)
2066
+ cute_tensors_abc[min_c_idx][2], # C with smallest (m, n)
2067
+ ]
2068
+
2069
+ hardware_info = utils.HardwareInfo()
2070
+ sm_count = hardware_info.get_max_active_clusters(1)
2071
+ max_active_clusters = hardware_info.get_max_active_clusters(
2072
+ cluster_shape_mn[0] * cluster_shape_mn[1]
2073
+ )
2074
+ # Prepare tensormap buffer for each SM
2075
+ num_tensormap_buffers = sm_count
2076
+ tensormap_shape = (
2077
+ num_tensormap_buffers,
2078
+ GroupedGemmKernel.num_tensormaps,
2079
+ GroupedGemmKernel.bytes_per_tensormap // 8,
2080
+ )
2081
+ tensor_of_tensormap, tensor_of_tensormap_torch = cutlass_torch.cute_tensor_like(
2082
+ torch.empty(tensormap_shape, dtype=torch.int64),
2083
+ cutlass.Int64,
2084
+ is_dynamic_layout=False,
2085
+ )
2086
+
2087
+ grouped_gemm = GroupedGemmKernel(
2088
+ acc_dtype,
2089
+ use_2cta_instrs,
2090
+ mma_tiler_mn,
2091
+ cluster_shape_mn,
2092
+ tensormap_update_mode,
2093
+ )
2094
+
2095
+ # layout (num_groups, 4):(4, 1)
2096
+ (
2097
+ tensor_of_dim_size_mnkl,
2098
+ tensor_of_dim_size_mnkl_torch,
2099
+ ) = cutlass_torch.cute_tensor_like(
2100
+ torch.tensor(problem_sizes_mnkl, dtype=torch.int32),
2101
+ cutlass.Int32,
2102
+ is_dynamic_layout=False,
2103
+ assumed_align=16,
2104
+ )
2105
+ # layout (num_groups, 3, 2):(6, 2, 1)
2106
+ tensor_of_strides_abc, tensor_of_strides_abc_torch = cutlass_torch.cute_tensor_like(
2107
+ torch.tensor(strides_abc, dtype=torch.int32),
2108
+ cutlass.Int32,
2109
+ is_dynamic_layout=False,
2110
+ assumed_align=16,
2111
+ )
2112
+
2113
+ # layout (num_groups,3):(3, 1)
2114
+ tensor_of_ptrs_abc, tensor_of_ptrs_abc_torch = cutlass_torch.cute_tensor_like(
2115
+ torch.tensor(ptrs_abc, dtype=torch.int64),
2116
+ cutlass.Int64,
2117
+ is_dynamic_layout=False,
2118
+ assumed_align=16,
2119
+ )
2120
+
2121
+ # Compute total number of cluster tiles we need to compute for given grouped GEMM problem
2122
+ def compute_total_num_clusters(
2123
+ problem_sizes_mnkl: List[tuple[int, int, int, int]],
2124
+ cluster_tile_shape_mn: tuple[int, int],
2125
+ ) -> int:
2126
+ total_num_clusters = 0
2127
+ for m, n, _, _ in problem_sizes_mnkl:
2128
+ num_clusters_mn = tuple(
2129
+ (x + y - 1) // y for x, y in zip((m, n), cluster_tile_shape_mn)
2130
+ )
2131
+ total_num_clusters += functools.reduce(lambda x, y: x * y, num_clusters_mn)
2132
+ return total_num_clusters
2133
+
2134
+ # Compute cluster tile shape
2135
+ def compute_cluster_tile_shape(
2136
+ mma_tiler_mn: tuple[int, int],
2137
+ cluster_shape_mn: tuple[int, int],
2138
+ use_2cta_instrs: bool,
2139
+ ) -> tuple[int, int]:
2140
+ cta_tile_shape_mn = list(mma_tiler_mn)
2141
+ if use_2cta_instrs:
2142
+ cta_tile_shape_mn[0] = cta_tile_shape_mn[0] // 2
2143
+ return tuple(x * y for x, y in zip(cta_tile_shape_mn, cluster_shape_mn))
2144
+
2145
+ cluster_tile_shape_mn = compute_cluster_tile_shape(
2146
+ mma_tiler_mn, cluster_shape_mn, use_2cta_instrs
2147
+ )
2148
+ total_num_clusters = compute_total_num_clusters(
2149
+ problem_sizes_mnkl, cluster_tile_shape_mn
2150
+ )
2151
+
2152
+ # Initialize Stream
2153
+ current_stream = cutlass_torch.default_stream()
2154
+
2155
+ # Compile grouped GEMM kernel
2156
+ compiled_grouped_gemm = cute.compile(
2157
+ grouped_gemm,
2158
+ initial_cute_tensors_abc[0],
2159
+ initial_cute_tensors_abc[1],
2160
+ initial_cute_tensors_abc[2],
2161
+ num_groups,
2162
+ tensor_of_dim_size_mnkl,
2163
+ tensor_of_strides_abc,
2164
+ tensor_of_ptrs_abc,
2165
+ total_num_clusters,
2166
+ tensor_of_tensormap,
2167
+ max_active_clusters,
2168
+ current_stream,
2169
+ )
2170
+
2171
+ if not skip_ref_check:
2172
+ compiled_grouped_gemm(
2173
+ initial_cute_tensors_abc[0],
2174
+ initial_cute_tensors_abc[1],
2175
+ initial_cute_tensors_abc[2],
2176
+ tensor_of_dim_size_mnkl,
2177
+ tensor_of_strides_abc,
2178
+ tensor_of_ptrs_abc,
2179
+ tensor_of_tensormap,
2180
+ current_stream,
2181
+ )
2182
+
2183
+ # Compute reference result
2184
+ for i, (a, b, c) in enumerate(torch_tensors_abc):
2185
+ ref = torch.einsum(
2186
+ "mkl,nkl->mnl",
2187
+ a.cpu().to(dtype=torch.float32),
2188
+ b.cpu().to(dtype=torch.float32),
2189
+ )
2190
+ print(f"checking group {i}")
2191
+ torch.testing.assert_close(
2192
+ c.cpu(),
2193
+ ref.to(cutlass_torch.dtype(c_dtype)),
2194
+ atol=tolerance,
2195
+ rtol=1e-05,
2196
+ )
2197
+
2198
+ def generate_tensors():
2199
+ # Reuse existing CPU tensors and create new GPU tensors from them
2200
+ (
2201
+ ptrs_abc_workspace,
2202
+ torch_tensors_abc_workspace,
2203
+ cute_tensors_abc_workspace,
2204
+ strides_abc_workspace,
2205
+ _,
2206
+ ) = create_tensors_for_all_groups(
2207
+ problem_sizes_mnkl,
2208
+ ab_dtype,
2209
+ c_dtype,
2210
+ a_major,
2211
+ b_major,
2212
+ c_major,
2213
+ torch_fp32_tensors_abc,
2214
+ )
2215
+
2216
+ initial_cute_tensors_abc_workspace = [
2217
+ cute_tensors_abc_workspace[min_a_idx][0], # A with smallest (m, k)
2218
+ cute_tensors_abc_workspace[min_b_idx][1], # B with smallest (n, k)
2219
+ cute_tensors_abc_workspace[min_c_idx][2], # C with smallest (m, n)
2220
+ ]
2221
+
2222
+ # Create new tensors for this workspace
2223
+ tensor_of_strides_abc_workspace, _ = cutlass_torch.cute_tensor_like(
2224
+ torch.tensor(strides_abc_workspace, dtype=torch.int32),
2225
+ cutlass.Int32,
2226
+ is_dynamic_layout=False,
2227
+ assumed_align=16,
2228
+ )
2229
+
2230
+ tensor_of_ptrs_abc_workspace, _ = cutlass_torch.cute_tensor_like(
2231
+ torch.tensor(ptrs_abc_workspace, dtype=torch.int64),
2232
+ cutlass.Int64,
2233
+ is_dynamic_layout=False,
2234
+ assumed_align=16,
2235
+ )
2236
+
2237
+ tensormap_workspace, _ = cutlass_torch.cute_tensor_like(
2238
+ torch.empty(tensormap_shape, dtype=torch.int64),
2239
+ cutlass.Int64,
2240
+ is_dynamic_layout=False,
2241
+ )
2242
+
2243
+ return testing.JitArguments(
2244
+ initial_cute_tensors_abc_workspace[0],
2245
+ initial_cute_tensors_abc_workspace[1],
2246
+ initial_cute_tensors_abc_workspace[2],
2247
+ tensor_of_dim_size_mnkl,
2248
+ tensor_of_strides_abc_workspace,
2249
+ tensor_of_ptrs_abc_workspace,
2250
+ tensormap_workspace,
2251
+ current_stream,
2252
+ )
2253
+
2254
+ workspace_count = 1
2255
+ if use_cold_l2:
2256
+ one_workspace_bytes = (
2257
+ sum(
2258
+ [
2259
+ sum(
2260
+ [
2261
+ torch_tensor.numel() * torch_tensor.element_size()
2262
+ for torch_tensor in group_tensors
2263
+ ]
2264
+ )
2265
+ for group_tensors in torch_tensors_abc
2266
+ ]
2267
+ )
2268
+ +
2269
+ # Add size of strides tensor
2270
+ tensor_of_strides_abc_torch.numel()
2271
+ * tensor_of_strides_abc_torch.element_size()
2272
+ +
2273
+ # Add size of ptrs tensor
2274
+ tensor_of_ptrs_abc_torch.numel() * tensor_of_ptrs_abc_torch.element_size()
2275
+ +
2276
+ # Add size of tensormap tensor
2277
+ tensor_of_tensormap_torch.numel() * tensor_of_tensormap_torch.element_size()
2278
+ )
2279
+ workspace_count = testing.get_workspace_count(
2280
+ one_workspace_bytes, warmup_iterations, iterations
2281
+ )
2282
+
2283
+ exec_time = testing.benchmark(
2284
+ compiled_grouped_gemm,
2285
+ workspace_generator=generate_tensors,
2286
+ workspace_count=workspace_count,
2287
+ stream=current_stream,
2288
+ warmup_iterations=warmup_iterations,
2289
+ iterations=iterations,
2290
+ )
2291
+
2292
+ return exec_time # Return execution time in microseconds
2293
+
2294
+
2295
+ if __name__ == "__main__":
2296
+
2297
+ def parse_comma_separated_ints(s: str) -> tuple[int, ...]:
2298
+ try:
2299
+ return tuple(int(x.strip()) for x in s.split(","))
2300
+ except ValueError:
2301
+ raise argparse.ArgumentTypeError(
2302
+ "Invalid format. Expected comma-separated integers."
2303
+ )
2304
+
2305
+ def parse_comma_separated_tuples(s: str) -> List[tuple[int, ...]]:
2306
+ if s.strip().startswith("("):
2307
+ # Split on ),( to separate tuples
2308
+ tuples = s.strip("()").split("),(")
2309
+ result = []
2310
+ tuple_len = None
2311
+
2312
+ for t in tuples:
2313
+ # Parse individual tuple
2314
+ nums = [int(x.strip()) for x in t.split(",")]
2315
+
2316
+ # Validate tuple length consistency
2317
+ if tuple_len is None:
2318
+ tuple_len = len(nums)
2319
+ elif len(nums) != tuple_len:
2320
+ raise argparse.ArgumentTypeError(
2321
+ "All tuples must have the same length"
2322
+ )
2323
+
2324
+ result.append(tuple(nums))
2325
+ return result
2326
+
2327
+ raise argparse.ArgumentTypeError(
2328
+ "Invalid format. Expected comma-separated integers or list of tuples"
2329
+ )
2330
+
2331
+ parser = argparse.ArgumentParser(
2332
+ description="Example of Grouped GEMM on Blackwell."
2333
+ )
2334
+ parser.add_argument(
2335
+ "--num_groups",
2336
+ type=int,
2337
+ default=2,
2338
+ help="Number of groups",
2339
+ )
2340
+ parser.add_argument(
2341
+ "--problem_sizes_mnkl",
2342
+ type=parse_comma_separated_tuples,
2343
+ default=((128, 128, 128, 1), (128, 128, 128, 1)),
2344
+ help="a tuple of problem sizes for each group (comma-separated tuples)",
2345
+ )
2346
+ parser.add_argument(
2347
+ "--mma_tiler_mn",
2348
+ type=parse_comma_separated_ints,
2349
+ default=(128, 128),
2350
+ help="Mma tile shape (comma-separated)",
2351
+ )
2352
+ parser.add_argument(
2353
+ "--cluster_shape_mn",
2354
+ type=parse_comma_separated_ints,
2355
+ default=(1, 1),
2356
+ help="Cluster shape (comma-separated)",
2357
+ )
2358
+ parser.add_argument(
2359
+ "--tensormap_update_mode",
2360
+ type=str,
2361
+ default="SMEM",
2362
+ help="Tensor map update mode",
2363
+ )
2364
+ parser.add_argument("--ab_dtype", type=cutlass.dtype, default=cutlass.Float16)
2365
+ parser.add_argument("--c_dtype", type=cutlass.dtype, default=cutlass.Float16)
2366
+ parser.add_argument("--acc_dtype", type=cutlass.dtype, default=cutlass.Float32)
2367
+ parser.add_argument(
2368
+ "--use_2cta_instrs",
2369
+ action="store_true",
2370
+ help="Enable 2CTA MMA instructions feature",
2371
+ )
2372
+ parser.add_argument("--a_major", choices=["k", "m"], type=str, default="k")
2373
+ parser.add_argument("--b_major", choices=["k", "n"], type=str, default="k")
2374
+ parser.add_argument("--c_major", choices=["n", "m"], type=str, default="n")
2375
+ parser.add_argument(
2376
+ "--tolerance", type=float, default=1e-01, help="Tolerance for validation"
2377
+ )
2378
+ parser.add_argument(
2379
+ "--warmup_iterations", type=int, default=0, help="Warmup iterations"
2380
+ )
2381
+ parser.add_argument(
2382
+ "--iterations",
2383
+ type=int,
2384
+ default=1,
2385
+ help="Number of iterations to run the kernel",
2386
+ )
2387
+ parser.add_argument(
2388
+ "--skip_ref_check", action="store_true", help="Skip reference checking"
2389
+ )
2390
+ parser.add_argument(
2391
+ "--use_cold_l2",
2392
+ action="store_true",
2393
+ default=False,
2394
+ help="Use circular buffer tensor sets to ensure L2 cold cache",
2395
+ )
2396
+
2397
+ args = parser.parse_args()
2398
+
2399
+ if (
2400
+ len(args.problem_sizes_mnkl) != 0
2401
+ and len(args.problem_sizes_mnkl) != args.num_groups
2402
+ ):
2403
+ parser.error("--problem_sizes_mnkl must contain exactly num_groups tuples")
2404
+
2405
+ # l mode must be 1 for all groups
2406
+ for _, _, _, l in args.problem_sizes_mnkl:
2407
+ if l != 1:
2408
+ parser.error("l must be 1 for all groups")
2409
+
2410
+ if len(args.mma_tiler_mn) != 2:
2411
+ parser.error("--mma_tiler_mn must contain exactly 2 values")
2412
+
2413
+ if len(args.cluster_shape_mn) != 2:
2414
+ parser.error("--cluster_shape_mn must contain exactly 2 values")
2415
+
2416
+ if args.tensormap_update_mode not in ["GMEM", "SMEM"]:
2417
+ parser.error("--tensormap_update_mode must be GMEM or SMEM")
2418
+
2419
+ if args.tensormap_update_mode == "GMEM":
2420
+ tensormap_update_mode = utils.TensorMapUpdateMode.GMEM
2421
+ else:
2422
+ tensormap_update_mode = utils.TensorMapUpdateMode.SMEM
2423
+
2424
+ torch.manual_seed(2025)
2425
+
2426
+ run(
2427
+ args.num_groups,
2428
+ args.problem_sizes_mnkl,
2429
+ args.ab_dtype,
2430
+ args.c_dtype,
2431
+ args.acc_dtype,
2432
+ args.a_major,
2433
+ args.b_major,
2434
+ args.c_major,
2435
+ args.mma_tiler_mn,
2436
+ args.cluster_shape_mn,
2437
+ args.use_2cta_instrs,
2438
+ tensormap_update_mode,
2439
+ args.tolerance,
2440
+ args.warmup_iterations,
2441
+ args.iterations,
2442
+ args.skip_ref_check,
2443
+ args.use_cold_l2,
2444
+ )
2445
+ print("PASS")
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd.py ADDED
The diff for this file is too large to render. See raw diff
 
build/torch210-cxx11-cu130-aarch64-linux/include/third-party/cutlass/examples/python/CuTeDSL/blackwell/mamba2_ssd/mamba2_ssd_reference.py ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+
14
+ # 3. Neither the name of the copyright holder nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ import torch
30
+ import torch.nn.functional as F
31
+
32
+
33
+ def ssd_reference_fp32_all(x, a, delta, B, C, Y_out, Fstate_out, D, has_d, d_has_hdim):
34
+ """
35
+ Rearrange tensor dimensions from cuda layout to reference layout, then directly call TriDao's ssd implementation
36
+ Arguments:
37
+ X/x: (D, L, C, H, B):(C*L, 1, L, D*C*L, H*D*C*L)
38
+ A/delta: (L, C, H, B):(1, L, C*L, H*C*L)
39
+ a: (H):(1)
40
+ B/C: (L, N, C, G, B):(1, C*L, L, N*C*L, G*N*C*L)
41
+ D: (1, H):(0, 1) or (D, H):(1, D)
42
+ has_d: bool
43
+ d_has_hdim: bool
44
+ Return:
45
+ Y_out: (L, D, C, H, B):(1, C*L, L, D*C*L, H*D*C*L)
46
+ Fstate_out: (D, N, H, B):(N, 1, D*N, H*D*N)
47
+ """
48
+ assert x.dtype == a.dtype == delta.dtype == B.dtype == C.dtype
49
+
50
+ A = delta * a.view(1, 1, -1, 1)
51
+ X = x * delta.unsqueeze(0)
52
+
53
+ # Rearrange to match cutlass layout to tridao's layout
54
+ block_len = A.shape[0]
55
+ initial_states = None
56
+ # A: l c h b-> b c l h
57
+ A = A.permute(3, 1, 0, 2)
58
+ # X: p l c h b -> b c l h p
59
+ X = X.permute(4, 2, 1, 3, 0)
60
+ # B: l n c g b -> b c l g n
61
+ B = B.permute(4, 2, 0, 3, 1)
62
+ # C: l n c g b -> b c l g n
63
+ C = C.permute(4, 2, 0, 3, 1)
64
+ # X/A/B/C: b c l ... -> b (c l) ...
65
+ X, A, B, C = [x.reshape(x.shape[0], -1, *x.shape[3:]) for x in (X, A, B, C)]
66
+
67
+ # Ngroup (g to h) mapping
68
+ B_val, CL_val, G_val, N_val = B.shape
69
+ H_val = X.shape[2]
70
+ ngroup_ratio = H_val // G_val
71
+ # B/C: (B, CL, H, N)
72
+ h_to_g_mapping = torch.arange(H_val, device=B.device) // ngroup_ratio
73
+ B = B.gather(2, h_to_g_mapping.view(1, 1, -1, 1).expand(B_val, CL_val, -1, N_val))
74
+ C = C.gather(2, h_to_g_mapping.view(1, 1, -1, 1).expand(B_val, CL_val, -1, N_val))
75
+
76
+ ###################################################################
77
+ # Call reference implementation from Tri Dao ssd_minimal_discrete
78
+ Y, final_state = ssd_minimal_discrete_fp32_all(
79
+ X, A, B, C, block_len, initial_states
80
+ )
81
+ ###################################################################
82
+
83
+ if has_d:
84
+ D_val = Y.shape[3]
85
+ if not d_has_hdim:
86
+ D = D.expand(D_val, -1)
87
+ Y = Y + torch.einsum("bchp,ph->bchp", X, D)
88
+
89
+ # Rearrange to match tridao's layout to cutlass layout
90
+ # Y: b (c l) h p -> b c l h p
91
+ Y = Y.reshape(Y.shape[0], -1, block_len, Y.shape[2], Y.shape[3])
92
+ # Y: b c l h p -> l p c h b
93
+ Y = Y.permute(2, 4, 1, 3, 0)
94
+ # Fstate_out: b h p n -> p n h b
95
+ Fstate_out.copy_(final_state.permute(2, 3, 1, 0))
96
+ Y_out.copy_(Y)
97
+ return
98
+
99
+
100
+ def ssd_reference_lowprecision_intermediates(
101
+ x, a, delta, B, C, Y_out, Fstate_out, intermediate_dtype, D, has_d, d_has_hdim
102
+ ):
103
+ """
104
+ Rearrange tensor dimensions from cuda layout to reference layout, then call a reduced intermediate dtype version of ssd implementation
105
+ Arguments:
106
+ X/x: (D, L, C, H, B):(C*L, 1, L, D*C*L, H*D*C*L)
107
+ A/delta: (L, C, H, B):(1, L, C*L, H*C*L)
108
+ a: (H):(1)
109
+ B/C: (L, N, C, G, B):(1, C*L, L, N*C*L, G*N*C*L)
110
+ intermediate_dtype: input and intermediate data type
111
+ D: (1, H):(0, 1) or (D, H):(1, D)
112
+ has_d: bool
113
+ d_has_hdim: bool
114
+ Return:
115
+ Y_out: (L, D, C, H, B):(1, C*L, L, D*C*L, H*D*C*L)
116
+ Fstate_out: (D, N, H, B):(N, 1, D*N, H*D*N)
117
+ """
118
+ assert x.dtype == a.dtype == delta.dtype == B.dtype == C.dtype
119
+
120
+ A = delta * a.view(1, 1, -1, 1)
121
+
122
+ # Rearrange to match cutlass layout to tridao's layout
123
+ block_len = A.shape[0]
124
+ initial_states = None
125
+ # A: l c h b-> b c l h
126
+ A = A.permute(3, 1, 0, 2)
127
+ # delta: l c h b-> b c l h
128
+ delta = delta.permute(3, 1, 0, 2)
129
+ # x: p l c h b -> b c l h p
130
+ x = x.permute(4, 2, 1, 3, 0)
131
+ # B: l n c g b -> b c l g n
132
+ B = B.permute(4, 2, 0, 3, 1)
133
+ # C: l n c g b -> b c l g n
134
+ C = C.permute(4, 2, 0, 3, 1)
135
+ # x/A/delta/B/C: b c l ... -> b (c l) ...
136
+ x, A, delta, B, C = [
137
+ tensor.reshape(tensor.shape[0], -1, *tensor.shape[3:])
138
+ for tensor in (x, A, delta, B, C)
139
+ ]
140
+
141
+ # Ngroup (g to h) mapping
142
+ B_val, CL_val, G_val, N_val = B.shape
143
+ H_val = x.shape[2]
144
+ ngroup_ratio = H_val // G_val
145
+ # B/C: (B, CL, H, N)
146
+ h_to_g_mapping = torch.arange(H_val, device=B.device) // ngroup_ratio
147
+ B = B.gather(2, h_to_g_mapping.view(1, 1, -1, 1).expand(B_val, CL_val, -1, N_val))
148
+ C = C.gather(2, h_to_g_mapping.view(1, 1, -1, 1).expand(B_val, CL_val, -1, N_val))
149
+
150
+ # Type convert input tensors to input dtype (same as intermediate dtype)
151
+ x = x.to(intermediate_dtype).to(torch.float32)
152
+ A = A.to(intermediate_dtype).to(torch.float32)
153
+ delta = delta.to(intermediate_dtype).to(torch.float32)
154
+ B = B.to(intermediate_dtype).to(torch.float32)
155
+ C = C.to(intermediate_dtype).to(torch.float32)
156
+
157
+ #########################################################################
158
+ # Call reference implementation ssd_minimal_discrete_bf16_intermediates
159
+ Y, final_state = ssd_minimal_discrete_lowprecision_intermediates(
160
+ x, A, delta, B, C, block_len, intermediate_dtype, initial_states
161
+ )
162
+ #########################################################################
163
+
164
+ if has_d:
165
+ D = D.to(intermediate_dtype).to(torch.float32)
166
+ D_val = Y.shape[3]
167
+ if not d_has_hdim:
168
+ D = D.expand(D_val, -1)
169
+ Y = Y + torch.einsum("bchp,ph->bchp", x, D)
170
+
171
+ # Type convert output tensors to output dtype (same as intermediate dtype)
172
+ Y = Y.to(intermediate_dtype).to(torch.float32)
173
+ final_state = final_state.to(intermediate_dtype).to(torch.float32)
174
+
175
+ # Rearrange to match tridao's layout to cutlass layout
176
+ # Y: b (c l) h p -> b c l h p
177
+ Y = Y.reshape(Y.shape[0], -1, block_len, Y.shape[2], Y.shape[3])
178
+ # Y: b c l h p -> l p c h b
179
+ Y = Y.permute(2, 4, 1, 3, 0)
180
+ # Fstate_out: b h p n -> p n h b
181
+ Fstate_out.copy_(final_state.permute(2, 3, 1, 0))
182
+ Y_out.copy_(Y)
183
+ return
184
+
185
+
186
+ def analyze_relative_diffs(actual, expected):
187
+ """
188
+ Print statistics of relative differences between actual and expected tensors
189
+ """
190
+ # Calculate relative differences
191
+ abs_diff = (actual - expected).abs()
192
+ rel_diff = abs_diff / (torch.maximum(expected.abs(), actual.abs()) + 0.00001)
193
+
194
+ total_elements = rel_diff.numel()
195
+
196
+ # Handle special cases first
197
+ nan_mask = torch.isnan(rel_diff)
198
+ inf_mask = torch.isinf(rel_diff)
199
+ nan_count = nan_mask.sum().item()
200
+ inf_count = inf_mask.sum().item()
201
+
202
+ # Find position and value of maximum relative difference
203
+ max_rel_diff = (
204
+ rel_diff[~nan_mask & ~inf_mask].max()
205
+ if (~nan_mask & ~inf_mask).any()
206
+ else float("nan")
207
+ )
208
+ max_rel_diff_pos = (
209
+ rel_diff[~nan_mask & ~inf_mask].argmax()
210
+ if (~nan_mask & ~inf_mask).any()
211
+ else -1
212
+ )
213
+
214
+ # Print max relative difference info
215
+ print(f"Maximum relative difference:")
216
+ print(f"Position: {max_rel_diff_pos}")
217
+ print(f"Value: {max_rel_diff:.6e}")
218
+ print(f"Actual value: {actual.flatten()[max_rel_diff_pos]}")
219
+ print(f"Expected value: {expected.flatten()[max_rel_diff_pos]}")
220
+ print(f"NaN values: {nan_count} ({100.0 * nan_count / total_elements:.2f}%)")
221
+ print(f"Inf values: {inf_count} ({100.0 * inf_count / total_elements:.2f}%)\n")
222
+
223
+ # Check different rtol thresholds
224
+ rtol_levels = [1e-5, 1e-4, 1e-3, 1e-2, 5e-02, 1e-01]
225
+
226
+ for i, rtol in enumerate(rtol_levels):
227
+ if i == 0:
228
+ mask = rel_diff <= rtol
229
+ else:
230
+ mask = (rel_diff <= rtol) & (rel_diff > rtol_levels[i - 1])
231
+
232
+ count = mask.sum().item()
233
+ percentage = (count / total_elements) * 100
234
+
235
+ if i == 0:
236
+ print(f"Elements with rtol <= {rtol:.0e}: {count} ({percentage:.2f}%)")
237
+ else:
238
+ print(
239
+ f"Elements with {rtol_levels[i-1]:.0e} < rtol <= {rtol:.0e}: {count} ({percentage:.2f}%)"
240
+ )
241
+
242
+ # Print elements exceeding the largest rtol
243
+ mask = rel_diff > rtol_levels[-1]
244
+ count = mask.sum().item()
245
+ percentage = (count / total_elements) * 100
246
+ print(f"Elements with rtol > {rtol_levels[-1]:.0e}: {count} ({percentage:.2f}%)\n")
247
+
248
+
249
+ def segsum(x):
250
+ """
251
+ More stable segment sum calculation.
252
+ x: b h c l
253
+ """
254
+ T = x.size(-1)
255
+ # x: b h c l -> b h c l l
256
+ x = x.unsqueeze(-1).expand(*x.shape, T)
257
+ mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=-1)
258
+ x = x.masked_fill(~mask, 0)
259
+ x_segsum = torch.cumsum(x, dim=-2)
260
+ mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=0)
261
+ x_segsum = x_segsum.masked_fill(~mask, -torch.inf)
262
+ return x_segsum
263
+
264
+
265
+ def ssd_minimal_discrete_fp32_all(X, A, B, C, block_len, initial_states=None):
266
+ """
267
+ This is same with https://github.com/state-spaces/mamba/blob/main/mamba_ssm/modules/ssd_minimal.py
268
+ (all accumulation and intermediate results in fp32)
269
+
270
+ Arguments:
271
+ X: (batch(B), length(C*L), n_heads(H), d_head(D))
272
+ A: (batch(B), length(C*L), n_heads(H))
273
+ B: (batch(B), length(C*L), n_heads(H), d_state(N))
274
+ C: (batch(B), length(C*L), n_heads(H), d_state(N))
275
+ Return:
276
+ Y: (batch(B), length(C*L), n_heads(H), d_head(D))
277
+ final_state: (B, H, D, N)
278
+ """
279
+ assert X.dtype == A.dtype == B.dtype == C.dtype
280
+ assert X.shape[1] % block_len == 0
281
+
282
+ # Rearrange into blocks/chunks
283
+ # X/A/B/C:b (c l) ... -> b c l ...
284
+ X, A, B, C = [
285
+ x.reshape(x.shape[0], -1, block_len, *x.shape[2:]) for x in (X, A, B, C)
286
+ ]
287
+
288
+ # A: b c l h -> b h c l
289
+ A = A.permute(0, 3, 1, 2)
290
+ # A_cumsum: (B, H, C, L)
291
+ A_cumsum = torch.cumsum(A, dim=-1)
292
+
293
+ # 1. Compute the output for each intra-chunk (diagonal blocks)
294
+ segsum_A = segsum(A)
295
+ L = torch.exp(segsum_A)
296
+ Y_diag = torch.einsum("bclhn,bcshn,bhcls,bcshp->bclhp", C, B, L, X)
297
+
298
+ # 2. Compute the state for each intra-chunk
299
+ # (right term of low-rank factorization of off-diagonal blocks; B terms)
300
+ decay_states = torch.exp((A_cumsum[:, :, :, -1:] - A_cumsum))
301
+ states = torch.einsum("bclhn,bhcl,bclhp->bchpn", B, decay_states, X)
302
+
303
+ # 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
304
+ # (middle term of factorization of off-diag blocks; A terms)
305
+ if initial_states is None:
306
+ initial_states = torch.zeros_like(states[:, :1])
307
+ states = torch.cat([initial_states, states], dim=1)
308
+ decay_chunk = torch.exp(segsum(F.pad(A_cumsum[:, :, :, -1], (1, 0))))
309
+ new_states = torch.einsum("bhzc,bchpn->bzhpn", decay_chunk, states)
310
+ states, final_state = new_states[:, :-1], new_states[:, -1]
311
+
312
+ # 4. Compute state -> output conversion per chunk
313
+ # (left term of low-rank factorization of off-diagonal blocks; C terms)
314
+ state_decay_out = torch.exp(A_cumsum)
315
+ Y_off = torch.einsum("bclhn,bchpn,bhcl->bclhp", C, states, state_decay_out)
316
+
317
+ # Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
318
+ # Y: b c l h p -> b (c l) h p
319
+ Y = (Y_diag + Y_off).reshape(Y_diag.shape[0], -1, Y_diag.shape[3], Y_diag.shape[4])
320
+ return Y, final_state
321
+
322
+
323
+ def ssd_minimal_discrete_lowprecision_intermediates(
324
+ X, A, delta, B, C, block_len, intermediate_dtype, initial_states=None
325
+ ):
326
+ """
327
+ This is adjusted from ssd_minimal_discrete_fp32_all, with exceptions:
328
+ 1. accumulation in fp32 but intermediates Q/b_tmem/P are in intermediate_dtype
329
+ 2. delta is not pre-multiplied with X, delta was applied to generate Q/b_tmem to match GPU implementation
330
+
331
+ Arguments:
332
+ X: (batch(B), length(C*L), n_heads(H), d_head(D))
333
+ A: (batch(B), length(C*L), n_heads(H))
334
+ delta: (batch(B), length(C*L), n_heads(H))
335
+ B: (batch(B), length(C*L), n_heads(H), d_state(N))
336
+ C: (batch(B), length(C*L), n_heads(H), d_state(N))
337
+ Return:
338
+ Y: (batch(B), length(C*L), n_heads(H), d_head(D))
339
+ final_state: (B, H, D, N)
340
+ """
341
+ assert X.dtype == A.dtype == B.dtype == C.dtype
342
+ assert X.shape[1] % block_len == 0
343
+
344
+ # Rearrange into blocks/chunks
345
+ # X/A/delta/B/C: b (c l) ... -> b c l ...
346
+ X, A, delta, B, C = [
347
+ x.reshape(x.shape[0], -1, block_len, *x.shape[2:]) for x in (X, A, delta, B, C)
348
+ ]
349
+
350
+ # A: b c l h -> b h c l
351
+ A = A.permute(0, 3, 1, 2)
352
+ # delta: b c l h -> b h c l
353
+ delta = delta.permute(0, 3, 1, 2)
354
+ # A_cumsum: (B, H, C, L)
355
+ A_cumsum = torch.cumsum(A, dim=-1)
356
+
357
+ # 1. Compute the output for each intra-chunk (diagonal blocks)
358
+ segsum_A = segsum(A)
359
+ L = torch.exp(segsum_A)
360
+ intra_acc_0 = torch.einsum("bclhn,bcshn->bclhs", C, B)
361
+ Q = torch.einsum("bclhs,bhcls,bhcs->bclhs", intra_acc_0, L, delta)
362
+ Y_diag = torch.einsum(
363
+ "bclhs,bcshp->bclhp", Q.to(intermediate_dtype).to(torch.float32), X
364
+ )
365
+
366
+ # 2. Compute the state for each intra-chunk
367
+ # (right term of low-rank factorization of off-diagonal blocks; B terms)
368
+ decay_states = torch.exp((A_cumsum[:, :, :, -1:] - A_cumsum))
369
+ b_tmem = torch.einsum("bclhn,bhcl,bhcl->bclhn", B, decay_states, delta)
370
+ states = torch.einsum(
371
+ "bclhn,bclhp->bchpn", b_tmem.to(intermediate_dtype).to(torch.float32), X
372
+ )
373
+
374
+ # 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
375
+ # (middle term of factorization of off-diag blocks; A terms)
376
+ if initial_states is None:
377
+ initial_states = torch.zeros_like(states[:, :1])
378
+ states = torch.cat([initial_states, states], dim=1)
379
+ decay_chunk = torch.exp(segsum(F.pad(A_cumsum[:, :, :, -1], (1, 0))))
380
+ new_states = torch.einsum("bhzc,bchpn->bzhpn", decay_chunk, states)
381
+ states, final_state = new_states[:, :-1], new_states[:, -1]
382
+ final_state = final_state
383
+
384
+ # 4. Compute state -> output conversion per chunk
385
+ # (left term of low-rank factorization of off-diagonal blocks; C terms)
386
+ state_decay_out = torch.exp(A_cumsum)
387
+ Y_off_tmp = torch.einsum(
388
+ "bclhn,bchpn->bclhp", C, states.to(intermediate_dtype).to(torch.float32)
389
+ )
390
+ Y_off = torch.einsum("bclhp,bhcl->bclhp", Y_off_tmp, state_decay_out)
391
+
392
+ # Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
393
+ # Y: b c l h p -> b (c l) h p
394
+ Y = (Y_diag + Y_off).reshape(
395
+ Y_diag.shape[0], -1, Y_diag.shape[3], Y_diag.shape[4]
396
+ ) # b (c l) h p
397
+ return Y, final_state