blob: 92487ccd0d95f0a5b55f3d5324fe1efd6140a64a [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// See LICENSE file in the project root for full license information.
4//
5
6#define LOG_TAG "ArmnnDriver"
7
8#include "ArmnnDriver.hpp"
9#include "ArmnnPreparedModel.hpp"
10#include "ModelToINetworkConverter.hpp"
11#include "Utils.hpp"
12
13#include <log/log.h>
14#include "SystemPropertiesUtils.hpp"
15
16#include "OperationsUtils.h"
17
18#include <boost/algorithm/string/predicate.hpp>
19#include <boost/program_options.hpp>
20
21#include <cassert>
22#include <functional>
23#include <string>
24#include <sstream>
25
26using namespace android;
27using namespace std;
28
29namespace
30{
31
32const char *g_Float32PerformanceExecTimeName = "ArmNN.float32Performance.execTime";
33const char *g_Float32PerformancePowerUsageName = "ArmNN.float32Performance.powerUsage";
34const char *g_Quantized8PerformanceExecTimeName = "ArmNN.quantized8Performance.execTime";
35const char *g_Quantized8PerformancePowerUsageName = "ArmNN.quantized8Performance.powerUsage";
36
37}; //namespace
38
39namespace armnn_driver
40{
41
42DriverOptions::DriverOptions(armnn::Compute computeDevice)
43: m_ComputeDevice(computeDevice)
44, m_VerboseLogging(false)
45, m_UseAndroidNnCpuExecutor(false)
surmeh0149b9e102018-05-17 14:11:25 +010046, m_ClTunedParametersMode(armnn::IClTunedParameters::Mode::UseTunedParameters)
telsoa015307bc12018-03-09 13:51:08 +000047{
48}
49
50DriverOptions::DriverOptions(int argc, char** argv)
51: m_ComputeDevice(armnn::Compute::GpuAcc)
52, m_VerboseLogging(false)
53, m_UseAndroidNnCpuExecutor(false)
54, m_ClTunedParametersMode(armnn::IClTunedParameters::Mode::UseTunedParameters)
55{
56 namespace po = boost::program_options;
57
58 std::string computeDeviceAsString;
59 std::string unsupportedOperationsAsString;
60 std::string clTunedParametersModeAsString;
61
62 po::options_description optionsDesc("Options");
63 optionsDesc.add_options()
64 ("compute,c",
65 po::value<std::string>(&computeDeviceAsString)->default_value("GpuAcc"),
66 "Which device to run layers on by default. Possible values are: CpuRef, CpuAcc, GpuAcc")
67
68 ("verbose-logging,v",
69 po::bool_switch(&m_VerboseLogging),
70 "Turns verbose logging on")
71
72 ("use-androidnn-cpu-executor,e",
73 po::bool_switch(&m_UseAndroidNnCpuExecutor),
74 "Forces the driver to satisfy requests via the Android-provided CpuExecutor")
75
76 ("request-inputs-and-outputs-dump-dir,d",
77 po::value<std::string>(&m_RequestInputsAndOutputsDumpDir)->default_value(""),
78 "If non-empty, the directory where request inputs and outputs should be dumped")
79
80 ("unsupported-operations,u",
81 po::value<std::string>(&unsupportedOperationsAsString)->default_value(""),
82 "If non-empty, a comma-separated list of operation indices which the driver will forcibly "
83 "consider unsupported")
84
85 ("cl-tuned-parameters-file,t",
86 po::value<std::string>(&m_ClTunedParametersFile)->default_value(""),
87 "If non-empty, the given file will be used to load/save CL tuned parameters. "
88 "See also --cl-tuned-parameters-mode")
89
90 ("cl-tuned-parameters-mode,m",
91 po::value<std::string>(&clTunedParametersModeAsString)->default_value("UseTunedParameters"),
92 "If 'UseTunedParameters' (the default), will read CL tuned parameters from the file specified by "
93 "--cl-tuned-parameters-file. "
94 "If 'UpdateTunedParameters', will also find the optimum parameters when preparing new networks and update "
95 "the file accordingly.");
96
97
98 po::variables_map variablesMap;
99 try
100 {
101 po::store(po::parse_command_line(argc, argv, optionsDesc), variablesMap);
102 po::notify(variablesMap);
103 }
104 catch (const po::error& e)
105 {
106 ALOGW("An error occurred attempting to parse program options: %s", e.what());
107 }
108
109 if (computeDeviceAsString == "CpuRef")
110 {
111 m_ComputeDevice = armnn::Compute::CpuRef;
112 }
113 else if (computeDeviceAsString == "GpuAcc")
114 {
115 m_ComputeDevice = armnn::Compute::GpuAcc;
116 }
117 else if (computeDeviceAsString == "CpuAcc")
118 {
119 m_ComputeDevice = armnn::Compute::CpuAcc;
120 }
121 else
122 {
123 ALOGW("Requested unknown compute device %s. Defaulting to compute id %s",
124 computeDeviceAsString.c_str(), GetComputeDeviceAsCString(m_ComputeDevice));
125 }
126
127 if (!unsupportedOperationsAsString.empty())
128 {
129 std::istringstream argStream(unsupportedOperationsAsString);
130
131 std::string s;
132 while (!argStream.eof())
133 {
134 std::getline(argStream, s, ',');
135 try
136 {
137 unsigned int operationIdx = std::stoi(s);
138 m_ForcedUnsupportedOperations.insert(operationIdx);
139 }
140 catch (const std::invalid_argument&)
141 {
142 ALOGW("Ignoring invalid integer argument in -u/--unsupported-operations value: %s", s.c_str());
143 }
144 }
145 }
146
147 if (!m_ClTunedParametersFile.empty())
148 {
149 // The mode is only relevant if the file path has been provided
150 if (clTunedParametersModeAsString == "UseTunedParameters")
151 {
152 m_ClTunedParametersMode = armnn::IClTunedParameters::Mode::UseTunedParameters;
153 }
154 else if (clTunedParametersModeAsString == "UpdateTunedParameters")
155 {
156 m_ClTunedParametersMode = armnn::IClTunedParameters::Mode::UpdateTunedParameters;
157 }
158 else
159 {
160 ALOGW("Requested unknown cl-tuned-parameters-mode '%s'. Defaulting to UseTunedParameters",
161 clTunedParametersModeAsString.c_str());
162 }
163 }
164}
165
166ArmnnDriver::ArmnnDriver(DriverOptions options)
167 : m_Runtime(nullptr, nullptr)
168 , m_ClTunedParameters(nullptr, nullptr)
169 , m_Options(std::move(options))
170{
171 ALOGV("ArmnnDriver::ArmnnDriver()");
172
173 armnn::ConfigureLogging(false, m_Options.IsVerboseLoggingEnabled(), armnn::LogSeverity::Trace);
174 if (m_Options.IsVerboseLoggingEnabled())
175 {
176 SetMinimumLogSeverity(base::VERBOSE);
177 }
178 else
179 {
180 SetMinimumLogSeverity(base::INFO);
181 }
182
183 try
184 {
185 armnn::IRuntime::CreationOptions options(m_Options.GetComputeDevice());
186 options.m_UseCpuRefAsFallback = false;
187 if (!m_Options.GetClTunedParametersFile().empty())
188 {
189 m_ClTunedParameters = armnn::IClTunedParameters::Create(m_Options.GetClTunedParametersMode());
190 try
191 {
192 m_ClTunedParameters->Load(m_Options.GetClTunedParametersFile().c_str());
193 }
194 catch (const armnn::Exception& error)
195 {
196 // This is only a warning because the file won't exist the first time you are generating it.
197 ALOGW("ArmnnDriver: Failed to load CL tuned parameters file '%s': %s",
198 m_Options.GetClTunedParametersFile().c_str(), error.what());
199 }
200 options.m_ClTunedParameters = m_ClTunedParameters.get();
201 }
202 m_Runtime = armnn::IRuntime::Create(options);
203 }
204 catch (const armnn::ClRuntimeUnavailableException& error)
205 {
206 ALOGE("ArmnnDriver: Failed to setup CL runtime: %s. Device will be unavailable.", error.what());
207 }
208}
209
210Return<void> ArmnnDriver::getCapabilities(getCapabilities_cb cb)
211{
212 ALOGV("ArmnnDriver::getCapabilities()");
213
214 Capabilities capabilities;
215 if (m_Runtime)
216 {
217 capabilities.float32Performance.execTime =
218 ParseSystemProperty(g_Float32PerformanceExecTimeName, 1.0f);
219
220 capabilities.float32Performance.powerUsage =
221 ParseSystemProperty(g_Float32PerformancePowerUsageName, 1.0f);
222
223 capabilities.quantized8Performance.execTime =
224 ParseSystemProperty(g_Quantized8PerformanceExecTimeName, 1.0f);
225
226 capabilities.quantized8Performance.powerUsage =
227 ParseSystemProperty(g_Quantized8PerformancePowerUsageName, 1.0f);
228
229 cb(ErrorStatus::NONE, capabilities);
230 }
231 else
232 {
233 capabilities.float32Performance.execTime = 0;
234 capabilities.float32Performance.powerUsage = 0;
235 capabilities.quantized8Performance.execTime = 0;
236 capabilities.quantized8Performance.powerUsage = 0;
237
238 cb(ErrorStatus::DEVICE_UNAVAILABLE, capabilities);
239 }
240
241 return Void();
242}
243
244Return<void> ArmnnDriver::getSupportedOperations(const Model& model, getSupportedOperations_cb cb)
245{
246 ALOGV("ArmnnDriver::getSupportedOperations()");
247
248 std::vector<bool> result;
249
250 if (!m_Runtime)
251 {
252 cb(ErrorStatus::DEVICE_UNAVAILABLE, result);
253 return Void();
254 }
255
256 // Run general model validation, if this doesn't pass we shouldn't analyse the model anyway
257 if (!android::nn::validateModel(model))
258 {
259 cb(ErrorStatus::INVALID_ARGUMENT, result);
260 return Void();
261 }
262
263 // Attempt to convert the model to an ArmNN input network (INetwork).
264 ModelToINetworkConverter modelConverter(m_Runtime->GetDeviceSpec().DefaultComputeDevice, model,
265 m_Options.GetForcedUnsupportedOperations());
266
267 if (modelConverter.GetConversionResult() != ConversionResult::Success
268 && modelConverter.GetConversionResult() != ConversionResult::UnsupportedFeature)
269 {
270 cb(ErrorStatus::GENERAL_FAILURE, result);
271 return Void();
272 }
273
274 // Check each operation if it was converted successfully and copy the flags
275 // into the result (vector<bool>) that we need to return to Android
276 result.reserve(model.operations.size());
277 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); operationIdx++)
278 {
279 bool operationSupported = modelConverter.IsOperationSupported(operationIdx);
280 result.push_back(operationSupported);
281 }
282
283 cb(ErrorStatus::NONE, result);
284 return Void();
285}
286
287namespace
288{
289
290void NotifyCallbackAndCheck(const sp<IPreparedModelCallback>& callback, ErrorStatus errorStatus,
291 const ::android::sp<IPreparedModel>& preparedModelPtr)
292{
293 Return<void> returned = callback->notify(errorStatus, preparedModelPtr);
294 // This check is required, if the callback fails and it isn't checked it will bring down the service
295 if (!returned.isOk())
296 {
297 ALOGE("ArmnnDriver::prepareModel: hidl callback failed to return properly: %s ",
298 returned.description().c_str());
299 }
300}
301
302Return<ErrorStatus> FailPrepareModel(ErrorStatus error,
303 const std::string& message,
304 const sp<IPreparedModelCallback>& callback)
305{
306 ALOGW("ArmnnDriver::prepareModel: %s", message.c_str());
307 NotifyCallbackAndCheck(callback, error, nullptr);
308 return error;
309}
310
311}
312
313Return<ErrorStatus> ArmnnDriver::prepareModel(const Model& model,
314 const sp<IPreparedModelCallback>& cb)
315{
316 ALOGV("ArmnnDriver::prepareModel()");
317
318 if (cb.get() == nullptr)
319 {
320 ALOGW("ArmnnDriver::prepareModel: Invalid callback passed to prepareModel");
321 return ErrorStatus::INVALID_ARGUMENT;
322 }
323
324 if (!m_Runtime)
325 {
326 return FailPrepareModel(ErrorStatus::DEVICE_UNAVAILABLE, "ArmnnDriver::prepareModel: Device unavailable", cb);
327 }
328
329 if (!android::nn::validateModel(model))
330 {
331 return FailPrepareModel(ErrorStatus::INVALID_ARGUMENT,
332 "ArmnnDriver::prepareModel: Invalid model passed as input", cb);
333 }
334
335 if (m_Options.UseAndroidNnCpuExecutor())
336 {
337 sp<AndroidNnCpuExecutorPreparedModel> preparedModel = new AndroidNnCpuExecutorPreparedModel(model,
338 m_Options.GetRequestInputsAndOutputsDumpDir());
339 if (preparedModel->Initialize())
340 {
341 NotifyCallbackAndCheck(cb, ErrorStatus::NONE, preparedModel);
342 return ErrorStatus::NONE;
343 }
344 else
345 {
346 NotifyCallbackAndCheck(cb, ErrorStatus::INVALID_ARGUMENT, preparedModel);
347 return ErrorStatus::INVALID_ARGUMENT;
348 }
349 }
350
351 // Deliberately ignore any unsupported operations requested by the options -
352 // at this point we're being asked to prepare a model that we've already declared support for
353 // and the operation indices may be different to those in getSupportedOperations anyway.
354 std::set<unsigned int> unsupportedOperations;
355 ModelToINetworkConverter modelConverter(m_Runtime->GetDeviceSpec().DefaultComputeDevice, model,
356 unsupportedOperations);
357
358 if (modelConverter.GetConversionResult() != ConversionResult::Success)
359 {
360 return FailPrepareModel(ErrorStatus::GENERAL_FAILURE, "ModelToINetworkConverter failed", cb);
361 }
362
363 // optimize the network
364 armnn::IOptimizedNetworkPtr optNet(nullptr, nullptr);
365 try
366 {
367 optNet = armnn::Optimize(*modelConverter.GetINetwork(), m_Runtime->GetDeviceSpec());
368 }
369 catch (armnn::Exception& e)
370 {
371 std::stringstream message;
372 message << "armnn::Exception ("<<e.what()<<") caught from optimize.";
373 return FailPrepareModel(ErrorStatus::GENERAL_FAILURE, message.str(), cb);
374 }
375
surmeh0176660052018-03-29 16:33:54 +0100376 // Check that the optimized network is valid.
377 if (!optNet)
378 {
379 return FailPrepareModel(ErrorStatus::GENERAL_FAILURE,
380 "ArmnnDriver::prepareModel: Invalid optimized network", cb);
381 }
382
383 // Export the optimized network graph to a dot file if an output dump directory
384 // has been specified in the drivers' arguments.
385 ExportNetworkGraphToDotFile(*optNet,
386 m_Options.GetRequestInputsAndOutputsDumpDir(),
387 model);
388
telsoa015307bc12018-03-09 13:51:08 +0000389 // load it into the runtime
390 armnn::NetworkId netId = 0;
391 try
392 {
393 if (m_Runtime->LoadNetwork(netId, std::move(optNet)) != armnn::Status::Success)
394 {
395 return FailPrepareModel(ErrorStatus::GENERAL_FAILURE,
396 "ArmnnDriver::prepareModel: Network could not be loaded", cb);
397 }
398 }
399 catch (armnn::Exception& e)
400 {
401 std::stringstream message;
402 message << "armnn::Exception (" << e.what()<< ") caught from LoadNetwork.";
403 return FailPrepareModel(ErrorStatus::GENERAL_FAILURE, message.str(), cb);
404 }
405
406 std::unique_ptr<ArmnnPreparedModel> preparedModel(new ArmnnPreparedModel(
407 netId,
408 m_Runtime.get(),
409 model,
410 m_Options.GetRequestInputsAndOutputsDumpDir()
411 ));
412
413 // Run a single 'dummy' inference of the model. This means that CL kernels will get compiled (and tuned if
414 // this is enabled) before the first 'real' inference which removes the overhead of the first inference.
415 preparedModel->ExecuteWithDummyInputs();
416
417 if (m_ClTunedParameters &&
418 m_Options.GetClTunedParametersMode() == armnn::IClTunedParameters::Mode::UpdateTunedParameters)
419 {
420 // Now that we've done one inference the CL kernel parameters will have been tuned, so save the updated file.
421 try
422 {
423 m_ClTunedParameters->Save(m_Options.GetClTunedParametersFile().c_str());
424 }
425 catch (const armnn::Exception& error)
426 {
427 ALOGE("ArmnnDriver: Failed to save CL tuned parameters file '%s': %s",
428 m_Options.GetClTunedParametersFile().c_str(), error.what());
429 }
430 }
431
432 NotifyCallbackAndCheck(cb, ErrorStatus::NONE, preparedModel.release());
433
434 return ErrorStatus::NONE;
435}
436
437Return<DeviceStatus> ArmnnDriver::getStatus()
438{
439 ALOGV("ArmnnDriver::getStatus()");
440 return DeviceStatus::AVAILABLE;
441}
442
443}