1 | /******************************************************************************* |
2 | * Copyright 2019-2022 Intel Corporation |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | *******************************************************************************/ |
16 | |
17 | #include <cctype> |
18 | |
19 | #include "utils/parser.hpp" |
20 | |
21 | #include "dnnl_common.hpp" |
22 | |
23 | namespace parser { |
24 | |
25 | bool last_parsed_is_problem = false; |
26 | const size_t eol = std::string::npos; |
27 | std::stringstream help_ss; |
28 | |
29 | static const std::string benchdnn_url |
30 | = "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn" ; |
31 | static const std::string doc_url = benchdnn_url + "/doc/" ; |
32 | |
33 | namespace parser_utils { |
34 | std::string get_pattern(const std::string &option_name, bool with_args) { |
35 | std::string s = std::string("--" ) + option_name; |
36 | if (with_args) s += "=" ; |
37 | return s; |
38 | } |
39 | |
40 | void add_option_to_help(const std::string &option, |
41 | const std::string &help_message, bool with_args) { |
42 | static std::vector<std::string> help_added; |
43 | for (const auto &e : help_added) |
44 | if (e == option) return; |
45 | |
46 | std::string option_str = get_pattern(option, with_args); |
47 | help_ss << option_str << help_message << "\n" ; |
48 | help_added.push_back(option); |
49 | } |
50 | } // namespace parser_utils |
51 | |
52 | // vector types |
53 | bool parse_dir(std::vector<dir_t> &dir, const std::vector<dir_t> &def_dir, |
54 | const char *str, const std::string &option_name /* = "dir"*/) { |
55 | static const std::string help |
56 | = "DIR (Default: `FWD_B` when bias applicable, `FWD_D` " |
57 | "otherwise)\n Specifies propagation kind `DIR` for operation. " |
58 | "Has bias support incorporated with `_B` suffix.\n `DIR` " |
59 | "values can be `FWD_B`, `FWD_D`, `FWD_I`, `BWD_D`, `BWD_WB`, " |
60 | "`BWD_W` and `BWD_DW`.\n More details at " |
61 | + doc_url + "knobs_dir.md\n" ; |
62 | return parse_vector_option(dir, def_dir, str2dir, str, option_name, help); |
63 | } |
64 | |
65 | bool parse_dt(std::vector<dnnl_data_type_t> &dt, |
66 | const std::vector<dnnl_data_type_t> &def_dt, const char *str, |
67 | const std::string &option_name /* = "dt"*/) { |
68 | static const std::string help |
69 | = "DT (Default: `f32`)\n Specifies data type `DT` for source " |
70 | "and/or destination.\n `DT` values can be `f32`, `bf16`, " |
71 | "`f16`, `s32`, `s8`, `u8`.\n" ; |
72 | return parse_vector_option(dt, def_dt, str2dt, str, option_name, help); |
73 | } |
74 | |
75 | bool parse_multi_dt(std::vector<std::vector<dnnl_data_type_t>> &dt, |
76 | const std::vector<std::vector<dnnl_data_type_t>> &def_dt, |
77 | const char *str, const std::string &option_name /* = "sdt"*/) { |
78 | static const std::string help |
79 | = "DT0:DT1[:DTi] (Default: `f32` for all)\n When the driver " |
80 | "supports the notion of multiple sources, the option specifies a " |
81 | "data type `DTi` for a source `i`.\n When the driver supports " |
82 | "the notion of source, weights (optional), and destination, the " |
83 | "option specifies data types for source, weights (optional) and " |
84 | "destination correspondently.\n The option may support " |
85 | "broadcast semantics (check the driver online documentation), " |
86 | "when a single value will be used for all inputs.\n `DT` " |
87 | "values can be `f32`, `bf16`, `f16`, `s32`, `s8`, `u8`.\n" ; |
88 | return parse_multivector_option(dt, def_dt, str2dt, str, option_name, help); |
89 | } |
90 | |
91 | bool parse_tag(std::vector<std::string> &tag, |
92 | const std::vector<std::string> &def_tag, const char *str, |
93 | const std::string &option_name /* = "tag"*/) { |
94 | static const std::string help |
95 | = "TAG (Default: `any` for compute-bound, `abx` for rest)\n " |
96 | "Specifies memory format tag `TAG` for source, weights, or " |
97 | "destination.\n Valid `TAG` values can be found at " |
98 | + doc_url + "knobs_tag.md\n" ; |
99 | |
100 | auto ret_string = [](const char *str) { return std::string(str); }; |
101 | bool ok = parse_vector_option( |
102 | tag, def_tag, ret_string, str, option_name, help); |
103 | if (!ok) return false; |
104 | |
105 | for (size_t i = 0; i < tag.size(); i++) { |
106 | if (check_tag(tag[i], allow_enum_tags_only) != OK) { |
107 | if (allow_enum_tags_only && check_tag(tag[i]) == OK) { |
108 | fprintf(stderr, |
109 | "ERROR: tag `%s` is valid but not found in " |
110 | "`dnnl::memory::format_tag`. To force the testing with " |
111 | "this tag, please specify `--allow-enum-tags-only=0` " |
112 | "prior to any tag option.\n" , |
113 | tag[i].c_str()); |
114 | } else { |
115 | fprintf(stderr, |
116 | "ERROR: unknown or invalid tag: `%s`, exiting...\n" , |
117 | tag[i].c_str()); |
118 | } |
119 | exit(2); |
120 | } |
121 | } |
122 | return true; |
123 | } |
124 | |
125 | bool parse_multi_tag(std::vector<std::vector<std::string>> &tag, |
126 | const std::vector<std::vector<std::string>> &def_tag, const char *str, |
127 | const std::string &option_name /* = "stag"*/) { |
128 | static const std::string help |
129 | = "TAG0:TAG1[:TAGi] (Default: `any` for compute-bound, `abx` " |
130 | "for rest)\n Specifies memory format tag `TAGi` for source " |
131 | "i.\n Valid `TAGi` values can be found at " |
132 | + doc_url + "knobs_tag.md\n" ; |
133 | auto ret_string = [](const char *str) { return std::string(str); }; |
134 | return parse_multivector_option( |
135 | tag, def_tag, ret_string, str, option_name, help); |
136 | } |
137 | |
138 | bool parse_mb(std::vector<int64_t> &mb, const std::vector<int64_t> &def_mb, |
139 | const char *str, const std::string &option_name /* = "mb"*/) { |
140 | static const std::string help |
141 | = "UINT (Default: `0`)\n Overrides mini-batch value " |
142 | "specified in a problem descriptor with `UINT` value.\n When " |
143 | "set to `0`, takes no effect.\n" ; |
144 | return parse_vector_option(mb, def_mb, atoi, str, option_name, help); |
145 | } |
146 | |
147 | bool parse_attr_oscale(std::vector<attr_t::scale_t> &oscale, const char *str, |
148 | const std::string &option_name /* = "attr-oscale"*/) { |
149 | static const std::string help |
150 | = "POLICY[:SCALE[*]]\n Specifies output scale attribute.\n " |
151 | "More details at " |
152 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn/" |
153 | "doc/knobs_attr.md\n" ; |
154 | return parse_subattr(oscale, str, option_name, help); |
155 | } |
156 | |
157 | bool parse_attr_post_ops(std::vector<attr_t::post_ops_t> &po, const char *str, |
158 | const std::string &option_name /* = "attr-post-ops"*/) { |
159 | static const std::string help |
160 | = "POST-OPS\n Specifies post-ops attribute. `POST-OPS` syntax " |
161 | "is one of those:\n * SUM[:SCALE[:ZERO_POINT[:DATA_TYPE]]]\n " |
162 | " * ELTWISE[:ALPHA[:BETA[:SCALE]]]\n * " |
163 | "DW:KkSsPp[:DST_DT[:OUTPUTSCALE]]\n * " |
164 | "BINARY:DT[:POLICY[:TAG]]\n More details at " |
165 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn/" |
166 | "doc/knobs_attr.md\n" ; |
167 | return parse_subattr(po, str, option_name, help); |
168 | } |
169 | |
170 | bool parse_attr_scales(std::vector<attr_t::arg_scales_t> &scales, |
171 | const char *str, const std::string &option_name /* = "attr-scales"*/) { |
172 | static const std::string help |
173 | = "ARG:POLICY[:SCALE[*]][+...]\n Specifies input scales " |
174 | "attribute.\n More details at " |
175 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn/" |
176 | "doc/knobs_attr.md\n" ; |
177 | return parse_subattr(scales, str, option_name, help); |
178 | } |
179 | |
180 | bool parse_attr_zero_points(std::vector<attr_t::zero_points_t> &zp, |
181 | const char *str, |
182 | const std::string &option_name /* = "attr-zero-points"*/) { |
183 | static const std::string help |
184 | = "ARG:POLICY:ZEROPOINT[*][+...]\n Specifies zero-points " |
185 | "attribute.\n More details at " |
186 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn/" |
187 | "doc/knobs_attr.md\n" ; |
188 | return parse_subattr(zp, str, option_name, help); |
189 | } |
190 | |
191 | bool parse_attr_scratchpad_mode( |
192 | std::vector<dnnl_scratchpad_mode_t> &scratchpad_mode, |
193 | const std::vector<dnnl_scratchpad_mode_t> &def_scratchpad_mode, |
194 | const char *str, |
195 | const std::string &option_name /* = "attr-scratchpad"*/) { |
196 | static const std::string help |
197 | = "MODE (Default: `library`)\n Specifies scratchpad " |
198 | "attribute. `MODE` values can be `library` or `user`.\n More " |
199 | "details at " |
200 | + doc_url + "knobs_attr.md\n" ; |
201 | return parse_vector_option(scratchpad_mode, def_scratchpad_mode, |
202 | str2scratchpad_mode, str, option_name, help); |
203 | } |
204 | |
205 | bool parse_attr_fpmath_mode(std::vector<dnnl_fpmath_mode_t> &fpmath_mode, |
206 | const std::vector<dnnl_fpmath_mode_t> &def_fpmath_mode, const char *str, |
207 | const std::string &option_name /* = "attr-fpmath"*/) { |
208 | static const std::string help |
209 | = "MODE (Default: `strict`)\n Specifies fpmath_mode " |
210 | "attribute. `MODE` values can be `strict` or `bf16`.\n" ; |
211 | return parse_vector_option(fpmath_mode, def_fpmath_mode, str2fpmath_mode, |
212 | str, option_name, help); |
213 | } |
214 | |
215 | bool parse_axis(std::vector<int> &axis, const std::vector<int> &def_axis, |
216 | const char *str, const std::string &option_name /* = "axis"*/) { |
217 | static const std::string help |
218 | = "UINT (Default: `1`)\n Specifies axis dimension `UINT` for " |
219 | "an operation.\n" ; |
220 | return parse_vector_option(axis, def_axis, atoi, str, option_name, help); |
221 | } |
222 | |
223 | bool parse_test_pattern_match(const char *&match, const char *str, |
224 | const std::string &option_name /* = "match"*/) { |
225 | static const std::string help |
226 | = "REGEX (Default: not specified)\n `REGEX` is a string " |
227 | "literal representing a regular expression that filters problem " |
228 | "descriptors.\n Matched descriptors are executed, rest are " |
229 | "skipped.\n" ; |
230 | const char *def_match = "" ; |
231 | const auto chars2chars = [](const char *str) { return str; }; |
232 | return parse_single_value_option( |
233 | match, def_match, chars2chars, str, option_name, help); |
234 | } |
235 | |
236 | bool parse_inplace(std::vector<bool> &inplace, |
237 | const std::vector<bool> &def_inplace, const char *str, |
238 | const std::string &option_name /* = "inplace"*/) { |
239 | static const std::string help |
240 | = "BOOL (Default: `false`)\n Instructs the driver to use " |
241 | "same memory data handle for source and destination when set to " |
242 | "`true`.\n" ; |
243 | return parse_vector_option( |
244 | inplace, def_inplace, str2bool, str, option_name, help); |
245 | } |
246 | |
247 | bool parse_skip_nonlinear(std::vector<bool> &skip, |
248 | const std::vector<bool> &def_skip, const char *str, |
249 | const std::string &option_name /* = "skip-nonlinear"*/) { |
250 | static const std::string help |
251 | = "BOOL (Default: `false`)\n Instructs the driver to treat " |
252 | "transcendental activations as linear when set to `true`.\n" ; |
253 | return parse_vector_option( |
254 | skip, def_skip, str2bool, str, option_name, help); |
255 | } |
256 | |
257 | bool parse_strides(std::vector<vdims_t> &strides, |
258 | const std::vector<vdims_t> &def_strides, const char *str, |
259 | const std::string &option_name /* = "strides"*/) { |
260 | static const std::string help |
261 | = "DIMS_SRC:DIMS_WEI:DIMS_DST (Default: not specified)\n " |
262 | "Specifies strides `DIMS_ARG` for correspondent `ARG`.\n If " |
263 | "correspondent `DIMS_ARG` is empty, it does not take an " |
264 | "effect.\n More details at " |
265 | + doc_url + "driver_matmul.md\n" ; |
266 | auto str2strides = [&](const char *str) -> vdims_t { |
267 | vdims_t strides(STRIDES_SIZE); |
268 | parse_multivector_str(strides, vdims_t(), atoi, str, ':', 'x'); |
269 | return strides; |
270 | }; |
271 | return parse_vector_option( |
272 | strides, def_strides, str2strides, str, option_name, help); |
273 | } |
274 | |
275 | bool parse_trivial_strides(std::vector<bool> &ts, |
276 | const std::vector<bool> &def_ts, const char *str, |
277 | const std::string &option_name /* = "trivial-strides"*/) { |
278 | static const std::string help |
279 | = "BOOL (Default: `false`)\n Instructs the driver to use " |
280 | "dense (trivial) strides when set to `true`.\n" ; |
281 | return parse_vector_option(ts, def_ts, str2bool, str, option_name, help); |
282 | } |
283 | |
284 | bool parse_scale_policy(std::vector<policy_t> &policy, |
285 | const std::vector<policy_t> &def_policy, const char *str, |
286 | const std::string &option_name /*= "scaling"*/) { |
287 | static const std::string help |
288 | = "POLICY (Default: `common`)\n Specifies a mask for scales " |
289 | "to be applied.\n More details at " |
290 | + doc_url + "knobs_attr.md\n" ; |
291 | return parse_vector_option( |
292 | policy, def_policy, attr_t::str2policy, str, option_name, help); |
293 | } |
294 | |
295 | // plain types |
296 | bool parse_perf_template(const char *&pt, const char *pt_def, |
297 | const char *pt_csv, const char *str, |
298 | const std::string &option_name /* = "perf-template"*/) { |
299 | static const std::string help |
300 | = "TEMPLATE (Default: `def`)\n Specifies performance output " |
301 | "template for perf mode. `TEMPLATE` values can be `def`, `csv` " |
302 | "or customized set.\n More details at " |
303 | + doc_url + "knobs_perf_report.md\n" ; |
304 | const auto str2pt = [&pt_def, &pt_csv](const char *str_) { |
305 | const std::string csv_pattern = "csv" ; |
306 | const std::string def_pattern = "def" ; |
307 | if (csv_pattern.find(str_, 0, csv_pattern.size()) != eol) |
308 | return pt_csv; |
309 | else if (def_pattern.find(str_, 0, def_pattern.size()) != eol) |
310 | return pt_def; |
311 | else |
312 | return str_; |
313 | }; |
314 | return parse_single_value_option( |
315 | pt, pt_def, str2pt, str, option_name, help); |
316 | } |
317 | |
318 | bool parse_batch(const bench_f bench, const char *str, |
319 | const std::string &option_name /* = "batch"*/) { |
320 | static const std::string help |
321 | = "FILE\n Instructs the driver to take options and problem " |
322 | "descriptors from a `FILE`.\n" ; |
323 | int status = OK; |
324 | const auto str2batch = [bench](const char *str_) { |
325 | SAFE(batch(str_, bench), CRIT); |
326 | return OK; |
327 | }; |
328 | return parse_single_value_option( |
329 | status, FAIL, str2batch, str, option_name, help); |
330 | } |
331 | |
332 | bool parse_help(const char *str, const std::string &option_name /* = "help"*/) { |
333 | std::string pattern = parser_utils::get_pattern(option_name, false); |
334 | if (pattern.find(str, 0, pattern.size()) == eol) return false; |
335 | |
336 | BENCHDNN_PRINT(0, "%s\n" , help_ss.str().c_str()); |
337 | exit(0); |
338 | } |
339 | |
340 | bool parse_main_help( |
341 | const char *str, const std::string &option_name /* = "help"*/) { |
342 | std::string pattern = parser_utils::get_pattern(option_name, false); |
343 | if (pattern.find(str, 0, pattern.size()) == eol) return false; |
344 | |
345 | static const std::string main_help |
346 | = "Usage:\n benchdnn --<driver> [global_options] " |
347 | "[driver_options] problem_description\n\nList of supported " |
348 | "<drivers> (lower case accepted only):\n * binary\n * " |
349 | "bnorm\n * concat\n * conv\n * deconv\n * eltwise\n " |
350 | " * ip\n * lnorm\n * lrn\n * matmul\n * pool\n * " |
351 | "prelu\n * reduction\n * reorder\n * resampling\n * " |
352 | "rnn\n * shuffle\n * softmax\n * sum\n * " |
353 | "zeropad\n\nFor global and specific driver options, use:\n " |
354 | "benchdnn --<driver> --help\n\nMore details at " |
355 | + benchdnn_url + "\n" ; |
356 | |
357 | BENCHDNN_PRINT(0, "%s\n" , main_help.c_str()); |
358 | exit(0); |
359 | } |
360 | |
361 | // prb_dims_t type |
362 | void parse_prb_vdims( |
363 | prb_vdims_t &prb_vdims, const std::string &str, size_t min_inputs) { |
364 | assert(!str.empty()); |
365 | |
366 | size_t start_pos = 0; |
367 | // `n` is an indicator for a name supplied with dims_t object. |
368 | std::string vdims_str = get_substr(str, start_pos, 'n'); |
369 | // Sanity check that dims start with a digit. |
370 | if (!std::isdigit(vdims_str[0])) { |
371 | BENCHDNN_PRINT(0, "%s\n%s \'%s\'\n" , |
372 | "ERROR: dims are expected to start with an integer value." , |
373 | "Given input:" , str.c_str()); |
374 | exit(1); |
375 | } |
376 | |
377 | parse_multivector_str( |
378 | prb_vdims.vdims, {dims_t()}, atoi, vdims_str, ':', 'x'); |
379 | |
380 | // Expect at least two inputs provided |
381 | SAFE_V(prb_vdims.vdims.size() >= min_inputs ? OK : FAIL); |
382 | |
383 | prb_vdims.ndims = static_cast<int>(prb_vdims.vdims[0].size()); |
384 | // If second and consecutive inputs are provided with less dimensions |
385 | // (ndims0 > ndims1), then fill these tensors with ones to match ndims, |
386 | // e.g., 8x3x5:8 -> 8x3x5:8x1x1 |
387 | // We put this implicit broadcast feature on parser since `ndims` would be |
388 | // set next and can't be updated by driver, but `ndims` mismatch triggers |
389 | // the library `invalid_arguments` error. |
390 | for (int i = 1; i < prb_vdims.n_inputs(); i++) |
391 | if (prb_vdims.ndims > static_cast<int>(prb_vdims.vdims[i].size())) |
392 | prb_vdims.vdims[i].resize(prb_vdims.ndims, 1); |
393 | |
394 | prb_vdims.dst_dims = prb_vdims.vdims[0]; |
395 | for_(int i = 1; i < prb_vdims.n_inputs(); i++) |
396 | for (int d = 0; d < prb_vdims.ndims; ++d) { |
397 | bool has_zero_dim |
398 | = prb_vdims.dst_dims[d] == 0 || prb_vdims.vdims[i][d] == 0; |
399 | prb_vdims.dst_dims[d] = has_zero_dim |
400 | ? 0 |
401 | : std::max(prb_vdims.dst_dims[d], prb_vdims.vdims[i][d]); |
402 | } |
403 | |
404 | if (start_pos != eol) prb_vdims.name = str.substr(start_pos); |
405 | } |
406 | |
407 | void parse_prb_dims(prb_dims_t &prb_dims, const std::string &str) { |
408 | size_t start_pos = 0; |
409 | // `n` is an indicator for a name supplied with dims_t object. |
410 | std::string dims_str = get_substr(str, start_pos, 'n'); |
411 | const auto atoi_except = [&](const char *s) { |
412 | std::string s_(s); |
413 | int64_t value = 0; |
414 | try { |
415 | value = std::stoll(s_); |
416 | } catch (const std::invalid_argument &) { |
417 | BENCHDNN_PRINT(0, "%s\n%s \'%s\'\n" , |
418 | "Error: dims value is expected to be an integer value." , |
419 | "Given input:" , s_.c_str()); |
420 | exit(1); |
421 | } |
422 | return value; |
423 | }; |
424 | parse_vector_str(prb_dims.dims, dims_t(), atoi_except, dims_str, 'x'); |
425 | |
426 | prb_dims.ndims = static_cast<int>(prb_dims.dims.size()); |
427 | |
428 | if (start_pos != eol) prb_dims.name = str.substr(start_pos); |
429 | } |
430 | |
431 | // Global options |
432 | static bool parse_allow_enum_tags_only(const char *str, |
433 | const std::string &option_name = "allow-enum-tags-only" ) { |
434 | static const std::string help |
435 | = "BOOL (Default: `true`)\n Instructs the driver to validate " |
436 | "format tags against the documented tags from " |
437 | "`dnnl_format_tag_t` enumeration only.\n When set to `true`, " |
438 | "the only allowed format tags are the ones from " |
439 | "`dnnl_format_tag_t` enumeration.\n" ; |
440 | return parse_single_value_option( |
441 | allow_enum_tags_only, true, str2bool, str, option_name, help); |
442 | } |
443 | |
444 | static bool parse_attr_same_pd_check(const char *str, |
445 | const std::string &option_name = "attr-same-pd-check" ) { |
446 | static const std::string help |
447 | = "BOOL (Default: `false`)\n Instructs the driver to compare " |
448 | "two primitive descriptors - one with requested attributes and " |
449 | "one without them.\n When set to `true`, check would return " |
450 | "an error if attributes caused fallback to a different " |
451 | "implementation.\n" ; |
452 | return parse_single_value_option( |
453 | attr_same_pd_check, false, str2bool, str, option_name, help); |
454 | } |
455 | |
456 | static bool parse_canonical( |
457 | const char *str, const std::string &option_name = "canonical" ) { |
458 | static const std::string help |
459 | = "BOOL (Default: `false`)\n Instructs the driver to print a " |
460 | "canonical form of a reproducer line.\n When set to `true`, " |
461 | "the driver prints all options and their values, including " |
462 | "default ones.\n" ; |
463 | return parse_single_value_option( |
464 | canonical, false, str2bool, str, option_name, help); |
465 | } |
466 | |
467 | static bool parse_cpu_isa_hints( |
468 | const char *str, const std::string &option_name = "cpu-isa-hints" ) { |
469 | static const std::string help |
470 | = "HINTS (Default: `none`)\n Specifies the ISA specific " |
471 | "hints for CPU engine.\n `HINTS` values can be `none`, " |
472 | "`no_hints` or `prefer_ymm`.\n" ; |
473 | const bool parsed |
474 | = parse_single_value_option(hints, isa_hints_t {isa_hints_t::none}, |
475 | isa_hints_t::str2hints, str, option_name, help); |
476 | if (parsed) init_isa_settings(); |
477 | return parsed; |
478 | } |
479 | |
480 | static bool parse_engine( |
481 | const char *str, const std::string &option_name = "engine" ) { |
482 | static const std::string help |
483 | = "KIND[:INDEX] (Default: `cpu`)\n Instructs the driver to " |
484 | "use an engine with requested `KIND`.\n `KIND` values can be " |
485 | "`cpu` or `gpu`.\n `INDEX` is an integer value specifying " |
486 | "which engine to use if several were identified.\n" ; |
487 | if (!parse_single_value_option(engine_tgt_kind, dnnl_cpu, str2engine_kind, |
488 | str, option_name, help)) |
489 | return false; |
490 | // Parse engine index if present |
491 | std::string s(str); |
492 | auto start_pos = s.find_first_of(':'); |
493 | if (start_pos != eol) engine_index = std::stoi(s.substr(start_pos + 1)); |
494 | |
495 | auto n_devices = dnnl_engine_get_count(engine_tgt_kind); |
496 | if (engine_index >= n_devices) { |
497 | fprintf(stderr, |
498 | "ERROR: requested engine with index %ld is not registered in " |
499 | "the system. Number of devices registered is %ld.\n" , |
500 | (long)engine_index, (long)n_devices); |
501 | exit(2); |
502 | } |
503 | return true; |
504 | } |
505 | |
506 | static bool parse_fast_ref_gpu( |
507 | const char *str, const std::string &option_name = "fast-ref-gpu" ) { |
508 | static const std::string help |
509 | = "BOOL (Default: `true`)\n Instructs the driver to use " |
510 | "faster reference path when doing correctness testing for " |
511 | "`--engine=gpu`.\n When set to `true`, the library best fit " |
512 | "CPU implementation is used to compute the reference path.\n" ; |
513 | bool parsed = parse_single_value_option( |
514 | fast_ref_gpu, true, str2bool, str, option_name, help); |
515 | #if DNNL_CPU_RUNTIME == DNNL_RUNTIME_NONE |
516 | if (parsed && fast_ref_gpu) { |
517 | fast_ref_gpu = false; |
518 | fprintf(stderr, |
519 | "%s driver: WARNING: option `fast_ref_gpu` is not supported " |
520 | "for GPU only configurations.\n" , |
521 | driver_name.c_str()); |
522 | } |
523 | #endif |
524 | return parsed; |
525 | } |
526 | |
527 | bool parse_ctx(std::vector<thr_ctx_t> &ctx, |
528 | const std::vector<thr_ctx_t> &def_ctx, const char *str, |
529 | const std::string &option_name) { |
530 | const std::string name_in_help |
531 | = (option_name == "ctx-init" ) ? "initialization." : "execution." ; |
532 | const std::string help |
533 | = std::string( |
534 | "MAX_CONCURENCY[:CORE_TYPE[:THREADS_PER_CORE]] " |
535 | "(Default:`auto:auto:auto`)\n Specifies the threading " |
536 | "context used during primitive " ) |
537 | + name_in_help |
538 | + std::string( |
539 | "\n MAX_CONCURRENCY is the maximum number of threads.\n " |
540 | " CORE_TYPE enables to select big (value 0) or small " |
541 | "cores (value 1) for hybrid CPUs (TBB runtime only).\n " |
542 | "THREADS_PER_CORE allows to enable/disable hyper-threading " |
543 | "(TBB runtime only).\n" ); |
544 | |
545 | auto str2ctx = [&option_name](const char *str) { |
546 | thr_ctx_t result = default_thr_ctx; |
547 | try { |
548 | size_t start_pos = 0; |
549 | /* concurrency piece */ |
550 | std::string val_str = get_substr(str, start_pos, ':'); |
551 | if (val_str != "auto" ) result.max_concurrency = std::stoll(val_str); |
552 | /* core_type piece */ |
553 | val_str = start_pos != eol ? get_substr(str, start_pos, ':') : "" ; |
554 | if (val_str != "auto" && !val_str.empty()) |
555 | result.core_type = std::stoll(val_str); |
556 | /* nthr_per_core piece */ |
557 | val_str = start_pos != eol ? get_substr(str, start_pos, ':') : "" ; |
558 | if (val_str != "auto" && !val_str.empty()) |
559 | result.nthr_per_core = std::stoll(val_str); |
560 | } catch (const std::invalid_argument &) { |
561 | BENCHDNN_PRINT(0, "%s %s\n" , option_name.c_str(), |
562 | "fields should be 'auto' or integer values" ); |
563 | exit(1); |
564 | } |
565 | |
566 | return result; |
567 | }; |
568 | |
569 | return parse_vector_option(ctx, def_ctx, str2ctx, str, option_name, help); |
570 | } |
571 | |
572 | bool parse_ctx_init(std::vector<thr_ctx_t> &ctx, |
573 | const std::vector<thr_ctx_t> &def_ctx, const char *str) { |
574 | return parse_ctx(ctx, def_ctx, str, "ctx-init" ); |
575 | } |
576 | bool parse_ctx_exe(std::vector<thr_ctx_t> &ctx, |
577 | const std::vector<thr_ctx_t> &def_ctx, const char *str) { |
578 | return parse_ctx(ctx, def_ctx, str, "ctx-exe" ); |
579 | } |
580 | |
581 | static bool parse_fix_times_per_prb( |
582 | const char *str, const std::string &option_name = "fix-times-per-prb" ) { |
583 | static const std::string help |
584 | = "UINT (Default: `0`)\n Specifies the limit in `UINT` " |
585 | "rounds for performance benchmarking per problem.\n If `UINT` " |
586 | "is greater than `0`, the number of rounds criterion takes place " |
587 | "over the time criterion.\n" ; |
588 | bool parsed = parse_single_value_option( |
589 | fix_times_per_prb, 0, atoi, str, option_name, help); |
590 | if (parsed) fix_times_per_prb = MAX2(0, fix_times_per_prb); |
591 | return parsed; |
592 | } |
593 | |
594 | static bool parse_max_ms_per_prb( |
595 | const char *str, const std::string &option_name = "max-ms-per-prb" ) { |
596 | static const std::string help |
597 | = "MS (Default: `3000`)\n Specifies the limit in `MS` " |
598 | "milliseconds for performance benchmarking per problem.\n " |
599 | "`MS` is a positive integer in a range [100, 60000].\n" ; |
600 | bool parsed = parse_single_value_option( |
601 | max_ms_per_prb, 3e3, atof, str, option_name, help); |
602 | if (parsed) max_ms_per_prb = MAX2(100, MIN2(max_ms_per_prb, 60e3)); |
603 | return parsed; |
604 | } |
605 | |
606 | static bool parse_mem_check( |
607 | const char *str, const std::string &option_name = "mem-check" ) { |
608 | static const std::string help |
609 | = "BOOL (Default: `true`)\n Instructs the driver to perform " |
610 | "a device RAM capability check if a problem fits a device, when " |
611 | "set to `true`.\n" ; |
612 | return parse_single_value_option( |
613 | mem_check, true, str2bool, str, option_name, help); |
614 | } |
615 | |
616 | static bool parse_memory_kind( |
617 | const char *str, const std::string &option_name = "memory-kind" ) { |
618 | static const std::string help |
619 | = "KIND (Default: `usm`)\n Specifies a memory `KIND` to test " |
620 | "with DPC++ and OpenCL engines.\n `KIND` values are `usm`, " |
621 | "`buffer`, `usm_device` (malloc_device) or `usm_shared` " |
622 | "(malloc_shared).\n" ; |
623 | bool parsed = parse_single_value_option(memory_kind, default_memory_kind, |
624 | str2memory_kind, str, option_name, help); |
625 | |
626 | #if !defined(DNNL_WITH_SYCL) && DNNL_GPU_RUNTIME != DNNL_RUNTIME_OCL |
627 | if (parsed) { |
628 | fprintf(stderr, |
629 | "ERROR: option `--%s` is supported with DPC++ and OpenCL " |
630 | "builds only, exiting...\n" , |
631 | option_name.c_str()); |
632 | exit(2); |
633 | } |
634 | #endif |
635 | return parsed; |
636 | } |
637 | |
638 | static bool parse_mode( |
639 | const char *str, const std::string &option_name = "mode" ) { |
640 | static const std::string help |
641 | = "MODE (Default: `C`)\n Specifies a `MODE` for " |
642 | "benchmarking.\n `MODE` values are:\n * `C` for " |
643 | "correctness testing.\n * `P` for performance testing.\n * " |
644 | "`CP` for both correctness and performance testing.\n * `R` " |
645 | "for run mode or no correctness validation.\n * `L` for " |
646 | "listing mode.\n More details at " |
647 | + doc_url + "benchdnn_general_info.md\n" ; |
648 | |
649 | const auto str2bench_mode = [](const std::string &_str) { |
650 | bench_mode_t mode; |
651 | for (size_t i = 0; i < _str.size(); i++) { |
652 | switch (_str[i]) { |
653 | case 'r': |
654 | case 'R': mode |= RUN; break; |
655 | case 'c': |
656 | case 'C': mode |= CORR; break; |
657 | case 'p': |
658 | case 'P': mode |= PERF; break; |
659 | case 'l': |
660 | case 'L': mode |= LIST; break; |
661 | case 'o': |
662 | case 'O': mode |= PROF; break; |
663 | default: { |
664 | fprintf(stderr, |
665 | "ERROR: Unsupported character for `--mode` " |
666 | "option: `%c`.\n" , |
667 | _str[i]); |
668 | exit(2); |
669 | } |
670 | } |
671 | } |
672 | if (!(mode & LIST).none() && mode.count() > 1) { |
673 | fprintf(stderr, |
674 | "ERROR: LIST mode is incompatible with any other modes. " |
675 | "Please use just `--mode=L` instead.\n" ); |
676 | exit(2); |
677 | } |
678 | if (mode.none()) { |
679 | fprintf(stderr, "ERROR: empty mode is not allowed.\n" ); |
680 | exit(2); |
681 | } |
682 | |
683 | return mode; |
684 | }; |
685 | |
686 | return parse_single_value_option( |
687 | bench_mode, CORR, str2bench_mode, str, option_name, help); |
688 | } |
689 | |
690 | static bool parse_skip_impl( |
691 | const char *str, const std::string &option_name = "skip-impl" ) { |
692 | static const std::string help |
693 | = "STRING (Default: not specified)\n Instructs the driver to " |
694 | "iterate over implementations when fetched implementation name " |
695 | "matching `STRING`.\n `STRING` is a string literal with no " |
696 | "spaces.\n When empty, option has no effect.\n" ; |
697 | const auto chars2chars = [](const char *str) { return str; }; |
698 | bool parsed = parse_single_value_option( |
699 | skip_impl, std::string(), chars2chars, str, option_name, help); |
700 | |
701 | // Remove all quotes from input string since they affect the search. |
702 | if (parsed) { |
703 | for (auto c : {'"', '\''}) { |
704 | size_t start_pos = 0; |
705 | while (start_pos != eol) { |
706 | start_pos = skip_impl.find_first_of(c, start_pos); |
707 | if (start_pos != eol) skip_impl.erase(start_pos, 1); |
708 | } |
709 | } |
710 | } |
711 | return parsed; |
712 | } |
713 | |
714 | static bool parse_start( |
715 | const char *str, const std::string &option_name = "start" ) { |
716 | static const std::string help |
717 | = "UINT (Default: `0`)\n Specifies the test case index " |
718 | "`UINT` to start execution. All test cases up to `UINT` will be " |
719 | "skipped.\n" ; |
720 | return parse_single_value_option( |
721 | test_start, 0, atoi, str, option_name, help); |
722 | } |
723 | |
724 | static bool parse_verbose( |
725 | const char *str, const std::string &option_name = "verbose" ) { |
726 | static const std::string help |
727 | = "UINT, -vUINT (Default: `0`)\n Instructs the driver to " |
728 | "print additional information depending on `UINT`.\n More " |
729 | "details at " |
730 | + doc_url + "knobs_verbose.md\n" ; |
731 | bool parsed = parse_single_value_option( |
732 | verbose, 0, atoi, str, option_name, help); |
733 | if (parsed) return parsed; |
734 | |
735 | const std::string pattern("-v" ); // check short option first |
736 | if (pattern.find(str, 0, pattern.size()) != eol) { |
737 | verbose = atoi(str + pattern.size()); |
738 | return true; |
739 | } |
740 | return false; |
741 | } |
742 | |
743 | bool parse_bench_settings(const char *str) { |
744 | last_parsed_is_problem = false; // if start parsing, expect an option |
745 | |
746 | static bool start_msg = false, end_msg = false; |
747 | if (!start_msg) { |
748 | help_ss << "===================\n" ; |
749 | help_ss << "= Global options: =\n" ; |
750 | help_ss << "===================\n" ; |
751 | help_ss << "(More technical details available at " |
752 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/" |
753 | "benchdnn/doc/knobs_common.md)\n\n" ; |
754 | start_msg = true; |
755 | } |
756 | |
757 | bool parsed = parse_allow_enum_tags_only(str) |
758 | || parse_attr_same_pd_check(str) || parse_canonical(str) |
759 | || parse_cpu_isa_hints(str) || parse_engine(str) |
760 | || parse_fast_ref_gpu(str) || parse_fix_times_per_prb(str) |
761 | || parse_max_ms_per_prb(str) || parse_mem_check(str) |
762 | || parse_memory_kind(str) || parse_mode(str) || parse_skip_impl(str) |
763 | || parse_start(str) || parse_verbose(str); |
764 | |
765 | // Last condition makes this help message to be triggered once driver_name |
766 | // is already known. |
767 | if (!parsed && !end_msg && !driver_name.empty()) { |
768 | help_ss << "===================\n" ; |
769 | help_ss << "= Driver options: =\n" ; |
770 | help_ss << "===================\n" ; |
771 | help_ss << "(More technical details available at " |
772 | "https://github.com/oneapi-src/oneDNN/blob/master/tests/" |
773 | "benchdnn/doc/driver_" |
774 | << driver_name << ".md)\n\n" ; |
775 | end_msg = true; |
776 | } |
777 | return parsed; |
778 | } |
779 | |
780 | // Service functions |
781 | void catch_unknown_options(const char *str) { |
782 | last_parsed_is_problem = true; // if reached, means problem parsing |
783 | |
784 | std::string pattern = "--" ; |
785 | if (pattern.find(str, 0, pattern.size()) != eol) { |
786 | BENCHDNN_PRINT(0, "%s %s \'%s\'\n" , |
787 | "driver: ERROR: unknown option:" , driver_name.c_str(), str); |
788 | exit(2); |
789 | } |
790 | |
791 | // Must stay after `--` check. |
792 | pattern = "-" ; |
793 | if (pattern.find(str, 0, pattern.size()) != eol) { |
794 | BENCHDNN_PRINT(0, "%s\n%s \'%s\'\n" , |
795 | "ERROR: options should be passed with `--` prefix." , |
796 | "Given input:" , str); |
797 | exit(2); |
798 | } |
799 | } |
800 | |
801 | int parse_last_argument() { |
802 | if (!last_parsed_is_problem) |
803 | fprintf(stderr, |
804 | "%s driver: WARNING: No problem found for a given option!\n" , |
805 | driver_name.c_str()); |
806 | return OK; |
807 | } |
808 | |
809 | std::string get_substr(const std::string &s, size_t &start_pos, char delim) { |
810 | auto end_pos = s.find_first_of(delim, start_pos); |
811 | auto sub = s.substr(start_pos, end_pos - start_pos); |
812 | start_pos = end_pos + (end_pos != eol); |
813 | return sub; |
814 | } |
815 | |
816 | } // namespace parser |
817 | |