blob: 153fe5bcc730611337b8516e186659e30a6da01b [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Sadik Armagana9c2ce12020-07-14 10:02:22 +01002// Copyright © 2017 Arm Ltd and Contributors. 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
Jan Eilers45274902020-10-15 18:34:43 +01006#include "NetworkExecutionUtils/NetworkExecutionUtils.hpp"
7#include "ExecuteNetworkProgramOptions.hpp"
Kevin Mayb4b3ac92021-05-21 16:42:21 +01008#include <armnn/IAsyncExecutionCallback.hpp>
9#include <AsyncExecutionCallback.hpp>
Jan Eilers45274902020-10-15 18:34:43 +010010
11#include <armnn/Logging.hpp>
Rob Hughes9542f902021-07-14 09:48:54 +010012#include <armnnUtils/Filesystem.hpp>
Francis Murtagh40d27412021-10-28 11:11:35 +010013#include <armnnUtils/TContainer.hpp>
Jim Flynn4c9ed1d2022-01-23 23:57:20 +000014#include <ProfilingOptionsConverter.hpp>
Jan Eilers45274902020-10-15 18:34:43 +010015#include <InferenceTest.hpp>
16
17#if defined(ARMNN_SERIALIZER)
18#include "armnnDeserializer/IDeserializer.hpp"
19#endif
Jan Eilers45274902020-10-15 18:34:43 +010020#if defined(ARMNN_TF_LITE_PARSER)
21#include "armnnTfLiteParser/ITfLiteParser.hpp"
22#endif
23#if defined(ARMNN_ONNX_PARSER)
24#include "armnnOnnxParser/IOnnxParser.hpp"
25#endif
Sadik Armagan5d03e312020-11-17 16:43:56 +000026#if defined(ARMNN_TFLITE_DELEGATE)
27#include <armnn_delegate.hpp>
28#include <DelegateOptions.hpp>
29
30#include <tensorflow/lite/builtin_ops.h>
31#include <tensorflow/lite/c/builtin_op_data.h>
32#include <tensorflow/lite/c/common.h>
33#include <tensorflow/lite/optional_debug_tools.h>
34#include <tensorflow/lite/kernels/builtin_op_kernels.h>
35#include <tensorflow/lite/interpreter.h>
36#include <tensorflow/lite/kernels/register.h>
37#endif
Jan Eilers45274902020-10-15 18:34:43 +010038
39#include <future>
Colm Donelan3cff15a2021-10-12 15:06:19 +010040
41/**
42 * Given a measured duration and a threshold time tell the user whether we succeeded or not.
43 *
44 * @param duration the measured inference duration.
45 * @param thresholdTime the threshold time in milliseconds.
46 * @return false if the measured time exceeded the threshold.
47 */
48bool CheckInferenceTimeThreshold(const std::chrono::duration<double, std::milli>& duration,
49 const double& thresholdTime)
50{
Jan Eilers17d34da2021-12-08 16:15:12 +000051 ARMNN_LOG(info) << "Inference time: " << std::setprecision(2)
Colm Donelan3cff15a2021-10-12 15:06:19 +010052 << std::fixed << duration.count() << " ms\n";
53 // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
54 if (thresholdTime != 0.0)
55 {
56 ARMNN_LOG(info) << "Threshold time: " << std::setprecision(2)
57 << std::fixed << thresholdTime << " ms";
58 auto thresholdMinusInference = thresholdTime - duration.count();
59 ARMNN_LOG(info) << "Threshold time - Inference time: " << std::setprecision(2)
60 << std::fixed << thresholdMinusInference << " ms" << "\n";
61 if (thresholdMinusInference < 0)
62 {
63 std::string errorMessage = "Elapsed inference time is greater than provided threshold time.";
64 ARMNN_LOG(fatal) << errorMessage;
65 return false;
66 }
67 }
68 return true;
69}
70
Sadik Armagan5d03e312020-11-17 16:43:56 +000071#if defined(ARMNN_TFLITE_DELEGATE)
Colm Donelan45142282021-10-21 23:39:52 +010072int TfLiteDelegateMainImpl(const ExecuteNetworkParams& params, const armnn::IRuntime::CreationOptions runtimeOptions)
Sadik Armagan5d03e312020-11-17 16:43:56 +000073{
Tamas Nyiri00564232021-11-28 21:31:33 +000074 // Build model and corresponding interpreter
Sadik Armagan5d03e312020-11-17 16:43:56 +000075 using namespace tflite;
Jan Eilers45274902020-10-15 18:34:43 +010076
Sadik Armagan5d03e312020-11-17 16:43:56 +000077 std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(params.m_ModelPath.c_str());
78
79 auto tfLiteInterpreter = std::make_unique<Interpreter>();
80 tflite::ops::builtin::BuiltinOpResolver resolver;
81
82 tflite::InterpreterBuilder builder(*model, resolver);
83 builder(&tfLiteInterpreter);
84 tfLiteInterpreter->AllocateTensors();
85
Finn Williamsf806c4d2021-02-22 15:13:12 +000086 int status = 0;
Tamas Nyiri00564232021-11-28 21:31:33 +000087
88 // Create & populate Armnn Delegate, then register it to TfLiteInterpreter
Finn Williamsf806c4d2021-02-22 15:13:12 +000089 if (params.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteDelegate)
Sadik Armagan19a1c032021-01-20 12:17:00 +000090 {
Finn Williamsf806c4d2021-02-22 15:13:12 +000091 // Create the Armnn Delegate
Colm Donelan3cff15a2021-10-12 15:06:19 +010092 // Populate a DelegateOptions from the ExecuteNetworkParams.
93 armnnDelegate::DelegateOptions delegateOptions = params.ToDelegateOptions();
Jim Flynn4c9ed1d2022-01-23 23:57:20 +000094 delegateOptions.SetExternalProfilingParams(
Cathal Corbett5aa9fd72022-02-25 15:33:28 +000095 arm::pipe::ConvertExternalProfilingOptions(runtimeOptions.m_ProfilingOptions));
Colm Donelan3cff15a2021-10-12 15:06:19 +010096
Finn Williamsf806c4d2021-02-22 15:13:12 +000097 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
98 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
99 armnnDelegate::TfLiteArmnnDelegateDelete);
100 // Register armnn_delegate to TfLiteInterpreter
101 status = tfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
Ryan OSheaab8a4462022-02-03 10:45:51 +0000102 if (status != kTfLiteOk)
Finn Williamsf806c4d2021-02-22 15:13:12 +0000103 {
104 ARMNN_LOG(fatal) << "Could not register ArmNN TfLite Delegate to TfLiteInterpreter!";
105 return EXIT_FAILURE;
106 }
Sadik Armagan19a1c032021-01-20 12:17:00 +0000107 }
Finn Williamsf806c4d2021-02-22 15:13:12 +0000108 else
109 {
110 std::cout << "Running on TfLite without ArmNN delegate\n";
111 }
112
Colm Donelan3cff15a2021-10-12 15:06:19 +0100113 const size_t numInputs = params.m_InputNames.size();
Tamas Nyiri00564232021-11-28 21:31:33 +0000114 // Populate input tensor of interpreter
Sadik Armagan5d03e312020-11-17 16:43:56 +0000115 for(unsigned int inputIndex = 0; inputIndex < numInputs; ++inputIndex)
116 {
Cathal Corbett5ee069e2022-05-24 17:25:57 +0100117 // Load (or generate) input data for inference
118 armnn::Optional<std::string> dataFile = params.m_GenerateTensorData ? armnn::EmptyOptional() :
119 armnn::MakeOptional<std::string>(params.m_InputTensorDataFilePaths[inputIndex]);
120
Sadik Armagan5d03e312020-11-17 16:43:56 +0000121 int input = tfLiteInterpreter->inputs()[inputIndex];
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000122 TfLiteIntArray* inputDims = tfLiteInterpreter->tensor(input)->dims;
123
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100124 unsigned int inputSize = 1;
125 if (params.m_InputTensorShapes.size() > 0)
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000126 {
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100127 inputSize = params.m_InputTensorShapes[inputIndex]->GetNumElements();
128 }
129 else
130 {
131 for (unsigned int dim = 0; dim < static_cast<unsigned int>(inputDims->size); ++dim)
132 {
133 inputSize *= inputDims->data[dim];
134 }
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000135 }
136
Sadik Armagan5d03e312020-11-17 16:43:56 +0000137 if (params.m_InputTypes[inputIndex].compare("float") == 0)
138 {
139 auto inputData = tfLiteInterpreter->typed_tensor<float>(input);
Finn Williamsbbbefec2020-11-25 14:32:42 +0000140
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000141 if(inputData == NULL)
Finn Williamsbbbefec2020-11-25 14:32:42 +0000142 {
143 ARMNN_LOG(fatal) << "Input tensor is null, input type: "
144 "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
145 return EXIT_FAILURE;
146 }
147
Finn Williams56870182020-11-20 13:57:53 +0000148 std::vector<float> tensorData;
149 PopulateTensorWithDataGeneric<float>(tensorData,
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100150 inputSize,
151 dataFile,
152 [](const std::string& s)
153 { return std::stof(s); });
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000154
Finn Williams56870182020-11-20 13:57:53 +0000155 std::copy(tensorData.begin(), tensorData.end(), inputData);
156 }
Finn Williams35e7c1d2022-01-21 19:33:46 +0000157 else if (params.m_InputTypes[inputIndex].compare("qsymms8") == 0 ||
158 params.m_InputTypes[inputIndex].compare("qasymms8") == 0)
Finn Williams56870182020-11-20 13:57:53 +0000159 {
160 auto inputData = tfLiteInterpreter->typed_tensor<int8_t>(input);
Finn Williamsbbbefec2020-11-25 14:32:42 +0000161
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000162 if(inputData == NULL)
Finn Williamsbbbefec2020-11-25 14:32:42 +0000163 {
164 ARMNN_LOG(fatal) << "Input tensor is null, input type: "
165 "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
166 return EXIT_FAILURE;
167 }
168
Finn Williams56870182020-11-20 13:57:53 +0000169 std::vector<int8_t> tensorData;
170 PopulateTensorWithDataGeneric<int8_t>(tensorData,
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100171 inputSize,
Finn Williams56870182020-11-20 13:57:53 +0000172 dataFile,
173 [](const std::string& s)
174 { return armnn::numeric_cast<int8_t>(std::stoi(s)); });
175
176 std::copy(tensorData.begin(), tensorData.end(), inputData);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000177 }
178 else if (params.m_InputTypes[inputIndex].compare("int") == 0)
179 {
180 auto inputData = tfLiteInterpreter->typed_tensor<int32_t>(input);
Finn Williamsbbbefec2020-11-25 14:32:42 +0000181
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000182 if(inputData == NULL)
Finn Williamsbbbefec2020-11-25 14:32:42 +0000183 {
184 ARMNN_LOG(fatal) << "Input tensor is null, input type: "
185 "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
186 return EXIT_FAILURE;
187 }
188
Finn Williams56870182020-11-20 13:57:53 +0000189 std::vector<int32_t> tensorData;
190 PopulateTensorWithDataGeneric<int32_t>(tensorData,
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100191 inputSize,
Finn Williams56870182020-11-20 13:57:53 +0000192 dataFile,
193 [](const std::string& s)
194 { return std::stoi(s); });
195
196 std::copy(tensorData.begin(), tensorData.end(), inputData);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000197 }
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100198 else if (params.m_InputTypes[inputIndex].compare("qasymm8") == 0 ||
199 params.m_InputTypes[inputIndex].compare("qasymmu8") == 0)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000200 {
201 auto inputData = tfLiteInterpreter->typed_tensor<uint8_t>(input);
Finn Williamsbbbefec2020-11-25 14:32:42 +0000202
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000203 if(inputData == NULL)
Finn Williamsbbbefec2020-11-25 14:32:42 +0000204 {
205 ARMNN_LOG(fatal) << "Input tensor is null, input type: "
206 "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
207 return EXIT_FAILURE;
208 }
209
Finn Williams56870182020-11-20 13:57:53 +0000210 std::vector<uint8_t> tensorData;
211 PopulateTensorWithDataGeneric<uint8_t>(tensorData,
Mike Kelly00e9ebf2021-09-01 17:09:12 +0100212 inputSize,
Finn Williams56870182020-11-20 13:57:53 +0000213 dataFile,
214 [](const std::string& s)
215 { return armnn::numeric_cast<uint8_t>(std::stoi(s)); });
216
217 std::copy(tensorData.begin(), tensorData.end(), inputData);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000218 }
219 else
220 {
221 ARMNN_LOG(fatal) << "Unsupported input tensor data type \"" << params.m_InputTypes[inputIndex] << "\". ";
222 return EXIT_FAILURE;
223 }
224 }
225
Tamas Nyiri00564232021-11-28 21:31:33 +0000226 // Run inference, print the output of the inference
Sadik Armagan5d03e312020-11-17 16:43:56 +0000227 for (size_t x = 0; x < params.m_Iterations; x++)
228 {
Colm Donelan3cff15a2021-10-12 15:06:19 +0100229 // Start timer to record inference time in milliseconds.
230 const auto start_time = armnn::GetTimeNow();
Sadik Armagan5d03e312020-11-17 16:43:56 +0000231 // Run the inference
Finn Williamsf806c4d2021-02-22 15:13:12 +0000232 status = tfLiteInterpreter->Invoke();
Colm Donelan3cff15a2021-10-12 15:06:19 +0100233 const auto duration = armnn::GetTimeDuration(start_time);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000234
Tamas Nyiri00564232021-11-28 21:31:33 +0000235 // The TFLite interpreter's outputs might be in a different order than the user inputted output names.
236 std::map<unsigned int, int> paramToTfliteOutputIndex;
237 for (unsigned int paramIndex = 0; paramIndex < params.m_OutputNames.size(); ++paramIndex)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000238 {
Tamas Nyiri00564232021-11-28 21:31:33 +0000239 paramToTfliteOutputIndex[paramIndex] = -1;
240 for (unsigned int tfLiteIndex = 0; tfLiteIndex < tfLiteInterpreter->outputs().size(); ++tfLiteIndex)
241 {
242 if (params.m_OutputNames[paramIndex] == tfLiteInterpreter->GetOutputName(tfLiteIndex))
243 {
244 paramToTfliteOutputIndex[paramIndex] = tfLiteIndex;
245 }
246 }
247 }
248
249 // Print out the output
250 for (unsigned int paramOutputIndex = 0; paramOutputIndex < params.m_OutputNames.size(); ++paramOutputIndex)
251 {
252 int outputIndex = paramToTfliteOutputIndex[paramOutputIndex];
253 if (outputIndex == -1)
254 {
255 std::cout << fmt::format("Output name: {} doesn't exist.", params.m_OutputNames[paramOutputIndex]) <<
256 std::endl;
257 continue;
258 }
Sadik Armagan5d03e312020-11-17 16:43:56 +0000259 auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[outputIndex];
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000260 TfLiteIntArray* outputDims = tfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims;
Colm Donelan3cff15a2021-10-12 15:06:19 +0100261 // If we've been asked to write to a file then set a file output stream. Otherwise use stdout.
262 FILE* outputTensorFile = stdout;
263 if (!params.m_OutputTensorFiles.empty())
264 {
265 outputTensorFile = fopen(params.m_OutputTensorFiles[outputIndex].c_str(), "w");
266 if (outputTensorFile == NULL)
267 {
268 ARMNN_LOG(fatal) << "Specified output tensor file, \"" <<
269 params.m_OutputTensorFiles[outputIndex] <<
270 "\", cannot be created. Defaulting to stdout. " <<
271 "Error was: " << std::strerror(errno);
272 outputTensorFile = stdout;
273 }
274 else
275 {
276 ARMNN_LOG(info) << "Writing output " << outputIndex << "' of iteration: " << x+1 << " to file: '"
277 << params.m_OutputTensorFiles[outputIndex] << "'";
278 }
279 }
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000280 long outputSize = 1;
Sadik Armagan5d03e312020-11-17 16:43:56 +0000281 for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim)
282 {
Sadik Armagan15f7fae2020-11-18 09:37:03 +0000283 outputSize *= outputDims->data[dim];
Sadik Armagan5d03e312020-11-17 16:43:56 +0000284 }
285
Tamas Nyiri00564232021-11-28 21:31:33 +0000286 std::cout << tfLiteInterpreter->GetOutputName(outputIndex) << ": ";
Ryan OSheaab8a4462022-02-03 10:45:51 +0000287 if (params.m_OutputTypes[paramOutputIndex].compare("float") == 0)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000288 {
289 auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<float>(tfLiteDelegateOutputId);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000290 if(tfLiteDelageOutputData == NULL)
291 {
292 ARMNN_LOG(fatal) << "Output tensor is null, output type: "
Ryan OSheaab8a4462022-02-03 10:45:51 +0000293 "\"" << params.m_OutputTypes[paramOutputIndex] << "\" may be incorrect.";
Sadik Armagan5d03e312020-11-17 16:43:56 +0000294 return EXIT_FAILURE;
295 }
296
Jan Eilers284b5d12021-09-07 12:46:15 +0100297 if (!params.m_DontPrintOutputs)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000298 {
Jan Eilers284b5d12021-09-07 12:46:15 +0100299 for (int i = 0; i < outputSize; ++i)
300 {
Colm Donelan3cff15a2021-10-12 15:06:19 +0100301 fprintf(outputTensorFile, "%f ", tfLiteDelageOutputData[i]);
Jan Eilers284b5d12021-09-07 12:46:15 +0100302 }
Sadik Armagan5d03e312020-11-17 16:43:56 +0000303 }
304 }
Ryan OSheaab8a4462022-02-03 10:45:51 +0000305 else if (params.m_OutputTypes[paramOutputIndex].compare("int") == 0)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000306 {
307 auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<int32_t>(tfLiteDelegateOutputId);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000308 if(tfLiteDelageOutputData == NULL)
309 {
310 ARMNN_LOG(fatal) << "Output tensor is null, output type: "
Ryan OSheaab8a4462022-02-03 10:45:51 +0000311 "\"" << params.m_OutputTypes[paramOutputIndex] << "\" may be incorrect.";
Sadik Armagan5d03e312020-11-17 16:43:56 +0000312 return EXIT_FAILURE;
313 }
314
Jan Eilers284b5d12021-09-07 12:46:15 +0100315 if (!params.m_DontPrintOutputs)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000316 {
Jan Eilers284b5d12021-09-07 12:46:15 +0100317 for (int i = 0; i < outputSize; ++i)
318 {
Colm Donelan3cff15a2021-10-12 15:06:19 +0100319 fprintf(outputTensorFile, "%d ", tfLiteDelageOutputData[i]);
Jan Eilers284b5d12021-09-07 12:46:15 +0100320 }
Sadik Armagan5d03e312020-11-17 16:43:56 +0000321 }
322 }
Ryan OSheaab8a4462022-02-03 10:45:51 +0000323 else if (params.m_OutputTypes[paramOutputIndex].compare("qsymms8") == 0 ||
324 params.m_OutputTypes[paramOutputIndex].compare("qasymms8") == 0)
Finn Williams56870182020-11-20 13:57:53 +0000325 {
326 auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<int8_t>(tfLiteDelegateOutputId);
327 if(tfLiteDelageOutputData == NULL)
328 {
329 ARMNN_LOG(fatal) << "Output tensor is null, output type: "
Ryan OSheaab8a4462022-02-03 10:45:51 +0000330 "\"" << params.m_OutputTypes[paramOutputIndex] << "\" may be incorrect.";
Finn Williams56870182020-11-20 13:57:53 +0000331 return EXIT_FAILURE;
332 }
333
Jan Eilers284b5d12021-09-07 12:46:15 +0100334 if (!params.m_DontPrintOutputs)
Finn Williams56870182020-11-20 13:57:53 +0000335 {
Jan Eilers284b5d12021-09-07 12:46:15 +0100336 for (int i = 0; i < outputSize; ++i)
337 {
Colm Donelan3cff15a2021-10-12 15:06:19 +0100338 fprintf(outputTensorFile, "%d ", tfLiteDelageOutputData[i]);
Jan Eilers284b5d12021-09-07 12:46:15 +0100339 }
Finn Williams56870182020-11-20 13:57:53 +0000340 }
341 }
Ryan OSheaab8a4462022-02-03 10:45:51 +0000342 else if (params.m_OutputTypes[paramOutputIndex].compare("qasymm8") == 0 ||
343 params.m_OutputTypes[paramOutputIndex].compare("qasymmu8") == 0)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000344 {
345 auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<uint8_t>(tfLiteDelegateOutputId);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000346 if(tfLiteDelageOutputData == NULL)
347 {
348 ARMNN_LOG(fatal) << "Output tensor is null, output type: "
Ryan OSheaab8a4462022-02-03 10:45:51 +0000349 "\"" << params.m_OutputTypes[paramOutputIndex] << "\" may be incorrect.";
Sadik Armagan5d03e312020-11-17 16:43:56 +0000350 return EXIT_FAILURE;
351 }
352
Jan Eilers284b5d12021-09-07 12:46:15 +0100353 if (!params.m_DontPrintOutputs)
Sadik Armagan5d03e312020-11-17 16:43:56 +0000354 {
Jan Eilers284b5d12021-09-07 12:46:15 +0100355 for (int i = 0; i < outputSize; ++i)
356 {
Colm Donelan3cff15a2021-10-12 15:06:19 +0100357 fprintf(outputTensorFile, "%u ", tfLiteDelageOutputData[i]);
Jan Eilers284b5d12021-09-07 12:46:15 +0100358 }
Sadik Armagan5d03e312020-11-17 16:43:56 +0000359 }
360 }
361 else
362 {
363 ARMNN_LOG(fatal) << "Output tensor is null, output type: "
Ryan OSheaab8a4462022-02-03 10:45:51 +0000364 "\"" << params.m_OutputTypes[paramOutputIndex] <<
Sadik Armagan5d03e312020-11-17 16:43:56 +0000365 "\" may be incorrect. Output type can be specified with -z argument";
366 return EXIT_FAILURE;
367 }
368 std::cout << std::endl;
369 }
Colm Donelan3cff15a2021-10-12 15:06:19 +0100370 CheckInferenceTimeThreshold(duration, params.m_ThresholdTime);
Sadik Armagan5d03e312020-11-17 16:43:56 +0000371 }
372
373 return status;
374}
375#endif
Jan Eilers45274902020-10-15 18:34:43 +0100376template<typename TParser, typename TDataType>
377int MainImpl(const ExecuteNetworkParams& params,
378 const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
379{
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100380 using namespace std::chrono;
Jan Eilers45274902020-10-15 18:34:43 +0100381
Francis Murtagh40d27412021-10-28 11:11:35 +0100382 std::vector<std::vector<armnnUtils::TContainer>> inputs;
383 std::vector<std::vector<armnnUtils::TContainer>> outputs;
Jan Eilers45274902020-10-15 18:34:43 +0100384
385 try
386 {
387 // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
388 typename InferenceModel<TParser, TDataType>::Params inferenceModelParams;
389 inferenceModelParams.m_ModelPath = params.m_ModelPath;
Mike Kelly80512b02022-05-16 23:10:42 +0100390 inferenceModelParams.m_AllowExpandedDims = params.m_AllowExpandedDims;
Jan Eilers45274902020-10-15 18:34:43 +0100391 inferenceModelParams.m_IsModelBinary = params.m_IsModelBinary;
392 inferenceModelParams.m_ComputeDevices = params.m_ComputeDevices;
393 inferenceModelParams.m_DynamicBackendsPath = params.m_DynamicBackendsPath;
394 inferenceModelParams.m_PrintIntermediateLayers = params.m_PrintIntermediate;
395 inferenceModelParams.m_VisualizePostOptimizationModel = params.m_EnableLayerDetails;
396 inferenceModelParams.m_ParseUnsupported = params.m_ParseUnsupported;
397 inferenceModelParams.m_InferOutputShape = params.m_InferOutputShape;
398 inferenceModelParams.m_EnableFastMath = params.m_EnableFastMath;
Matthew Sloyan42432112021-01-08 10:30:51 +0000399 inferenceModelParams.m_SaveCachedNetwork = params.m_SaveCachedNetwork;
400 inferenceModelParams.m_CachedNetworkFilePath = params.m_CachedNetworkFilePath;
Matthew Sloyan0a7dc6b2021-02-10 16:50:53 +0000401 inferenceModelParams.m_NumberOfThreads = params.m_NumberOfThreads;
Finn Williams40646322021-02-11 16:16:42 +0000402 inferenceModelParams.m_MLGOTuningFilePath = params.m_MLGOTuningFilePath;
Sadik Armagana04a9d72021-04-27 10:02:10 +0100403 inferenceModelParams.m_AsyncEnabled = params.m_Concurrent;
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100404 inferenceModelParams.m_ThreadPoolSize = params.m_ThreadPoolSize;
Keith Davisf4874862021-08-09 16:49:18 +0100405 inferenceModelParams.m_OutputDetailsToStdOut = params.m_OutputDetailsToStdOut;
Keith Davis4914d0c2021-08-18 17:14:05 +0100406 inferenceModelParams.m_OutputDetailsOnlyToStdOut = params.m_OutputDetailsOnlyToStdOut;
Jim Flynn15425812022-02-15 16:53:13 +0000407 inferenceModelParams.m_ImportInputsIfAligned = params.m_ImportInputsIfAligned;
Jan Eilers45274902020-10-15 18:34:43 +0100408
409 for(const std::string& inputName: params.m_InputNames)
410 {
411 inferenceModelParams.m_InputBindings.push_back(inputName);
412 }
413
414 for(unsigned int i = 0; i < params.m_InputTensorShapes.size(); ++i)
415 {
416 inferenceModelParams.m_InputShapes.push_back(*params.m_InputTensorShapes[i]);
417 }
418
419 for(const std::string& outputName: params.m_OutputNames)
420 {
421 inferenceModelParams.m_OutputBindings.push_back(outputName);
422 }
423
424 inferenceModelParams.m_SubgraphId = params.m_SubgraphId;
425 inferenceModelParams.m_EnableFp16TurboMode = params.m_EnableFp16TurboMode;
426 inferenceModelParams.m_EnableBf16TurboMode = params.m_EnableBf16TurboMode;
427
428 InferenceModel<TParser, TDataType> model(inferenceModelParams,
429 params.m_EnableProfiling,
430 params.m_DynamicBackendsPath,
431 runtime);
432
433 const size_t numInputs = inferenceModelParams.m_InputBindings.size();
Sadik Armagana04a9d72021-04-27 10:02:10 +0100434
435 armnn::Optional<QuantizationParams> qParams = params.m_QuantizeInput ?
436 armnn::MakeOptional<QuantizationParams>(
437 model.GetInputQuantizationParams()) :
438 armnn::EmptyOptional();
439
Jan Eilersf17fcd52021-07-26 22:20:00 +0100440 if (params.m_InputTensorDataFilePaths.size() > numInputs)
441 {
442 ARMNN_LOG(info) << "Given network has " << numInputs << " input/s. One input-tensor-data file is required "
443 << "for each input. The user provided "
444 << params.m_InputTensorDataFilePaths.size()
445 << " input-tensor-data file/s which will be used to fill the input/s.\n";
446 }
447
Jan Eilers45274902020-10-15 18:34:43 +0100448 const size_t numOutputs = inferenceModelParams.m_OutputBindings.size();
Jan Eilers45274902020-10-15 18:34:43 +0100449
Colm Donelanc5e41982021-10-28 20:19:43 +0100450 // The user is allowed to specify the data type of each output tensor. It is used here to construct the
451 // result tensors for each iteration. It is possible for the user to specify a type that does not match
452 // the data type of the corresponding model output. It may not make sense, but it is historically allowed.
453 // The potential problem here is a buffer overrun when a larger data type is written into the space for a
454 // smaller one. Issue a warning to highlight the potential problem.
455 for (unsigned int outputIdx = 0; outputIdx < model.GetOutputBindingInfos().size(); ++outputIdx)
456 {
457 armnn::DataType type = model.GetOutputBindingInfo(outputIdx).second.GetDataType();
458 switch (type)
459 {
David Monahan67cc5fc2021-11-03 12:56:41 +0000460 // --output-type only supports float, int, qasymms8 or qasymmu8.
Colm Donelanc5e41982021-10-28 20:19:43 +0100461 case armnn::DataType::Float32:
462 if (params.m_OutputTypes[outputIdx].compare("float") != 0)
463 {
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100464 ARMNN_LOG(warning) << "Model output index: " << outputIdx << " has data type Float32. The "
465 << "corresponding --output-type is " << params.m_OutputTypes[outputIdx] <<
Colm Donelanc5e41982021-10-28 20:19:43 +0100466 ". This may cause unexpected problems or random failures.";
467 }
468 break;
469 case armnn::DataType::QAsymmU8:
470 if (params.m_OutputTypes[outputIdx].compare("qasymmu8") != 0)
471 {
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100472 ARMNN_LOG(warning) << "Model output index: " << outputIdx << " has data type QAsymmU8. The "
473 << "corresponding --output-type is " << params.m_OutputTypes[outputIdx] <<
474 ". This may cause unexpected problems or random failures.";
Colm Donelanc5e41982021-10-28 20:19:43 +0100475 }
476 break;
477 case armnn::DataType::Signed32:
478 if (params.m_OutputTypes[outputIdx].compare("int") != 0)
479 {
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100480 ARMNN_LOG(warning) << "Model output index: " << outputIdx << " has data type Signed32. The "
481 << "corresponding --output-type is " << params.m_OutputTypes[outputIdx] <<
Colm Donelanc5e41982021-10-28 20:19:43 +0100482 ". This may cause unexpected problems or random failures.";
483 }
484 break;
485 case armnn::DataType::QAsymmS8:
486 if (params.m_OutputTypes[outputIdx].compare("qasymms8") != 0)
487 {
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100488 ARMNN_LOG(warning) << "Model output index: " << outputIdx << " has data type QAsymmS8. The "
489 << "corresponding --output-type is " << params.m_OutputTypes[outputIdx] <<
Colm Donelanc5e41982021-10-28 20:19:43 +0100490 ". This may cause unexpected problems or random failures.";
491 }
492 break;
493 default:
494 break;
495 }
496 }
Sadik Armagana04a9d72021-04-27 10:02:10 +0100497
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100498 if (!params.m_ReuseBuffers)
499 {
500 for (unsigned int j = 0; j < params.m_Iterations; ++j)
501 {
502 std::vector<armnnUtils::TContainer> inputDataContainers;
503 for (unsigned int i = 0; i < numInputs; ++i)
504 {
505 // If there are fewer input files given than required for the execution of
506 // params.m_Iterations we simply start with the first input file again
507 size_t inputFileIndex = j * numInputs + i;
508 if (!params.m_InputTensorDataFilePaths.empty())
509 {
510 inputFileIndex = inputFileIndex % params.m_InputTensorDataFilePaths.size();
511 }
512
513 armnn::Optional<std::string> dataFile = params.m_GenerateTensorData ?
514 armnn::EmptyOptional() :
515 armnn::MakeOptional<std::string>(
516 params.m_InputTensorDataFilePaths.at(
517 inputFileIndex));
518
519 unsigned int numElements = model.GetInputSize(i);
520 if (params.m_InputTensorShapes.size() > i && params.m_InputTensorShapes[i])
521 {
522 // If the user has provided a tensor shape for the current input,
523 // override numElements
524 numElements = params.m_InputTensorShapes[i]->GetNumElements();
525 }
526
527 armnnUtils::TContainer tensorData;
528 PopulateTensorWithData(tensorData,
529 numElements,
530 params.m_InputTypes[i],
531 qParams,
532 dataFile);
533
534 inputDataContainers.push_back(tensorData);
535 }
536 inputs.push_back(inputDataContainers);
537 }
538
539 for (unsigned int j = 0; j < params.m_Iterations; ++j)
540 {
541 std::vector<armnnUtils::TContainer> outputDataContainers;
542 for (unsigned int i = 0; i < numOutputs; ++i)
543 {
544 if (params.m_OutputTypes[i].compare("float") == 0)
545 {
546 outputDataContainers.push_back(std::vector<float>(model.GetOutputSize(i)));
547 }
548 else if (params.m_OutputTypes[i].compare("int") == 0)
549 {
550 outputDataContainers.push_back(std::vector<int>(model.GetOutputSize(i)));
551 }
552 else if (params.m_OutputTypes[i].compare("qasymm8") == 0 ||
553 params.m_OutputTypes[i].compare("qasymmu8") == 0)
554 {
555 outputDataContainers.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
556 }
557 else if (params.m_OutputTypes[i].compare("qasymms8") == 0)
558 {
559 outputDataContainers.push_back(std::vector<int8_t>(model.GetOutputSize(i)));
560 }
561 else
562 {
563 ARMNN_LOG(fatal) << "Unsupported tensor data type \"" << params.m_OutputTypes[i] << "\". ";
564 return EXIT_FAILURE;
565 }
566 }
567 outputs.push_back(outputDataContainers);
568 }
569 }
Jan Eilersf17fcd52021-07-26 22:20:00 +0100570 if (params.m_Iterations > 1)
571 {
572 std::stringstream msg;
573 msg << "Network will be executed " << params.m_Iterations;
574 if (params.m_Concurrent)
575 {
576 msg << " times in an asynchronous manner. ";
577 }
578 else
579 {
580 msg << " times successively. ";
581 }
582 msg << "The input-tensor-data files will be reused recursively if the user didn't provide enough to "
583 "cover each execution.";
584 ARMNN_LOG(info) << msg.str();
585 }
586
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100587 // Synchronous execution
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100588 if (!params.m_Concurrent && !params.m_ReuseBuffers)
Sadik Armagana04a9d72021-04-27 10:02:10 +0100589 {
Sadik Armagana04a9d72021-04-27 10:02:10 +0100590 for (size_t x = 0; x < params.m_Iterations; x++)
591 {
592 // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
Jan Eilersf17fcd52021-07-26 22:20:00 +0100593 auto inference_duration = model.Run(inputs[x], outputs[x]);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100594
595 if (params.m_GenerateTensorData)
596 {
597 ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
598 }
Jan Eilers284b5d12021-09-07 12:46:15 +0100599 if (params.m_DontPrintOutputs)
600 {
601 ARMNN_LOG(info) << "Printing outputs to console is disabled.";
602 }
Sadik Armagana04a9d72021-04-27 10:02:10 +0100603
604 // Print output tensors
605 const auto& infosOut = model.GetOutputBindingInfos();
606 for (size_t i = 0; i < numOutputs; i++)
607 {
608 const armnn::TensorInfo& infoOut = infosOut[i].second;
Jan Eilersf17fcd52021-07-26 22:20:00 +0100609
Jan Eilers284b5d12021-09-07 12:46:15 +0100610 // We've made sure before that the number of output files either equals numOutputs, in which
611 // case we override those files when processing the results of each iteration (only the result
612 // of the last iteration will be stored), or there are enough
Jan Eilersf17fcd52021-07-26 22:20:00 +0100613 // output files for each output of each iteration.
614 size_t outputFileIndex = x * numOutputs + i;
615 if (!params.m_OutputTensorFiles.empty())
616 {
617 outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size();
618 ARMNN_LOG(info) << "Writing output " << i << " named: '"
619 << inferenceModelParams.m_OutputBindings[i]
620 << "' of iteration: " << x+1 << " to file: '"
621 << params.m_OutputTensorFiles[outputFileIndex] << "'";
622 }
623 auto outputTensorFile = params.m_OutputTensorFiles.empty()
624 ? ""
625 : params.m_OutputTensorFiles[outputFileIndex];
Sadik Armagana04a9d72021-04-27 10:02:10 +0100626
627 TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
628 infoOut,
629 outputTensorFile,
Jan Eilers284b5d12021-09-07 12:46:15 +0100630 params.m_DequantizeOutput,
631 !params.m_DontPrintOutputs);
Jan Eilersf17fcd52021-07-26 22:20:00 +0100632 mapbox::util::apply_visitor(printer, outputs[x][i]);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100633 }
634
635 ARMNN_LOG(info) << "\nInference time: " << std::setprecision(2)
636 << std::fixed << inference_duration.count() << " ms\n";
637
638 // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
639 if (params.m_ThresholdTime != 0.0)
640 {
641 ARMNN_LOG(info) << "Threshold time: " << std::setprecision(2)
642 << std::fixed << params.m_ThresholdTime << " ms";
643 auto thresholdMinusInference = params.m_ThresholdTime - inference_duration.count();
644 ARMNN_LOG(info) << "Threshold time - Inference time: " << std::setprecision(2)
645 << std::fixed << thresholdMinusInference << " ms" << "\n";
646
647 if (thresholdMinusInference < 0)
648 {
649 std::string errorMessage = "Elapsed inference time is greater than provided threshold time.";
650 ARMNN_LOG(fatal) << errorMessage;
651 }
652 }
653 }
654 }
Ryan OSheadfbec2d2022-03-28 10:55:48 +0100655 // Synchronous Execution using a single buffer for input and output data
656 else if(!params.m_Concurrent)
657 {
658 std::vector<armnnUtils::TContainer> input;
659 std::vector<armnnUtils::TContainer> output;
660
661 for (unsigned int i = 0; i < numInputs; ++i)
662 {
663 // If there are fewer input files given than required for the execution of
664 // params.m_Iterations we simply start with the first input file again
665 size_t inputFileIndex = numInputs + i;
666 if (!params.m_InputTensorDataFilePaths.empty())
667 {
668 inputFileIndex = inputFileIndex % params.m_InputTensorDataFilePaths.size();
669 }
670
671 armnn::Optional<std::string> dataFile = params.m_GenerateTensorData ?
672 armnn::EmptyOptional() :
673 armnn::MakeOptional<std::string>(
674 params.m_InputTensorDataFilePaths.at(
675 inputFileIndex));
676
677 unsigned int numElements = model.GetInputSize(i);
678 if (params.m_InputTensorShapes.size() > i && params.m_InputTensorShapes[i])
679 {
680 // If the user has provided a tensor shape for the current input,
681 // override numElements
682 numElements = params.m_InputTensorShapes[i]->GetNumElements();
683 }
684
685 armnnUtils::TContainer tensorData;
686 PopulateTensorWithData(tensorData,
687 numElements,
688 params.m_InputTypes[i],
689 qParams,
690 dataFile);
691
692 input.push_back(tensorData);
693 }
694
695 for (unsigned int i = 0; i < numOutputs; ++i)
696 {
697 if (params.m_OutputTypes[i].compare("float") == 0)
698 {
699 output.push_back(std::vector<float>(model.GetOutputSize(i)));
700 } else if (params.m_OutputTypes[i].compare("int") == 0) {
701 output.push_back(std::vector<int>(model.GetOutputSize(i)));
702 } else if (params.m_OutputTypes[i].compare("qasymm8") == 0 ||
703 params.m_OutputTypes[i].compare("qasymmu8") == 0)
704 {
705 output.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
706 } else if (params.m_OutputTypes[i].compare("qasymms8") == 0)
707 {
708 output.push_back(std::vector<int8_t>(model.GetOutputSize(i)));
709 } else {
710 ARMNN_LOG(fatal) << "Unsupported tensor data type \"" << params.m_OutputTypes[i] << "\". ";
711 return EXIT_FAILURE;
712 }
713 }
714
715 std::vector<std::chrono::duration<double, std::milli>> timings;
716 timings.reserve(params.m_Iterations);
717 for (size_t x = 0; x < params.m_Iterations; x++)
718 {
719 // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
720 auto inference_duration = model.Run(input, output);
721 timings.push_back(inference_duration);
722 }
723
724 if (params.m_GenerateTensorData)
725 {
726 ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
727 }
728 if (params.m_DontPrintOutputs)
729 {
730 ARMNN_LOG(info) << "Printing outputs to console is disabled.";
731 }
732
733 // Print output. This only needs to happen once as input is the same for each iteration.
734 const auto &infosOut = model.GetOutputBindingInfos();
735 for (size_t i = 0; i < numOutputs; i++)
736 {
737 const armnn::TensorInfo &infoOut = infosOut[i].second;
738
739 // We've made sure before that the number of output files either equals numOutputs, in which
740 // case we override those files when processing the results of each iteration (only the result
741 // of the last iteration will be stored), or there are enough
742 // output files for each output of each iteration.
743 size_t outputFileIndex = numOutputs + i;
744 if (!params.m_OutputTensorFiles.empty())
745 {
746 outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size();
747 ARMNN_LOG(info) << "Writing output " << i << " named: '"
748 << inferenceModelParams.m_OutputBindings[i] <<" to file: '"
749 << params.m_OutputTensorFiles[outputFileIndex] << "'";
750 }
751 auto outputTensorFile = params.m_OutputTensorFiles.empty()
752 ? ""
753 : params.m_OutputTensorFiles[outputFileIndex];
754
755 TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
756 infoOut,
757 outputTensorFile,
758 params.m_DequantizeOutput,
759 !params.m_DontPrintOutputs);
760 mapbox::util::apply_visitor(printer, output[i]);
761 }
762
763 for(auto inference: timings)
764 {
765
766 ARMNN_LOG(info) << "\nInference time: " << std::setprecision(2)
767 << std::fixed << inference.count() << " ms\n";
768
769 // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
770 if (params.m_ThresholdTime != 0.0)
771 {
772 ARMNN_LOG(info) << "Threshold time: " << std::setprecision(2)
773 << std::fixed << params.m_ThresholdTime << " ms";
774 auto thresholdMinusInference = params.m_ThresholdTime - inference.count();
775 ARMNN_LOG(info) << "Threshold time - Inference time: " << std::setprecision(2)
776 << std::fixed << thresholdMinusInference << " ms" << "\n";
777
778 if (thresholdMinusInference < 0)
779 {
780 std::string errorMessage = "Elapsed inference time is greater than provided threshold time.";
781 ARMNN_LOG(fatal) << errorMessage;
782 }
783 }
784 }
785 }
786
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100787 // Asynchronous execution using the Arm NN thread pool
Kevin May94dd4db2021-05-26 16:01:08 +0100788 else if (params.m_ThreadPoolSize >= 1)
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100789 {
790 try
791 {
792 ARMNN_LOG(info) << "Asynchronous execution with Arm NN thread pool... \n";
Finn Williamsf364d532021-06-09 17:07:33 +0100793 armnn::AsyncCallbackManager callbackManager;
Francis Murtagh40d27412021-10-28 11:11:35 +0100794 std::unordered_map<armnn::InferenceId, std::vector<armnnUtils::TContainer>&> inferenceOutputMap;
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100795
796 // Declare the latest and earliest inference times here to be used when calculating overall time
797 std::chrono::high_resolution_clock::time_point earliestStartTime;
798 std::chrono::high_resolution_clock::time_point latestEndTime =
799 std::chrono::high_resolution_clock::now();
800
801 // For the asynchronous execution, we are adding a pool of working memory handles (1 per thread) in the
802 // LoadedNetwork with each scheduled inference having a specific priority
Jan Eilersf17fcd52021-07-26 22:20:00 +0100803 for (size_t i = 0; i < params.m_Iterations; ++i)
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100804 {
Finn Williamsf364d532021-06-09 17:07:33 +0100805 std::shared_ptr<armnn::AsyncExecutionCallback> cb = callbackManager.GetNewCallback();
806 inferenceOutputMap.insert({cb->GetInferenceId(), outputs[i]});
807 model.RunAsync(inputs[i], outputs[i], cb);
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100808 }
809
810 // Check the results
811 unsigned int j = 0;
Jan Eilersf17fcd52021-07-26 22:20:00 +0100812 for (size_t iteration = 0; iteration < params.m_Iterations; ++iteration)
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100813 {
Finn Williamsf364d532021-06-09 17:07:33 +0100814 auto cb = callbackManager.GetNotifiedCallback();
815
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100816 // Get the results
817 auto endTime = time_point_cast<std::chrono::milliseconds>(cb->GetEndTime());
818 auto startTime = time_point_cast<std::chrono::milliseconds>(cb->GetStartTime());
819 auto inferenceDuration = endTime - startTime;
820
821 if (latestEndTime < cb->GetEndTime())
822 {
823 latestEndTime = cb->GetEndTime();
824 }
825
826 if (earliestStartTime.time_since_epoch().count() == 0)
827 {
828 earliestStartTime = cb->GetStartTime();
829 }
830 else if (earliestStartTime > cb->GetStartTime())
831 {
832 earliestStartTime = cb->GetStartTime();
833 }
834
835 if (params.m_GenerateTensorData)
836 {
837 ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
838 }
Jan Eilers284b5d12021-09-07 12:46:15 +0100839 if (params.m_DontPrintOutputs)
840 {
841 ARMNN_LOG(info) << "Printing outputs to console is disabled.";
842 }
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100843
844 // Print output tensors
845 const auto& infosOut = model.GetOutputBindingInfos();
846 for (size_t i = 0; i < numOutputs; i++)
847 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100848 // We've made sure before that the number of output files either equals numOutputs, in which
Jan Eilers284b5d12021-09-07 12:46:15 +0100849 // case we override those files when processing the results of each iteration (only the
850 // result of the last iteration will be stored), or there are enough
Jan Eilersf17fcd52021-07-26 22:20:00 +0100851 // output files for each output of each iteration.
852 size_t outputFileIndex = iteration * numOutputs + i;
853 if (!params.m_OutputTensorFiles.empty())
854 {
855 outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size();
856 ARMNN_LOG(info) << "Writing output " << i << " named: '"
857 << inferenceModelParams.m_OutputBindings[i]
858 << "' of iteration: " << iteration+1 << " to file: '"
859 << params.m_OutputTensorFiles[outputFileIndex] << "'";
860 }
861
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100862 const armnn::TensorInfo& infoOut = infosOut[i].second;
863 auto outputTensorFile = params.m_OutputTensorFiles.empty()
864 ? ""
Jan Eilersf17fcd52021-07-26 22:20:00 +0100865 : params.m_OutputTensorFiles[outputFileIndex];
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100866
867 TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
868 infoOut,
869 outputTensorFile,
Jan Eilers284b5d12021-09-07 12:46:15 +0100870 params.m_DequantizeOutput,
871 !params.m_DontPrintOutputs);
Finn Williamsf364d532021-06-09 17:07:33 +0100872 mapbox::util::apply_visitor(printer, inferenceOutputMap.at(cb->GetInferenceId())[i]);
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100873 }
874
Colm Donelan3cff15a2021-10-12 15:06:19 +0100875 CheckInferenceTimeThreshold(inferenceDuration, params.m_ThresholdTime);
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100876 ++j;
877 }
878 //print duration difference between overallStartTime and overallEndTime
879 auto overallEndTime = time_point_cast<std::chrono::milliseconds>(latestEndTime);
880 auto overallStartTime = time_point_cast<std::chrono::milliseconds>(earliestStartTime);
881 auto totalInferenceDuration = overallEndTime - overallStartTime;
882 ARMNN_LOG(info) << "\nOverall Inference time: " << std::setprecision(2)
883 << std::fixed << totalInferenceDuration.count() << " ms\n";
884 }
885 catch (const armnn::Exception& e)
886 {
887 ARMNN_LOG(fatal) << "Armnn Error: " << e.what();
888 return EXIT_FAILURE;
889 }
890 }
891 // Asynchronous execution using std::launch::async
Sadik Armagana04a9d72021-04-27 10:02:10 +0100892 else
893 {
894 try
895 {
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100896 ARMNN_LOG(info) << "Asynchronous Execution with std::launch:async... \n";
Finn Williamsf364d532021-06-09 17:07:33 +0100897 std::vector<std::future<std::tuple<unsigned int,
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100898 std::chrono::duration<double, std::milli>>>> inferenceResults;
Jan Eilersf17fcd52021-07-26 22:20:00 +0100899 inferenceResults.reserve(params.m_Iterations);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100900
901 // Create WorkingMemHandles for each inference
902 std::vector<std::unique_ptr<armnn::experimental::IWorkingMemHandle>> workingMemHandles;
Jan Eilersf17fcd52021-07-26 22:20:00 +0100903 workingMemHandles.reserve(params.m_Iterations);
904 for (unsigned int i = 0; i < params.m_Iterations; ++i)
Sadik Armagana04a9d72021-04-27 10:02:10 +0100905 {
906 workingMemHandles.push_back(model.CreateWorkingMemHandle());
907 }
908
909 // Run each inference in its own thread
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100910 // start a timer
911 const auto start_time = armnn::GetTimeNow();
Jan Eilersf17fcd52021-07-26 22:20:00 +0100912 for (unsigned int i = 0; i < params.m_Iterations; ++i)
Sadik Armagana04a9d72021-04-27 10:02:10 +0100913 {
914 armnn::experimental::IWorkingMemHandle& workingMemHandleRef = *workingMemHandles[i].get();
Finn Williamsf364d532021-06-09 17:07:33 +0100915
Sadik Armagana04a9d72021-04-27 10:02:10 +0100916 inferenceResults.push_back(std::async(
917 std::launch::async, [&model, &workingMemHandleRef, &inputs, &outputs, i]() {
Finn Williamsf364d532021-06-09 17:07:33 +0100918 return model.RunAsync(workingMemHandleRef, inputs[i], outputs[i], i);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100919 }
920 ));
921 }
922
923 // Check the results
924 for (unsigned int j = 0; j < inferenceResults.size(); ++j)
925 {
926 // Get the results
927 auto inferenceResult = inferenceResults[j].get();
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100928 auto inferenceDuration = std::get<1>(inferenceResult);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100929 auto inferenceID = std::get<0>(inferenceResult);
930
931 if (params.m_GenerateTensorData)
932 {
933 ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
934 }
Jan Eilers284b5d12021-09-07 12:46:15 +0100935 if (params.m_DontPrintOutputs)
936 {
937 ARMNN_LOG(info) << "Printing outputs to console is disabled.";
938 }
Sadik Armagana04a9d72021-04-27 10:02:10 +0100939
940 // Print output tensors
941 const auto& infosOut = model.GetOutputBindingInfos();
942 for (size_t i = 0; i < numOutputs; i++)
943 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100944 // We've made sure before that the number of output files either equals numOutputs, in which
Jan Eilers284b5d12021-09-07 12:46:15 +0100945 // case we override those files when processing the results of each iteration (only the
946 // result of the last iteration will be stored), or there are enough
Jan Eilersf17fcd52021-07-26 22:20:00 +0100947 // output files for each output of each iteration.
948 size_t outputFileIndex = j * numOutputs + i;
949 if (!params.m_OutputTensorFiles.empty())
950 {
951 outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size();
952 ARMNN_LOG(info) << "Writing output " << i << " named: '"
953 << inferenceModelParams.m_OutputBindings[i]
954 << "' of iteration: " << j+1 << " to file: '"
955 << params.m_OutputTensorFiles[outputFileIndex] << "'";
956 }
Sadik Armagana04a9d72021-04-27 10:02:10 +0100957 const armnn::TensorInfo& infoOut = infosOut[i].second;
958 auto outputTensorFile = params.m_OutputTensorFiles.empty()
959 ? ""
Jan Eilersf17fcd52021-07-26 22:20:00 +0100960 : params.m_OutputTensorFiles[outputFileIndex];
Sadik Armagana04a9d72021-04-27 10:02:10 +0100961
962 TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
963 infoOut,
964 outputTensorFile,
Jan Eilers284b5d12021-09-07 12:46:15 +0100965 params.m_DequantizeOutput,
966 !params.m_DontPrintOutputs);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100967 mapbox::util::apply_visitor(printer, outputs[j][i]);
968 }
Colm Donelan3cff15a2021-10-12 15:06:19 +0100969 CheckInferenceTimeThreshold(inferenceDuration, params.m_ThresholdTime);
Sadik Armagana04a9d72021-04-27 10:02:10 +0100970 ARMNN_LOG(info) << "Asynchronous Execution is finished for Inference ID: " << inferenceID << " \n";
Sadik Armagana04a9d72021-04-27 10:02:10 +0100971 }
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100972 // finish timer
973 const auto duration = armnn::GetTimeDuration(start_time);
974 ARMNN_LOG(info) << "\nOverall Inference time: " << std::setprecision(2)
975 << std::fixed << duration.count() << " ms\n";
Sadik Armagana04a9d72021-04-27 10:02:10 +0100976 }
977 catch (const armnn::Exception& e)
978 {
979 ARMNN_LOG(fatal) << "Armnn Error: " << e.what();
980 return EXIT_FAILURE;
981 }
Jan Eilers45274902020-10-15 18:34:43 +0100982 }
983 }
984 catch (const armnn::Exception& e)
985 {
986 ARMNN_LOG(fatal) << "Armnn Error: " << e.what();
987 return EXIT_FAILURE;
988 }
989
990 return EXIT_SUCCESS;
991}
992
James Conroy7b4886f2019-04-11 10:23:58 +0100993// MAIN
telsoa01c577f2c2018-08-31 09:22:23 +0100994int main(int argc, const char* argv[])
995{
996 // Configures logging for both the ARMNN library and this test program.
Jan Eilers45274902020-10-15 18:34:43 +0100997 #ifdef NDEBUG
telsoa01c577f2c2018-08-31 09:22:23 +0100998 armnn::LogSeverity level = armnn::LogSeverity::Info;
Jan Eilers45274902020-10-15 18:34:43 +0100999 #else
telsoa01c577f2c2018-08-31 09:22:23 +01001000 armnn::LogSeverity level = armnn::LogSeverity::Debug;
Jan Eilers45274902020-10-15 18:34:43 +01001001 #endif
telsoa01c577f2c2018-08-31 09:22:23 +01001002 armnn::ConfigureLogging(true, true, level);
telsoa01c577f2c2018-08-31 09:22:23 +01001003
telsoa01c577f2c2018-08-31 09:22:23 +01001004
Jan Eilers45274902020-10-15 18:34:43 +01001005 // Get ExecuteNetwork parameters and runtime options from command line
Jan Eilersf17fcd52021-07-26 22:20:00 +01001006 // This might throw an InvalidArgumentException if the user provided invalid inputs
1007 ProgramOptions ProgramOptions;
1008 try {
1009 ProgramOptions.ParseOptions(argc, argv);
1010 } catch (const std::exception &e){
1011 ARMNN_LOG(fatal) << e.what();
1012 return EXIT_FAILURE;
1013 }
Narumol Prangnawaratd8cc8112020-03-24 13:54:05 +00001014
Keith Davis4914d0c2021-08-18 17:14:05 +01001015 if ((ProgramOptions.m_ExNetParams.m_OutputDetailsToStdOut ||
1016 ProgramOptions.m_ExNetParams.m_OutputDetailsOnlyToStdOut)
1017 && !ProgramOptions.m_ExNetParams.m_EnableProfiling)
Keith Davisf4874862021-08-09 16:49:18 +01001018 {
1019 ARMNN_LOG(fatal) << "You must enable profiling if you would like to output layer details";
1020 return EXIT_FAILURE;
1021 }
1022
Jan Eilers45274902020-10-15 18:34:43 +01001023 std::string modelFormat = ProgramOptions.m_ExNetParams.m_ModelFormat;
1024
1025 // Forward to implementation based on the parser type
1026 if (modelFormat.find("armnn") != std::string::npos)
Finn Williamsd7fcafa2020-04-23 17:55:18 +01001027 {
Jan Eilers45274902020-10-15 18:34:43 +01001028 #if defined(ARMNN_SERIALIZER)
Cathal Corbett5b0d8872021-12-06 17:06:12 +00001029 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(ProgramOptions.m_RuntimeOptions));
Jan Eilers45274902020-10-15 18:34:43 +01001030 return MainImpl<armnnDeserializer::IDeserializer, float>(ProgramOptions.m_ExNetParams, runtime);
1031 #else
1032 ARMNN_LOG(fatal) << "Not built with serialization support.";
Finn Williamsd7fcafa2020-04-23 17:55:18 +01001033 return EXIT_FAILURE;
Jan Eilers45274902020-10-15 18:34:43 +01001034 #endif
Finn Williamsd7fcafa2020-04-23 17:55:18 +01001035 }
Jan Eilers45274902020-10-15 18:34:43 +01001036 else if (modelFormat.find("onnx") != std::string::npos)
telsoa01c577f2c2018-08-31 09:22:23 +01001037 {
Jan Eilers45274902020-10-15 18:34:43 +01001038 #if defined(ARMNN_ONNX_PARSER)
Cathal Corbett5b0d8872021-12-06 17:06:12 +00001039 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(ProgramOptions.m_RuntimeOptions));
Jan Eilers45274902020-10-15 18:34:43 +01001040 return MainImpl<armnnOnnxParser::IOnnxParser, float>(ProgramOptions.m_ExNetParams, runtime);
1041 #else
1042 ARMNN_LOG(fatal) << "Not built with Onnx parser support.";
1043 return EXIT_FAILURE;
1044 #endif
1045 }
Jan Eilers45274902020-10-15 18:34:43 +01001046 else if(modelFormat.find("tflite") != std::string::npos)
1047 {
Finn Williamsf806c4d2021-02-22 15:13:12 +00001048 if (ProgramOptions.m_ExNetParams.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteParser)
1049 {
1050 #if defined(ARMNN_TF_LITE_PARSER)
Cathal Corbett5b0d8872021-12-06 17:06:12 +00001051 std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(ProgramOptions.m_RuntimeOptions));
1052 return MainImpl<armnnTfLiteParser::ITfLiteParser, float>(ProgramOptions.m_ExNetParams, runtime);
Finn Williamsf806c4d2021-02-22 15:13:12 +00001053 #else
Cathal Corbett5b0d8872021-12-06 17:06:12 +00001054 ARMNN_LOG(fatal) << "Not built with Tensorflow-Lite parser support.";
1055 return EXIT_FAILURE;
Finn Williamsf806c4d2021-02-22 15:13:12 +00001056 #endif
1057 }
1058 else if (ProgramOptions.m_ExNetParams.m_TfLiteExecutor ==
1059 ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteDelegate ||
1060 ProgramOptions.m_ExNetParams.m_TfLiteExecutor ==
1061 ExecuteNetworkParams::TfLiteExecutor::TfliteInterpreter)
Sadik Armagan5d03e312020-11-17 16:43:56 +00001062 {
1063 #if defined(ARMNN_TF_LITE_DELEGATE)
Colm Donelan45142282021-10-21 23:39:52 +01001064 return TfLiteDelegateMainImpl(ProgramOptions.m_ExNetParams, ProgramOptions.m_RuntimeOptions);
Sadik Armagan5d03e312020-11-17 16:43:56 +00001065 #else
Finn Williamsbbbefec2020-11-25 14:32:42 +00001066 ARMNN_LOG(fatal) << "Not built with Arm NN Tensorflow-Lite delegate support.";
Sadik Armagan5d03e312020-11-17 16:43:56 +00001067 return EXIT_FAILURE;
1068 #endif
1069 }
Jan Eilers45274902020-10-15 18:34:43 +01001070 }
1071 else
1072 {
1073 ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat
Nikhil Raj5d955cf2021-04-19 16:59:48 +01001074 << "'. Please include 'tflite' or 'onnx'";
Jan Eilers45274902020-10-15 18:34:43 +01001075 return EXIT_FAILURE;
telsoa014fcda012018-03-09 14:13:49 +00001076 }
1077}