← CuTe 笔记 / cute_flash_attention_trace.cu · 下载原文件
1// Host-only layout checks for cutlass_cute_flash_attention_walkthrough_zh.md.
2// nvcc -std=c++17 -Iinclude cute_flash_attention_trace.cu -o /tmp/cute_flash_attention_trace
3#include <cute/tensor.hpp>
4#include <cstdio>
5#include <cstdlib>
6using namespace cute;
7
8void require(bool ok, char const* what) {
9 if (!ok) { std::fprintf(stderr, "FAILED: %s\n", what); std::exit(1); }
10}
11template<class T> void show(char const* name, T const& x) {
12 std::printf("%s: ", name); print(x); print("\n");
13}
14template<class L> auto rowcol(L l) {
15 auto d = logical_divide(l, Shape<_2>{});
16 return make_layout(make_layout(get<1>(get<0>(d)), get<1>(d)),
17 make_layout(get<0>(get<0>(d)), get<2>(d)));
18}
19template<class L> auto aregs(L l) {
20 using X = Underscore;
21 auto d = logical_divide(l, Shape<X, Shape<X, _2>>{});
22 return make_layout(make_layout(get<0>(get<1>(d)), get<0>(get<0>(d)),
23 get<0>(get<1>(get<1>(d)))),
24 get<1>(get<0>(d)), get<1>(get<1>(get<1>(d))));
25}
26template<class Copy, class Fragment> void steps(Copy, Fragment const& f) {
27 auto v = size<0>(f);
28 using NumThr = typename Copy::TiledNumThr;
29 auto mn = upcast<NumThr{} * v>(
30 right_inverse(typename Copy::TiledLayout_TV{})
31 .with_shape(shape(typename Copy::Tiler_MN{})));
32 auto fv = zipped_divide(logical_product(make_layout(v), right_inverse(mn)),
33 make_layout(typename Copy::AtomNumVal{}));
34 show("frg_layout_mn", mn);
35 show("gather extents", product_each(shape(mn)));
36 show("frg_layout_v", fv);
37}
38int main() {
39 using Mma = TiledMMA<MMA_Atom<SM80_16x8x16_F32F16F16F32_TN>,
40 Layout<Shape<_4,_1,_1>>, Tile<_64,_16,_16>>;
41 using Smem = decltype(tile_to_shape(composition(Swizzle<3,3,3>{},
42 Layout<Shape<_8,_64>, Stride<_64,_1>>{}), Shape<_64,_64>{}));
43 using SmemVt = decltype(tile_to_shape(composition(Swizzle<3,3,3>{},
44 Layout<Shape<_64,_64>, Stride<_1,_64>>{}), Shape<_64,_64>{}));
45 Mma mma;
46 alignas(16) half_t storage[64*64]{};
47 auto smem = make_tensor(make_smem_ptr(storage), Smem{});
48 auto coords = make_identity_tensor(Shape<_64,_64>{});
49 auto ca = make_tiled_copy_A(Copy_Atom<SM75_U32x4_LDSM_N,half_t>{}, mma);
50 auto cb = make_tiled_copy_B(Copy_Atom<SM75_U32x4_LDSM_N,half_t>{}, mma);
51 int qa = 0, kb = 0, pa = 0, cc = 0;
52 for (int tid = 0; tid < 128; ++tid) {
53 auto thr = mma.get_slice(tid);
54 auto a = thr.partition_fragment_A(smem);
55 auto b = thr.partition_fragment_B(smem);
56 auto c = partition_fragment_C(mma, Shape<_64,_64>{});
57 auto xa = ca.get_slice(tid).retile_D(a);
58 auto xb = cb.get_slice(tid).retile_D(b);
59 require(xa.data() == a.data() && xb.data() == b.data(), "retile aliases storage");
60 auto ac = thr.partition_A(coords);
61 auto bc = thr.partition_B(coords);
62 auto dc = thr.partition_C(coords);
63 auto axc = ca.get_slice(tid).retile_D(ac);
64 auto bxc = cb.get_slice(tid).retile_D(bc);
65 auto ad = ca.get_slice(tid).partition_D(coords);
66 auto bd = cb.get_slice(tid).partition_D(coords);
67 for (int i = 0; i < size(ad); ++i) {
68 require(ad(i) == axc(i), "Q Copy destination matches MMA"); ++qa;
69 }
70 for (int i = 0; i < size(bd); ++i) {
71 require(bd(i) == bxc(i), "K Copy destination matches MMA"); ++kb;
72 }
73 auto plain = make_tensor(make_smem_ptr(storage),
74 Layout<Shape<_64,_64>, Stride<_64,_1>>{});
75 auto bp = thr.partition_fragment_B(plain);
76 auto xbp = cb.get_slice(tid).retile_D(bp);
77 for (int k = 0; k < 4; ++k)
78 for (int n = 0; n < 4; ++n)
79 for (int v = 0; v < 8; ++v) {
80 require(int(xb.layout()(make_coord(v,0),n,k)) ==
81 v + 8*n + 32*k, "swizzled K x4 slot formula");
82 require(int(xbp.layout()(make_coord(v,0),n,k)) ==
83 v%4 + 16*(2*n+v/4) + 4*k, "plain K x4 slot formula");
84 }
85 int slots[32];
86 for (int i = 0; i < 32; ++i) slots[i] = i;
87 auto rc = make_tensor(make_rmem_ptr(&slots[0]), rowcol(c.layout()));
88 auto pc = make_tensor(make_rmem_ptr(&slots[0]), aregs(rc.layout()));
89 for (int i = 0; i < size(ac); ++i) {
90 require(dc(pc(i)) == ac(i), "C to A stays in the same lane"); ++pa;
91 }
92 for (int j = 0; j < 16; ++j)
93 for (int r = 0; r < 2; ++r) {
94 require(dc(rc(r,j)) == make_coord(16*(tid/32)+(tid%32)/4+8*r,
95 2*(tid%4)+j%2+8*(j/2)), "score coordinates");
96 ++cc;
97 }
98 if (tid == 0) {
99 show("tCrQ", a.layout()); show("tCrK", b.layout());
100 show("tCrS", c.layout()); show("tXrQ", xa.layout());
101 show("tXrK", xb.layout()); show("tCrS_rowcol", rc.layout());
102 show("tCrP", pc.layout());
103 show("plain tCrK", bp.layout()); show("plain tXrK", xbp.layout());
104 print("Q retile internals\n"); steps(ca,a);
105 print("K retile internals\n"); steps(cb,b);
106 for (int v = 0; v < 8; ++v) {
107 std::printf("K x4 value %d -> actual slot %d, plain slot %d -> ", v,
108 int(xb.layout()(make_coord(v,0),0,0)),
109 int(xbp.layout()(make_coord(v,0),0,0)));
110 print(bxc(make_coord(v,0),0,0)); print("\n");
111 }
112 }
113 }
114 for (int key = 0; key < 64; ++key)
115 for (int dim = 0; dim < 64; ++dim)
116 require(Smem{}(key,dim) == SmemVt{}(dim,key), "V transpose aliases same half");
117 std::printf("PASS: Q %d, K %d, C-to-A %d, score coordinates %d, V addresses 4096\n",
118 qa,kb,pa,cc);
119 std::puts("Host layout checks only; no GPU instruction executed.");
120}
121