Imported Upstream version 1.9.0
[platform/core/ml/nnfw.git] / tests / tools / nnpackage_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 <json/json.h>
22
23 namespace
24 {
25
26 // This function parses a json object and returns as a vector of integers
27 // For example,
28 // [0, [1, 2, 3, 4], 3, 40, 4, []] in JSON
29 // is converted to:
30 // {
31 //  0 -> [1, 2, 3, 4]
32 //  3 -> 40
33 //  4 -> []
34 // } in std::unordered_map. Note that the value type is still Json::Value.
35 std::unordered_map<uint32_t, Json::Value> argArrayToMap(const Json::Value &jsonval)
36 {
37   if (!jsonval.isArray() || (jsonval.size() % 2 != 0))
38   {
39     std::cerr << "JSON argument must be an even-sized array in JSON\n";
40     exit(1);
41   }
42
43   std::unordered_map<uint32_t, Json::Value> ret;
44   for (uint32_t i = 0; i < jsonval.size(); i += 2)
45   {
46     if (!jsonval[i].isUInt())
47     {
48       std::cerr << "Key values(values in even indices) must be unsigned integers\n";
49       exit(1);
50     }
51     uint32_t key = jsonval[i].asUInt();
52     Json::Value val = jsonval[i + 1];
53     ret[key] = jsonval[i + 1];
54   }
55   return ret;
56 }
57
58 // param shape_str is a form of, e.g., "[1, [2, 3], 3, []]" or "h5"
59 void handleShapeJsonParam(nnpkg_run::TensorShapeMap &shape_map, const std::string &shape_str)
60 {
61   Json::Value root;
62   Json::Reader reader;
63   if (!reader.parse(shape_str, root, false))
64   {
65     std::cerr << "Invalid JSON format for output_sizes \"" << shape_str << "\"\n";
66     exit(1);
67   }
68
69   auto arg_map = argArrayToMap(root);
70   for (auto &pair : arg_map)
71   {
72     uint32_t key = pair.first;
73     Json::Value &shape_json = pair.second;
74     if (!shape_json.isArray())
75     {
76       std::cerr << "All the values must be list: " << shape_str << "\n";
77       exit(1);
78     }
79
80     std::vector<int> shape;
81     for (auto &dim_json : shape_json)
82     {
83       if (!dim_json.isUInt())
84       {
85         std::cerr << "All the dims should be dim >= 0: " << shape_str << "\n";
86         exit(1);
87       }
88
89       shape.emplace_back(dim_json.asUInt64());
90     }
91
92     shape_map[key] = shape;
93   }
94 }
95
96 } // namespace
97
98 namespace nnpkg_run
99 {
100
101 Args::Args(const int argc, char **argv)
102 {
103   Initialize();
104   Parse(argc, argv);
105 }
106
107 void Args::Initialize(void)
108 {
109   auto process_nnpackage = [&](const std::string &package_filename) {
110     _package_filename = package_filename;
111
112     std::cerr << "Package Filename " << _package_filename << std::endl;
113     if (_package_filename.empty())
114     {
115       // TODO Print usage instead of the below message
116       std::cerr << "Please specify nnpackage file. Run with `--help` for usage."
117                 << "\n";
118
119       exit(1);
120     }
121     else
122     {
123       if (access(_package_filename.c_str(), F_OK) == -1)
124       {
125         std::cerr << "nnpackage not found: " << _package_filename << "\n";
126       }
127     }
128   };
129
130   auto process_output_sizes = [&](const std::string &output_sizes_json_str) {
131     Json::Value root;
132     Json::Reader reader;
133     if (!reader.parse(output_sizes_json_str, root, false))
134     {
135       std::cerr << "Invalid JSON format for output_sizes \"" << output_sizes_json_str << "\"\n";
136       exit(1);
137     }
138
139     auto arg_map = argArrayToMap(root);
140     for (auto &pair : arg_map)
141     {
142       uint32_t key = pair.first;
143       Json::Value &val_json = pair.second;
144       if (!val_json.isUInt())
145       {
146         std::cerr << "All the values in `output_sizes` must be unsigned integers\n";
147         exit(1);
148       }
149       uint32_t val = val_json.asUInt();
150       _output_sizes[key] = val;
151     }
152   };
153
154   auto process_shape_prepare = [&](const std::string &shape_str) {
155 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
156     if (shape_str == "H5" || shape_str == "h5")
157     {
158       _when_to_use_h5_shape = WhenToUseH5Shape::PREPARE;
159       return;
160     }
161 #endif
162     try
163     {
164       handleShapeJsonParam(_shape_prepare, shape_str);
165     }
166     catch (const std::exception &e)
167     {
168       std::cerr << "error with '--shape_prepare' option: " << shape_str << std::endl;
169       exit(1);
170     }
171   };
172
173   auto process_shape_run = [&](const std::string &shape_str) {
174 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
175     if (shape_str == "H5" || shape_str == "h5")
176     {
177       _when_to_use_h5_shape = WhenToUseH5Shape::RUN;
178       return;
179     }
180 #endif
181     try
182     {
183       handleShapeJsonParam(_shape_run, shape_str);
184     }
185     catch (const std::exception &e)
186     {
187       std::cerr << "error with '--shape_run' option: " << shape_str << std::endl;
188       exit(1);
189     }
190   };
191
192   // General options
193   po::options_description general("General options", 100);
194
195   // clang-format off
196   general.add_options()
197     ("help,h", "Print available options")
198     ("version", "Print version and exit immediately")
199     ("nnpackage", po::value<std::string>()->required()->notifier(process_nnpackage))
200 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
201     ("dump,d", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _dump_filename = v; }), "Output filename")
202     ("load,l", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _load_filename = v; }), "Input filename")
203 #endif
204     ("output_sizes", po::value<std::string>()->notifier(process_output_sizes),
205         "The output buffer size in JSON 1D array\n"
206         "If not given, the model's output sizes are used\n"
207         "e.g. '[0, 40, 2, 80]' to set 0th tensor to 40 and 2nd tensor to 80.\n")
208     ("num_runs,r", po::value<int>()->default_value(1)->notifier([&](const auto &v) { _num_runs = v; }), "The number of runs")
209     ("warmup_runs,w", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _warmup_runs = v; }), "The number of warmup runs")
210     ("run_delay,t", po::value<int>()->default_value(-1)->notifier([&](const auto &v) { _run_delay = v; }), "Delay time(ms) between runs (as default no delay")
211     ("gpumem_poll,g", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _gpumem_poll = v; }), "Check gpu memory polling separately")
212     ("mem_poll,m", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _mem_poll = v; }), "Check memory polling")
213     ("write_report,p", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _write_report = v; }),
214          "Write report\n"
215          "{exec}-{nnpkg}-{backend}.csv will be generated.\n"
216          "e.g. nnpackage_run-UNIT_Add_000-acl_cl.csv.\n"
217          "{nnpkg} name may be changed to realpath if you use symbolic-link.")
218     ("shape_prepare", po::value<std::string>()->default_value("[]")->notifier(process_shape_prepare),
219          "set shape of specified tensor before compilation (before calling nnfw_prepare()).\n"
220          "'h5': read shape(s) from H5 input file. '--load' should also be provided.\n"
221          "'[0, [1, 2], 2, []]': set 0th tensor to [1, 2] and 2nd tensor to [].")
222     ("shape_run", po::value<std::string>()->default_value("[]")->notifier(process_shape_run),
223          "set shape of specified tensor before running (before calling nnfw_run()).\n"
224          "'h5': read shape(s) from H5 input file. '--load' should also be provided.\n"
225          "'[0, [1, 2], 2, []]': set 0th tensor to [1, 2] and 2nd tensor to [].")
226     ("verbose_level,v", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _verbose_level = v; }),
227          "Verbose level\n"
228          "0: prints the only result. Messages btw run don't print\n"
229          "1: prints result and message btw run\n"
230          "2: prints all of messages to print\n")
231     ;
232   // clang-format on
233
234   _options.add(general);
235   _positional.add("nnpackage", 1);
236 }
237
238 void Args::Parse(const int argc, char **argv)
239 {
240   po::variables_map vm;
241   po::store(po::command_line_parser(argc, argv).options(_options).positional(_positional).run(),
242             vm);
243
244   {
245     auto conflicting_options = [&](const std::string &o1, const std::string &o2) {
246       if ((vm.count(o1) && !vm[o1].defaulted()) && (vm.count(o2) && !vm[o2].defaulted()))
247       {
248         throw boost::program_options::error(std::string("Two options '") + o1 + "' and '" + o2 +
249                                             "' cannot be given at once.");
250       }
251     };
252   }
253
254   if (vm.count("help"))
255   {
256     std::cout << "nnpackage_run\n\n";
257     std::cout << "Usage: " << argv[0] << " path to nnpackage root directory [<options>]\n\n";
258     std::cout << _options;
259     std::cout << "\n";
260
261     exit(0);
262   }
263
264   if (vm.count("version"))
265   {
266     _print_version = true;
267     return;
268   }
269
270   try
271   {
272     po::notify(vm);
273   }
274   catch (const std::bad_cast &e)
275   {
276     std::cerr << "Bad cast error - " << e.what() << '\n';
277     exit(1);
278   }
279
280   // This must be run after `notify` as `_warm_up_runs` must have been processed before.
281   if (vm.count("mem_poll"))
282   {
283     // Instead of EXECUTE to avoid overhead, memory polling runs on WARMUP
284     if (_mem_poll && _warmup_runs == 0)
285     {
286       _warmup_runs = 1;
287     }
288   }
289 }
290
291 } // end of namespace nnpkg_run