blob: aed4fa14262abf3e270b9331315cb242ca2ba096 [file] [log] [blame]
Kevin May42477c12020-03-26 13:34:14 +00001//
2// Copyright © 2020 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Sadik Armagand7be72e2020-04-23 12:56:05 +01005// Note: the ArmnnFencedExecutionCallback and code snippet in the executeFenced() function
6// in this file is based on Android code
7// under the Apache 2.0 license. See comments below for details.
8//
Kevin May42477c12020-03-26 13:34:14 +00009
10#define LOG_TAG "ArmnnDriver"
11
12#include "ArmnnPreparedModel_1_3.hpp"
13#include "Utils.hpp"
14
15#include <Utils.h>
Sadik Armagand7be72e2020-04-23 12:56:05 +010016#include <android/sync.h>
Kevin May42477c12020-03-26 13:34:14 +000017#include <log/log.h>
18#include <OperationsUtils.h>
19#include <ExecutionBurstServer.h>
20#include <ValidateHal.h>
21
22#include <cassert>
23#include <cinttypes>
24
25using namespace android;
26using namespace android::hardware;
27
28namespace {
29
30static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX};
31using namespace armnn_driver;
32using TimePoint = std::chrono::steady_clock::time_point;
33
34TimePoint Now()
35{
36 return std::chrono::steady_clock::now();
37}
38
39unsigned long MicrosecondsDuration(TimePoint endPoint, TimePoint startPoint)
40{
41 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
42 endPoint - startPoint).count());
43}
44
45void NotifyCallbackAndCheck(const ::android::sp<V1_0::IExecutionCallback>& callback,
46 V1_3::ErrorStatus errorStatus,
47 std::vector<OutputShape>,
48 const Timing,
49 std::string callingFunction)
50{
51 Return<void> returned = callback->notify(convertToV1_0(errorStatus));
52 // This check is required, if the callback fails and it isn't checked it will bring down the service
53 if (!returned.isOk())
54 {
55 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
56 callingFunction.c_str(), returned.description().c_str());
57 }
58}
59
60void NotifyCallbackAndCheck(const ::android::sp<V1_2::IExecutionCallback>& callback,
61 V1_3::ErrorStatus errorStatus,
62 std::vector<OutputShape> outputShapes,
63 const Timing timing,
64 std::string callingFunction)
65{
66 Return<void> returned = callback->notify_1_2(convertToV1_0(errorStatus), outputShapes, timing);
67 // This check is required, if the callback fails and it isn't checked it will bring down the service
68 if (!returned.isOk())
69 {
70 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
71 callingFunction.c_str(), returned.description().c_str());
72 }
73}
74
75void NotifyCallbackAndCheck(const ::android::sp<V1_3::IExecutionCallback>& callback,
76 V1_3::ErrorStatus errorStatus,
77 std::vector<OutputShape> outputShapes,
78 const Timing timing,
79 std::string callingFunction)
80{
81 Return<void> returned = callback->notify_1_3(errorStatus, outputShapes, timing);
82 // This check is required, if the callback fails and it isn't checked it will bring down the service
83 if (!returned.isOk())
84 {
85 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
86 callingFunction.c_str(), returned.description().c_str());
87 }
88}
89
90bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo)
91{
92 if (requestArg.dimensions.size() != 0)
93 {
94 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
95 {
96 ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)",
97 requestArg.dimensions.size(), tensorInfo.GetNumDimensions());
98 return false;
99 }
100
101 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
102 {
Finn Williamsa4983ce2020-07-23 12:55:12 +0100103 if (requestArg.dimensions[d] != 0 && requestArg.dimensions[d] != tensorInfo.GetShape()[d])
Kevin May42477c12020-03-26 13:34:14 +0000104 {
105 ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)",
106 d, requestArg.dimensions[d], tensorInfo.GetShape()[d]);
107 return false;
108 }
109 }
110 }
111
112 return true;
113}
114
115armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg,
116 const armnn::TensorInfo& tensorInfo,
117 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
118{
119 if (!ValidateRequestArgument(requestArg, tensorInfo))
120 {
121 return armnn::Tensor();
122 }
123
124 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
125}
126
127inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
128{
129 return tensorNamePrefix + std::to_string(index);
130}
131
132} // anonymous namespace
133
134using namespace android::hardware;
135
136namespace armnn_driver
137{
138
139template<typename HalVersion>
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100140RequestThread_1_3<ArmnnPreparedModel_1_3, HalVersion, CallbackContext_1_3>
Kevin May42477c12020-03-26 13:34:14 +0000141 ArmnnPreparedModel_1_3<HalVersion>::m_RequestThread;
142
143template<typename HalVersion>
144template<typename TensorBindingCollection>
145void ArmnnPreparedModel_1_3<HalVersion>::DumpTensorsIfRequired(char const* tensorNamePrefix,
146 const TensorBindingCollection& tensorBindings)
147{
148 if (!m_RequestInputsAndOutputsDumpDir.empty())
149 {
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100150 const std::string requestName = std::to_string(m_NetworkId) + "_" + std::to_string(m_RequestCount) + ".dump";
Kevin May42477c12020-03-26 13:34:14 +0000151 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
152 {
153 DumpTensor(m_RequestInputsAndOutputsDumpDir,
154 requestName,
155 BuildTensorName(tensorNamePrefix, i),
156 tensorBindings[i].second);
157 }
158 }
159}
160
161template<typename HalVersion>
162ArmnnPreparedModel_1_3<HalVersion>::ArmnnPreparedModel_1_3(armnn::NetworkId networkId,
163 armnn::IRuntime* runtime,
164 const V1_3::Model& model,
165 const std::string& requestInputsAndOutputsDumpDir,
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100166 const bool gpuProfilingEnabled,
167 V1_3::Priority priority)
Kevin May42477c12020-03-26 13:34:14 +0000168 : m_NetworkId(networkId)
169 , m_Runtime(runtime)
170 , m_Model(model)
171 , m_RequestCount(0)
172 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
173 , m_GpuProfilingEnabled(gpuProfilingEnabled)
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100174 , m_ModelPriority(priority)
Kevin May42477c12020-03-26 13:34:14 +0000175{
176 // Enable profiling if required.
177 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
178}
179
180template<typename HalVersion>
181ArmnnPreparedModel_1_3<HalVersion>::~ArmnnPreparedModel_1_3()
182{
183 // Get a hold of the profiler used by this model.
184 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
185
186 // Unload the network associated with this model.
187 m_Runtime->UnloadNetwork(m_NetworkId);
188
189 // Dump the profiling info to a file if required.
190 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
191}
192
193template<typename HalVersion>
194Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute(const V1_0::Request& request,
195 const ::android::sp<V1_0::IExecutionCallback>& callback)
196{
197 if (callback.get() == nullptr)
198 {
199 ALOGE("ArmnnPreparedModel_1_3::execute invalid callback passed");
200 return V1_0::ErrorStatus::INVALID_ARGUMENT;
201 }
202
203 auto cb = [callback](V1_3::ErrorStatus errorStatus,
204 std::vector<OutputShape> outputShapes,
205 const Timing& timing,
206 std::string callingFunction)
207 {
208 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
209 };
210
211
212 return convertToV1_0(Execute(convertToV1_3(request), MeasureTiming::NO, cb));
213}
214
215template<typename HalVersion>
216Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_2(
217 const V1_0::Request& request,
218 MeasureTiming measureTiming,
219 const sp<V1_2::IExecutionCallback>& callback)
220{
221 if (callback.get() == nullptr)
222 {
223 ALOGE("ArmnnPreparedModel_1_3::execute_1_2 invalid callback passed");
224 return V1_0::ErrorStatus::INVALID_ARGUMENT;
225 }
226
227 auto cb = [callback](V1_3::ErrorStatus errorStatus,
228 std::vector<OutputShape> outputShapes,
229 const Timing& timing,
230 std::string callingFunction)
231 {
232 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
233 };
234
235 return convertToV1_0(Execute(convertToV1_3(request), measureTiming, cb));
236}
237
238template<typename HalVersion>
239Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::execute_1_3(
240 const V1_3::Request& request,
241 MeasureTiming measureTiming,
242 const V1_3::OptionalTimePoint&,
Kevin May352d8382020-03-31 15:03:42 +0100243 const V1_3::OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000244 const sp<V1_3::IExecutionCallback>& callback)
245{
246 if (callback.get() == nullptr)
247 {
248 ALOGE("ArmnnPreparedModel_1_3::execute_1_3 invalid callback passed");
249 return V1_3::ErrorStatus::INVALID_ARGUMENT;
250 }
251
252 auto cb = [callback](V1_3::ErrorStatus errorStatus,
253 std::vector<OutputShape> outputShapes,
254 const Timing& timing,
255 std::string callingFunction)
256 {
257 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
258 };
259
260 return Execute(request, measureTiming, cb);
261}
262
Sadik Armagand7be72e2020-04-23 12:56:05 +0100263/// This class is inspired by the sample implementation in Android named SampleFencedExecutionCallback.
264/// The original code is licensed under Apache-2.0 and can be found at the following link:
265/// https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.h
266class ArmnnFencedExecutionCallback : public V1_3::IFencedExecutionCallback
267{
268public:
269 ArmnnFencedExecutionCallback(V1_3::ErrorStatus errorStatus, Timing timing, Timing fenceTiming)
270 : m_ErrorStatus(errorStatus), m_Timing(timing), m_FenceTiming(fenceTiming) {}
271 ~ArmnnFencedExecutionCallback() {}
272
273 Return<void> getExecutionInfo(getExecutionInfo_cb callback) override
274 {
275 callback(m_ErrorStatus, m_Timing, m_FenceTiming);
276 return Void();
277 }
278private:
279 V1_3::ErrorStatus m_ErrorStatus;
280 Timing m_Timing;
281 Timing m_FenceTiming;
282};
283
Kevin May42477c12020-03-26 13:34:14 +0000284template<typename HalVersion>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100285Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeFenced(const V1_3::Request& request,
286 const hidl_vec<hidl_handle>& fenceWaitFor,
287 MeasureTiming measureTiming,
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100288 const OptionalTimePoint& deadline,
289 const OptionalTimeoutDuration& loopTimeoutDuration,
Kevin May352d8382020-03-31 15:03:42 +0100290 const OptionalTimeoutDuration&,
Kevin May42477c12020-03-26 13:34:14 +0000291 executeFenced_cb cb)
292{
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100293 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...)");
294 if (cb == nullptr)
295 {
296 ALOGE("ArmnnPreparedModel_1_3::executeFenced invalid callback passed");
297 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
298 return Void();
299 }
300
301 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
302 {
303 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter deadline is set but not supported.");
304 }
305
306 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
307 {
308 ALOGW("ArmnnPreparedModel_1_3::executeFenced parameter loopTimeoutDuration is set but not supported.");
309 }
310
Finn Williamsa4983ce2020-07-23 12:55:12 +0100311 if (!android::nn::validateRequest(request, m_Model, /*allowUnspecifiedOutput=*/false))
312 {
313 ALOGV("ArmnnPreparedModel_1_3::executeFenced outputs must be specified for fenced execution ");
314 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
315 return Void();
316 }
317
Sadik Armagand7be72e2020-04-23 12:56:05 +0100318 ExecutionContext_1_3 ctx;
319 if (measureTiming == MeasureTiming::YES)
320 {
321 ctx.measureTimings = measureTiming;
322 ctx.driverStart = Now();
323 }
324
325 ALOGV("ArmnnPreparedModel_1_3::executeFenced(): %s", GetModelSummary(m_Model).c_str());
326 m_RequestCount++;
327
Sadik Armagand7be72e2020-04-23 12:56:05 +0100328 if (!m_RequestInputsAndOutputsDumpDir.empty())
329 {
330 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&cb));
331 }
332
333 // This code snippet is inspired by the sample implementation in Android named SampleDriver::executeFenced()
334 // function. The original code is licensed under Apache-2.0 and can be found at the following link:
335 // https://android.googlesource.com/platform/frameworks/ml/+/master/nn/driver/sample/SampleDriver.cpp
336 const auto fenceSize = fenceWaitFor.size();
337 for (unsigned int index = 0; index < fenceSize; ++index)
338 {
339 auto fenceNativeHandle = fenceWaitFor[index].getNativeHandle();
340 if (!fenceNativeHandle)
341 {
342 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
343 return Void();
344 }
345
346 if (sync_wait(fenceNativeHandle->data[0], -1) < 0)
347 {
348 ALOGE("ArmnnPreparedModel_1_3::executeFenced sync fence failed.");
349 cb(ErrorStatus::GENERAL_FAILURE, hidl_handle(nullptr), nullptr);
350 return Void();
351 }
352 }
353
354 TimePoint fenceExecutionStart;
355 if (measureTiming == MeasureTiming::YES)
356 {
357 fenceExecutionStart = Now();
358 }
359
360 // map the memory pool into shared pointers
361 // use a shared memory pools vector on the heap, as it is passed to the request thread
362 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
363
364 // allocate the tensors on the heap, as they are passed to the request thread
365 auto inputs = std::make_shared<armnn::InputTensors>();
366 auto outputs = std::make_shared<armnn::OutputTensors>();
367
368 auto [status, outShapes, timings, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
369 if (status != V1_3::ErrorStatus::NONE)
370 {
371 cb(ErrorStatus::INVALID_ARGUMENT, hidl_handle(nullptr), nullptr);
372 return Void();
373 }
374
375 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) before ExecuteGraph");
376
377 // call it with nullCallback for now as we will report the error status from here..
378 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
379 CallbackContext_1_3 cbCtx;
380 cbCtx.callback = nullCallback;
381 cbCtx.ctx = ctx;
382
383 auto errorStatus = ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
384 if (errorStatus != V1_3::ErrorStatus::NONE)
385 {
386 cb(errorStatus, hidl_handle(nullptr), nullptr);
387 return Void();
388 }
389 ALOGV("ArmnnPreparedModel_1_3::executeFenced(...) after ExecuteGraph");
390
391 Timing timing = g_NoTiming;
392 Timing fenceTiming = g_NoTiming;
393 if (measureTiming == MeasureTiming::YES)
394 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100395 fenceTiming.timeOnDevice = MicrosecondsDuration(ctx.deviceEnd, ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100396 fenceTiming.timeInDriver = MicrosecondsDuration(ctx.driverEnd, fenceExecutionStart);
397 ALOGV("ArmnnPreparedModel_1_3::fenceFinishExecutionTiming - Device = %lu Driver = %lu",
Sadik Armagand7be72e2020-04-23 12:56:05 +0100398 fenceTiming.timeOnDevice, fenceTiming.timeInDriver);
399 }
400
401 sp<ArmnnFencedExecutionCallback> armnnFencedExecutionCallback =
402 new ArmnnFencedExecutionCallback(ErrorStatus::NONE, timing, fenceTiming);
403 cb(ErrorStatus::NONE, hidl_handle(nullptr), armnnFencedExecutionCallback);
Kevin May42477c12020-03-26 13:34:14 +0000404 return Void();
405}
406
407template<typename HalVersion>
408Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForInputs(
409 armnn::InputTensors& inputs,
410 const V1_3::Request& request,
411 const std::vector<android::nn::RunTimePoolInfo>& memPools)
412{
413 inputs.reserve(request.inputs.size());
414 for (unsigned int i = 0; i < request.inputs.size(); i++)
415 {
416 const auto& inputArg = request.inputs[i];
417
418 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
419 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, memPools);
420
421 if (inputTensor.GetMemoryArea() == nullptr)
422 {
423 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
424 return V1_3::ErrorStatus::GENERAL_FAILURE;
425 }
426
427 inputs.emplace_back(i, inputTensor);
428 }
429
430 return V1_3::ErrorStatus::NONE;
431}
432
433template<typename HalVersion>
434Return<V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForOutputs(
435 armnn::OutputTensors& outputs,
436 std::vector<OutputShape> &outputShapes,
437 const V1_3::Request& request,
438 const std::vector<android::nn::RunTimePoolInfo>& memPools)
439{
440 outputs.reserve(request.outputs.size());
441 for (unsigned int i = 0; i < request.outputs.size(); i++)
442 {
443 const auto& outputArg = request.outputs[i];
444
Finn Williamsa4983ce2020-07-23 12:55:12 +0100445 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
Kevin May42477c12020-03-26 13:34:14 +0000446 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, memPools);
447 if (outputTensor.GetMemoryArea() == nullptr)
448 {
449 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
450 return V1_3::ErrorStatus::GENERAL_FAILURE;
451 }
452
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100453 const size_t outputSize = outputTensorInfo.GetNumBytes();
454
Finn Williamsa4983ce2020-07-23 12:55:12 +0100455 unsigned int count = 0;
456 std::for_each(outputArg.dimensions.begin(), outputArg.dimensions.end(), [&](auto dim)
457 {
458 if (dim != 0)
459 {
460 outputTensorInfo.GetShape()[count] = dim;
461 }
462 else
463 {
464 outputTensorInfo.GetShape()[count] = outputArg.dimensions.size();
465 }
466
467 count++;
468 });
469
Finn Williamsa4983ce2020-07-23 12:55:12 +0100470 outputs.emplace_back(i, outputTensor);
471 outputShapes[i] = ComputeShape(outputTensorInfo);
472
473 if (outputArg.location.length < outputSize)
474 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100475 ALOGW("ArmnnPreparedModel_1_3::Execute failed outputArg.location.length (%s) < outputSize (%s)",
476 std::to_string(outputArg.location.length).c_str(), std::to_string(outputSize).c_str());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100477 outputShapes[i].isSufficient = false;
478 return V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
479 }
480
Kevin May42477c12020-03-26 13:34:14 +0000481 const size_t bufferSize = memPools.at(outputArg.location.poolIndex).getHidlMemory().size();
482 if (bufferSize < outputSize)
483 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100484 ALOGW("ArmnnPreparedModel_1_3::Execute failed bufferSize (%s) < outputSize (%s)",
485 std::to_string(bufferSize).c_str(), std::to_string(outputSize).c_str());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100486 outputShapes[i].isSufficient = false;
Kevin May42477c12020-03-26 13:34:14 +0000487 return V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
488 }
Kevin May42477c12020-03-26 13:34:14 +0000489 }
490
491 return V1_3::ErrorStatus::NONE;
492}
493
494template<typename HalVersion>
495std::tuple<V1_3::ErrorStatus, hidl_vec<OutputShape>, Timing, std::string>
496 ArmnnPreparedModel_1_3<HalVersion>::PrepareMemoryForIO(armnn::InputTensors& inputs,
497 armnn::OutputTensors& outputs,
498 std::vector<android::nn::RunTimePoolInfo>& memPools,
499 const V1_3::Request& request)
500{
501 if (!setRunTimePoolInfosFromMemoryPools(&memPools, request.pools))
502 {
Sadik Armaganef8a3932020-04-09 17:21:50 +0100503 return {ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
Kevin May42477c12020-03-26 13:34:14 +0000504 }
505
506 // add the inputs and outputs with their data
507 try
508 {
509 if (PrepareMemoryForInputs(inputs, request, memPools) != V1_3::ErrorStatus::NONE)
510 {
511 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
512 }
513
514 std::vector<OutputShape> outputShapes(request.outputs.size());
515
516 auto errorStatus = PrepareMemoryForOutputs(outputs, outputShapes, request, memPools);
517 if (errorStatus != V1_3::ErrorStatus::NONE)
518 {
519 return {errorStatus, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
520 }
521 }
522 catch (armnn::Exception& e)
523 {
524 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
525 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
526 }
527 catch (std::exception& e)
528 {
529 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
530 return {ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
531 }
532
533 return {V1_3::ErrorStatus::NONE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute"};
534}
535
536template<typename HalVersion>
537template<typename CallbackContext>
538Return<void> ArmnnPreparedModel_1_3<HalVersion>::ExecuteSynchronously(const V1_3::Request& request,
539 CallbackContext cbCtx)
540{
541 if (cbCtx.ctx.measureTimings == MeasureTiming::YES)
542 {
543 cbCtx.ctx.driverStart = Now();
544 }
545
546 if (!android::nn::validateRequest(convertToV1_3(request), m_Model))
547 {
548 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
549 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
550 {},
551 g_NoTiming,
552 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
553 return Void();
554 }
555
556 if (!android::nn::validateRequest(request, m_Model))
557 {
558 ALOGE("ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
559 cbCtx.callback(V1_3::ErrorStatus::INVALID_ARGUMENT,
560 {},
561 g_NoTiming,
562 "ArmnnPreparedModel_1_3::ExecuteSynchronously invalid request model");
Sadik Armaganef8a3932020-04-09 17:21:50 +0100563 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000564 }
565
566
567 // map the memory pool into shared pointers
568 // use a shared memory pools vector on the heap, as it is passed to the request thread
569 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
570
571 // allocate the tensors on the heap, as they are passed to the request thread
572 auto inputs = std::make_shared<armnn::InputTensors>();
573 auto outputs = std::make_shared<armnn::OutputTensors>();
574
575 auto [status, outputShapes, timing, message] = PrepareMemoryForIO(*inputs, *outputs, *memPools, request);
576 if (status != V1_3::ErrorStatus::NONE)
577 {
578 cbCtx.callback(status, outputShapes, timing, message);
Sadik Armaganef8a3932020-04-09 17:21:50 +0100579 return Void();
Kevin May42477c12020-03-26 13:34:14 +0000580 }
581
582 ALOGV("ArmnnPreparedModel_1_3::ExecuteSynchronously() before Execution");
583
584 ExecuteGraph(memPools, *inputs, *outputs, cbCtx);
585 return Void();
586}
587
588template<typename HalVersion>
589Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously(const V1_0::Request& request,
590 MeasureTiming measureTiming,
591 executeSynchronously_cb cb)
592{
593 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
594 m_RequestCount++;
595
596 if (cb == nullptr)
597 {
598 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously invalid callback passed");
599 return Void();
600 }
601
602 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
603 std::vector<OutputShape> outputShapes,
604 const Timing& timing,
605 std::string)
606 {
607 cb(convertToV1_0(errorStatus), outputShapes, timing);
608 };
609
610 CallbackContext_1_3 cbCtx;
611 cbCtx.callback = cbWrapper;
612 cbCtx.ctx.measureTimings = measureTiming;
613
614 ExecuteSynchronously(convertToV1_3(request), cbCtx);
615 return Void();
616}
617
618template<typename HalVersion>
Kevin May352d8382020-03-31 15:03:42 +0100619Return<void> ArmnnPreparedModel_1_3<HalVersion>::executeSynchronously_1_3(
620 const V1_3::Request& request,
621 MeasureTiming measureTiming,
622 const V1_3::OptionalTimePoint& deadline,
623 const V1_3::OptionalTimeoutDuration& loopTimeoutDuration,
624 executeSynchronously_1_3_cb cb)
Kevin May42477c12020-03-26 13:34:14 +0000625{
626 ALOGV("ArmnnPreparedModel_1_3::executeSynchronously_1_3(): %s", GetModelSummary(m_Model).c_str());
627 m_RequestCount++;
628
629 if (cb == nullptr)
630 {
631 ALOGE("ArmnnPreparedModel_1_3::executeSynchronously_1_3 invalid callback passed");
632 return Void();
633 }
634
635 if (deadline.getDiscriminator() != OptionalTimePoint::hidl_discriminator::none)
636 {
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100637 ALOGW("ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter deadline is set but not supported.");
Kevin May42477c12020-03-26 13:34:14 +0000638 }
639
Kevin May352d8382020-03-31 15:03:42 +0100640 if (loopTimeoutDuration.getDiscriminator() != OptionalTimeoutDuration::hidl_discriminator::none)
Sadik Armagan7b9ce8d2020-04-21 10:39:28 +0100641 {
642 ALOGW(
643 "ArmnnPreparedModel_1_3::executeSynchronously_1_3 parameter loopTimeoutDuration is set but not supported.");
Kevin May352d8382020-03-31 15:03:42 +0100644 }
645
Kevin May42477c12020-03-26 13:34:14 +0000646 auto cbWrapper = [cb](V1_3::ErrorStatus errorStatus,
647 std::vector<OutputShape> outputShapes,
648 const Timing& timing,
649 std::string)
650 {
651 cb(errorStatus, outputShapes, timing);
652 };
653
654 CallbackContext_1_3 cbCtx;
655 cbCtx.callback = cbWrapper;
656 cbCtx.ctx.measureTimings = measureTiming;
657
658 ExecuteSynchronously(request, cbCtx);
659 return Void();
660}
661
662template<typename HalVersion>
663Return<void> ArmnnPreparedModel_1_3<HalVersion>::configureExecutionBurst(
664 const sp<V1_2::IBurstCallback>& callback,
665 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
666 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
667 V1_3::IPreparedModel::configureExecutionBurst_cb cb)
668{
669 ALOGV("ArmnnPreparedModel_1_3::configureExecutionBurst");
670 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
671 requestChannel,
672 resultChannel,
673 this);
674
675 if (burst == nullptr)
676 {
677 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
678 }
679 else
680 {
681 cb(V1_0::ErrorStatus::NONE, burst);
682 }
683 return Void();
684}
685
686template<typename HalVersion>
687template<typename CallbackContext>
Sadik Armagand7be72e2020-04-23 12:56:05 +0100688Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::ExecuteGraph(
Kevin May42477c12020-03-26 13:34:14 +0000689 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
690 armnn::InputTensors& inputTensors,
691 armnn::OutputTensors& outputTensors,
692 CallbackContext cb)
693{
694 ALOGV("ArmnnPreparedModel_1_3::ExecuteGraph(...)");
695
Kevin May42477c12020-03-26 13:34:14 +0000696 DumpTensorsIfRequired("Input", inputTensors);
697
698 std::vector<OutputShape> outputShapes(outputTensors.size());
699 for (unsigned int i = 0; i < outputTensors.size(); i++)
700 {
701 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
702 const armnn::Tensor outputTensor = outputTensorPair.second;
703 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
704
705 outputShapes[i] = ComputeShape(outputTensorInfo);
706 }
707
708 // run it
709 try
710 {
711 if (cb.ctx.measureTimings == MeasureTiming::YES)
712 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100713 cb.ctx.deviceStart = Now();
Kevin May42477c12020-03-26 13:34:14 +0000714 }
715
716 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
717
718 if (cb.ctx.measureTimings == MeasureTiming::YES)
719 {
Sadik Armagand7be72e2020-04-23 12:56:05 +0100720 cb.ctx.deviceEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000721 }
722 if (status != armnn::Status::Success)
723 {
724 ALOGW("EnqueueWorkload failed");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100725 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
726 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000727 }
728 }
729 catch (armnn::Exception& e)
730 {
731 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
732 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100733 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000734 }
735 catch (std::exception& e)
736 {
737 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
738 cb.callback(V1_3::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100739 return V1_3::ErrorStatus::GENERAL_FAILURE;
Kevin May42477c12020-03-26 13:34:14 +0000740 }
741
742 CommitPools(*pMemPools);
743
744 DumpTensorsIfRequired("Output", outputTensors);
745
746 if (cb.ctx.measureTimings == MeasureTiming::YES)
747 {
Kevin May949a69e2020-04-24 10:21:40 +0100748 cb.ctx.driverEnd = Now();
Kevin May42477c12020-03-26 13:34:14 +0000749 Timing timing;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100750 timing.timeOnDevice = MicrosecondsDuration(cb.ctx.deviceEnd, cb.ctx.deviceStart);
Kevin May949a69e2020-04-24 10:21:40 +0100751 timing.timeInDriver = MicrosecondsDuration(cb.ctx.driverEnd, cb.ctx.driverStart);
752 ALOGV("ArmnnPreparedModel_1_3::execute timing - Device = %lu Driver = %lu", timing.timeOnDevice,
Kevin May42477c12020-03-26 13:34:14 +0000753 timing.timeInDriver);
754 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, timing, "ArmnnPreparedModel_1_3::ExecuteGraph");
Sadik Armagand7be72e2020-04-23 12:56:05 +0100755 } else
756 {
Kevin May42477c12020-03-26 13:34:14 +0000757 cb.callback(V1_3::ErrorStatus::NONE, outputShapes, g_NoTiming, "ArmnnPreparedModel_1_3::ExecuteGraph");
758 }
Sadik Armagand7be72e2020-04-23 12:56:05 +0100759 return V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000760}
761
762template<typename HalVersion>
763bool ArmnnPreparedModel_1_3<HalVersion>::ExecuteWithDummyInputs()
764{
765 std::vector<std::vector<char>> storage;
766 armnn::InputTensors inputTensors;
767 for (unsigned int i = 0; i < getMainModel(m_Model).inputIndexes.size(); i++)
768 {
769 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
770 storage.emplace_back(inputTensorInfo.GetNumBytes());
771 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
772
773 inputTensors.emplace_back(i, inputTensor);
774 }
775
776 armnn::OutputTensors outputTensors;
777 for (unsigned int i = 0; i < getMainModel(m_Model).outputIndexes.size(); i++)
778 {
779 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
780 storage.emplace_back(outputTensorInfo.GetNumBytes());
781 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
782
783 outputTensors.emplace_back(i, outputTensor);
784 }
785
786 auto nullCallback = [](V1_3::ErrorStatus, std::vector<OutputShape>, const Timing&, std::string) {};
787 CallbackContext_1_3 callbackContext;
788 callbackContext.callback = nullCallback;
789 callbackContext.ctx.measureTimings = MeasureTiming::NO;
790 auto memPools = std::make_shared<std::vector<::android::nn::RunTimePoolInfo>>();
Sadik Armagand7be72e2020-04-23 12:56:05 +0100791
792 auto errorStatus = ExecuteGraph(memPools,
793 inputTensors,
794 outputTensors,
795 callbackContext);
796 return errorStatus == V1_3::ErrorStatus::NONE;
Kevin May42477c12020-03-26 13:34:14 +0000797}
798
799template<typename HalVersion>
800Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<HalVersion>::Execute(const V1_3::Request& request,
801 MeasureTiming measureTiming,
802 CallbackAsync_1_3 callback)
803{
804 ExecutionContext_1_3 ctx;
805 if (measureTiming == MeasureTiming::YES)
806 {
807 ctx.measureTimings = measureTiming;
808 ctx.driverStart = Now();
809 }
810
811 ALOGV("ArmnnPreparedModel_1_3::execute(): %s", GetModelSummary(m_Model).c_str());
812 m_RequestCount++;
813
814 if (!android::nn::validateRequest(request, m_Model))
815 {
816 callback(V1_3::ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_3::execute");
817 return V1_3::ErrorStatus::INVALID_ARGUMENT;
818 }
819
820 if (!m_RequestInputsAndOutputsDumpDir.empty())
821 {
822 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
823 }
824
825 // map the memory pool into shared pointers
826 // use a shared memory pools vector on the heap, as it is passed to the request thread
827 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
828
829 // allocate the tensors on the heap, as they are passed to the request thread
830 auto inputTensors = std::make_shared<armnn::InputTensors>();
831 auto outputTensors = std::make_shared<armnn::OutputTensors>();
832
833 auto [status, outShapes, timing, message] = PrepareMemoryForIO(*inputTensors, *outputTensors,
834 *memPools, request);
835 if (status != V1_3::ErrorStatus::NONE)
836 {
837 callback(status, outShapes, timing, message);
838 }
839
840 switch(status)
841 {
842 case V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
843 return V1_3::ErrorStatus::NONE;
844 case V1_3::ErrorStatus::GENERAL_FAILURE:
845 return V1_3::ErrorStatus::GENERAL_FAILURE;
846 default:
847 {}
848 }
849
850 ALOGV("ArmnnPreparedModel_1_3::execute(...) before PostMsg");
851
852 // post the request for asynchronous execution
853 CallbackContext_1_3 cb;
854 cb.callback = callback;
855 cb.ctx = ctx;
856 m_RequestThread.PostMsg(this, memPools, inputTensors, outputTensors, cb);
857 ALOGV("ArmnnPreparedModel_1_3::execute(...) after PostMsg");
858 return V1_3::ErrorStatus::NONE;
859}
860
Narumol Prangnawaratcad4e912020-06-02 12:07:43 +0100861template<typename HalVersion>
862V1_3::Priority ArmnnPreparedModel_1_3<HalVersion>::GetModelPriority()
863{
864 return m_ModelPriority;
865}
866
Kevin May42477c12020-03-26 13:34:14 +0000867#ifdef ARMNN_ANDROID_NN_V1_3
868template class ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>;
Sadik Armagand7be72e2020-04-23 12:56:05 +0100869template Return <V1_3::ErrorStatus> ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>::ExecuteGraph<CallbackContext_1_3>(
Kevin May42477c12020-03-26 13:34:14 +0000870 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
871 armnn::InputTensors& pInputTensors,
872 armnn::OutputTensors& pOutputTensors,
873 CallbackContext_1_3 cb);
874#endif
875
876} // namespace armnn_driver