← CuTe 笔记 / cute_ldmatrix_mma_coordinate_trace.cu · 下载原文件
1// Host-only companion to cute_ldmatrix_mma_coordinate_walkthrough_zh.md.
2// No ldmatrix or mma.sync instruction is executed.
3// From the CUTLASS root:
4// g++ -x c++ -std=c++17 -Iinclude -I/usr/local/cuda/include \
5// -I/usr/local/cuda/include/cccl cute_ldmatrix_mma_coordinate_trace.cu \
6// -o /tmp/cute_ldmatrix_mma_coordinate_trace
7#include <cute/tensor.hpp>
8#include <cute/atom/copy_traits_sm75.hpp>
9#include <cute/atom/mma_traits_sm80.hpp>
10#include <cstdio>
11#include <cstdlib>
12
13using namespace cute;
14
15void require(bool condition, char const* message) {
16 if (!condition) {
17 std::fprintf(stderr, "FAILED: %s\n", message);
18 std::exit(1);
19 }
20}
21
22template<class T>
23void show(char const* name, T const& value) {
24 std::printf("%s = ", name);
25 print(value);
26 print("\n");
27}
28
29int main() {
30 alignas(16) half_t storage[16*16];
31 auto sA = make_tensor(make_smem_ptr(storage),
32 Layout<Shape<_16,_16>, Stride<_16,_1>>{});
33 for (int m = 0; m < 16; ++m) {
34 for (int k = 0; k < 16; ++k) {
35 sA(m,k) = half_t(16*m+k);
36 }
37 }
38 // Keep the transported value distinct from element ids and address offsets.
39 sA(0,9) = half_t(37);
40
41 auto mma = make_tiled_mma(SM80_16x8x16_F16F16F16F16_TN{});
42 auto load = Copy_Atom<SM75_U32x4_LDSM_N, half_t>{};
43 auto tiled_copy = make_tiled_copy_A(load, mma);
44 using Copy = decltype(tiled_copy);
45 using Src = typename Copy::AtomLayoutSrc;
46 using Dst = typename Copy::AtomLayoutDst;
47 using Ref = typename Copy::AtomLayoutRef;
48 auto F = mma.get_layoutA_TV();
49 auto src_to_ref = right_inverse(Ref{}).compose(Src{});
50 auto dst_to_src = right_inverse(Src{}).compose(Dst{});
51 auto coordinates = make_identity_tensor(shape(sA));
52
53 show("MMA ALayout", typename decltype(mma)::AtomLayoutA_TV{});
54 show("TiledLayout_TV", F);
55 show("AtomLayoutSrc (half units)", Src{});
56 show("AtomLayoutDst (half units)", Dst{});
57 show("AtomLayoutRef (half units)", Ref{});
58 show("source TV -> reference TV", src_to_ref);
59 int target_q = int(F(0,5));
60 int copy_destination_q = int(tiled_copy.get_layoutD_TV()(0,5));
61 int route_e = int(Dst{}(0,5));
62 int source_tv = int(dst_to_src(0,5));
63 require(target_q == 144 && copy_destination_q == target_q && route_e == 129 &&
64 source_tv%32 == 16 && source_tv/32 == 1,
65 "Destination slot 5 requires A(0,9) and traces back to source lane 16 half 1");
66 std::printf("\nMMA target (0,5): q=%d; Copy final destination q=%d\n",
67 target_q, copy_destination_q);
68 std::printf("Copy route: destination (0,5) -> e=%d -> source (%d,%d)\n",
69 route_e, source_tv%32, source_tv/32);
70 std::puts("\nsource TV | e | reference TV | tv | q | A(m,k) | half offset | example byte address");
71
72 auto src16_coordinates = tiled_copy.get_slice(16).partition_S(coordinates);
73 for (int p = 0; p < 2; ++p) {
74 int e = int(Src{}(16,p));
75 int tv = int(src_to_ref(16,p));
76 int ref_lane = tv % 32;
77 int ref_value = tv / 32;
78 int q = int(F(ref_lane,ref_value));
79 auto coord = src16_coordinates(p,0,0);
80 int m = int(get<0>(coord));
81 int k = int(get<1>(coord));
82 int offset = int(sA.layout()(coord));
83 require(e == 128+p && ref_lane == 0 && ref_value == 4+p,
84 "The two entries of source lane 16 recover reference values 4 and 5");
85 require(q == 128+16*p && m == 0 && k == 8+p && offset == 8+p,
86 "The example distinguishes matrix encoding from memory offset");
87 std::printf("(16,%d) | %d | (%d,%d) | %d | %d | A(%d,%d) | %d | 0x%x\n",
88 p, e, ref_lane, ref_value, tv, q, m, k, offset, 0x1000+2*offset);
89 }
90
91 // Check the non-transposed m8n8.x4 hardware distribution independently
92 // of the inverse-layout calculation. A source lane supplies one row.
93 int visited[32][8] = {};
94 int route_count = 0;
95 std::puts("\nSource row addresses inferred by partition_S:");
96 for (int src_lane = 0; src_lane < 32; ++src_lane) {
97 auto src_coords = tiled_copy.get_slice(src_lane).partition_S(coordinates);
98 auto src_values = tiled_copy.get_slice(src_lane).partition_S(sA);
99 auto first = src_coords(0,0,0);
100 int first_offset = int(sA.layout()(first));
101 require((2*first_offset)%16 == 0, "Every source row is 16-byte aligned");
102 int block = src_lane / 8;
103 int row = src_lane % 8;
104 int expected_m = row + 8*(block%2);
105 int expected_k_base = 8*(block/2);
106 require(first == make_coord(expected_m,expected_k_base),
107 "Each group of eight address lanes describes the expected A quadrant");
108 if (row == 0) {
109 std::printf("lanes %2d..%2d: A rows %d..%d, K %d..%d\n",
110 8*block, 8*block+7, expected_m, expected_m+7,
111 expected_k_base, expected_k_base+7);
112 }
113 for (int col = 0; col < 8; ++col) {
114 int dst_lane = 4*row + col/2;
115 int dst_value = 2*block + col%2;
116 auto source_coord = src_coords(col,0,0);
117 auto mma_coord = mma.get_slice(dst_lane).partition_A(coordinates)(dst_value,0,0);
118 require(source_coord == make_coord(expected_m,expected_k_base+col),
119 "The source row is contiguous in K");
120 require(int(sA.layout()(source_coord)) == first_offset+col,
121 "The actual source address agrees with the row offset");
122 require(int(Src{}(src_lane,col)) == int(Dst{}(dst_lane,dst_value)),
123 "The Copy traits describe the hardware's source-to-destination route");
124 int tv = int(src_to_ref(src_lane,col));
125 int q = int(F(tv%32,tv/32));
126 require(q == int(get<0>(source_coord)) + 16*int(get<1>(source_coord)),
127 "Source TV reaches its matrix coordinate through reference TV");
128 require(source_coord == mma_coord,
129 "The hardware route reaches the slot requiring this matrix element");
130 require(float(src_values(col,0,0)) == float(sA(mma_coord)),
131 "The address holds the value required by the destination MMA slot");
132 ++visited[dst_lane][dst_value];
133 ++route_count;
134 }
135 }
136
137 int slot_count = 0;
138 int operand_count = 0;
139 for (int lane = 0; lane < 32; ++lane) {
140 auto thr_mma = mma.get_slice(lane);
141 auto tCrA = thr_mma.partition_fragment_A(sA);
142 auto thr_copy = tiled_copy.get_slice(lane);
143 auto tXrA = thr_copy.retile_D(tCrA);
144 auto expected_coords = thr_mma.partition_A(coordinates);
145 require(tXrA.data() == tCrA.data(), "retile_D preserves the data iterator");
146 for (int v = 0; v < 8; ++v) {
147 require(visited[lane][v] == 1, "Every destination slot is reached exactly once");
148 require(&tXrA(v,0,0) == &tCrA(v,0,0),
149 "Copy value v aliases MMA value v for this x4 configuration");
150 int src_tv = int(dst_to_src(lane,v));
151 int source_lane = src_tv%32;
152 int source_value = src_tv/32;
153 auto source = tiled_copy.get_slice(source_lane).partition_S(sA);
154 // Model the fixed hardware route on the host, then read through the MMA view.
155 tXrA(v,0,0) = source(source_value,0,0);
156 auto coord = expected_coords(v,0,0);
157 require(float(tCrA(v,0,0)) == float(sA(coord)),
158 "The Copy route fills the correct MMA value slot");
159 ++slot_count;
160 }
161 auto mma_operands = recast<uint32_t>(tCrA(_,0,0));
162 auto copy_operands = recast<uint32_t>(tXrA(_,0,0));
163 require(int(size(mma_operands)) == 4 && int(size(copy_operands)) == 4,
164 "Copy and MMA both expose four 32-bit A operands");
165 for (int j = 0; j < 4; ++j) {
166 uint32_t expected = uint32_t(tCrA(2*j,0,0).raw()) |
167 (uint32_t(tCrA(2*j+1,0,0).raw()) << 16);
168 require(&copy_operands(j) == &mma_operands(j),
169 "Copy output j and MMA input j refer to the same packed storage");
170 require(mma_operands(j) == expected, "The low and high half fields retain their bits");
171 ++operand_count;
172 }
173 if (lane == 0) {
174 show("\nlane 0 tCrA layout", tCrA.layout());
175 show("lane 0 tXrA layout", tXrA.layout());
176 std::puts("lane 0 operands: initial labels A(m,k)=16*m+k, with A(0,9) changed to 37:");
177 for (int j = 0; j < 4; ++j) {
178 std::printf("dst%d / a%d: low=%.0f, high=%.0f\n", j, j,
179 float(tCrA(2*j,0,0)), float(tCrA(2*j+1,0,0)));
180 }
181 require(float(tCrA(5,0,0)) == 37.0f, "The value 37 at A(0,9) reaches lane 0 slot 5");
182 }
183 }
184 std::printf("\nPASS: %d hardware routes, %d fragment slots, %d packed operands verified (host only).\n",
185 route_count, slot_count, operand_count);
186}
187