1e9d1aa69bd80f7fae72f68f7c091b31141ed16c
[platform/core/ml/nnfw.git] / tests / tools / onert_run / src / args.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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 "args.h"
18
19 #include <functional>
20 #include <iostream>
21 #include <sys/stat.h>
22 #include <json/json.h>
23
24 namespace
25 {
26
27 // This function parses a json object and returns as a vector of integers
28 // For example,
29 // [0, [1, 2, 3, 4], 3, 40, 4, []] in JSON
30 // is converted to:
31 // {
32 //  0 -> [1, 2, 3, 4]
33 //  3 -> 40
34 //  4 -> []
35 // } in std::unordered_map. Note that the value type is still Json::Value.
36 std::unordered_map<uint32_t, Json::Value> argArrayToMap(const Json::Value &jsonval)
37 {
38   if (!jsonval.isArray() || (jsonval.size() % 2 != 0))
39   {
40     std::cerr << "JSON argument must be an even-sized array in JSON\n";
41     exit(1);
42   }
43
44   std::unordered_map<uint32_t, Json::Value> ret;
45   for (uint32_t i = 0; i < jsonval.size(); i += 2)
46   {
47     if (!jsonval[i].isUInt())
48     {
49       std::cerr << "Key values(values in even indices) must be unsigned integers\n";
50       exit(1);
51     }
52     uint32_t key = jsonval[i].asUInt();
53     Json::Value val = jsonval[i + 1];
54     ret[key] = jsonval[i + 1];
55   }
56   return ret;
57 }
58
59 // param shape_str is a form of, e.g., "[1, [2, 3], 3, []]" or "h5"
60 void handleShapeJsonParam(onert_run::TensorShapeMap &shape_map, const std::string &shape_str)
61 {
62   Json::Value root;
63   Json::Reader reader;
64   if (!reader.parse(shape_str, root, false))
65   {
66     std::cerr << "Invalid JSON format for output_sizes \"" << shape_str << "\"\n";
67     exit(1);
68   }
69
70   auto arg_map = argArrayToMap(root);
71   for (auto &pair : arg_map)
72   {
73     uint32_t key = pair.first;
74     Json::Value &shape_json = pair.second;
75     if (!shape_json.isArray())
76     {
77       std::cerr << "All the values must be list: " << shape_str << "\n";
78       exit(1);
79     }
80
81     std::vector<int> shape;
82     for (auto &dim_json : shape_json)
83     {
84       if (!dim_json.isUInt())
85       {
86         std::cerr << "All the dims should be dim >= 0: " << shape_str << "\n";
87         exit(1);
88       }
89
90       shape.emplace_back(dim_json.asUInt64());
91     }
92
93     shape_map[key] = shape;
94   }
95 }
96
97 void checkModelfile(const std::string &model_filename)
98 {
99   if (model_filename.empty())
100   {
101     // TODO Print usage instead of the below message
102     std::cerr << "Please specify model file. Run with `--help` for usage."
103               << "\n";
104
105     exit(1);
106   }
107   else
108   {
109     if (access(model_filename.c_str(), F_OK) == -1)
110     {
111       std::cerr << "Model file not found: " << model_filename << "\n";
112       exit(1);
113     }
114   }
115 }
116
117 void checkPackage(const std::string &package_filename)
118 {
119   if (package_filename.empty())
120   {
121     // TODO Print usage instead of the below message
122     std::cerr << "Please specify nnpackage file. Run with `--help` for usage."
123               << "\n";
124
125     exit(1);
126   }
127   else
128   {
129     if (access(package_filename.c_str(), F_OK) == -1)
130     {
131       std::cerr << "nnpackage not found: " << package_filename << "\n";
132       exit(1);
133     }
134   }
135 }
136
137 } // namespace
138
139 namespace onert_run
140 {
141
142 Args::Args(const int argc, char **argv)
143 {
144   Initialize();
145   Parse(argc, argv);
146 }
147
148 void Args::Initialize(void)
149 {
150   auto process_nnpackage = [&](const std::string &package_filename) {
151     _package_filename = package_filename;
152
153     std::cerr << "Package Filename " << _package_filename << std::endl;
154     checkPackage(package_filename);
155   };
156
157   auto process_modelfile = [&](const std::string &model_filename) {
158     _model_filename = model_filename;
159
160     std::cerr << "Model Filename " << _model_filename << std::endl;
161     checkModelfile(model_filename);
162
163     _use_single_model = true;
164   };
165
166   auto process_path = [&](const std::string &path) {
167     struct stat sb;
168     if (stat(path.c_str(), &sb) == 0)
169     {
170       if (sb.st_mode & S_IFDIR)
171       {
172         _package_filename = path;
173         checkPackage(path);
174         std::cerr << "Package Filename " << path << std::endl;
175       }
176       else
177       {
178         _model_filename = path;
179         checkModelfile(path);
180         std::cerr << "Model Filename " << path << std::endl;
181         _use_single_model = true;
182       }
183     }
184     else
185     {
186       std::cerr << "Cannot find: " << path << "\n";
187       exit(1);
188     }
189   };
190
191   auto process_output_sizes = [&](const std::string &output_sizes_json_str) {
192     Json::Value root;
193     Json::Reader reader;
194     if (!reader.parse(output_sizes_json_str, root, false))
195     {
196       std::cerr << "Invalid JSON format for output_sizes \"" << output_sizes_json_str << "\"\n";
197       exit(1);
198     }
199
200     auto arg_map = argArrayToMap(root);
201     for (auto &pair : arg_map)
202     {
203       uint32_t key = pair.first;
204       Json::Value &val_json = pair.second;
205       if (!val_json.isUInt())
206       {
207         std::cerr << "All the values in `output_sizes` must be unsigned integers\n";
208         exit(1);
209       }
210       uint32_t val = val_json.asUInt();
211       _output_sizes[key] = val;
212     }
213   };
214
215   auto process_shape_prepare = [&](const std::string &shape_str) {
216 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
217     if (shape_str == "H5" || shape_str == "h5")
218     {
219       _when_to_use_h5_shape = WhenToUseH5Shape::PREPARE;
220       return;
221     }
222 #endif
223     try
224     {
225       handleShapeJsonParam(_shape_prepare, shape_str);
226     }
227     catch (const std::exception &e)
228     {
229       std::cerr << "error with '--shape_prepare' option: " << shape_str << std::endl;
230       exit(1);
231     }
232   };
233
234   auto process_shape_run = [&](const std::string &shape_str) {
235 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
236     if (shape_str == "H5" || shape_str == "h5")
237     {
238       _when_to_use_h5_shape = WhenToUseH5Shape::RUN;
239       return;
240     }
241 #endif
242     try
243     {
244       handleShapeJsonParam(_shape_run, shape_str);
245     }
246     catch (const std::exception &e)
247     {
248       std::cerr << "error with '--shape_run' option: " << shape_str << std::endl;
249       exit(1);
250     }
251   };
252
253   // General options
254   po::options_description general("General options", 100);
255
256   // clang-format off
257   general.add_options()
258     ("help,h", "Print available options")
259     ("version", "Print version and exit immediately")
260     ("nnpackage", po::value<std::string>()->notifier(process_nnpackage), "NN Package file(directory) name")
261     ("modelfile", po::value<std::string>()->notifier(process_modelfile), "NN Model filename")
262     ("path", po::value<std::string>()->notifier(process_path), "NN Package or NN Modelfile path")
263 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
264     ("dump,d", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _dump_filename = v; }), "Output filename")
265     ("load,l", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _load_filename = v; }), "Input filename")
266 #endif
267     ("dump:raw", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _dump_raw_filename = v; }), "Raw Output filename")
268     ("load:raw", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _load_raw_filename = v; }), "Raw Input filename")
269     ("output_sizes", po::value<std::string>()->notifier(process_output_sizes),
270         "The output buffer size in JSON 1D array\n"
271         "If not given, the model's output sizes are used\n"
272         "e.g. '[0, 40, 2, 80]' to set 0th tensor to 40 and 2nd tensor to 80.\n")
273     ("num_runs,r", po::value<int>()->default_value(1)->notifier([&](const auto &v) { _num_runs = v; }), "The number of runs")
274     ("warmup_runs,w", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _warmup_runs = v; }), "The number of warmup runs")
275     ("run_delay,t", po::value<int>()->default_value(-1)->notifier([&](const auto &v) { _run_delay = v; }), "Delay time(us) between runs (as default no delay")
276     ("gpumem_poll,g", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _gpumem_poll = v; }), "Check gpu memory polling separately")
277     ("mem_poll,m", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _mem_poll = v; }), "Check memory polling")
278     ("write_report,p", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _write_report = v; }),
279          "Write report\n"
280          "{exec}-{nnpkg|modelfile}-{backend}.csv will be generated.\n"
281          "e.g. onert_run-UNIT_Add_000-acl_cl.csv.\n"
282          "{nnpkg|modelfile} name may be changed to realpath if you use symbolic-link.")
283     ("shape_prepare", po::value<std::string>()->default_value("[]")->notifier(process_shape_prepare),
284          "Please refer to the description of 'shape_run'")
285     ("shape_run", po::value<std::string>()->default_value("[]")->notifier(process_shape_run),
286          "'--shape_prepare: set shape of tensors before compilation (before calling nnfw_prepare()).\n"
287          "'--shape_run: set shape of tensors before running (before calling nnfw_run()).\n"
288          "Allowed value:.\n"
289          "'[0, [1, 2], 2, []]': set 0th tensor to [1, 2] and 2nd tensor to [] (scalar).\n"
290 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
291          "'h5': read shape(s) from H5 input file. '--load' should also be provided.\n"
292          "if '--load' option is provided but '--shape_prepare' or '--shape_run' is not provided,\n"
293          "'--shape_run h5' will be used by default.\n"
294 #endif
295          "For detailed description, please consutl the description of nnfw_set_input_tensorinfo()\n"
296          )
297     ("verbose_level,v", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _verbose_level = v; }),
298          "Verbose level\n"
299          "0: prints the only result. Messages btw run don't print\n"
300          "1: prints result and message btw run\n"
301          "2: prints all of messages to print\n")
302     ;
303   // clang-format on
304
305   _options.add(general);
306   _positional.add("path", -1);
307 }
308
309 void Args::Parse(const int argc, char **argv)
310 {
311   po::variables_map vm;
312   po::store(po::command_line_parser(argc, argv).options(_options).positional(_positional).run(),
313             vm);
314
315   if (vm.count("help"))
316   {
317     std::cout << "onert_run\n\n";
318     std::cout << "Usage: " << argv[0] << " path to nnpackage root directory [<options>]\n\n";
319     std::cout << _options;
320     std::cout << "\n";
321
322     exit(0);
323   }
324
325   if (vm.count("version"))
326   {
327     _print_version = true;
328     return;
329   }
330
331   {
332     auto conflicting_options = [&](const std::string &o1, const std::string &o2) {
333       if ((vm.count(o1) && !vm[o1].defaulted()) && (vm.count(o2) && !vm[o2].defaulted()))
334       {
335         throw boost::program_options::error(std::string("Two options '") + o1 + "' and '" + o2 +
336                                             "' cannot be given at once.");
337       }
338     };
339
340     // calling, e.g., "onert_run .. -- shape_prepare .. --shape_run .." should theoretically
341     // work but allowing both options together on command line makes the usage and implemenation
342     // of onert_run too complicated. Therefore let's not allow those option together.
343     conflicting_options("shape_prepare", "shape_run");
344
345     // Cannot use both single model file and nnpackage at once
346     conflicting_options("modelfile", "nnpackage");
347
348     // Require modelfile, nnpackage, or path
349     if (!vm.count("modelfile") && !vm.count("nnpackage") && !vm.count("path"))
350       throw boost::program_options::error(
351         std::string("Require one of options modelfile, nnpackage, or path."));
352   }
353
354   try
355   {
356     po::notify(vm);
357   }
358   catch (const std::bad_cast &e)
359   {
360     std::cerr << "Bad cast error - " << e.what() << '\n';
361     exit(1);
362   }
363
364   // This must be run after `notify` as `_warm_up_runs` must have been processed before.
365   if (vm.count("mem_poll"))
366   {
367     // Instead of EXECUTE to avoid overhead, memory polling runs on WARMUP
368     if (_mem_poll && _warmup_runs == 0)
369     {
370       _warmup_runs = 1;
371     }
372   }
373 }
374
375 bool Args::shapeParamProvided()
376 {
377   bool provided = false;
378 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
379   // "--shape_run h5" or "--shape_prepare h5" was provided
380   provided = (getWhenToUseH5Shape() != WhenToUseH5Shape::NOT_PROVIDED);
381 #endif
382   // specific shape was provided
383   // e.g., "--shape_run '[0, [10, 1]]'" or "--shape_prepare '[0, [10, 1]]'"
384   provided |= (!getShapeMapForPrepare().empty()) || (!getShapeMapForRun().empty());
385
386   return provided;
387 }
388
389 } // end of namespace onert_run