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
23namespace parser {
24
25bool last_parsed_is_problem = false;
26const size_t eol = std::string::npos;
27std::stringstream help_ss;
28
29static const std::string benchdnn_url
30 = "https://github.com/oneapi-src/oneDNN/blob/master/tests/benchdnn";
31static const std::string doc_url = benchdnn_url + "/doc/";
32
33namespace parser_utils {
34std::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
40void 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
53bool 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
65bool 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
75bool 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
91bool 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
125bool 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
138bool 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
147bool 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
157bool 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
170bool 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
180bool 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
191bool 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
205bool 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
215bool 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
223bool 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
236bool 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
247bool 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
257bool 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
275bool 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
284bool 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
296bool 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
318bool 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
332bool 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
340bool 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
362void 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
407void 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
432static 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
444static 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
456static 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
467static 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
480static 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
506static 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
527bool 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
572bool 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}
576bool 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
581static 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
594static 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
606static 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
616static 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
638static 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
690static 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
714static 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
724static 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
743bool 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
781void 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
801int 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
809std::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