cb4a7dbaab4a5b1ca607340ede0c7c22735c42b0
[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, []]"
59 void handleShapeParam(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     try
156     {
157       handleShapeParam(_shape_prepare, shape_str);
158     }
159     catch (const std::exception &e)
160     {
161       std::cerr << "error with '--shape_prepare' option: " << shape_str << std::endl;
162       exit(1);
163     }
164   };
165
166   auto process_shape_run = [&](const std::string &shape_str) {
167     try
168     {
169       handleShapeParam(_shape_run, shape_str);
170     }
171     catch (const std::exception &e)
172     {
173       std::cerr << "error with '--shape_run' option: " << shape_str << std::endl;
174       exit(1);
175     }
176   };
177
178   // General options
179   po::options_description general("General options", 100);
180
181   // clang-format off
182   general.add_options()
183     ("help,h", "Print available options")
184     ("version", "Print version and exit immediately")
185     ("nnpackage", po::value<std::string>()->required()->notifier(process_nnpackage))
186 #if defined(ONERT_HAVE_HDF5) && ONERT_HAVE_HDF5 == 1
187     ("dump,d", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _dump_filename = v; }), "Output filename")
188     ("load,l", po::value<std::string>()->default_value("")->notifier([&](const auto &v) { _load_filename = v; }), "Input filename")
189 #endif
190     ("output_sizes", po::value<std::string>()->notifier(process_output_sizes),
191         "The output buffer size in JSON 1D array\n"
192         "If not given, the model's output sizes are used\n"
193         "e.g. '[0, 40, 2, 80]' to set 0th tensor to 40 and 2nd tensor to 80.\n")
194     ("num_runs,r", po::value<int>()->default_value(1)->notifier([&](const auto &v) { _num_runs = v; }), "The number of runs")
195     ("warmup_runs,w", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _warmup_runs = v; }), "The number of warmup runs")
196     ("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")
197     ("gpumem_poll,g", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _gpumem_poll = v; }), "Check gpu memory polling separately")
198     ("mem_poll,m", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _mem_poll = v; }), "Check memory polling")
199     ("write_report,p", po::value<bool>()->default_value(false)->notifier([&](const auto &v) { _write_report = v; }),
200          "Write report\n"
201          "{exec}-{nnpkg}-{backend}.csv will be generated.\n"
202          "e.g. nnpackage_run-UNIT_Add_000-acl_cl.csv.\n"
203          "{nnpkg} name may be changed to realpath if you use symbolic-link.")
204     ("shape_prepare", po::value<std::string>()->default_value("[]")->notifier(process_shape_prepare),
205          "set shape of specified tensor before compilation\n"
206          "e.g. '[0, [1, 2], 2, []]' to set 0th tensor to [1, 2] and 2nd tensor to [].\n")
207     ("shape_run", po::value<std::string>()->default_value("[]")->notifier(process_shape_run),
208          "set shape of specified tensor right before running\n"
209          "e.g. '[1, [1, 2]]` to set 1st tensor to [1, 2].\n")
210     ("verbose_level,v", po::value<int>()->default_value(0)->notifier([&](const auto &v) { _verbose_level = v; }),
211          "Verbose level\n"
212          "0: prints the only result. Messages btw run don't print\n"
213          "1: prints result and message btw run\n"
214          "2: prints all of messages to print\n")
215     ;
216   // clang-format on
217
218   _options.add(general);
219   _positional.add("nnpackage", 1);
220 }
221
222 void Args::Parse(const int argc, char **argv)
223 {
224   po::variables_map vm;
225   po::store(po::command_line_parser(argc, argv).options(_options).positional(_positional).run(),
226             vm);
227
228   {
229     auto conflicting_options = [&](const std::string &o1, const std::string &o2) {
230       if ((vm.count(o1) && !vm[o1].defaulted()) && (vm.count(o2) && !vm[o2].defaulted()))
231       {
232         throw boost::program_options::error(std::string("Two options '") + o1 + "' and '" + o2 +
233                                             "' cannot be given at once.");
234       }
235     };
236   }
237
238   if (vm.count("help"))
239   {
240     std::cout << "nnpackage_run\n\n";
241     std::cout << "Usage: " << argv[0] << " path to nnpackage root directory [<options>]\n\n";
242     std::cout << _options;
243     std::cout << "\n";
244
245     exit(0);
246   }
247
248   if (vm.count("version"))
249   {
250     _print_version = true;
251     return;
252   }
253
254   try
255   {
256     po::notify(vm);
257   }
258   catch (const std::bad_cast &e)
259   {
260     std::cerr << "Bad cast error - " << e.what() << '\n';
261     exit(1);
262   }
263
264   // This must be run after `notify` as `_warm_up_runs` must have been processed before.
265   if (vm.count("mem_poll"))
266   {
267     // Instead of EXECUTE to avoid overhead, memory polling runs on WARMUP
268     if (_mem_poll && _warmup_runs == 0)
269     {
270       _warmup_runs = 1;
271     }
272   }
273 }
274
275 } // end of namespace nnpkg_run