blob: 9b7904434a57408055ee56781f6baf64579ec916 [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//
Jan Eilers43a430d2020-02-28 15:40:44 +00005// Note: the ArmnnBurstExecutorWithCache in this file is based on Android code
6// under the Apache 2.0 license. See comment below for details.
7//
Mike Kellyb5fdf382019-06-11 16:35:25 +01008
9#define LOG_TAG "ArmnnDriver"
10
11#include "ArmnnPreparedModel_1_2.hpp"
12#include "Utils.hpp"
13
14#include <boost/format.hpp>
15#include <log/log.h>
16#include <OperationsUtils.h>
17#include <ExecutionBurstServer.h>
18#include <ValidateHal.h>
19
20#include <cassert>
21#include <cinttypes>
22
23using namespace android;
24using namespace android::hardware;
25
Mike Kellyb5fdf382019-06-11 16:35:25 +010026namespace {
27
Mike Kelly44381512019-07-08 17:37:35 +010028static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX};
Mike Kellyb5fdf382019-06-11 16:35:25 +010029using namespace armnn_driver;
Mike Kelly44381512019-07-08 17:37:35 +010030using TimePoint = std::chrono::steady_clock::time_point;
31
32TimePoint Now()
33{
34 return std::chrono::steady_clock::now();
35}
36
37unsigned long MicrosecondsDuration(TimePoint endPoint, TimePoint startPoint)
38{
39 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
40 endPoint - startPoint).count());
41}
Mike Kellyb5fdf382019-06-11 16:35:25 +010042
Mike Kelly65c42dc2019-07-22 14:06:00 +010043void NotifyCallbackAndCheck(const ::android::sp<V1_0::IExecutionCallback>& callback,
Kevin Mayec1e5b82020-02-26 17:00:39 +000044 V1_0::ErrorStatus errorStatus,
Mike Kelly65c42dc2019-07-22 14:06:00 +010045 std::vector<OutputShape>,
46 const Timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010047 std::string callingFunction)
48{
49 Return<void> returned = callback->notify(errorStatus);
50 // This check is required, if the callback fails and it isn't checked it will bring down the service
51 if (!returned.isOk())
52 {
53 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
54 callingFunction.c_str(), returned.description().c_str());
55 }
56}
57
Mike Kelly65c42dc2019-07-22 14:06:00 +010058void NotifyCallbackAndCheck(const ::android::sp<V1_2::IExecutionCallback>& callback,
Kevin Mayec1e5b82020-02-26 17:00:39 +000059 V1_0::ErrorStatus errorStatus,
Mike Kelly65c42dc2019-07-22 14:06:00 +010060 std::vector<OutputShape> outputShapes,
61 const Timing timing,
Mike Kellyb5fdf382019-06-11 16:35:25 +010062 std::string callingFunction)
63{
Mike Kelly65c42dc2019-07-22 14:06:00 +010064 Return<void> returned = callback->notify_1_2(errorStatus, outputShapes, timing);
Mike Kellyb5fdf382019-06-11 16:35:25 +010065 // This check is required, if the callback fails and it isn't checked it will bring down the service
66 if (!returned.isOk())
67 {
68 ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s",
69 callingFunction.c_str(), returned.description().c_str());
70 }
71}
72
73bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo)
74{
75 if (requestArg.dimensions.size() != 0)
76 {
77 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
78 {
79 ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)",
80 requestArg.dimensions.size(), tensorInfo.GetNumDimensions());
81 return false;
82 }
83
84 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
85 {
86 if (requestArg.dimensions[d] != tensorInfo.GetShape()[d])
87 {
88 ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)",
89 d, requestArg.dimensions[d], tensorInfo.GetShape()[d]);
90 return false;
91 }
92 }
93 }
94
95 return true;
96}
97
98armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg,
99 const armnn::TensorInfo& tensorInfo,
100 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
101{
102 if (!ValidateRequestArgument(requestArg, tensorInfo))
103 {
104 return armnn::Tensor();
105 }
106
107 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
108}
109
110inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
111{
112 return tensorNamePrefix + std::to_string(index);
113}
114
115} // anonymous namespace
116
117using namespace android::hardware;
118
119namespace armnn_driver
120{
121
122template<typename HalVersion>
Mike Kelly65c42dc2019-07-22 14:06:00 +0100123RequestThread<ArmnnPreparedModel_1_2, HalVersion, ArmnnCallback_1_2>
124 ArmnnPreparedModel_1_2<HalVersion>::m_RequestThread;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100125
126template<typename HalVersion>
127template<typename TensorBindingCollection>
128void ArmnnPreparedModel_1_2<HalVersion>::DumpTensorsIfRequired(char const* tensorNamePrefix,
129 const TensorBindingCollection& tensorBindings)
130{
131 if (!m_RequestInputsAndOutputsDumpDir.empty())
132 {
133 const std::string requestName = boost::str(boost::format("%1%_%2%.dump") % m_NetworkId % m_RequestCount);
134 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
135 {
136 DumpTensor(m_RequestInputsAndOutputsDumpDir,
137 requestName,
138 BuildTensorName(tensorNamePrefix, i),
139 tensorBindings[i].second);
140 }
141 }
142}
143
144template<typename HalVersion>
145ArmnnPreparedModel_1_2<HalVersion>::ArmnnPreparedModel_1_2(armnn::NetworkId networkId,
146 armnn::IRuntime* runtime,
147 const V1_2::Model& model,
148 const std::string& requestInputsAndOutputsDumpDir,
149 const bool gpuProfilingEnabled)
150 : m_NetworkId(networkId)
151 , m_Runtime(runtime)
152 , m_Model(model)
153 , m_RequestCount(0)
154 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
155 , m_GpuProfilingEnabled(gpuProfilingEnabled)
156{
157 // Enable profiling if required.
158 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
159}
160
161template<typename HalVersion>
162ArmnnPreparedModel_1_2<HalVersion>::~ArmnnPreparedModel_1_2()
163{
164 // Get a hold of the profiler used by this model.
165 std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId);
166
167 // Unload the network associated with this model.
168 m_Runtime->UnloadNetwork(m_NetworkId);
169
170 // Dump the profiling info to a file if required.
171 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get());
172}
173
174template<typename HalVersion>
Kevin Mayec1e5b82020-02-26 17:00:39 +0000175Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute(const V1_0::Request& request,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100176 const ::android::sp<V1_0::IExecutionCallback>& callback)
177{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100178 if (callback.get() == nullptr)
179 {
180 ALOGE("ArmnnPreparedModel_1_2::execute invalid callback passed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000181 return V1_0::ErrorStatus::INVALID_ARGUMENT;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100182 }
183
Kevin Mayec1e5b82020-02-26 17:00:39 +0000184 auto cb = [callback](V1_0::ErrorStatus errorStatus,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100185 std::vector<OutputShape> outputShapes,
186 const Timing& timing,
187 std::string callingFunction)
188 {
189 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
190 };
191
192 return Execute(request, MeasureTiming::NO, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100193}
194
195template<typename HalVersion>
Kevin Mayec1e5b82020-02-26 17:00:39 +0000196Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute_1_2(
197 const V1_0::Request& request,
198 MeasureTiming measureTiming,
199 const sp<V1_2::IExecutionCallback>& callback)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100200{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100201 if (callback.get() == nullptr)
202 {
203 ALOGE("ArmnnPreparedModel_1_2::execute_1_2 invalid callback passed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000204 return V1_0::ErrorStatus::INVALID_ARGUMENT;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100205 }
206
Kevin Mayec1e5b82020-02-26 17:00:39 +0000207 auto cb = [callback](V1_0::ErrorStatus errorStatus,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100208 std::vector<OutputShape> outputShapes,
209 const Timing& timing,
210 std::string callingFunction)
211 {
212 NotifyCallbackAndCheck(callback, errorStatus, outputShapes, timing, callingFunction);
213 };
214
215 return Execute(request, measureTiming, cb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100216}
217
218template<typename HalVersion>
Kevin Mayec1e5b82020-02-26 17:00:39 +0000219Return<void> ArmnnPreparedModel_1_2<HalVersion>::executeSynchronously(const V1_0::Request& request,
Mike Kelly44381512019-07-08 17:37:35 +0100220 MeasureTiming measureTiming,
221 executeSynchronously_cb cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100222{
223 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously(): %s", GetModelSummary(m_Model).c_str());
224 m_RequestCount++;
225
226 if (cb == nullptr)
227 {
228 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid callback passed");
229 return Void();
230 }
231
Mike Kelly44381512019-07-08 17:37:35 +0100232 TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
233
234 if (measureTiming == MeasureTiming::YES)
235 {
236 driverStart = Now();
237 }
238
Mike Kellyb5fdf382019-06-11 16:35:25 +0100239 if (!android::nn::validateRequest(request, m_Model))
240 {
Mike Kelly44381512019-07-08 17:37:35 +0100241 ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid request model");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000242 cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100243 return Void();
244 }
245
246 // allocate the tensors on the heap, as they are passed to the request thread
247 auto pInputTensors = std::make_shared<armnn::InputTensors>();
248 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
249
250 // map the memory pool into shared pointers
251 // use a shared memory pools vector on the heap, as it is passed to the request thread
252 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
253
254 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
255 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000256 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100257 return Void();
258 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100259 std::vector<OutputShape> outputShapes(request.outputs.size());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100260
Mike Kellyb5fdf382019-06-11 16:35:25 +0100261 try
262 {
263 pInputTensors->reserve(request.inputs.size());
264 for (unsigned int i = 0; i < request.inputs.size(); i++)
265 {
266 const auto& inputArg = request.inputs[i];
267
268 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
269 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
270
271 if (inputTensor.GetMemoryArea() == nullptr)
272 {
273 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000274 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100275 return Void();
276 }
277
278 pInputTensors->emplace_back(i, inputTensor);
279 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100280 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100281
Mike Kellyb5fdf382019-06-11 16:35:25 +0100282 for (unsigned int i = 0; i < request.outputs.size(); i++)
283 {
284 const auto& outputArg = request.outputs[i];
285
286 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
287 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
288
289 if (outputTensor.GetMemoryArea() == nullptr)
290 {
291 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000292 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100293 return Void();
294 }
Mike Kelly65c42dc2019-07-22 14:06:00 +0100295 const size_t outputSize = outputTensorInfo.GetNumBytes();
296 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
297
298 hidl_vec<uint32_t> dimensions;
299
300 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
301 const unsigned int numDims = tensorShape.GetNumDimensions();
302 dimensions.resize(numDims);
303
304 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
305 {
306 dimensions[outputIdx] = tensorShape[outputIdx];
307 }
308 outputShapes[i].dimensions = dimensions;
309 outputShapes[i].isSufficient = bufferSize >= outputSize;
310
311 if (bufferSize < outputSize)
312 {
313 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000314 cb(V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, outputShapes, g_NoTiming);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100315 return Void();
316 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100317
318 pOutputTensors->emplace_back(i, outputTensor);
319 }
320 }
Kevin May7bdaac52020-02-10 12:10:07 +0000321 catch (armnn::Exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100322 {
Kevin May7bdaac52020-02-10 12:10:07 +0000323 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000324 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100325 return Void();
326 }
Kevin May7bdaac52020-02-10 12:10:07 +0000327 catch (std::exception& e)
328 {
329 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000330 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Kevin May7bdaac52020-02-10 12:10:07 +0000331 return Void();
332 }
333
Mike Kellyb5fdf382019-06-11 16:35:25 +0100334 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() before Execution");
335
336 DumpTensorsIfRequired("Input", *pInputTensors);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100337 // run it
338 try
339 {
Mike Kelly44381512019-07-08 17:37:35 +0100340 if (measureTiming == MeasureTiming::YES)
341 {
342 deviceStart = Now();
343 }
344
Mike Kellyb5fdf382019-06-11 16:35:25 +0100345 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
346
Mike Kelly44381512019-07-08 17:37:35 +0100347 if (measureTiming == MeasureTiming::YES)
348 {
349 deviceEnd = Now();
350 }
351
Mike Kellyb5fdf382019-06-11 16:35:25 +0100352 if (status != armnn::Status::Success)
353 {
354 ALOGW("EnqueueWorkload failed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000355 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100356 return Void();
357 }
358 }
Kevin May7bdaac52020-02-10 12:10:07 +0000359 catch (armnn::Exception& e)
360 {
361 ALOGW("armnn::Exception caught from EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000362 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Kevin May7bdaac52020-02-10 12:10:07 +0000363 return Void();
364 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000365 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100366 {
Kevin May7bdaac52020-02-10 12:10:07 +0000367 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000368 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100369 return Void();
370 }
371
372 DumpTensorsIfRequired("Output", *pOutputTensors);
373
374 // Commit output buffers.
375 // Note that we update *all* pools, even if they aren't actually used as outputs -
376 // this is simpler and is what the CpuExecutor does.
377 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
378 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000379 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
380 // update() has been removed and flush() added.
381 #if defined(ARMNN_ANDROID_R) // Use the new Android implementation.
382 pool.flush();
383 #else
384 pool.update();
385 #endif
Mike Kellyb5fdf382019-06-11 16:35:25 +0100386 }
Kevin Mayec1e5b82020-02-26 17:00:39 +0000387
Mike Kellyb5fdf382019-06-11 16:35:25 +0100388 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() after Execution");
Mike Kelly44381512019-07-08 17:37:35 +0100389
390 if (measureTiming == MeasureTiming::YES)
391 {
392 driverEnd = Now();
393 Timing timing;
394 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
395 timing.timeInDriver = MicrosecondsDuration(driverEnd, driverStart);
396 ALOGV("ArmnnPreparedModel_1_2::executeSynchronously timing Device = %lu Driver = %lu", timing.timeOnDevice,
397 timing.timeInDriver);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000398 cb(V1_0::ErrorStatus::NONE, outputShapes, timing);
Mike Kelly44381512019-07-08 17:37:35 +0100399 }
400 else
401 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000402 cb(V1_0::ErrorStatus::NONE, outputShapes, g_NoTiming);
Mike Kelly44381512019-07-08 17:37:35 +0100403 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100404 return Void();
405}
406
Jan Eilers43a430d2020-02-28 15:40:44 +0000407/// This class is strongly inspired by the default implementation in Android named DefaultBurstExecutorWithCache.
408/// The original code is licensed under Apache-2.0 and can be found at the following link:
409/// https://android.googlesource.com/platform/frameworks/
410/// ml/+/refs/tags/android-10.0.0_r20/nn/common/ExecutionBurstServer.cpp
Mike Kelly65c42dc2019-07-22 14:06:00 +0100411class ArmnnBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
412public:
Kevin Mayec1e5b82020-02-26 17:00:39 +0000413 ArmnnBurstExecutorWithCache(V1_2::IPreparedModel* preparedModel)
Mike Kelly65c42dc2019-07-22 14:06:00 +0100414 : m_PreparedModel(preparedModel)
415 {}
416
417 bool isCacheEntryPresent(int32_t slot) const override
418 {
419 const auto it = m_MemoryCache.find(slot);
420 return (it != m_MemoryCache.end()) && it->second.valid();
421 }
422
423 void addCacheEntry(const hidl_memory& memory, int32_t slot) override
424 {
425 m_MemoryCache[slot] = memory;
426 }
427
428 void removeCacheEntry(int32_t slot) override
429 {
430 m_MemoryCache.erase(slot);
431 }
432
Kevin Mayec1e5b82020-02-26 17:00:39 +0000433 std::tuple<V1_0::ErrorStatus, hidl_vec<OutputShape>, Timing> execute(
434 const V1_0::Request& request, const std::vector<int32_t>& slots,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100435 MeasureTiming measure) override
436 {
437 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache::execute");
438 hidl_vec<hidl_memory> pools(slots.size());
439
440 std::transform(slots.begin(), slots.end(), pools.begin(), [this](int32_t slot)
441 {
442 return m_MemoryCache[slot];
443 });
444
Kevin Mayec1e5b82020-02-26 17:00:39 +0000445 V1_0::Request fullRequest = request;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100446 fullRequest.pools = std::move(pools);
447
448 // Setup Callback
Kevin Mayec1e5b82020-02-26 17:00:39 +0000449 V1_0::ErrorStatus returnedStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100450 hidl_vec<OutputShape> returnedOutputShapes;
451 Timing returnedTiming;
Kevin Mayec1e5b82020-02-26 17:00:39 +0000452 auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](V1_0::ErrorStatus status,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100453 const hidl_vec<OutputShape>& outputShapes,
454 const Timing& timing)
455 {
456 returnedStatus = status;
457 returnedOutputShapes = outputShapes;
458 returnedTiming = timing;
459 };
460
461 // Execute
462 ALOGV("ArmnnPreparedModel_1_2::BurstExecutorWithCache executing");
463 const Return<void> ret = m_PreparedModel->executeSynchronously(fullRequest, measure, cb);
464
Kevin Mayec1e5b82020-02-26 17:00:39 +0000465 if (!ret.isOk() || returnedStatus != V1_0::ErrorStatus::NONE)
Mike Kelly65c42dc2019-07-22 14:06:00 +0100466 {
467 ALOGE("ArmnnPreparedModel_1_2::BurstExecutorWithCache::error executing");
468 }
469 return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
470 }
471
472private:
Kevin Mayec1e5b82020-02-26 17:00:39 +0000473 V1_2::IPreparedModel* const m_PreparedModel;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100474 std::map<int, hidl_memory> m_MemoryCache;
475};
476
477
Mike Kellyb5fdf382019-06-11 16:35:25 +0100478template<typename HalVersion>
479Return<void> ArmnnPreparedModel_1_2<HalVersion>::configureExecutionBurst(
480 const sp<V1_2::IBurstCallback>& callback,
481 const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
482 const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
483 V1_2::IPreparedModel::configureExecutionBurst_cb cb)
484{
485 ALOGV("ArmnnPreparedModel_1_2::configureExecutionBurst");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100486 const std::shared_ptr<ArmnnBurstExecutorWithCache> executorWithCache =
487 std::make_shared<ArmnnBurstExecutorWithCache>(this);
488 const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(callback,
489 requestChannel,
490 resultChannel,
491 executorWithCache);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100492
Mike Kelly44381512019-07-08 17:37:35 +0100493 if (burst == nullptr)
494 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000495 cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
Mike Kelly44381512019-07-08 17:37:35 +0100496 }
497 else
498 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000499 cb(V1_0::ErrorStatus::NONE, burst);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100500 }
501 return Void();
502}
503
504template<typename HalVersion>
505void ArmnnPreparedModel_1_2<HalVersion>::ExecuteGraph(
506 std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
507 std::shared_ptr<armnn::InputTensors>& pInputTensors,
508 std::shared_ptr<armnn::OutputTensors>& pOutputTensors,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100509 ArmnnCallback_1_2 cb)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100510{
511 ALOGV("ArmnnPreparedModel_1_2::ExecuteGraph(...)");
512
Mike Kelly65c42dc2019-07-22 14:06:00 +0100513 TimePoint driverEnd, deviceStart, deviceEnd;
514
Mike Kellyb5fdf382019-06-11 16:35:25 +0100515 DumpTensorsIfRequired("Input", *pInputTensors);
516
Mike Kelly65c42dc2019-07-22 14:06:00 +0100517 std::vector<std::pair<int, armnn::Tensor> > outputTensors = *pOutputTensors.get();
518 std::vector<OutputShape> outputShapes(outputTensors.size());
519
520 for (unsigned int i = 0; i < outputTensors.size(); i++)
521 {
522 std::pair<int, armnn::Tensor> outputTensorPair = outputTensors[i];
523 const armnn::Tensor outputTensor = outputTensorPair.second;
524 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
525
526 hidl_vec<uint32_t> dimensions;
527
528 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
529 const unsigned int numDims = tensorShape.GetNumDimensions();
530 dimensions.resize(numDims);
531
532 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
533 {
534 dimensions[outputIdx] = tensorShape[outputIdx];
535 }
536 outputShapes[i].dimensions = dimensions;
537 outputShapes[i].isSufficient = true;
538 }
539
Mike Kellyb5fdf382019-06-11 16:35:25 +0100540 // run it
541 try
542 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100543 if (cb.measureTiming == MeasureTiming::YES)
544 {
545 deviceStart = Now();
546 }
547
Mike Kellyb5fdf382019-06-11 16:35:25 +0100548 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100549
550 if (cb.measureTiming == MeasureTiming::YES)
551 {
552 deviceEnd = Now();
553 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100554 if (status != armnn::Status::Success)
555 {
556 ALOGW("EnqueueWorkload failed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000557 cb.callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100558 "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100559 return;
560 }
561 }
Kevin May7bdaac52020-02-10 12:10:07 +0000562 catch (armnn::Exception& e)
563 {
564 ALOGW("armnn:Exception caught from EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000565 cb.callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
Kevin May7bdaac52020-02-10 12:10:07 +0000566 return;
567 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000568 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100569 {
Kevin May7bdaac52020-02-10 12:10:07 +0000570 ALOGE("std::exception caught from EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000571 cb.callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::ExecuteGraph");
Mike Kellyb5fdf382019-06-11 16:35:25 +0100572 return;
573 }
574
575 DumpTensorsIfRequired("Output", *pOutputTensors);
576
577 // Commit output buffers.
578 // Note that we update *all* pools, even if they aren't actually used as outputs -
579 // this is simpler and is what the CpuExecutor does.
580 for (android::nn::RunTimePoolInfo& pool : *pMemPools)
581 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000582 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
583 // update() has been removed and flush() added.
584 #if defined(ARMNN_ANDROID_R) // Use the new Android implementation.
585 pool.flush();
586 #else
587 pool.update();
588 #endif
Mike Kellyb5fdf382019-06-11 16:35:25 +0100589 }
590
Mike Kelly65c42dc2019-07-22 14:06:00 +0100591 if (cb.measureTiming == MeasureTiming::YES)
592 {
593 driverEnd = Now();
594 Timing timing;
595 timing.timeOnDevice = MicrosecondsDuration(deviceEnd, deviceStart);
596 timing.timeInDriver = MicrosecondsDuration(driverEnd, cb.driverStart);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000597 cb.callback(V1_0::ErrorStatus::NONE, outputShapes, timing, "ExecuteGraph");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100598 } else {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000599 cb.callback(V1_0::ErrorStatus::NONE, outputShapes, g_NoTiming, "ExecuteGraph");
Mike Kelly65c42dc2019-07-22 14:06:00 +0100600 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100601}
602
603template<typename HalVersion>
604bool ArmnnPreparedModel_1_2<HalVersion>::ExecuteWithDummyInputs()
605{
606 std::vector<std::vector<char>> storage;
607 armnn::InputTensors inputTensors;
608 for (unsigned int i = 0; i < m_Model.inputIndexes.size(); i++)
609 {
610 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
611 storage.emplace_back(inputTensorInfo.GetNumBytes());
612 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
613
614 inputTensors.emplace_back(i, inputTensor);
615 }
616
617 armnn::OutputTensors outputTensors;
618 for (unsigned int i = 0; i < m_Model.outputIndexes.size(); i++)
619 {
620 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
621 storage.emplace_back(outputTensorInfo.GetNumBytes());
622 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
623
624 outputTensors.emplace_back(i, outputTensor);
625 }
626
627 try
628 {
629 armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
630 if (status != armnn::Status::Success)
631 {
632 ALOGW("ExecuteWithDummyInputs: EnqueueWorkload failed");
633 return false;
634 }
635 }
Kevin May7bdaac52020-02-10 12:10:07 +0000636 catch (armnn::Exception& e)
637 {
638 ALOGW("ExecuteWithDummyInputs: armnn::Exception caught from EnqueueWorkload: %s", e.what());
639 return false;
640 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000641 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100642 {
Kevin May7bdaac52020-02-10 12:10:07 +0000643 ALOGE("ExecuteWithDummyInputs: std::exception caught from EnqueueWorkload: %s", e.what());
Mike Kellyb5fdf382019-06-11 16:35:25 +0100644 return false;
645 }
646 return true;
647}
648
649template<typename HalVersion>
Kevin Mayec1e5b82020-02-26 17:00:39 +0000650Return <V1_0::ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::Execute(const V1_0::Request& request,
651 MeasureTiming measureTiming,
652 armnnExecuteCallback_1_2 callback)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100653{
Mike Kelly65c42dc2019-07-22 14:06:00 +0100654 TimePoint driverStart;
655
656 if (measureTiming == MeasureTiming::YES)
657 {
658 driverStart = Now();
659 }
660
Mike Kellyb5fdf382019-06-11 16:35:25 +0100661 ALOGV("ArmnnPreparedModel_1_2::execute(): %s", GetModelSummary(m_Model).c_str());
662 m_RequestCount++;
663
Mike Kellyb5fdf382019-06-11 16:35:25 +0100664 if (!android::nn::validateRequest(request, m_Model))
665 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000666 callback(V1_0::ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
667 return V1_0::ErrorStatus::INVALID_ARGUMENT;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100668 }
669
670 if (!m_RequestInputsAndOutputsDumpDir.empty())
671 {
Mike Kelly65c42dc2019-07-22 14:06:00 +0100672 ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast<std::uintptr_t>(&callback));
Mike Kellyb5fdf382019-06-11 16:35:25 +0100673 }
674
675 // allocate the tensors on the heap, as they are passed to the request thread
676 auto pInputTensors = std::make_shared<armnn::InputTensors>();
677 auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
678
679 // map the memory pool into shared pointers
680 // use a shared memory pools vector on the heap, as it is passed to the request thread
681 auto pMemPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
682
683 if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools))
684 {
Kevin Mayec1e5b82020-02-26 17:00:39 +0000685 callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
686 return V1_0::ErrorStatus::GENERAL_FAILURE;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100687 }
688
689 // add the inputs and outputs with their data
690 try
691 {
692 pInputTensors->reserve(request.inputs.size());
693 for (unsigned int i = 0; i < request.inputs.size(); i++)
694 {
695 const auto& inputArg = request.inputs[i];
696
697 const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
698 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools);
699
700 if (inputTensor.GetMemoryArea() == nullptr)
701 {
702 ALOGE("Cannot execute request. Error converting request input %u to tensor", i);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000703 callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
704 return V1_0::ErrorStatus::GENERAL_FAILURE;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100705 }
706
707 pInputTensors->emplace_back(i, inputTensor);
708 }
709
710 pOutputTensors->reserve(request.outputs.size());
Mike Kelly65c42dc2019-07-22 14:06:00 +0100711 std::vector<OutputShape> outputShapes(request.outputs.size());
712
Mike Kellyb5fdf382019-06-11 16:35:25 +0100713 for (unsigned int i = 0; i < request.outputs.size(); i++)
714 {
715 const auto& outputArg = request.outputs[i];
716
717 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
718 const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools);
719 if (outputTensor.GetMemoryArea() == nullptr)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100720 {
721 ALOGE("Cannot execute request. Error converting request output %u to tensor", i);
Kevin Mayec1e5b82020-02-26 17:00:39 +0000722 callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
723 return V1_0::ErrorStatus::GENERAL_FAILURE;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100724 }
725
Mike Kelly65c42dc2019-07-22 14:06:00 +0100726 const size_t outputSize = outputTensorInfo.GetNumBytes();
727 const size_t bufferSize = pMemPools->at(outputArg.location.poolIndex).getHidlMemory().size();
Mike Kellyb5fdf382019-06-11 16:35:25 +0100728 pOutputTensors->emplace_back(i, outputTensor);
Mike Kelly65c42dc2019-07-22 14:06:00 +0100729
730 hidl_vec<uint32_t> dimensions;
731
732 armnn::TensorShape tensorShape = outputTensorInfo.GetShape();
733 const unsigned int numDims = tensorShape.GetNumDimensions();
734 dimensions.resize(numDims);
735
736 for (unsigned int outputIdx = 0u; outputIdx < numDims; ++outputIdx)
737 {
738 dimensions[outputIdx] = tensorShape[outputIdx];
739 }
740 outputShapes[i].dimensions = dimensions;
741 outputShapes[i].isSufficient = bufferSize >= outputSize;
742
743 if (bufferSize < outputSize)
744 {
745 ALOGW("ArmnnPreparedModel_1_2::Execute failed");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000746 callback(V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
Mike Kelly65c42dc2019-07-22 14:06:00 +0100747 outputShapes,
748 g_NoTiming,
749 "ArmnnPreparedModel_1_2::Execute");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000750 return V1_0::ErrorStatus::NONE;
Mike Kelly65c42dc2019-07-22 14:06:00 +0100751 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100752 }
753 }
Kevin May7bdaac52020-02-10 12:10:07 +0000754 catch (armnn::Exception& e)
755 {
756 ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000757 callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
758 return V1_0::ErrorStatus::GENERAL_FAILURE;
Kevin May7bdaac52020-02-10 12:10:07 +0000759 }
Derek Lambertib9cb8442019-11-28 13:34:48 +0000760 catch (std::exception& e)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100761 {
Kevin May7bdaac52020-02-10 12:10:07 +0000762 ALOGE("std::exception caught while preparing for EnqueueWorkload: %s", e.what());
Kevin Mayec1e5b82020-02-26 17:00:39 +0000763 callback(V1_0::ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming, "ArmnnPreparedModel_1_2::execute");
764 return V1_0::ErrorStatus::GENERAL_FAILURE;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100765 }
766
767 ALOGV("ArmnnPreparedModel_1_2::execute(...) before PostMsg");
768 // post the request for asynchronous execution
Mike Kelly65c42dc2019-07-22 14:06:00 +0100769 ArmnnCallback_1_2 armnnCb;
770 armnnCb.callback = callback;
771 armnnCb.measureTiming = measureTiming;
772 armnnCb.driverStart = driverStart;
773 m_RequestThread.PostMsg(this, pMemPools, pInputTensors, pOutputTensors, armnnCb);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100774 ALOGV("ArmnnPreparedModel_1_2::execute(...) after PostMsg");
Kevin Mayec1e5b82020-02-26 17:00:39 +0000775 return V1_0::ErrorStatus::NONE;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100776}
777
Mike Kellyb5fdf382019-06-11 16:35:25 +0100778#ifdef ARMNN_ANDROID_NN_V1_2
779template class ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>;
780#endif
781
782} // namespace armnn_driver