blob: 261e7d2d9c374f41da79e950224a8cdb0ef320a1 [file] [log] [blame]
David Manselle39334c2018-07-06 17:53:35 +01001/*
Georgios Pinitas5aa1a0b2020-07-02 20:02:20 +01002 * Copyright (c) 2018-2020 Arm Limited.
David Manselle39334c2018-07-06 17:53:35 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
Georgios Pinitas5aa1a0b2020-07-02 20:02:20 +010025#include "arm_gemm.hpp"
Georgios Pinitas7cd26d42019-01-09 18:35:17 +000026
David Mansell318c9f42020-07-08 13:28:45 +010027#include <cstdint>
Georgios Pinitas7cd26d42019-01-09 18:35:17 +000028#include <functional>
David Manselle39334c2018-07-06 17:53:35 +010029
30namespace arm_gemm {
31
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010032/* Structure describing an implementation. For each supported combination
33 * of types, a static list of these structures is built up to describe the
34 * implementations available.
35 */
36template<typename Top, typename Tret, class OutputStage = Nothing>
Georgios Pinitas7cd26d42019-01-09 18:35:17 +000037struct GemmImplementation {
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010038 const GemmMethod method;
39 const char * name;
40 std::function<bool(const GemmArgs &, const OutputStage &)> is_supported;
David Mansell318c9f42020-07-08 13:28:45 +010041 std::function<uint64_t(const GemmArgs &, const OutputStage &)> cycle_estimate;
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010042 std::function<GemmCommon<Top, Tret> *(const GemmArgs &, const OutputStage &)> instantiate;
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010043
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010044 bool do_is_supported(const GemmArgs &args, const OutputStage &os) const {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010045 if (is_supported != nullptr) {
46 return is_supported(args, os);
47 } else {
48 return true;
49 }
50 }
51
David Mansell318c9f42020-07-08 13:28:45 +010052 uint64_t do_cycle_estimate(const GemmArgs &args, const OutputStage &os) const {
53 if (cycle_estimate != nullptr) {
54 return cycle_estimate(args, os);
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010055 } else {
David Mansell318c9f42020-07-08 13:28:45 +010056 return 0;
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010057 }
58 }
59
David Mansell318c9f42020-07-08 13:28:45 +010060 GemmImplementation(const GemmImplementation &) = default;
61 GemmImplementation &operator= (const GemmImplementation &) = default;
62
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010063 GemmCommon<Top, Tret> *do_instantiate(const GemmArgs &args, const OutputStage &os) const {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010064 return instantiate(args, os);
65 }
David Mansell318c9f42020-07-08 13:28:45 +010066
67 GemmImplementation(GemmMethod m, const char *n,
68 std::function<bool(const GemmArgs &, const OutputStage &)> is_supported, std::function<bool(const GemmArgs &, const OutputStage &)> is_recommended,
69 std::function<GemmCommon<Top, Tret> *(const GemmArgs &, const OutputStage &)> instantiate) :
70 method(m), name(n), is_supported(is_supported),
71 cycle_estimate( [is_recommended](const GemmArgs &args, const OutputStage &os) { return (is_recommended == nullptr) ? 0 : (is_recommended(args, os) ? 0 : UINT64_MAX); } ),
72 instantiate(instantiate) { }
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010073};
74
75/* Slightly different version of above for straightforward GEMMs with no
76 * output stage, so the std::functions there don't have to deal with the
77 * unnecessary second argument. */
78template<typename Top, typename Tret>
79struct GemmImplementation<Top, Tret, Nothing> {
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010080 const GemmMethod method;
81 const char * name;
82 std::function<bool(const GemmArgs &)> is_supported;
David Mansell318c9f42020-07-08 13:28:45 +010083 std::function<uint64_t(const GemmArgs &)> cycle_estimate;
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010084 std::function<GemmCommon<Top, Tret> *(const GemmArgs &)> instantiate;
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010085
Georgios Pinitas48b3ef82019-10-14 19:03:09 +010086 bool do_is_supported(const GemmArgs &args, const Nothing &) const {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010087 if (is_supported != nullptr) {
88 return is_supported(args);
89 } else {
90 return true;
91 }
92 }
93
David Mansell318c9f42020-07-08 13:28:45 +010094 uint64_t do_cycle_estimate(const GemmArgs &args, const Nothing &) const {
95 if (cycle_estimate != nullptr) {
96 return cycle_estimate(args);
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010097 } else {
David Mansell318c9f42020-07-08 13:28:45 +010098 return 0;
Georgios Pinitascfa2bba2019-06-27 17:00:52 +010099 }
100 }
101
Georgios Pinitas48b3ef82019-10-14 19:03:09 +0100102 GemmCommon<Top, Tret> *do_instantiate(const GemmArgs &args, const Nothing &) const {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100103 return instantiate(args);
104 }
David Mansell318c9f42020-07-08 13:28:45 +0100105
106
107 static GemmImplementation with_estimate(GemmMethod m, const char *n,
108 std::function<bool(const GemmArgs &)> is_supported, std::function<uint64_t(const GemmArgs &)> cycle_estimate,
109 std::function<GemmCommon<Top, Tret> *(const GemmArgs &)> instantiate) {
110 GemmImplementation impl(m,n);
111
112 impl.is_supported=is_supported;
113 impl.cycle_estimate=cycle_estimate;
114 impl.instantiate=instantiate;
115
116 return impl;
117 }
118
119 GemmImplementation(GemmMethod m, const char * n) : method(m), name(n), is_supported(nullptr), cycle_estimate(nullptr), instantiate(nullptr) {}
120
121 GemmImplementation(GemmMethod m, const char *n,
122 std::function<bool(const GemmArgs &)> is_supported, std::function<bool(const GemmArgs &)> is_recommended,
123 std::function<GemmCommon<Top, Tret> *(const GemmArgs &)> instantiate) :
124 method(m), name(n), is_supported(is_supported),
125 cycle_estimate( [is_recommended](const GemmArgs &args) -> uint64_t { return (is_recommended == nullptr) ? 0 : (is_recommended(args) ? 0 : UINT64_MAX); } ),
126 instantiate(instantiate) { }
127
128 GemmImplementation(const GemmImplementation &) = default;
129 GemmImplementation &operator=(const GemmImplementation &) = default;
David Manselle39334c2018-07-06 17:53:35 +0100130};
131
132/* "Master" function implemented for each valid combination of types.
133 * Returns a list of GEMM implementation descriptors for processing by the
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000134 * other functions, terminated by an implementation with
135 * method==GemmMethod::DEFAULT. */
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100136template<typename Top, typename Tret, class OutputStage = Nothing>
137const GemmImplementation<Top, Tret, OutputStage> *gemm_implementation_list();
David Manselle39334c2018-07-06 17:53:35 +0100138
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000139/*
140 * Select a GEMM implementation for the given arguments.
141 *
David Mansell318c9f42020-07-08 13:28:45 +0100142 * The logic here returns the method on the list which supports the
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000143 * requested problem parameters, matches the provided filters (method and/or
David Mansell318c9f42020-07-08 13:28:45 +0100144 * name string match) and offers the lowest cycle estimate. A cycle
145 * estimate of '0' is treated as a special value, causing the corresponding
146 * method to be selected immediately.
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000147 *
148 * If no method supports the requested parameters and passes the filters,
149 * this function returns false and doesn't touch the provided pointer
150 * reference.
151 */
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100152template<typename Top, typename Tret, class OutputStage>
Georgios Pinitas48b3ef82019-10-14 19:03:09 +0100153bool find_implementation(const GemmArgs &args, const OutputStage &os, const GemmImplementation<Top, Tret, OutputStage> * &impl) {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100154 auto gemms = gemm_implementation_list<Top, Tret, OutputStage>();
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000155 const GemmConfig *cfg = args._cfg;
David Manselle39334c2018-07-06 17:53:35 +0100156
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100157 const GemmImplementation<Top, Tret, OutputStage> *saved_impl = nullptr;
David Mansell318c9f42020-07-08 13:28:45 +0100158 uint64_t best_estimate = 0;
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000159
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100160 for (const GemmImplementation<Top, Tret, OutputStage> *i = gemms; i->method != GemmMethod::DEFAULT; i++) {
David Manselle39334c2018-07-06 17:53:35 +0100161 /* Skip if this implementation doesn't support these args. */
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100162 if (!i->do_is_supported(args, os)) {
David Manselle39334c2018-07-06 17:53:35 +0100163 continue;
164 }
165
166 /* Skip if a specific method is requested and this is a different one. */
167 if (cfg && cfg->method != GemmMethod::DEFAULT && i->method != cfg->method) {
168 continue;
169 }
170
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000171 /* Skip if a filter is to be applied and it doesn't match. */
172 if (cfg && cfg->filter != "" && !strstr(i->name, cfg->filter.c_str())) {
David Manselle39334c2018-07-06 17:53:35 +0100173 continue;
174 }
175
David Mansell318c9f42020-07-08 13:28:45 +0100176 /* Test the cycle estimate */
177 uint64_t estimate = i->do_cycle_estimate(args, os);
178
179 /* Short circuit - if the estimate is zero, return this one immediately. */
180 if (estimate==0) {
181 impl=i;
182 return true;
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000183 }
184
David Mansell318c9f42020-07-08 13:28:45 +0100185 /* Otherwise, remember this is our best so far if we don't yet have
186 * a valid candidate, or we beat the estimate. */
187 if ((saved_impl == nullptr) || (estimate < best_estimate)) {
188 saved_impl = i;
189 best_estimate = estimate;
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000190 }
David Manselle39334c2018-07-06 17:53:35 +0100191 }
192
David Mansell318c9f42020-07-08 13:28:45 +0100193 /* Return whichever method gave the best estimate. */
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000194 if (saved_impl != nullptr) {
195 impl = saved_impl;
David Manselle39334c2018-07-06 17:53:35 +0100196 return true;
197 }
198
199 return false;
200}
201
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100202template<typename Top, typename Tret, class OutputStage>
Georgios Pinitas48b3ef82019-10-14 19:03:09 +0100203std::vector<KernelDescription> get_compatible_kernels(const GemmArgs &args, const OutputStage &os) {
Georgios Pinitas14613832019-03-01 19:07:11 +0000204 std::vector<KernelDescription> res;
205
206 /* Find out what the default implementation in so we can set the flag accordingly later. */
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100207 const GemmImplementation<Top, Tret, OutputStage> *default_impl;
208 find_implementation(args, os, default_impl);
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000209
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100210 auto gemms = gemm_implementation_list<Top, Tret, OutputStage>();
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000211
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100212 for (const GemmImplementation<Top, Tret, OutputStage> *i = gemms; i->method != GemmMethod::DEFAULT; i++) {
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000213 /* Check that this implementation supports the presented problem. */
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100214 if (!i->do_is_supported(args, os)) {
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000215 continue;
216 }
217
David Mansell318c9f42020-07-08 13:28:45 +0100218 res.push_back(KernelDescription(i->method, i->name, i==default_impl, i->do_cycle_estimate(args, os)));
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000219 }
220
221 return res;
222}
223
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100224template<typename Top, typename Tret, class OutputStage>
Georgios Pinitas48b3ef82019-10-14 19:03:09 +0100225UniqueGemmCommon<Top, Tret> gemm(const GemmArgs &args, const OutputStage &os) {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100226 const GemmImplementation<Top, Tret, OutputStage> *impl;
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000227
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100228 if (find_implementation<Top, Tret, OutputStage>(args, os, impl)) {
229 return UniqueGemmCommon<Top, Tret>(impl->do_instantiate(args, os));
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000230 }
231
232 return UniqueGemmCommon<Top, Tret>(nullptr);
233}
234
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100235template<typename Top, typename Tret, class OutputStage>
Georgios Pinitas48b3ef82019-10-14 19:03:09 +0100236KernelDescription get_gemm_method(const GemmArgs &args, const OutputStage &os) {
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100237 const GemmImplementation<Top, Tret, OutputStage> *impl;
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000238
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100239 if (find_implementation<Top, Tret>(args, os, impl)) {
Georgios Pinitas7cd26d42019-01-09 18:35:17 +0000240 return KernelDescription(impl->method, impl->name);
241 }
242
243 /* This shouldn't happen - there should always be at least one valid implementation. */
244 return KernelDescription();
245}
246
Georgios Pinitascfa2bba2019-06-27 17:00:52 +0100247} // namespace arm_gemm