blob: ee7d2c1f5cd450b179fe8939505a13fc8a828daf [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
telsoa01c577f2c2018-08-31 09:22:23 +01005
Francis Murtaghbee4bc92019-06-18 12:30:37 +01006#include "../NetworkExecutionUtils/NetworkExecutionUtils.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01007
James Conroy7b4886f2019-04-11 10:23:58 +01008// MAIN
telsoa01c577f2c2018-08-31 09:22:23 +01009int main(int argc, const char* argv[])
10{
11 // Configures logging for both the ARMNN library and this test program.
12#ifdef NDEBUG
13 armnn::LogSeverity level = armnn::LogSeverity::Info;
14#else
15 armnn::LogSeverity level = armnn::LogSeverity::Debug;
16#endif
17 armnn::ConfigureLogging(true, true, level);
18 armnnUtils::ConfigureLogging(boost::log::core::get().get(), true, true, level);
19
20 std::string testCasesFile;
21
22 std::string modelFormat;
23 std::string modelPath;
Ferran Balaguerc602f292019-02-08 17:09:55 +000024 std::string inputNames;
25 std::string inputTensorShapes;
26 std::string inputTensorDataFilePaths;
27 std::string outputNames;
28 std::string inputTypes;
Éanna Ó Catháinb3d481a2019-02-26 11:26:24 +000029 std::string outputTypes;
Matteo Martincigh00dda4a2019-08-14 11:42:30 +010030 std::string dynamicBackendsPath;
Sadik Armagan77086282019-09-02 11:46:28 +010031 std::string outputTensorFiles;
telsoa01c577f2c2018-08-31 09:22:23 +010032
Jim Flynn4951b8c2019-10-03 10:04:30 -070033 // external profiling parameters
34 std::string outgoingCaptureFile;
35 std::string incomingCaptureFile;
36 uint32_t counterCapturePeriod;
37
James Conroy7b4886f2019-04-11 10:23:58 +010038 double thresholdTime = 0.0;
39
telsoa01c577f2c2018-08-31 09:22:23 +010040 size_t subgraphId = 0;
41
Matthew Jackson07882f12019-09-05 15:55:55 +010042 const std::string backendsMessage = "REQUIRED: Which device to run layers on by default. Possible choices: "
Aron Virginas-Tar5cc8e562018-10-23 15:14:46 +010043 + armnn::BackendRegistryInstance().GetBackendIdsAsString();
telsoa01c577f2c2018-08-31 09:22:23 +010044 po::options_description desc("Options");
45 try
46 {
47 desc.add_options()
48 ("help", "Display usage information")
Matthew Jackson07882f12019-09-05 15:55:55 +010049 ("compute,c", po::value<std::vector<std::string>>()->multitoken()->required(),
50 backendsMessage.c_str())
telsoa01c577f2c2018-08-31 09:22:23 +010051 ("test-cases,t", po::value(&testCasesFile), "Path to a CSV file containing test cases to run. "
52 "If set, further parameters -- with the exception of compute device and concurrency -- will be ignored, "
53 "as they are expected to be defined in the file for each test in particular.")
54 ("concurrent,n", po::bool_switch()->default_value(false),
55 "Whether or not the test cases should be executed in parallel")
Matteo Martincigh49124022019-01-11 13:25:59 +000056 ("model-format,f", po::value(&modelFormat)->required(),
Aron Virginas-Tar64e4ccb2019-02-12 11:27:53 +000057 "armnn-binary, caffe-binary, caffe-text, onnx-binary, onnx-text, tflite-binary, tensorflow-binary or "
58 "tensorflow-text.")
59 ("model-path,m", po::value(&modelPath)->required(), "Path to model file, e.g. .armnn, .caffemodel, "
60 ".prototxt, .tflite, .onnx")
Matteo Martincigh00dda4a2019-08-14 11:42:30 +010061 ("dynamic-backends-path,b", po::value(&dynamicBackendsPath),
62 "Path where to load any available dynamic backend from. "
63 "If left empty (the default), dynamic backends will not be used.")
Ferran Balaguerc602f292019-02-08 17:09:55 +000064 ("input-name,i", po::value(&inputNames),
65 "Identifier of the input tensors in the network separated by comma.")
telsoa01c577f2c2018-08-31 09:22:23 +010066 ("subgraph-number,x", po::value<size_t>(&subgraphId)->default_value(0), "Id of the subgraph to be executed."
67 "Defaults to 0")
Ferran Balaguerc602f292019-02-08 17:09:55 +000068 ("input-tensor-shape,s", po::value(&inputTensorShapes),
Francis Murtagh1555cbd2019-10-08 14:47:46 +010069 "The shape of the input tensors in the network as a flat array of integers separated by comma."
70 "Several shapes can be passed by separating them with a colon (:)."
telsoa01c577f2c2018-08-31 09:22:23 +010071 "This parameter is optional, depending on the network.")
Ferran Balaguerc602f292019-02-08 17:09:55 +000072 ("input-tensor-data,d", po::value(&inputTensorDataFilePaths),
73 "Path to files containing the input data as a flat array separated by whitespace. "
Francis Murtagh1555cbd2019-10-08 14:47:46 +010074 "Several paths can be passed by separating them with a comma.")
Ferran Balaguerc602f292019-02-08 17:09:55 +000075 ("input-type,y",po::value(&inputTypes), "The type of the input tensors in the network separated by comma. "
76 "If unset, defaults to \"float\" for all defined inputs. "
Éanna Ó Catháinb3d481a2019-02-26 11:26:24 +000077 "Accepted values (float, int or qasymm8)")
Narumol Prangnawarat610256f2019-06-26 15:10:46 +010078 ("quantize-input,q",po::bool_switch()->default_value(false),
79 "If this option is enabled, all float inputs will be quantized to qasymm8. "
80 "If unset, default to not quantized. "
81 "Accepted values (true or false)")
Éanna Ó Catháinb3d481a2019-02-26 11:26:24 +000082 ("output-type,z",po::value(&outputTypes),
83 "The type of the output tensors in the network separated by comma. "
84 "If unset, defaults to \"float\" for all defined outputs. "
85 "Accepted values (float, int or qasymm8).")
Ferran Balaguerc602f292019-02-08 17:09:55 +000086 ("output-name,o", po::value(&outputNames),
87 "Identifier of the output tensors in the network separated by comma.")
Sadik Armagan77086282019-09-02 11:46:28 +010088 ("write-outputs-to-file,w", po::value(&outputTensorFiles),
89 "Comma-separated list of output file paths keyed with the binding-id of the output slot. "
90 "If left empty (the default), the output tensors will not be written to a file.")
telsoa01c577f2c2018-08-31 09:22:23 +010091 ("event-based-profiling,e", po::bool_switch()->default_value(false),
Ruomei Yan2fcce082019-04-02 16:47:34 +010092 "Enables built in profiler. If unset, defaults to off.")
Andre Ghattas23ae2ea2019-08-07 12:18:38 +010093 ("visualize-optimized-model,v", po::bool_switch()->default_value(false),
94 "Enables built optimized model visualizer. If unset, defaults to off.")
Ruomei Yan2fcce082019-04-02 16:47:34 +010095 ("fp16-turbo-mode,h", po::bool_switch()->default_value(false), "If this option is enabled, FP32 layers, "
James Conroy7b4886f2019-04-11 10:23:58 +010096 "weights and biases will be converted to FP16 where the backend supports it")
97 ("threshold-time,r", po::value<double>(&thresholdTime)->default_value(0.0),
98 "Threshold time is the maximum allowed time for inference measured in milliseconds. If the actual "
99 "inference time is greater than the threshold time, the test will fail. By default, no threshold "
Matthew Jackson54658b92019-08-27 15:35:59 +0100100 "time is used.")
101 ("print-intermediate-layers,p", po::bool_switch()->default_value(false),
Jim Flynn4951b8c2019-10-03 10:04:30 -0700102 "If this option is enabled, the output of every graph layer will be printed.")
103 ("enable-external-profiling,a", po::bool_switch()->default_value(false),
104 "If enabled external profiling will be switched on")
105 ("outgoing-capture-file,j", po::value(&outgoingCaptureFile),
106 "If specified the outgoing external profiling packets will be captured in this binary file")
107 ("incoming-capture-file,k", po::value(&incomingCaptureFile),
108 "If specified the incoming external profiling packets will be captured in this binary file")
109 ("file-only-external-profiling,g", po::bool_switch()->default_value(false),
110 "If enabled then the 'file-only' test mode of external profiling will be enabled")
111 ("counter-capture-period,u", po::value<uint32_t>(&counterCapturePeriod)->default_value(150u),
112 "If profiling is enabled in 'file-only' mode this is the capture period that will be used in the test");
telsoa01c577f2c2018-08-31 09:22:23 +0100113 }
114 catch (const std::exception& e)
115 {
116 // Coverity points out that default_value(...) can throw a bad_lexical_cast,
117 // and that desc.add_options() can throw boost::io::too_few_args.
118 // They really won't in any of these cases.
119 BOOST_ASSERT_MSG(false, "Caught unexpected exception");
120 BOOST_LOG_TRIVIAL(fatal) << "Fatal internal error: " << e.what();
121 return EXIT_FAILURE;
122 }
123
124 // Parses the command-line.
125 po::variables_map vm;
126 try
127 {
128 po::store(po::parse_command_line(argc, argv, desc), vm);
129
130 if (CheckOption(vm, "help") || argc <= 1)
131 {
132 std::cout << "Executes a neural network model using the provided input tensor. " << std::endl;
133 std::cout << "Prints the resulting output tensor." << std::endl;
134 std::cout << std::endl;
135 std::cout << desc << std::endl;
136 return EXIT_SUCCESS;
137 }
138
139 po::notify(vm);
140 }
141 catch (const po::error& e)
142 {
143 std::cerr << e.what() << std::endl << std::endl;
144 std::cerr << desc << std::endl;
145 return EXIT_FAILURE;
146 }
147
148 // Get the value of the switch arguments.
149 bool concurrent = vm["concurrent"].as<bool>();
150 bool enableProfiling = vm["event-based-profiling"].as<bool>();
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100151 bool enableLayerDetails = vm["visualize-optimized-model"].as<bool>();
Ruomei Yan2fcce082019-04-02 16:47:34 +0100152 bool enableFp16TurboMode = vm["fp16-turbo-mode"].as<bool>();
Narumol Prangnawarat610256f2019-06-26 15:10:46 +0100153 bool quantizeInput = vm["quantize-input"].as<bool>();
Matthew Jackson54658b92019-08-27 15:35:59 +0100154 bool printIntermediate = vm["print-intermediate-layers"].as<bool>();
Jim Flynn4951b8c2019-10-03 10:04:30 -0700155 bool enableExternalProfiling = vm["enable-external-profiling"].as<bool>();
156 bool fileOnlyExternalProfiling = vm["file-only-external-profiling"].as<bool>();
telsoa01c577f2c2018-08-31 09:22:23 +0100157
158 // Check whether we have to load test cases from a file.
159 if (CheckOption(vm, "test-cases"))
160 {
161 // Check that the file exists.
162 if (!boost::filesystem::exists(testCasesFile))
163 {
164 BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" does not exist";
165 return EXIT_FAILURE;
166 }
167
168 // Parse CSV file and extract test cases
169 armnnUtils::CsvReader reader;
170 std::vector<armnnUtils::CsvRow> testCases = reader.ParseFile(testCasesFile);
171
172 // Check that there is at least one test case to run
173 if (testCases.empty())
174 {
175 BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" has no test cases";
176 return EXIT_FAILURE;
177 }
178
179 // Create runtime
180 armnn::IRuntime::CreationOptions options;
Nina Drozd549ae372018-09-10 14:26:44 +0100181 options.m_EnableGpuProfiling = enableProfiling;
Nikhil Raj0898b332019-10-22 17:19:23 +0100182 options.m_DynamicBackendsPath = dynamicBackendsPath;
Jim Flynn4951b8c2019-10-03 10:04:30 -0700183 options.m_ProfilingOptions.m_EnableProfiling = enableExternalProfiling;
184 options.m_ProfilingOptions.m_IncomingCaptureFile = incomingCaptureFile;
185 options.m_ProfilingOptions.m_OutgoingCaptureFile = outgoingCaptureFile;
186 options.m_ProfilingOptions.m_FileOnly = fileOnlyExternalProfiling;
187 options.m_ProfilingOptions.m_CapturePeriod = counterCapturePeriod;
telsoa01c577f2c2018-08-31 09:22:23 +0100188 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(options));
189
190 const std::string executableName("ExecuteNetwork");
191
192 // Check whether we need to run the test cases concurrently
193 if (concurrent)
194 {
195 std::vector<std::future<int>> results;
196 results.reserve(testCases.size());
197
198 // Run each test case in its own thread
199 for (auto& testCase : testCases)
200 {
201 testCase.values.insert(testCase.values.begin(), executableName);
Nina Drozd549ae372018-09-10 14:26:44 +0100202 results.push_back(std::async(std::launch::async, RunCsvTest, std::cref(testCase), std::cref(runtime),
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100203 enableProfiling, enableFp16TurboMode, thresholdTime, printIntermediate,
204 enableLayerDetails));
telsoa01c577f2c2018-08-31 09:22:23 +0100205 }
206
207 // Check results
208 for (auto& result : results)
209 {
210 if (result.get() != EXIT_SUCCESS)
211 {
212 return EXIT_FAILURE;
213 }
214 }
215 }
216 else
217 {
218 // Run tests sequentially
219 for (auto& testCase : testCases)
220 {
221 testCase.values.insert(testCase.values.begin(), executableName);
Matthew Jackson54658b92019-08-27 15:35:59 +0100222 if (RunCsvTest(testCase, runtime, enableProfiling,
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100223 enableFp16TurboMode, thresholdTime, printIntermediate,
224 enableLayerDetails) != EXIT_SUCCESS)
telsoa01c577f2c2018-08-31 09:22:23 +0100225 {
226 return EXIT_FAILURE;
227 }
228 }
229 }
230
231 return EXIT_SUCCESS;
232 }
233 else // Run single test
234 {
Aron Virginas-Tar382e21c2019-01-22 14:10:39 +0000235 // Get the preferred order of compute devices. If none are specified, default to using CpuRef
236 const std::string computeOption("compute");
Matteo Martincigh00dda4a2019-08-14 11:42:30 +0100237 std::vector<std::string> computeDevicesAsStrings =
238 CheckOption(vm, computeOption.c_str()) ?
239 vm[computeOption].as<std::vector<std::string>>() :
240 std::vector<std::string>();
Matteo Martincigh067112f2018-10-29 11:01:09 +0000241 std::vector<armnn::BackendId> computeDevices(computeDevicesAsStrings.begin(), computeDevicesAsStrings.end());
telsoa01c577f2c2018-08-31 09:22:23 +0100242
243 // Remove duplicates from the list of compute devices.
244 RemoveDuplicateDevices(computeDevices);
245
telsoa01c577f2c2018-08-31 09:22:23 +0100246 try
247 {
248 CheckOptionDependencies(vm);
249 }
250 catch (const po::error& e)
251 {
252 std::cerr << e.what() << std::endl << std::endl;
253 std::cerr << desc << std::endl;
254 return EXIT_FAILURE;
255 }
Colm Donelanb682d842019-10-16 12:24:20 +0100256 // Create runtime
257 armnn::IRuntime::CreationOptions options;
258 options.m_EnableGpuProfiling = enableProfiling;
Nikhil Raj0898b332019-10-22 17:19:23 +0100259 options.m_DynamicBackendsPath = dynamicBackendsPath;
Colm Donelanb682d842019-10-16 12:24:20 +0100260 options.m_ProfilingOptions.m_EnableProfiling = enableExternalProfiling;
261 options.m_ProfilingOptions.m_IncomingCaptureFile = incomingCaptureFile;
262 options.m_ProfilingOptions.m_OutgoingCaptureFile = outgoingCaptureFile;
263 options.m_ProfilingOptions.m_FileOnly = fileOnlyExternalProfiling;
264 options.m_ProfilingOptions.m_CapturePeriod = counterCapturePeriod;
265 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(options));
Matteo Martincigh00dda4a2019-08-14 11:42:30 +0100266 return RunTest(modelFormat, inputTensorShapes, computeDevices, dynamicBackendsPath, modelPath, inputNames,
Narumol Prangnawarat610256f2019-06-26 15:10:46 +0100267 inputTensorDataFilePaths, inputTypes, quantizeInput, outputTypes, outputNames,
Sadik Armagan77086282019-09-02 11:46:28 +0100268 outputTensorFiles, enableProfiling, enableFp16TurboMode, thresholdTime, printIntermediate,
Colm Donelanb682d842019-10-16 12:24:20 +0100269 subgraphId, enableLayerDetails, runtime);
telsoa014fcda012018-03-09 14:13:49 +0000270 }
271}