blob: b3d18cdfd12a4154fd143a9ab92700795ca65981 [file] [log] [blame]
Jan Eilers45274902020-10-15 18:34:43 +01001//
2// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "ExecuteNetworkParams.hpp"
7
8#include "NetworkExecutionUtils/NetworkExecutionUtils.hpp"
9#include <InferenceModel.hpp>
10#include <armnn/Logging.hpp>
11
12#include <fmt/format.h>
13
14bool IsModelBinary(const std::string& modelFormat)
15{
16 // Parse model binary flag from the model-format string we got from the command-line
17 if (modelFormat.find("binary") != std::string::npos)
18 {
19 return true;
20 }
21 else if (modelFormat.find("txt") != std::string::npos || modelFormat.find("text") != std::string::npos)
22 {
23 return false;
24 }
25 else
26 {
27 throw armnn::InvalidArgumentException(fmt::format("Unknown model format: '{}'. "
28 "Please include 'binary' or 'text'",
29 modelFormat));
30 }
31}
32
33void CheckModelFormat(const std::string& modelFormat)
34{
35 // Forward to implementation based on the parser type
36 if (modelFormat.find("armnn") != std::string::npos)
37 {
38#if defined(ARMNN_SERIALIZER)
39#else
Keith Daviscb8e3502020-11-12 10:27:19 +000040 throw armnn::InvalidArgumentException("Can't run model in armnn format without a "
41 "built with serialization support.");
Jan Eilers45274902020-10-15 18:34:43 +010042#endif
43 }
Jan Eilers45274902020-10-15 18:34:43 +010044 else if (modelFormat.find("onnx") != std::string::npos)
45 {
46#if defined(ARMNN_ONNX_PARSER)
47#else
48 throw armnn::InvalidArgumentException("Can't run model in onnx format without a "
49 "built with Onnx parser support.");
50#endif
51 }
Keith Daviscb8e3502020-11-12 10:27:19 +000052 else if (modelFormat.find("tflite") != std::string::npos)
Jan Eilers45274902020-10-15 18:34:43 +010053 {
54#if defined(ARMNN_TF_LITE_PARSER)
55 if (!IsModelBinary(modelFormat))
56 {
Keith Daviscb8e3502020-11-12 10:27:19 +000057 throw armnn::InvalidArgumentException(fmt::format("Unknown model format: '{}'. Only 'binary' "
58 "format supported for tflite files",
Jan Eilers45274902020-10-15 18:34:43 +010059 modelFormat));
60 }
Sadik Armagan5d03e312020-11-17 16:43:56 +000061#elif defined(ARMNN_TFLITE_DELEGATE)
Jan Eilers45274902020-10-15 18:34:43 +010062#else
63 throw armnn::InvalidArgumentException("Can't run model in tflite format without a "
64 "built with Tensorflow Lite parser support.");
65#endif
66 }
67 else
68 {
69 throw armnn::InvalidArgumentException(fmt::format("Unknown model format: '{}'. "
Kevin Mayeb03e0f2021-04-28 16:16:22 +010070 "Please include 'tflite' or 'onnx'",
Jan Eilers45274902020-10-15 18:34:43 +010071 modelFormat));
72 }
73}
74
75void CheckClTuningParameter(const int& tuningLevel,
76 const std::string& tuningPath,
77 const std::vector<armnn::BackendId> computeDevices)
78{
79 if (!tuningPath.empty())
80 {
Keith Daviscb8e3502020-11-12 10:27:19 +000081 if (tuningLevel == 0)
Jan Eilers45274902020-10-15 18:34:43 +010082 {
83 ARMNN_LOG(info) << "Using cl tuning file: " << tuningPath << "\n";
Keith Daviscb8e3502020-11-12 10:27:19 +000084 if (!ValidatePath(tuningPath, true))
Jan Eilers45274902020-10-15 18:34:43 +010085 {
86 throw armnn::InvalidArgumentException("The tuning path is not valid");
87 }
88 }
89 else if ((1 <= tuningLevel) && (tuningLevel <= 3))
90 {
91 ARMNN_LOG(info) << "Starting execution to generate a cl tuning file: " << tuningPath << "\n"
92 << "Tuning level in use: " << tuningLevel << "\n";
93 }
94 else if ((0 < tuningLevel) || (tuningLevel > 3))
95 {
Keith Daviscb8e3502020-11-12 10:27:19 +000096 throw armnn::InvalidArgumentException(fmt::format("The tuning level {} is not valid.",
97 tuningLevel));
Jan Eilers45274902020-10-15 18:34:43 +010098 }
99
100 // Ensure that a GpuAcc is enabled. Otherwise no tuning data are used or genereted
101 // Only warn if it's not enabled
102 auto it = std::find(computeDevices.begin(), computeDevices.end(), "GpuAcc");
103 if (it == computeDevices.end())
104 {
105 ARMNN_LOG(warning) << "To use Cl Tuning the compute device GpuAcc needs to be active.";
106 }
107 }
108
Jan Eilers45274902020-10-15 18:34:43 +0100109}
110
111void ExecuteNetworkParams::ValidateParams()
112{
Jan Eilersf17fcd52021-07-26 22:20:00 +0100113 if (m_DynamicBackendsPath == "")
Jan Eilers45274902020-10-15 18:34:43 +0100114 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100115 // Check compute devices are valid unless they are dynamically loaded at runtime
116 std::string invalidBackends;
117 if (!CheckRequestedBackendsAreValid(m_ComputeDevices, armnn::Optional<std::string&>(invalidBackends)))
Francis Murtaghbf18a262020-10-27 15:20:40 +0000118 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100119 ARMNN_LOG(fatal) << "The list of preferred devices contains invalid backend IDs: "
120 << invalidBackends;
Francis Murtaghbf18a262020-10-27 15:20:40 +0000121 }
Jan Eilers45274902020-10-15 18:34:43 +0100122 }
Jan Eilersf17fcd52021-07-26 22:20:00 +0100123
124 CheckClTuningParameter(m_TuningLevel, m_TuningPath, m_ComputeDevices);
125
126 if (m_EnableBf16TurboMode && m_EnableFp16TurboMode)
Keith Daviscb8e3502020-11-12 10:27:19 +0000127 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100128 throw armnn::InvalidArgumentException("BFloat16 and Float16 turbo mode cannot be "
129 "enabled at the same time.");
130 }
131
132 m_IsModelBinary = IsModelBinary(m_ModelFormat);
133
134 CheckModelFormat(m_ModelFormat);
135
136 // Check input tensor shapes
137 if ((m_InputTensorShapes.size() != 0) &&
138 (m_InputTensorShapes.size() != m_InputNames.size()))
139 {
140 throw armnn::InvalidArgumentException("input-name and input-tensor-shape must have "
141 "the same amount of elements. ");
142 }
143
144 if (m_InputTensorDataFilePaths.size() != 0)
145 {
146 if (!ValidatePaths(m_InputTensorDataFilePaths, true))
Keith Daviscb8e3502020-11-12 10:27:19 +0000147 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100148 throw armnn::InvalidArgumentException("One or more input data file paths are not valid.");
Keith Daviscb8e3502020-11-12 10:27:19 +0000149 }
Jan Eilersf17fcd52021-07-26 22:20:00 +0100150
151 if (m_InputTensorDataFilePaths.size() < m_InputNames.size())
Keith Daviscb8e3502020-11-12 10:27:19 +0000152 {
Jan Eilersf17fcd52021-07-26 22:20:00 +0100153 throw armnn::InvalidArgumentException(
154 fmt::format("According to the number of input names the user provided the network has {} "
155 "inputs. But only {} input-tensor-data file paths were provided. Each input of the "
156 "model is expected to be stored in it's own file.",
157 m_InputNames.size(),
158 m_InputTensorDataFilePaths.size()));
159 }
160 else if (m_InputTensorDataFilePaths.size() % m_InputNames.size() != 0)
161 {
162 throw armnn::InvalidArgumentException(
163 fmt::format("According to the number of input names the user provided the network has {} "
164 "inputs. The user specified {} input-tensor-data file paths which is not "
165 "divisible by the number of inputs.",
166 m_InputNames.size(),
167 m_InputTensorDataFilePaths.size()));
Keith Daviscb8e3502020-11-12 10:27:19 +0000168 }
169 }
Jan Eilersf17fcd52021-07-26 22:20:00 +0100170
171 if (m_InputTypes.size() == 0)
172 {
173 //Defaults the value of all inputs to "float"
174 m_InputTypes.assign(m_InputNames.size(), "float");
175 }
176 else if ((m_InputTypes.size() != 0) &&
177 (m_InputTypes.size() != m_InputNames.size()))
178 {
179 throw armnn::InvalidArgumentException("input-name and input-type must have the same amount of elements.");
180 }
181
182 // Make sure that the number of input files given is divisible by the number of inputs of the model
183 if (!(m_InputTensorDataFilePaths.size() % m_InputNames.size() == 0))
184 {
185 throw armnn::InvalidArgumentException(
186 fmt::format("The number of input-tensor-data files ({0}) is not divisible by the "
187 "number of inputs ({1} according to the number of input names).",
188 m_InputTensorDataFilePaths.size(),
189 m_InputNames.size()));
190 }
191
192 if (m_OutputTypes.size() == 0)
193 {
194 //Defaults the value of all outputs to "float"
195 m_OutputTypes.assign(m_OutputNames.size(), "float");
196 }
197 else if ((m_OutputTypes.size() != 0) &&
198 (m_OutputTypes.size() != m_OutputNames.size()))
199 {
200 throw armnn::InvalidArgumentException("output-name and output-type must have the same amount of elements.");
201 }
202
203 // Make sure that the number of output files given is equal to the number of outputs of the model
204 // or equal to the number of outputs of the model multiplied with the number of iterations
205 if (!m_OutputTensorFiles.empty())
206 {
207 if ((m_OutputTensorFiles.size() != m_OutputNames.size()) &&
208 (m_OutputTensorFiles.size() != m_OutputNames.size() * m_Iterations))
209 {
210 std::stringstream errmsg;
211 auto numOutputs = m_OutputNames.size();
212 throw armnn::InvalidArgumentException(
213 fmt::format("The user provided {0} output-tensor files. The only allowed number of output-tensor "
214 "files is the number of outputs of the network ({1} according to the number of "
215 "output names) or the number of outputs multiplied with the number of times the "
216 "network should be executed (NumOutputs * NumIterations = {1} * {2} = {3}).",
217 m_OutputTensorFiles.size(),
218 numOutputs,
219 m_Iterations,
220 numOutputs*m_Iterations));
221 }
222 }
223
224 // Check that threshold time is not less than zero
225 if (m_ThresholdTime < 0)
226 {
227 throw armnn::InvalidArgumentException("Threshold time supplied as a command line argument is less than zero.");
228 }
Jan Eilers45274902020-10-15 18:34:43 +0100229
230 // Warn if ExecuteNetwork will generate dummy input data
231 if (m_GenerateTensorData)
232 {
233 ARMNN_LOG(warning) << "No input files provided, input tensors will be filled with 0s.";
234 }
Colm Donelan3cff15a2021-10-12 15:06:19 +0100235}
236
237#if defined(ARMNN_TFLITE_DELEGATE)
238/**
239 * A utility method that populates a DelegateOptions object from this ExecuteNetworkParams.
240 *
241 * @return a populated armnnDelegate::DelegateOptions object.
242 */
243armnnDelegate::DelegateOptions ExecuteNetworkParams::ToDelegateOptions() const
244{
245 armnnDelegate::DelegateOptions delegateOptions(m_ComputeDevices);
246 delegateOptions.SetDynamicBackendsPath(m_DynamicBackendsPath);
247 delegateOptions.SetGpuProfilingState(m_EnableProfiling);
248
249 armnn::OptimizerOptions options;
250 options.m_ReduceFp32ToFp16 = m_EnableFp16TurboMode;
251 options.m_ReduceFp32ToBf16 = m_EnableBf16TurboMode;
252 options.m_Debug = m_PrintIntermediate;
Colm Donelan45142282021-10-21 23:39:52 +0100253 options.m_ProfilingEnabled = m_EnableProfiling;
254 delegateOptions.SetInternalProfilingParams(m_EnableProfiling, armnn::ProfilingDetailsMethod::DetailsWithEvents);
Colm Donelan3cff15a2021-10-12 15:06:19 +0100255 options.m_shapeInferenceMethod = armnn::ShapeInferenceMethod::ValidateOnly;
256 if (m_InferOutputShape)
257 {
258 options.m_shapeInferenceMethod = armnn::ShapeInferenceMethod::InferAndValidate;
259 }
260
261 armnn::BackendOptions gpuAcc("GpuAcc",
262 {
263 { "FastMathEnabled", m_EnableFastMath },
264 { "SaveCachedNetwork", m_SaveCachedNetwork },
265 { "CachedNetworkFilePath", m_CachedNetworkFilePath },
266 { "TuningLevel", m_TuningLevel},
267 { "TuningFile", m_TuningPath.c_str()},
268 { "KernelProfilingEnabled", m_EnableProfiling},
269 { "MLGOTuningFilePath", m_MLGOTuningFilePath}
270 });
271
272 armnn::BackendOptions cpuAcc("CpuAcc",
273 {
274 { "FastMathEnabled", m_EnableFastMath },
275 { "NumberOfThreads", m_NumberOfThreads }
276 });
277 options.m_ModelOptions.push_back(gpuAcc);
278 options.m_ModelOptions.push_back(cpuAcc);
279
280 delegateOptions.SetOptimizerOptions(options);
281
282 // If v,visualize-optimized-model is enabled then construct a file name for the dot file.
283 if (m_EnableLayerDetails)
284 {
285 fs::path filename = m_ModelPath;
286 filename.replace_extension("dot");
287 delegateOptions.SetSerializeToDot(filename);
288 }
289
290 return delegateOptions;
291}
292#endif