choice.hh
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @brief gemm: direct, tiled, small, reg, wide, vendor. evidence: docs/perf/gemm.md @ingroup api_selection_ops
5// Sycl-free on purpose: tests and the tuner include it to name the choices the library runs.
6
8
9#include <array>
10#include <complex>
11#include <cstdint>
12#include <string_view>
13#include <type_traits>
14#include <variant>
15
17
18// Fields are the knobs a selector chooses. Derived in the launcher, never fields: the transpose
19// instantiation, the aligned vs predicated leg, the small bucket, TR/TC/stages (tables below).
20struct Direct : select::NoFields<"direct"> {}; ///< gemm_direct: one work-item per element, any form; max_wg >= 64
21struct Tiled : select::NoFields<"tiled"> {}; ///< gemm_tiled: 16x16 shared-memory tile, any form; max_wg >= 256
22struct Small : select::NoFields<"small"> {}; ///< gemm_small: several matrices per work-group; real, see small_fits
23/// gemm_reg, float only: m x n x k macro tile with k unroll u; must name a reg_configs entry that
24/// instantiates the call's transpose form, and max_wg must cover its threads().
25struct Reg {
26 int m = 0, n = 0, k = 0, u = 1;
27 static constexpr std::string_view name = "reg";
28 static constexpr std::array<std::string_view, 4> fields{"m", "n", "k", "u"};
29 std::array<int, 4> values() const { return {m, n, k, u}; }
30 static Reg from(std::array<int, 4> v) { return {v[0], v[1], v[2], v[3]}; }
31 bool operator==(const Reg&) const = default;
32};
33/// gemm_wide, every scalar: 16-byte-granule m x n x k tiles; must name a wide_configs entry that
34/// instantiates the call's form, and max_wg must cover its threads().
35struct Wide {
36 int m = 0, n = 0, k = 0;
37 static constexpr std::string_view name = "wide";
38 static constexpr std::array<std::string_view, 3> fields{"m", "n", "k"};
39 std::array<int, 3> values() const { return {m, n, k}; }
40 static Wide from(std::array<int, 3> v) { return {v[0], v[1], v[2]}; }
41 bool operator==(const Wide&) const = default;
42};
43struct Vendor : select::NoFields<"vendor"> {}; ///< backend::gemm_vendor; needs the level-3 library
44
45using GemmChoice = std::variant<Direct, Tiled, Small, Reg, Wide, Vendor>; ///< natives: GPU or vendor-free host, Default precision
46
47/// Transpose forms an instantiation exists for, after the real-scalar C->T fold.
48struct Forms {
49 bool nn = false, nt = false, tn = false, tt = false;
50};
51
52/// One compiled register config: tr/tc thread tile, stages, the forms instantiated, and whether
53/// NN takes the unpredicated leg when the layout allows (derived per call, never a gate).
54struct RegCfg {
55 int m, n, k, u, tr, tc, stages;
58 constexpr int threads() const { return (m / tr) * (n / tc); }
59 /// Float local memory per work-group (gemm_kernels.cc static_asserts it against each kernel).
60 constexpr std::int64_t slm_bytes() const {
61 if (m == 128 && n == 128) return std::int64_t(4) * ((32 * 132 > 2 * k * m ? 32 * 132 : 2 * k * m) + 2 * k * n);
62 return std::int64_t(4) * stages * ((m + 1) * k + (k + 1) * n);
63 }
64};
65inline constexpr std::array<RegCfg, 10> reg_configs{{
66 {32, 32, 8, 1, 2, 2, 1, {true, false, false, false}, false},
67 {64, 64, 8, 1, 4, 4, 1, {true, false, false, false}, false},
68 {64, 64, 16, 1, 4, 4, 1, {true, true, true, true}, false},
69 {128, 32, 16, 1, 4, 4, 2, {true, true, true, true}, false},
70 {128, 32, 32, 1, 4, 4, 2, {true, true, true, true}, true},
71 {128, 64, 16, 1, 4, 4, 1, {false, true, true, true}, false},
72 {32, 128, 16, 1, 4, 4, 1, {true, false, true, true}, false},
73 {128, 64, 32, 4, 8, 4, 2, {true, false, false, false}, true},
74 {128, 64, 32, 2, 8, 4, 2, {true, false, false, false}, true},
75 {128, 128, 8, 1, 8, 8, 2, {true, false, false, false}, true}, // its own kernel (register_128x128.hh)
76}};
77
78/// One compiled wide config. NN, CN (ConjTrans A) and NC (ConjTrans B) instantiations; a real
79/// Trans is served by a ConjTrans one (conj is the identity), a complex Trans is not.
80struct WideCfg {
81 int m, n, k, ttm, ttn;
82 bool nn, cn, nc;
83 constexpr int threads() const { return (m / ttm) * (n / ttn); }
84};
85inline constexpr std::array<WideCfg, 5> wide_configs{{
86 {64, 64, 16, 4, 4, true, true, true},
87 {128, 32, 16, 4, 4, false, false, true}, // the potrf trailing update, W = 32
88 {32, 128, 16, 4, 4, false, true, false}, // the geqrf panel update V^H A22, nb = 32
89 {32, 32, 16, 4, 4, true, false, false},
90 {16, 16, 16, 2, 2, true, false, false},
91}};
92
93// sycl-free copies of the kernels' limits; gemm.cc static_asserts them against the kernels.
94inline constexpr int kSmallMaxDim = 64;
95inline constexpr int kSmallWg = 128;
96inline constexpr int kSmallTiledMaxDim = 56; // float NN above 32: one matrix per (NB/4)^2 lanes
97
98/// The work-group `small` launches: the batched kernel's 128 lanes, or the float NN tiled leg's
99/// (NB/4)^2 with NB = 48 or 56 (small_batched.hh).
100template <class T>
101constexpr int small_wg(bool nn, int max_dim) {
102 if (!std::is_same_v<T, float> || !nn || max_dim <= 32 || max_dim > kSmallTiledMaxDim) return kSmallWg;
103 const int nb = max_dim <= 48 ? 48 : 56;
104 return (nb / 4) * (nb / 4);
105}
106inline constexpr int kDirectWg = 64;
107inline constexpr int kTiledWg = 256;
108/// direct, tiled, reg and wide put the batch in SYCL dim 0 = CUDA grid z (65535); small is 1-D.
109inline constexpr std::int64_t kMaxGridBatch = 65535;
110
111template <class T>
112inline constexpr bool is_complex_v = !std::is_same_v<T, float> && !std::is_same_v<T, double>;
113
114/// small's batched leg is [[sycl::reqd_sub_group_size(32)]]; only the float NN tiled leg is not.
115template <class T>
116constexpr bool small_needs_sg32(bool nn, int max_dim) {
117 return !(std::is_same_v<T, float> && nn && max_dim > 32 && max_dim <= kSmallTiledMaxDim);
118}
119/// The shape-and-device half of small's can_run, here so tests can probe synthetic devices.
120template <class T>
121bool small_fits(const select::Device& d, bool nn, std::int64_t max_dim) {
122 if (is_complex_v<T> || max_dim < 1 || max_dim > kSmallMaxDim) return false;
123 const int mx = static_cast<int>(max_dim);
124 return d.max_wg >= small_wg<T>(nn, mx) && (d.has_sg32 || !small_needs_sg32<T>(nn, mx));
125}
126
127/// Every compiled choice, once, in tie-break order (§6.3): simpler first, vendor last.
128template <class T>
129constexpr auto candidates() {
130 constexpr std::array<Wide, 5> wides{Wide{64, 64, 16}, Wide{128, 32, 16}, Wide{32, 128, 16}, Wide{32, 32, 16},
131 Wide{16, 16, 16}};
132 if constexpr (std::is_same_v<T, float>) {
133 std::array<GemmChoice, 19> out{};
134 std::size_t i = 0;
135 out[i++] = Direct{};
136 out[i++] = Tiled{};
137 out[i++] = Small{};
138 for (const RegCfg& r : reg_configs) out[i++] = Reg{r.m, r.n, r.k, r.u};
139 for (const Wide& w : wides) out[i++] = w;
140 out[i++] = Vendor{};
141 return out;
142 } else if constexpr (std::is_same_v<T, double>) {
143 return std::array<GemmChoice, 9>{Direct{}, Tiled{}, Small{}, wides[0], wides[1], wides[2], wides[3],
144 wides[4], Vendor{}};
145 } else {
146 return std::array<GemmChoice, 8>{Direct{}, Tiled{}, wides[0], wides[1], wides[2], wides[3], wides[4],
147 Vendor{}};
148 }
149}
150
151/// Generality order (§5.5): direct serves every GPU shape, vendor everything else (CPU, precision).
152inline constexpr std::array<std::string_view, 2> last_resort{"direct", "vendor"};
153inline constexpr select::OpSpec spec{Op::gemm, select::Lib::level3, {last_resort}}; ///< op, vendor library, rules
154
155/// Table keys: ta, tb (N|T|C; C folds to T for a real scalar) and layout (packed = A, B, C
156/// contiguous with 16-byte bases) exact; m, n, k, batch log, each weight 1 (work ~ m n k batch).
157inline constexpr std::array<std::string_view, 7> key_names{"ta:exact", "tb:exact", "layout:exact", "m:log",
158 "n:log", "k:log", "batch:log"};
159
160/// The tuner's demand-driven grid; the transcriber (tuned/README.md) spells it again. Squares for
161/// every form and both layouts; panels and skinny shapes for the issued forms only, packed panels
162/// from m, n >= 128. evidence: docs/design/flat-kernel-selection-phase3-plan.md (§3)
163inline constexpr std::array<int, 14> grid_square{8, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024};
164inline constexpr std::array<int, 7> grid_panel_mn{32, 64, 128, 256, 512, 1024, 2048};
165inline constexpr std::array<int, 6> grid_panel_k{8, 16, 32, 64, 96, 128};
166inline constexpr std::array<int, 6> grid_skinny_mn{64, 128, 256, 512, 1024, 2048};
167inline constexpr std::array<int, 2> grid_skinny_k{256, 1024};
168inline constexpr std::array<int, 3> grid_batch{128, 2048, 32768};
169inline constexpr int grid_packed_panel_min = 128;
170inline constexpr std::array<std::string_view, 4> grid_real_forms{"NN", "NT", "TN", "TT"};
171inline constexpr std::array<std::string_view, 3> grid_real_panel_forms{"NN", "NT", "TN"};
172inline constexpr std::array<std::string_view, 6> grid_complex_forms{"NN", "NT", "TN", "NC", "CN", "CT"};
173inline constexpr std::array<std::string_view, 5> grid_complex_panel_forms{"NN", "NT", "NC", "TN", "CN"};
174
175} // namespace batchlas::ops::gemm
constexpr std::array< RegCfg, 10 > reg_configs
Definition choice.hh:65
constexpr std::array< int, 7 > grid_panel_mn
Definition choice.hh:164
constexpr std::array< std::string_view, 5 > grid_complex_panel_forms
Definition choice.hh:173
constexpr std::array< int, 2 > grid_skinny_k
Definition choice.hh:167
constexpr bool small_needs_sg32(bool nn, int max_dim)
small's batched leg is [[sycl::reqd_sub_group_size(32)]]; only the float NN tiled leg is not.
Definition choice.hh:116
constexpr std::array< std::string_view, 2 > last_resort
Generality order (§5.5): direct serves every GPU shape, vendor everything else (CPU,...
Definition choice.hh:152
constexpr std::array< int, 6 > grid_skinny_mn
Definition choice.hh:166
constexpr std::array< std::string_view, 7 > key_names
Table keys: ta, tb (N|T|C; C folds to T for a real scalar) and layout (packed = A,...
Definition choice.hh:157
constexpr auto candidates()
Every compiled choice, once, in tie-break order (§6.3): simpler first, vendor last.
Definition choice.hh:129
constexpr std::array< std::string_view, 3 > grid_real_panel_forms
Definition choice.hh:171
constexpr int kSmallWg
Definition choice.hh:95
constexpr int kSmallTiledMaxDim
Definition choice.hh:96
constexpr std::array< std::string_view, 6 > grid_complex_forms
Definition choice.hh:172
constexpr std::array< int, 14 > grid_square
The tuner's demand-driven grid; the transcriber (tuned/README.md) spells it again.
Definition choice.hh:163
constexpr int grid_packed_panel_min
Definition choice.hh:169
std::variant< Direct, Tiled, Small, Reg, Wide, Vendor > GemmChoice
natives: GPU or vendor-free host, Default precision
Definition choice.hh:45
constexpr select::OpSpec spec
op, vendor library, rules
Definition choice.hh:153
constexpr std::array< int, 3 > grid_batch
Definition choice.hh:168
constexpr std::array< std::string_view, 4 > grid_real_forms
Definition choice.hh:170
constexpr std::array< int, 6 > grid_panel_k
Definition choice.hh:165
constexpr int small_wg(bool nn, int max_dim)
The work-group small launches: the batched kernel's 128 lanes, or the float NN tiled leg's (NB/4)^2 w...
Definition choice.hh:101
constexpr int kTiledWg
Definition choice.hh:107
bool small_fits(const select::Device &d, bool nn, std::int64_t max_dim)
The shape-and-device half of small's can_run, here so tests can probe synthetic devices.
Definition choice.hh:121
constexpr int kDirectWg
Definition choice.hh:106
constexpr bool is_complex_v
Definition choice.hh:112
constexpr int kSmallMaxDim
Definition choice.hh:94
constexpr std::array< WideCfg, 5 > wide_configs
Definition choice.hh:85
constexpr std::int64_t kMaxGridBatch
direct, tiled, reg and wide put the batch in SYCL dim 0 = CUDA grid z (65535); small is 1-D.
Definition choice.hh:109
Flat kernel selection: one choose() per op over measured per-device tables.
gemm_direct: one work-item per element, any form; max_wg >= 64
Definition choice.hh:20
Transpose forms an instantiation exists for, after the real-scalar C->T fold.
Definition choice.hh:48
One compiled register config: tr/tc thread tile, stages, the forms instantiated, and whether NN takes...
Definition choice.hh:54
constexpr int threads() const
Definition choice.hh:58
constexpr std::int64_t slm_bytes() const
Float local memory per work-group (gemm_kernels.cc static_asserts it against each kernel).
Definition choice.hh:60
gemm_reg, float only: m x n x k macro tile with k unroll u; must name a reg_configs entry that instan...
Definition choice.hh:25
bool operator==(const Reg &) const =default
static constexpr std::string_view name
Definition choice.hh:27
static Reg from(std::array< int, 4 > v)
Definition choice.hh:30
std::array< int, 4 > values() const
Definition choice.hh:29
static constexpr std::array< std::string_view, 4 > fields
Definition choice.hh:28
gemm_small: several matrices per work-group; real, see small_fits
Definition choice.hh:22
gemm_tiled: 16x16 shared-memory tile, any form; max_wg >= 256
Definition choice.hh:21
backend::gemm_vendor; needs the level-3 library
Definition choice.hh:43
One compiled wide config.
Definition choice.hh:80
constexpr int threads() const
Definition choice.hh:83
gemm_wide, every scalar: 16-byte-granule m x n x k tiles; must name a wide_configs entry that instant...
Definition choice.hh:35
static constexpr std::array< std::string_view, 3 > fields
Definition choice.hh:38
std::array< int, 3 > values() const
Definition choice.hh:39
static Wide from(std::array< int, 3 > v)
Definition choice.hh:40
static constexpr std::string_view name
Definition choice.hh:37
bool operator==(const Wide &) const =default
The device facts a can_run predicate and the table lookup read (§5.1).
Definition select.hh:44
int max_wg
MAX_WORK_GROUP_SIZE.
Definition select.hh:52
Base of a family with no knobs: struct Cta : NoFields<"cta"> {};.
Definition select.hh:101
What an op's choice.hh declares once: its name, the library its Vendor family calls (one gate for can...
Definition select.hh:529