blob: a8f3b3d71d6363cf303a81fdc3775fe4c7c559a3 [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;
telsoa01c577f2c2018-08-31 09:22:23 +010030
James Conroy7b4886f2019-04-11 10:23:58 +010031 double thresholdTime = 0.0;
32
telsoa01c577f2c2018-08-31 09:22:23 +010033 size_t subgraphId = 0;
34
Aron Virginas-Tar5cc8e562018-10-23 15:14:46 +010035 const std::string backendsMessage = "Which device to run layers on by default. Possible choices: "
36 + armnn::BackendRegistryInstance().GetBackendIdsAsString();
37
telsoa01c577f2c2018-08-31 09:22:23 +010038 po::options_description desc("Options");
39 try
40 {
41 desc.add_options()
42 ("help", "Display usage information")
43 ("test-cases,t", po::value(&testCasesFile), "Path to a CSV file containing test cases to run. "
44 "If set, further parameters -- with the exception of compute device and concurrency -- will be ignored, "
45 "as they are expected to be defined in the file for each test in particular.")
46 ("concurrent,n", po::bool_switch()->default_value(false),
47 "Whether or not the test cases should be executed in parallel")
Matteo Martincigh49124022019-01-11 13:25:59 +000048 ("model-format,f", po::value(&modelFormat)->required(),
Aron Virginas-Tar64e4ccb2019-02-12 11:27:53 +000049 "armnn-binary, caffe-binary, caffe-text, onnx-binary, onnx-text, tflite-binary, tensorflow-binary or "
50 "tensorflow-text.")
51 ("model-path,m", po::value(&modelPath)->required(), "Path to model file, e.g. .armnn, .caffemodel, "
52 ".prototxt, .tflite, .onnx")
David Beckf0b48452018-10-19 15:20:56 +010053 ("compute,c", po::value<std::vector<std::string>>()->multitoken(),
Aron Virginas-Tar5cc8e562018-10-23 15:14:46 +010054 backendsMessage.c_str())
Ferran Balaguerc602f292019-02-08 17:09:55 +000055 ("input-name,i", po::value(&inputNames),
56 "Identifier of the input tensors in the network separated by comma.")
telsoa01c577f2c2018-08-31 09:22:23 +010057 ("subgraph-number,x", po::value<size_t>(&subgraphId)->default_value(0), "Id of the subgraph to be executed."
58 "Defaults to 0")
Ferran Balaguerc602f292019-02-08 17:09:55 +000059 ("input-tensor-shape,s", po::value(&inputTensorShapes),
60 "The shape of the input tensors in the network as a flat array of integers separated by comma. "
61 "Several shapes can be passed separating them by semicolon. "
telsoa01c577f2c2018-08-31 09:22:23 +010062 "This parameter is optional, depending on the network.")
Ferran Balaguerc602f292019-02-08 17:09:55 +000063 ("input-tensor-data,d", po::value(&inputTensorDataFilePaths),
64 "Path to files containing the input data as a flat array separated by whitespace. "
65 "Several paths can be passed separating them by comma. ")
66 ("input-type,y",po::value(&inputTypes), "The type of the input tensors in the network separated by comma. "
67 "If unset, defaults to \"float\" for all defined inputs. "
Éanna Ó Catháinb3d481a2019-02-26 11:26:24 +000068 "Accepted values (float, int or qasymm8)")
Narumol Prangnawarat610256f2019-06-26 15:10:46 +010069 ("quantize-input,q",po::bool_switch()->default_value(false),
70 "If this option is enabled, all float inputs will be quantized to qasymm8. "
71 "If unset, default to not quantized. "
72 "Accepted values (true or false)")
Éanna Ó Catháinb3d481a2019-02-26 11:26:24 +000073 ("output-type,z",po::value(&outputTypes),
74 "The type of the output tensors in the network separated by comma. "
75 "If unset, defaults to \"float\" for all defined outputs. "
76 "Accepted values (float, int or qasymm8).")
Ferran Balaguerc602f292019-02-08 17:09:55 +000077 ("output-name,o", po::value(&outputNames),
78 "Identifier of the output tensors in the network separated by comma.")
telsoa01c577f2c2018-08-31 09:22:23 +010079 ("event-based-profiling,e", po::bool_switch()->default_value(false),
Ruomei Yan2fcce082019-04-02 16:47:34 +010080 "Enables built in profiler. If unset, defaults to off.")
81 ("fp16-turbo-mode,h", po::bool_switch()->default_value(false), "If this option is enabled, FP32 layers, "
James Conroy7b4886f2019-04-11 10:23:58 +010082 "weights and biases will be converted to FP16 where the backend supports it")
83 ("threshold-time,r", po::value<double>(&thresholdTime)->default_value(0.0),
84 "Threshold time is the maximum allowed time for inference measured in milliseconds. If the actual "
85 "inference time is greater than the threshold time, the test will fail. By default, no threshold "
86 "time is used.");
telsoa01c577f2c2018-08-31 09:22:23 +010087 }
88 catch (const std::exception& e)
89 {
90 // Coverity points out that default_value(...) can throw a bad_lexical_cast,
91 // and that desc.add_options() can throw boost::io::too_few_args.
92 // They really won't in any of these cases.
93 BOOST_ASSERT_MSG(false, "Caught unexpected exception");
94 BOOST_LOG_TRIVIAL(fatal) << "Fatal internal error: " << e.what();
95 return EXIT_FAILURE;
96 }
97
98 // Parses the command-line.
99 po::variables_map vm;
100 try
101 {
102 po::store(po::parse_command_line(argc, argv, desc), vm);
103
104 if (CheckOption(vm, "help") || argc <= 1)
105 {
106 std::cout << "Executes a neural network model using the provided input tensor. " << std::endl;
107 std::cout << "Prints the resulting output tensor." << std::endl;
108 std::cout << std::endl;
109 std::cout << desc << std::endl;
110 return EXIT_SUCCESS;
111 }
112
113 po::notify(vm);
114 }
115 catch (const po::error& e)
116 {
117 std::cerr << e.what() << std::endl << std::endl;
118 std::cerr << desc << std::endl;
119 return EXIT_FAILURE;
120 }
121
122 // Get the value of the switch arguments.
123 bool concurrent = vm["concurrent"].as<bool>();
124 bool enableProfiling = vm["event-based-profiling"].as<bool>();
Ruomei Yan2fcce082019-04-02 16:47:34 +0100125 bool enableFp16TurboMode = vm["fp16-turbo-mode"].as<bool>();
Narumol Prangnawarat610256f2019-06-26 15:10:46 +0100126 bool quantizeInput = vm["quantize-input"].as<bool>();
telsoa01c577f2c2018-08-31 09:22:23 +0100127
128 // Check whether we have to load test cases from a file.
129 if (CheckOption(vm, "test-cases"))
130 {
131 // Check that the file exists.
132 if (!boost::filesystem::exists(testCasesFile))
133 {
134 BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" does not exist";
135 return EXIT_FAILURE;
136 }
137
138 // Parse CSV file and extract test cases
139 armnnUtils::CsvReader reader;
140 std::vector<armnnUtils::CsvRow> testCases = reader.ParseFile(testCasesFile);
141
142 // Check that there is at least one test case to run
143 if (testCases.empty())
144 {
145 BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" has no test cases";
146 return EXIT_FAILURE;
147 }
148
149 // Create runtime
150 armnn::IRuntime::CreationOptions options;
Nina Drozd549ae372018-09-10 14:26:44 +0100151 options.m_EnableGpuProfiling = enableProfiling;
152
telsoa01c577f2c2018-08-31 09:22:23 +0100153 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(options));
154
155 const std::string executableName("ExecuteNetwork");
156
157 // Check whether we need to run the test cases concurrently
158 if (concurrent)
159 {
160 std::vector<std::future<int>> results;
161 results.reserve(testCases.size());
162
163 // Run each test case in its own thread
164 for (auto& testCase : testCases)
165 {
166 testCase.values.insert(testCase.values.begin(), executableName);
Nina Drozd549ae372018-09-10 14:26:44 +0100167 results.push_back(std::async(std::launch::async, RunCsvTest, std::cref(testCase), std::cref(runtime),
James Conroy7b4886f2019-04-11 10:23:58 +0100168 enableProfiling, enableFp16TurboMode, thresholdTime));
telsoa01c577f2c2018-08-31 09:22:23 +0100169 }
170
171 // Check results
172 for (auto& result : results)
173 {
174 if (result.get() != EXIT_SUCCESS)
175 {
176 return EXIT_FAILURE;
177 }
178 }
179 }
180 else
181 {
182 // Run tests sequentially
183 for (auto& testCase : testCases)
184 {
185 testCase.values.insert(testCase.values.begin(), executableName);
James Conroy7b4886f2019-04-11 10:23:58 +0100186 if (RunCsvTest(testCase, runtime, enableProfiling, enableFp16TurboMode, thresholdTime) != EXIT_SUCCESS)
telsoa01c577f2c2018-08-31 09:22:23 +0100187 {
188 return EXIT_FAILURE;
189 }
190 }
191 }
192
193 return EXIT_SUCCESS;
194 }
195 else // Run single test
196 {
Aron Virginas-Tar382e21c2019-01-22 14:10:39 +0000197 // Get the preferred order of compute devices. If none are specified, default to using CpuRef
198 const std::string computeOption("compute");
199 std::vector<std::string> computeDevicesAsStrings = CheckOption(vm, computeOption.c_str()) ?
200 vm[computeOption].as<std::vector<std::string>>() :
201 std::vector<std::string>({ "CpuRef" });
Matteo Martincigh067112f2018-10-29 11:01:09 +0000202 std::vector<armnn::BackendId> computeDevices(computeDevicesAsStrings.begin(), computeDevicesAsStrings.end());
telsoa01c577f2c2018-08-31 09:22:23 +0100203
204 // Remove duplicates from the list of compute devices.
205 RemoveDuplicateDevices(computeDevices);
206
207 // Check that the specified compute devices are valid.
Aron Virginas-Tar5cc8e562018-10-23 15:14:46 +0100208 std::string invalidBackends;
209 if (!CheckRequestedBackendsAreValid(computeDevices, armnn::Optional<std::string&>(invalidBackends)))
telsoa01c577f2c2018-08-31 09:22:23 +0100210 {
Aron Virginas-Tar5cc8e562018-10-23 15:14:46 +0100211 BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains invalid backend IDs: "
212 << invalidBackends;
telsoa01c577f2c2018-08-31 09:22:23 +0100213 return EXIT_FAILURE;
214 }
215
216 try
217 {
218 CheckOptionDependencies(vm);
219 }
220 catch (const po::error& e)
221 {
222 std::cerr << e.what() << std::endl << std::endl;
223 std::cerr << desc << std::endl;
224 return EXIT_FAILURE;
225 }
226
James Conroy7b4886f2019-04-11 10:23:58 +0100227 return RunTest(modelFormat, inputTensorShapes, computeDevices, modelPath, inputNames,
Narumol Prangnawarat610256f2019-06-26 15:10:46 +0100228 inputTensorDataFilePaths, inputTypes, quantizeInput, outputTypes, outputNames,
James Conroy7b4886f2019-04-11 10:23:58 +0100229 enableProfiling, enableFp16TurboMode, thresholdTime, subgraphId);
telsoa014fcda012018-03-09 14:13:49 +0000230 }
231}