blob: 29aaa1e02c624e10c6de56cc8a33fbe81c37c737 [file] [log] [blame]
Mike Kellyb5fdf382019-06-11 16:35:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#define LOG_TAG "ArmnnDriver"
7
8#include "ArmnnPreparedModel_1_2.hpp"
9#include "Utils.hpp"
10
11#include <boost/format.hpp>
12#include <log/log.h>
13#include <OperationsUtils.h>
14#include <ExecutionBurstServer.h>
15#include <ValidateHal.h>
16
17#include <cassert>
18#include <cinttypes>
19
20using namespace android;
21using namespace android::hardware;
22
Mike Kellyb5fdf382019-06-11 16:35:25 +010023namespace {
24
Mike Kelly44381512019-07-08 17:37:35 +010025static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX};
Mike Kellyb5fdf382019-06-11 16:35:25 +010026using namespace armnn_driver;
Mike Kelly44381512019-07-08 17:37:35 +010027using TimePoint = std::chrono::steady_clock::time_point;
28
29TimePoint Now()
30{
31 return std::chrono::steady_clock::now();
32}
33
34unsigned long MicrosecondsDuration(TimePoint endPoint, TimePoint startPoint)
35{
36 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
37 endPoint - startPoint).count());
38}
Mike Kellyb5fdf382019-06-11 16:35:25 +010039
Mike Kelly65c42dc2019-07-22 14:06:00 +010040void NotifyCallbackAndCheck(const ::android::sp<V1_0::IExecutionCallback>& callback,
41 ErrorStatus errorStatus,
42 std::vector<OutputShape>,
43 const Timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010044 std::string callingFunction)
45{
46 Return<void> returned = callback->notify(errorStatus);
47 // This check is required, if the callback fails and it isn't checked it will bring down the service
48 if (!returned.isOk())
49 {
50 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
51 callingFunction.c_str(), returned.description().c_str());
52 }
53}
54
Mike Kelly65c42dc2019-07-22 14:06:00 +010055void NotifyCallbackAndCheck(const ::android::sp<V1_2::IExecutionCallback>& callback,
56 ErrorStatus errorStatus,
57 std::vector<OutputShape> outputShapes,
58 const Timing timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010059 std::string callingFunction)
60{
Mike Kelly65c42dc2019-07-22 14:06:00 +010061 Return<void> returned = callback->notify_1_2(errorStatus, outputShapes, timing);
Mike Kellyb5fdf382019-06-11 16:35:25 +010062 // This check is required, if the callback fails and it isn't checked it will bring down the service
63 if (!returned.isOk())
64 {
65 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
66 callingFunction.c_str(), returned.description().c_str());
67 }
68}
69
70bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo)
71{
72 if (requestArg.dimensions.size() != 0)
73 {
74 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
75 {
76 ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)",
77 requestArg.dimensions.size(), tensorInfo.GetNumDimensions());
78 return false;
79 }
80
81 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
82 {
83 if (requestArg.dimensions[d] != tensorInfo.GetShape()[d])
84 {
85 ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)",
86 d, requestArg.dimensions[d], tensorInfo.GetShape()[d]);
87 return false;
88 }
89 }
90 }
91
92 return true;
93}
94
95armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg,
96 const armnn::TensorInfo& tensorInfo,
97 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
98{
99 if (!ValidateRequestArgument(requestArg, tensorInfo))
100 {
101 return armnn::Tensor();
102 }
103
104 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
105}
106
107inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
108{
109 return tensorNamePrefix + std::to_string(index);
110}
111
112} // anonymous namespace
113
114using namespace android::hardware;
115
116namespace armnn_driver
117{
118
119template<typename HalVersion>
Mike Kelly65c42dc2019-07-22 14:06:00 +0100120RequestThread<ArmnnPreparedModel_1_2, HalVersion, ArmnnCallback_1_2>
121 ArmnnPreparedModel_1_2<HalVersion>::m_RequestThread;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100122
123template<typename HalVersion>
124template<typename TensorBindingCollection>
125void ArmnnPreparedModel_1_2<HalVersion>::DumpTensorsIfRequired(char const* tensorNamePrefix,
126 const TensorBindingCollection& tensorBindings)
127{
128 if (!m_RequestInputsAndOutputsDumpDir.empty())
129 {
130 const std::string requestName = boost::str(boost::format("%1%_%2%.dump") % m_NetworkId % m_RequestCount);
131 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
132 {
133 DumpTensor(m_RequestInputsAndOutputsDumpDir,
134 requestName,
135 BuildTensorName(tensorNamePrefix, i),
136 tensorBindings[i].second);
137 }
138 }
139}
140
141template<typename HalVersion>
142ArmnnPreparedModel_1_2<HalVersion>::ArmnnPreparedModel_1_2(armnn::NetworkId networkId,
143 armnn::IRuntime* runtime,
144 const V1_2::Model& model,
145 const std::string& requestInputsAndOutputsDumpDir,
146 const bool gpuProfilingEnabled)
147 : m_NetworkId(networkId)
148 , m_Runtime(runtime)
149 , m_Model(model)
150 , m_RequestCount(0)
151 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
152 , m_GpuProfilingEnabled(gpuProfilingEnabled)
153{
154 // Enable profiling if required.
155 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
156}
157
158template<typename HalVersion>
159ArmnnPreparedModel_1_2<HalVersion>::~ArmnnPreparedModel_1_2()
160{
161 // Get a hold of the profiler used by this model.
162 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
163
164 // Unload the network associated with this model.
165 m_Runtime->UnloadNetwork(m_NetworkId);
166
167 // Dump the profiling info to a file if required.
168 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
169}
170
171template<typename HalVersion>
172Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute(const Request& request,
173 const ::android::sp<V1_0::IExecutionCallback>& callback)
174{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100175 if (callback.get() == nullptr)
176 {
177 ALOGE("ArmnnPreparedModel_1_2::execute invalid callback passed");
178 return ErrorStatus::INVALID_ARGUMENT;
179 }
180
181 auto cb = [callback](ErrorStatus errorStatus,
182 std::vector<OutputShape> outputShapes,
183 const Timing& timing,
184 std::string callingFunction)
185 {
186 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
187 };
188
189 return Execute(request, MeasureTiming::NO, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100190}
191
192template<typename HalVersion>
193Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute_1_2(const Request& request,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100194 MeasureTiming measureTiming,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100195 const sp<V1_2::IExecutionCallback>& callback)
196{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100197 if (callback.get() == nullptr)
198 {
199 ALOGE("ArmnnPreparedModel_1_2::execute_1_2 invalid callback passed");
200 return ErrorStatus::INVALID_ARGUMENT;
201 }
202
203 auto cb = [callback](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 return Execute(request, measureTiming, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100212}
213
214template<typename HalVersion>
215Return<void> ArmnnPreparedModel_1_2<HalVersion>::executeSynchronously(const Request& request,
Mike Kelly44381512019-07-08 17:37:35 +0100216 MeasureTiming measureTiming,
217 executeSynchronously_cb cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100218{
219 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
220 m_RequestCount++;
221
222 if (cb == nullptr)
223 {
224 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid callback passed");
225 return Void();
226 }
227
Mike Kelly44381512019-07-08 17:37:35 +0100228 TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
229
230 if (measureTiming == MeasureTiming::YES)
231 {
232 driverStart = Now();
233 }
234
Mike Kellyb5fdf382019-06-11 16:35:25 +0100235 if (!android::nn::validateRequest(request, m_Model))
236 {
Mike Kelly44381512019-07-08 17:37:35 +0100237 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid request model");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100238 cb(ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming);
239 return Void();
240 }
241
242 // allocate the tensors on the heap, as they are passed to the request thread
243 auto pInputTensors = std::make_shared<armnn::InputTensors>();
244 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
245
246 // map the memory pool into shared pointers
247 // use a shared memory pools vector on the heap, as it is passed to the request thread
248 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
249
250 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
251 {
252 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
253 return Void();
254 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100255 std::vector<OutputShape> outputShapes(request.outputs.size());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100256
Mike Kellyb5fdf382019-06-11 16:35:25 +0100257 try
258 {
259 pInputTensors->reserve(request.inputs.size());
260 for (unsigned int i = 0; i < request.inputs.size(); i++)
261 {
262 const auto& inputArg = request.inputs[i];
263
264 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
265 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
266
267 if (inputTensor.GetMemoryArea() == nullptr)
268 {
269 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
270 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
271 return Void();
272 }
273
274 pInputTensors->emplace_back(i, inputTensor);
275 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100276 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100277
Mike Kellyb5fdf382019-06-11 16:35:25 +0100278 for (unsigned int i = 0; i < request.outputs.size(); i++)
279 {
280 const auto& outputArg = request.outputs[i];
281
282 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
283 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
284
285 if (outputTensor.GetMemoryArea() == nullptr)
286 {
287 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
288 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
289 return Void();
290 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100291 const size_t outputSize = outputTensorInfo.GetNumBytes();
292 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
293
294 hidl_vec<uint32_t> dimensions;
295
296 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
297 const unsigned int numDims = tensorShape.GetNumDimensions();
298 dimensions.resize(numDims);
299
300 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
301 {
302 dimensions[outputIdx] = tensorShape[outputIdx];
303 }
304 outputShapes[i].dimensions = dimensions;
305 outputShapes[i].isSufficient = bufferSize >= outputSize;
306
307 if (bufferSize < outputSize)
308 {
309 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
310 cb(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, outputShapes, g_NoTiming);
311 return Void();
312 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100313
314 pOutputTensors->emplace_back(i, outputTensor);
315 }
316 }
Kevin May7bdaac52020-02-10 12:10:07 +0000317 catch (armnn::Exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100318 {
Kevin May7bdaac52020-02-10 12:10:07 +0000319 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100320 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
321 return Void();
322 }
Kevin May7bdaac52020-02-10 12:10:07 +0000323 catch (std::exception& e)
324 {
325 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
326 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
327 return Void();
328 }
329
Mike Kellyb5fdf382019-06-11 16:35:25 +0100330 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() before Execution");
331
332 DumpTensorsIfRequired("Input", *pInputTensors);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100333 // run it
334 try
335 {
Mike Kelly44381512019-07-08 17:37:35 +0100336 if (measureTiming == MeasureTiming::YES)
337 {
338 deviceStart = Now();
339 }
340
Mike Kellyb5fdf382019-06-11 16:35:25 +0100341 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
342
Mike Kelly44381512019-07-08 17:37:35 +0100343 if (measureTiming == MeasureTiming::YES)
344 {
345 deviceEnd = Now();
346 }
347
Mike Kellyb5fdf382019-06-11 16:35:25 +0100348 if (status != armnn::Status::Success)
349 {
350 ALOGW("EnqueueWorkload failed");
351 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
352 return Void();
353 }
354 }
Kevin May7bdaac52020-02-10 12:10:07 +0000355 catch (armnn::Exception& e)
356 {
357 ALOGW("armnn::Exception caught from EnqueueWorkload: %s", e.what());
358 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
359 return Void();
360 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000361 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100362 {
Kevin May7bdaac52020-02-10 12:10:07 +0000363 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100364 cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
365 return Void();
366 }
367
368 DumpTensorsIfRequired("Output", *pOutputTensors);
369
370 // Commit output buffers.
371 // Note that we update *all* pools, even if they aren't actually used as outputs -
372 // this is simpler and is what the CpuExecutor does.
373 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
374 {
375 pool.update();
376 }
377 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() after Execution");
Mike Kelly44381512019-07-08 17:37:35 +0100378
379 if (measureTiming == MeasureTiming::YES)
380 {
381 driverEnd = Now();
382 Timing timing;
383 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
384 timing.timeInDriver = MicrosecondsDuration(driverEnd, driverStart);
385 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously timing Device = %lu Driver = %lu", timing.timeOnDevice,
386 timing.timeInDriver);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100387 cb(ErrorStatus::NONE, outputShapes, timing);
Mike Kelly44381512019-07-08 17:37:35 +0100388 }
389 else
390 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100391 cb(ErrorStatus::NONE, outputShapes, g_NoTiming);
Mike Kelly44381512019-07-08 17:37:35 +0100392 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100393 return Void();
394}
395
Mike Kelly65c42dc2019-07-22 14:06:00 +0100396class ArmnnBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
397public:
398 ArmnnBurstExecutorWithCache(IPreparedModel* preparedModel)
399 : m_PreparedModel(preparedModel)
400 {}
401
402 bool isCacheEntryPresent(int32_t slot) const override
403 {
404 const auto it = m_MemoryCache.find(slot);
405 return (it != m_MemoryCache.end()) && it->second.valid();
406 }
407
408 void addCacheEntry(const hidl_memory& memory, int32_t slot) override
409 {
410 m_MemoryCache[slot] = memory;
411 }
412
413 void removeCacheEntry(int32_t slot) override
414 {
415 m_MemoryCache.erase(slot);
416 }
417
418 std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing> execute(
419 const Request& request, const std::vector<int32_t>& slots,
420 MeasureTiming measure) override
421 {
422 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache::execute");
423 hidl_vec<hidl_memory> pools(slots.size());
424
425 std::transform(slots.begin(), slots.end(), pools.begin(), [this](int32_t slot)
426 {
427 return m_MemoryCache[slot];
428 });
429
430 Request fullRequest = request;
431 fullRequest.pools = std::move(pools);
432
433 // Setup Callback
434 ErrorStatus returnedStatus = ErrorStatus::GENERAL_FAILURE;
435 hidl_vec<OutputShape> returnedOutputShapes;
436 Timing returnedTiming;
437 auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](ErrorStatus status,
438 const hidl_vec<OutputShape>& outputShapes,
439 const Timing& timing)
440 {
441 returnedStatus = status;
442 returnedOutputShapes = outputShapes;
443 returnedTiming = timing;
444 };
445
446 // Execute
447 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache executing");
448 const Return<void> ret = m_PreparedModel->executeSynchronously(fullRequest, measure, cb);
449
450 if (!ret.isOk() || returnedStatus != ErrorStatus::NONE)
451 {
452 ALOGE("ArmnnPreparedModel_1_2::BurstExecutorWithCache::error executing");
453 }
454 return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
455 }
456
457private:
458 IPreparedModel* const m_PreparedModel;
459 std::map<int, hidl_memory> m_MemoryCache;
460};
461
462
Mike Kellyb5fdf382019-06-11 16:35:25 +0100463template<typename HalVersion>
464Return<void> ArmnnPreparedModel_1_2<HalVersion>::configureExecutionBurst(
465 const sp<V1_2::IBurstCallback>& callback,
466 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
467 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
468 V1_2::IPreparedModel::configureExecutionBurst_cb cb)
469{
470 ALOGV("ArmnnPreparedModel_1_2::configureExecutionBurst");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100471 const std::shared_ptr<ArmnnBurstExecutorWithCache> executorWithCache =
472 std::make_shared<ArmnnBurstExecutorWithCache>(this);
473 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
474 requestChannel,
475 resultChannel,
476 executorWithCache);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100477
Mike Kelly44381512019-07-08 17:37:35 +0100478 if (burst == nullptr)
479 {
Mike Kellyb5fdf382019-06-11 16:35:25 +0100480 cb(ErrorStatus::GENERAL_FAILURE, {});
Mike Kelly44381512019-07-08 17:37:35 +0100481 }
482 else
483 {
Mike Kellyb5fdf382019-06-11 16:35:25 +0100484 cb(ErrorStatus::NONE, burst);
485 }
486 return Void();
487}
488
489template<typename HalVersion>
490void ArmnnPreparedModel_1_2<HalVersion>::ExecuteGraph(
491 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
492 std::shared_ptr<armnn::InputTensors>& pInputTensors,
493 std::shared_ptr<armnn::OutputTensors>& pOutputTensors,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100494 ArmnnCallback_1_2 cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100495{
496 ALOGV("ArmnnPreparedModel_1_2::ExecuteGraph(...)");
497
Mike Kelly65c42dc2019-07-22 14:06:00 +0100498 TimePoint driverEnd, deviceStart, deviceEnd;
499
Mike Kellyb5fdf382019-06-11 16:35:25 +0100500 DumpTensorsIfRequired("Input", *pInputTensors);
501
Mike Kelly65c42dc2019-07-22 14:06:00 +0100502 std::vector<std::pair<int, armnn::Tensor> > outputTensors = *pOutputTensors.get();
503 std::vector<OutputShape> outputShapes(outputTensors.size());
504
505 for (unsigned int i = 0; i < outputTensors.size(); i++)
506 {
507 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
508 const armnn::Tensor outputTensor = outputTensorPair.second;
509 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
510
511 hidl_vec<uint32_t> dimensions;
512
513 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
514 const unsigned int numDims = tensorShape.GetNumDimensions();
515 dimensions.resize(numDims);
516
517 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
518 {
519 dimensions[outputIdx] = tensorShape[outputIdx];
520 }
521 outputShapes[i].dimensions = dimensions;
522 outputShapes[i].isSufficient = true;
523 }
524
Mike Kellyb5fdf382019-06-11 16:35:25 +0100525 // run it
526 try
527 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100528 if (cb.measureTiming == MeasureTiming::YES)
529 {
530 deviceStart = Now();
531 }
532
Mike Kellyb5fdf382019-06-11 16:35:25 +0100533 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100534
535 if (cb.measureTiming == MeasureTiming::YES)
536 {
537 deviceEnd = Now();
538 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100539 if (status != armnn::Status::Success)
540 {
541 ALOGW("EnqueueWorkload failed");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100542 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming,
543 "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100544 return;
545 }
546 }
Kevin May7bdaac52020-02-10 12:10:07 +0000547 catch (armnn::Exception& e)
548 {
549 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
550 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
551 return;
552 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000553 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100554 {
Kevin May7bdaac52020-02-10 12:10:07 +0000555 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
556 cb.callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100557 return;
558 }
559
560 DumpTensorsIfRequired("Output", *pOutputTensors);
561
562 // Commit output buffers.
563 // Note that we update *all* pools, even if they aren't actually used as outputs -
564 // this is simpler and is what the CpuExecutor does.
565 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
566 {
567 pool.update();
568 }
569
Mike Kelly65c42dc2019-07-22 14:06:00 +0100570 if (cb.measureTiming == MeasureTiming::YES)
571 {
572 driverEnd = Now();
573 Timing timing;
574 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
575 timing.timeInDriver = MicrosecondsDuration(driverEnd, cb.driverStart);
576 cb.callback(ErrorStatus::NONE, outputShapes, timing, "ExecuteGraph");
577 } else {
578 cb.callback(ErrorStatus::NONE, outputShapes, g_NoTiming, "ExecuteGraph");
579 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100580}
581
582template<typename HalVersion>
583bool ArmnnPreparedModel_1_2<HalVersion>::ExecuteWithDummyInputs()
584{
585 std::vector<std::vector<char>> storage;
586 armnn::InputTensors inputTensors;
587 for (unsigned int i = 0; i < m_Model.inputIndexes.size(); i++)
588 {
589 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
590 storage.emplace_back(inputTensorInfo.GetNumBytes());
591 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
592
593 inputTensors.emplace_back(i, inputTensor);
594 }
595
596 armnn::OutputTensors outputTensors;
597 for (unsigned int i = 0; i < m_Model.outputIndexes.size(); i++)
598 {
599 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
600 storage.emplace_back(outputTensorInfo.GetNumBytes());
601 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
602
603 outputTensors.emplace_back(i, outputTensor);
604 }
605
606 try
607 {
608 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
609 if (status != armnn::Status::Success)
610 {
611 ALOGW("ExecuteWithDummyInputs: EnqueueWorkload failed");
612 return false;
613 }
614 }
Kevin May7bdaac52020-02-10 12:10:07 +0000615 catch (armnn::Exception& e)
616 {
617 ALOGW("ExecuteWithDummyInputs: armnn::Exception caught from EnqueueWorkload: %s", e.what());
618 return false;
619 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000620 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100621 {
Kevin May7bdaac52020-02-10 12:10:07 +0000622 ALOGE("ExecuteWithDummyInputs: std::exception caught from EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100623 return false;
624 }
625 return true;
626}
627
628template<typename HalVersion>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100629Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::Execute(const Request& request,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100630 MeasureTiming measureTiming,
631 armnnExecuteCallback_1_2 callback)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100632{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100633 TimePoint driverStart;
634
635 if (measureTiming == MeasureTiming::YES)
636 {
637 driverStart = Now();
638 }
639
Mike Kellyb5fdf382019-06-11 16:35:25 +0100640 ALOGV("ArmnnPreparedModel_1_2::execute(): %s", GetModelSummary(m_Model).c_str());
641 m_RequestCount++;
642
Mike Kellyb5fdf382019-06-11 16:35:25 +0100643 if (!android::nn::validateRequest(request, m_Model))
644 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100645 callback(ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100646 return ErrorStatus::INVALID_ARGUMENT;
647 }
648
649 if (!m_RequestInputsAndOutputsDumpDir.empty())
650 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100651 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
Mike Kellyb5fdf382019-06-11 16:35:25 +0100652 }
653
654 // allocate the tensors on the heap, as they are passed to the request thread
655 auto pInputTensors = std::make_shared<armnn::InputTensors>();
656 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
657
658 // map the memory pool into shared pointers
659 // use a shared memory pools vector on the heap, as it is passed to the request thread
660 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
661
662 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
663 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100664 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100665 return ErrorStatus::GENERAL_FAILURE;
666 }
667
668 // add the inputs and outputs with their data
669 try
670 {
671 pInputTensors->reserve(request.inputs.size());
672 for (unsigned int i = 0; i < request.inputs.size(); i++)
673 {
674 const auto& inputArg = request.inputs[i];
675
676 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
677 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
678
679 if (inputTensor.GetMemoryArea() == nullptr)
680 {
681 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100682 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100683 return ErrorStatus::GENERAL_FAILURE;
684 }
685
686 pInputTensors->emplace_back(i, inputTensor);
687 }
688
689 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100690 std::vector<OutputShape> outputShapes(request.outputs.size());
691
Mike Kellyb5fdf382019-06-11 16:35:25 +0100692 for (unsigned int i = 0; i < request.outputs.size(); i++)
693 {
694 const auto& outputArg = request.outputs[i];
695
696 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
697 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
698 if (outputTensor.GetMemoryArea() == nullptr)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100699 {
700 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100701 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100702 return ErrorStatus::GENERAL_FAILURE;
703 }
704
Mike Kelly65c42dc2019-07-22 14:06:00 +0100705 const size_t outputSize = outputTensorInfo.GetNumBytes();
706 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
Mike Kellyb5fdf382019-06-11 16:35:25 +0100707 pOutputTensors->emplace_back(i, outputTensor);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100708
709 hidl_vec<uint32_t> dimensions;
710
711 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
712 const unsigned int numDims = tensorShape.GetNumDimensions();
713 dimensions.resize(numDims);
714
715 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
716 {
717 dimensions[outputIdx] = tensorShape[outputIdx];
718 }
719 outputShapes[i].dimensions = dimensions;
720 outputShapes[i].isSufficient = bufferSize >= outputSize;
721
722 if (bufferSize < outputSize)
723 {
724 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
725 callback(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
726 outputShapes,
727 g_NoTiming,
728 "ArmnnPreparedModel_1_2::Execute");
729 return ErrorStatus::NONE;
730 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100731 }
732 }
Kevin May7bdaac52020-02-10 12:10:07 +0000733 catch (armnn::Exception& e)
734 {
735 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
736 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
737 return ErrorStatus::GENERAL_FAILURE;
738 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000739 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100740 {
Kevin May7bdaac52020-02-10 12:10:07 +0000741 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100742 callback(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100743 return ErrorStatus::GENERAL_FAILURE;
744 }
745
746 ALOGV("ArmnnPreparedModel_1_2::execute(...) before PostMsg");
747 // post the request for asynchronous execution
Mike Kelly65c42dc2019-07-22 14:06:00 +0100748 ArmnnCallback_1_2 armnnCb;
749 armnnCb.callback = callback;
750 armnnCb.measureTiming = measureTiming;
751 armnnCb.driverStart = driverStart;
752 m_RequestThread.PostMsg(this, pMemPools, pInputTensors, pOutputTensors, armnnCb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100753 ALOGV("ArmnnPreparedModel_1_2::execute(...) after PostMsg");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100754 return ErrorStatus::NONE;
755}
756
Mike Kellyb5fdf382019-06-11 16:35:25 +0100757#ifdef ARMNN_ANDROID_NN_V1_2
758template class ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>;
759#endif
760
761} // namespace armnn_driver