14#include <batchlas/export.hh>
81 if constexpr (std::is_same_v<T, float>)
return "float";
82 else if constexpr (std::is_same_v<T, double>)
return "double";
83 else if constexpr (std::is_same_v<T, std::complex<float>>)
return "cfloat";
85 static_assert(std::is_same_v<T, std::complex<double>>,
"unsupported scalar");
93template <std::
size_t N>
96 constexpr FamilyName(
const char (&a)[N]) { std::copy_n(a, N,
s); }
100template <FamilyName L>
102 static constexpr std::string_view
name{L.s,
sizeof(L.s) - 1};
103 static constexpr std::array<std::string_view, 0>
fields{};
104 std::array<int, 0>
values()
const {
return {}; }
109template <
class Choice>
111 return [&]<std::size_t... I>(std::index_sequence<I...>) {
112 return std::array<Choice,
sizeof...(I)>{Choice{std::variant_alternative_t<I, Choice>{}}...};
113 }(std::make_index_sequence<std::variant_size_v<Choice>>{});
124template <
class Choice,
class F>
126 [&]<std::size_t... I>(std::index_sequence<I...>) {
127 (f(std::type_identity<std::variant_alternative_t<I, Choice>>{}), ...);
128 }(std::make_index_sequence<std::variant_size_v<Choice>>{});
131inline std::vector<std::string_view>
split(std::string_view s,
char sep) {
132 std::vector<std::string_view> out;
133 std::size_t start = 0;
134 for (std::size_t i = 0; i <= s.size(); ++i) {
135 if (i == s.size() || s[i] == sep) {
136 out.push_back(s.substr(start, i - start));
144 if (t.empty())
return false;
145 const auto r = std::from_chars(t.data(), t.data() + t.size(), out);
146 return r.ec == std::errc{} && r.ptr == t.data() + t.size();
152template <
class Choice>
154 return std::visit([](
const auto& a) {
return std::decay_t<
decltype(a)>::name; }, c);
158template <
class Choice>
160 return std::visit([](
const auto& a) {
161 using A = std::decay_t<
decltype(a)>;
162 std::string s(A::name);
163 const auto v = a.values();
164 for (std::size_t i = 0; i < v.size(); ++i) {
168 s += std::to_string(v[i]);
176template <
class Choice>
177std::optional<Choice>
parse(std::string_view text, std::string* err =
nullptr) {
179 std::optional<Choice> out;
180 std::string why =
"unknown family '" + std::string(tok[0]) +
"'";
182 using A =
typename decltype(id)::type;
183 if (out || tok[0] != A::name)
return;
184 constexpr std::size_t K = A::fields.size();
185 if (tok.size() - 1 != K) {
186 why = std::string(A::name) +
" takes " + std::to_string(K) +
" field(s), got " +
187 std::to_string(tok.size() - 1);
190 std::array<int, K> vals{};
191 std::array<bool, K> seen{};
192 for (std::size_t i = 1; i < tok.size(); ++i) {
193 std::string_view t = tok[i];
194 std::size_t idx = i - 1;
195 if (
const auto eq = t.find(
'='); eq != std::string_view::npos) {
196 const auto f = std::find(A::fields.begin(), A::fields.end(), t.substr(0, eq));
197 if (f == A::fields.end()) {
198 why =
"unknown field '" + std::string(t.substr(0, eq)) +
"' of " + std::string(A::name);
201 idx =
static_cast<std::size_t
>(f - A::fields.begin());
202 t = t.substr(eq + 1);
205 why =
"field '" + std::string(A::fields[idx]) +
"' given twice";
209 why =
"'" + std::string(t) +
"' is not an integer";
214 if constexpr (K == 0) out = Choice{A{}};
215 else out = Choice{A::from(vals)};
217 if (!out && err) *err =
"'" + std::string(text) +
"': " + why;
238using Key = std::vector<KeyField>;
293BATCHLAS_API std::vector<Table>
parse_tables(std::string_view text, std::string_view file);
311BATCHLAS_API
void note_decision(std::string_view op, std::
string spelling, std::
string detail, std::
string tag);
318BATCHLAS_API std::
string format_detail(
double ms, const std::
string* next,
double next_ms);
321BATCHLAS_API std::optional<std::
string>
pin_text(std::string_view op, std::
string* source);
322BATCHLAS_API
void push_pin(std::string_view op, std::
string text,
bool strict = false);
324BATCHLAS_API
void pop_pin(std::string_view op);
327BATCHLAS_API
bool trace_open(std::string_view op, const std::
string& spelling,
bool vendor,
332template <class Choice>
334 static std::mutex mu;
335 static std::set<const Table*> done;
336 std::lock_guard<std::mutex> lock(mu);
337 if (done.count(&t))
return;
338 for (
const auto& row : t.rows)
339 for (
const auto& e : row.ranked) {
342 throw std::runtime_error(t.file +
":" + std::to_string(row.line) +
": " + err);
347template <
class Choice, std::
size_t N>
348bool is_candidate(
const Choice& c,
const std::array<Choice, N>& candidates) {
349 return std::find(candidates.begin(), candidates.end(), c) != candidates.end();
353template <
class Choice, std::
size_t N,
class CanRun>
354std::optional<Choice>
walk(std::string_view op, std::string_view dtype,
const Device& d,
const Key& key,
355 const std::array<Choice, N>& candidates, CanRun& can_run,
const Rules& rules,
357 auto eligible = [&](
const Choice& c) {
360 const char* pin_note = native_only ?
", pinned native" :
"";
362 const bool own = std::any_of(tables.begin(), tables.end(), [&](
const Table* t) { return t->device == d.key; });
363 for (
const Table* t : tables) {
367 for (std::size_t i = 0; i < row->
ranked.size(); ++i) {
369 if (!eligible(*c))
continue;
375 for (std::size_t j = i + 1; j < row->
ranked.size() && !next; ++j)
382 std::vector<Choice> order;
384 for (
const Choice& c : candidates)
385 if (
family_of(c) == fam) order.push_back(c);
386 for (
const Choice& c : candidates)
387 if (std::find(order.begin(), order.end(), c) == order.end()) order.push_back(c);
388 for (
const Choice& c : order) {
389 if (!eligible(c))
continue;
393 if (native_only)
return std::nullopt;
394 throw std::runtime_error(std::string(op) +
": no runnable kernel on " + d.
key);
401template <
class Choice, std::
size_t N,
class CanRun>
403 const std::array<Choice, N>& candidates, CanRun& can_run,
const Rules& rules,
404 const std::string& text,
const std::string& source,
bool strict =
false) {
405 const std::string where = std::string(op) +
": " + source +
"=\"" + text +
"\"";
406 if (text ==
"auto")
return std::nullopt;
407 const std::string refused = where +
" (strict): no " + text +
" candidate can run this shape on " + d.
key;
408 if (text ==
"native") {
409 if (
auto c =
walk(op, dtype, d, key, candidates, can_run, rules,
true))
return c;
410 if (strict)
throw std::invalid_argument(refused);
414 if (text ==
"vendor") {
415 for (
const Choice& k : candidates)
420 if (strict)
throw std::invalid_argument(refused);
426 if (!c)
throw std::invalid_argument(where +
" is not a valid choice: " + err);
428 throw std::invalid_argument(where +
": " +
to_string(*c) +
" is not a compiled " + std::string(op) +
" " +
429 std::string(dtype) +
" candidate");
431 throw std::invalid_argument(where +
": " +
to_string(*c) +
" cannot run this shape on " + d.
key);
446template <
class Choice>
467template <
class Choice, std::
size_t N,
class CanRun>
468Choice
choose(std::string_view op, std::string_view dtype,
const Device& d,
const Key& key,
469 const std::array<Choice, N>& candidates, CanRun&& can_run,
const Rules& rules = {}) {
472 if (
auto c =
detail::resolve_pin(op, dtype, d, key, candidates, can_run, rules, *text, source,
475 return *
detail::walk(op, dtype, d, key, candidates, can_run, rules,
false);
490template <
class Choice, std::
size_t N,
class CanRun>
493 for (
const Choice& c : candidates)
507 template <
class Choice>
509 const Key& fields = {}) {
520 bool active_ =
false;
537template <Backend B,
class T>
545template <Backend B,
class T,
class Choice, std::
size_t N,
class CanRun>
548 auto ok = [&](
const Choice& c) {
return can_run(c, d); };
551 }
catch (
const std::runtime_error&) {
553 std::none_of(candidates.begin(), candidates.end(), ok))
559template <Backend B,
class T,
class Choice, std::
size_t N,
class CanRun>
560Choice
pick(
const OpSpec& op,
const Queue& q,
const Key& key,
const std::array<Choice, N>& candidates,
569template <Backend B,
class T,
class Choice, std::
size_t N,
class CanRun,
class Launch>
570decltype(
auto)
run(
const OpSpec& op,
Queue& q,
const Key& key,
const std::array<Choice, N>& candidates,
574 const Choice c =
pick<B, T>(op, d, key, candidates, can_run);
589 Queue in_order(q,
true);
592 auto result = f(in_order);
ScopedPin(const ScopedPin &)=delete
ScopedPin & operator=(const ScopedPin &)=delete
ScopedPin(std::string_view op, const Choice &c)
ScopedPin(std::string_view op, std::string_view word)
ScopedPin(std::string_view op, std::string_view word, StrictPin)
Prints the last choose() decision for op under BATCHLAS_SELECT_TRACE=1, indents nested scopes,...
TraceScope & operator=(const TraceScope &)=delete
TraceScope(const TraceScope &)=delete
TraceScope(std::string_view op, const Choice &c, const coverage::Shape &shape, NativeFacts facts={}, const Key &fields={})
What this build links and what a run reached, as CSV in BATCHLAS_COVERAGE_OUT.pid; the columns are a ...
Enumerations, scalar traits and scalar concepts shared by every public header.
constexpr std::string_view to_string(MatrixFormat v)
Spelling of v as in source.
Backend
Library family an entry point dispatches to, used as a template argument.
auto on_in_order_queue(Queue &q, F &&f)
Runs f(queue) on q if it is in order, else on an in-order queue that first waits on q's pending work ...
const Device & describe(const batchlas::Device &dev, Backend b, bool has_vendor)
The facts of dev for backend b; memoized per (device, backend, has_vendor).
const Device & device_of(const Queue &q, Lib vendor=Lib::none)
The facts of q's device, with Device::has_vendor answering for the library group vendor.
std::optional< Choice > parse(std::string_view text, std::string *err=nullptr)
Accepts the long form and positional shorthand ("lpanel:8"); exact case, no whitespace.
Table parse_table(std::string_view text, std::string_view file)
Parses one single-dtype table: op and device from the header or file name, dtype from the header.
Lib
The library group an op's Vendor family calls; an op names exactly one (OpSpec::vendor).
std::string to_string(const Choice &c)
Output spelling, always the long form: family[:field=value]...
constexpr bool has_library(Lib l)
Is library group l compiled in for backend B? Always false for Lib::none.
constexpr std::array< std::string_view, 2 > kBlockedThenVendor
default last resort
constexpr const char * library_name(Lib l)
constexpr std::string_view dtype_name()
The dtype key of a table row (the first, exact key of every tuned file): float, double,...
constexpr auto all_of()
Every alternative of a field-less choice variant, in declaration (= tie-break) order.
Choice pick(const OpSpec &op, const Device &d, const Key &key, const std::array< Choice, N > &candidates, CanRun &&can_run)
choose(), with "nothing runnable" in a build without the op's library reported as NoRouteError plus a...
std::span< const EmbeddedTable > embedded_tables()
generated from tuned/*.txt
void no_vendor(const OpSpec &op)
The Vendor arm of an op without its library: record the miss and throw NoRouteError.
std::string_view family_of(const Choice &c)
The family name of c, e.g. "lpanel" for lpanel:panel=8.
decltype(auto) run(const OpSpec &op, Queue &q, const Key &key, const std::array< Choice, N > &candidates, CanRun &&can_run, coverage::Shape shape, const Key &trace_fields, Launch &&launch)
A public entry point after validation: pick, open the trace/coverage scope (the shape's scalar and ba...
Device device_from_key(std::string_view key)
Key, family and arch only; the capability fields stay default. Tables and tests use it.
std::vector< const Table * > tables_in_borrow_order(std::string_view op, std::string_view dtype, const Device &d)
§5.5: own key; same family at-or-below by nearest arch, then above; then sm, gfx, intel,...
std::string apple_device_key(std::string_view device_name)
The table key of an Apple GPU from its device name: "Apple M5 Max" -> "apple_m5_max".
std::vector< KeyField > Key
void throw_no_vendor_route(Op op, Backend backend, const char *library)
The single funnel for "nothing can serve this call": records the miss, then throws NoRouteError.
NativeFacts native_facts(const std::array< Choice, N > &candidates, CanRun &&can_run)
Over the op's candidates: any non-vendor compiled, and any of those passing can_run.
Choice choose(std::string_view op, std::string_view dtype, const Device &d, const Key &key, const std::array< Choice, N > &candidates, CanRun &&can_run, const Rules &rules={})
The only selection algorithm: the pin, else the first candidate passing can_run in the nearest row of...
std::vector< Table > parse_tables(std::string_view text, std::string_view file)
Parses one tuned table file, named op.device.txt with the exact key dtype first, into one Table per d...
void require_device_fp64(const Queue &ctx, const char *op)
Throws throw_no_device_fp64 for a double or complex<double> T on a GPU without fp64.
bool pin_strict(std::string_view op)
void push_pin(std::string_view op, std::string text, bool strict=false)
void note_borrow(std::string_view op, std::string_view dtype, const Device &d, const Table &t)
void warn_pin_fallback(std::string_view op, std::string_view word, const Device &d)
void for_each_family(F &&f)
bool trace_open(std::string_view op, const std::string &spelling, bool vendor, const coverage::Shape &shape, const NativeFacts &facts, const Key &fields)
std::optional< std::string > pin_text(std::string_view op, std::string *source)
std::string format_detail(double ms, const std::string *next, double next_ms)
bool parse_int(std::string_view t, int &out)
bool trace_enabled() noexcept
std::vector< std::string_view > split(std::string_view s, char sep)
void note_decision(std::string_view op, std::string spelling, std::string detail, std::string tag)
std::optional< Choice > resolve_pin(std::string_view op, std::string_view dtype, const Device &d, const Key &key, const std::array< Choice, N > &candidates, CanRun &can_run, const Rules &rules, const std::string &text, const std::string &source, bool strict=false)
void pop_pin(std::string_view op)
std::optional< Choice > walk(std::string_view op, std::string_view dtype, const Device &d, const Key &key, const std::array< Choice, N > &candidates, CanRun &can_run, const Rules &rules, bool native_only)
bool is_candidate(const Choice &c, const std::array< Choice, N > &candidates)
std::string entry_detail(const TableRow &row, std::size_t i, const TableEntry *next)
void validate(const Table &t)
std::string table_tag(const Table &t, const Device &d, bool own_table_exists)
std::size_t parse_count()
table files parsed since load (the loader's cost)
void set_builtin_tables(std::vector< std::pair< std::string, std::string > > files)
void use_embedded_tables()
constexpr std::string_view op_name(Op o)
constexpr ScalarKind scalar_kind_of
Backend deduction from the Queue and the USM pointer check.
Link facts for if constexpr gates: is an op's vendor library compiled in for a backend,...
A handle to one SYCL device: its type and its index among devices of that type.
Completion handle of enqueued work, returned by every entry point.
Where BatchLAS work runs: a device, a backend, an ordered SYCL queue and a workspace arena.
Event get_event() const
An Event that completes after everything enqueued on this Queue so far.
void enqueue(Event &event)
Order all later work on this Queue after event, without a host wait.
bool in_order() const
True for an in-order Queue.
Device device() const
The device this Queue runs on.
The device facts a can_run predicate and the table lookup read (§5.1).
std::int64_t slm_per_item
local memory the runtime adds per work-item (Metal: 8)
int arch_number
120, 89, 90 (gfx90a), 5 (apple_m5_max), 0 for cpu
int max_wg
MAX_WORK_GROUP_SIZE.
constexpr std::int64_t slm_budget_for(int wg) const
slm_budget for a launch of wg work-items (>= slm_budget when wg <= max_wg).
std::int64_t slm_budget
LOCAL_MEM_SIZE less a 4 KiB reserve, bytes: any work-group size.
bool has_vendor
the asking op's library group (OpSpec::vendor) is compiled in
std::string family
"sm", "gfx", "intel", "apple", "cpu": borrowing stays in a family first
std::int64_t max_groups_yz
Work-groups in SYCL dim 0/1 of a 2-D/3-D launch (CUDA grid z/y); Metal: 2^32 - 1.
std::string key
table key: "sm_120", "gfx90a", "intel", "apple_m5_max", "cpu"
A tuned/*.txt file compiled into the library: its basename and text.
constexpr FamilyName(const char(&a)[N])
KeyField(std::string_view n, std::string_view v)
KeyField(std::string_view n, std::int64_t v)
Base of a family with no knobs: struct Cta : NoFields<"cta"> {};.
static constexpr std::array< std::string_view, 0 > fields
std::array< int, 0 > values() const
bool operator==(const NoFields &) const =default
static constexpr std::string_view name
What an op's choice.hh declares once: its name, the library its Vendor family calls (one gate for can...
Lib vendor
Lib::none: the op has no vendor family.
constexpr std::string_view name() const
table, pin and trace name
What an op adds to the generic walk: its last-resort order.
std::span< const std::string_view > last_resort
Families in generality order (§5.5); the rest follow in candidate-list order.
Tag for a strict ScopedPin: a class word ("native", "vendor") that cannot serve the shape throws std:...
One ranked candidate of a row: its spelling and milliseconds per call for the whole batch.
One entry of a table's # keys: line.
double weight
":log:<w>": this key's share of the distance
bool log
false: ":exact", compared as text
One measured (or transcribed) shape and every candidate ranked there, fastest first.
std::vector< double > log2_keys
log2 of each :log key, 0 for :exact
std::vector< TableEntry > ranked
bool timed
False for a transcribed row ("<spelling> -" entries, every ms 0): ranked, never timed.
std::vector< std::string > keys
in Table::keys order
One dtype's table: a tuned/<op>.<device>.txt file holds one per dtype.
std::vector< TableKey > keys
std::string file
basename, e.g. "potrf.sm_120.txt"
std::shared_ptr< Memo > memo
std::vector< TableRow > rows
std::string source
the header's source= value, e.g. "transcribed:2b46acab"
bool is_override
read from BATCHLAS_TUNED_DIR
const TableRow * nearest(const Key &key) const
Rows matching the longest prefix of the :exact keys (in '# keys:' order; exact keys are dropped from ...
const TableRow * nearest_scan(const std::vector< std::string > &kv, const std::vector< double > &kl) const
The coverage row's native_route_existed / native_route_supported (tri-state, -1 unknown).
std::visit over a choice with one lambda per family.
batchlas::Device, batchlas::Event and batchlas::Queue: where work runs and how it is ordered.