blob: 22e09008baca2d1d6c40cfea68c4548ad50be6db [file] [log] [blame]
Sadik Armagan8f397a12022-06-17 15:38:22 +01001//
2// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#define LOG_TAG "arm-armnn-sl"
7
8#include "ArmnnPreparedModel.hpp"
9#include "CanonicalUtils.hpp"
10
11#include <DefaultExecution.h>
12#include <LegacyUtils.h>
13#include <nnapi/IBurst.h>
14#include <nnapi/IPreparedModel.h>
15#include <nnapi/Result.h>
16#include <nnapi/SharedMemory.h>
17#include <nnapi/TypeUtils.h>
18#include <nnapi/Types.h>
19#include <nnapi/Validation.h>
20
21#include <memory>
22#include <tuple>
23#include <utility>
24#include <vector>
25
26using namespace android;
27using namespace android::nn;
28
29static const Timing g_NoTiming = {};
30
31namespace {
32
33using namespace armnn_driver;
34
35unsigned long MicrosecondsDuration(android::nn::TimePoint endPoint, android::nn::TimePoint startPoint)
36{
37 return static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::microseconds>(
38 endPoint - startPoint).count());
39}
40
41bool ValidateRequestArgument(const Request::Argument& requestArg, const armnn::TensorInfo& tensorInfo)
42{
43 if (requestArg.dimensions.size() != 0)
44 {
45 if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions())
46 {
47 VLOG(DRIVER) << "Mismatched dimensions (request argument: "
48 << requestArg.dimensions.size() << " expected: " << tensorInfo.GetNumDimensions();
49 return false;
50 }
51
52 for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d)
53 {
54 if (requestArg.dimensions[d] != 0 && requestArg.dimensions[d] != tensorInfo.GetShape()[d])
55 {
56 VLOG(DRIVER) << "Mismatched dimensions " << d
57 << " (request argument: " << requestArg.dimensions[d]
58 << " expected: " << tensorInfo.GetShape()[d];
59 return false;
60 }
61 }
62 }
63
64 return true;
65}
66
67armnn::Tensor GetTensorForRequestArgument(const Request::Argument& requestArg,
68 const armnn::TensorInfo& tensorInfo,
69 const std::vector<::android::nn::RunTimePoolInfo>& requestPools)
70{
71 if (!ValidateRequestArgument(requestArg, tensorInfo))
72 {
73 return armnn::Tensor();
74 }
75
76 if (requestArg.lifetime == Request::Argument::LifeTime::POINTER)
77 {
78 return armnn::Tensor(tensorInfo, GetMemoryFromPointer(requestArg));
79 }
80 else if (requestArg.lifetime == Request::Argument::LifeTime::POOL)
81 {
82 return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools));
83 }
84 return armnn::Tensor();
85}
86
87inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index)
88{
89 return tensorNamePrefix + std::to_string(index);
90}
91
92bool IsPointerTypeMemory(const Request& request)
93{
94 for (auto& input : request.inputs)
95 {
96 if (input.lifetime == Request::Argument::LifeTime::POINTER)
97 {
98 return true;
99 }
100 }
101
102 for (auto& output: request.outputs)
103 {
104 if (output.lifetime == Request::Argument::LifeTime::POINTER)
105 {
106 return true;
107 }
108 }
109
110 return false;
111}
112
113} // anonymous namespace
114
115using namespace android::nn;
116
117namespace armnn_driver
118{
119
120void ArmnnPreparedModel::Init()
121{
122 // Enable profiling if required.
123 m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled);
124}
125
126ArmnnPreparedModel::ArmnnPreparedModel(armnn::NetworkId networkId,
127 armnn::IRuntime* runtime,
128 const Model& model,
129 const std::string& requestInputsAndOutputsDumpDir,
130 const bool gpuProfilingEnabled,
131 Priority priority)
132 : m_NetworkId(networkId)
133 , m_Runtime(runtime)
134 , m_Model(model)
135 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
136 , m_GpuProfilingEnabled(gpuProfilingEnabled)
137 , m_ModelPriority(priority)
138 , m_PrepareFromCache(false)
139{
140 Init();
141}
142
143ArmnnPreparedModel::ArmnnPreparedModel(armnn::NetworkId networkId,
144 armnn::IRuntime* runtime,
145 const std::string& requestInputsAndOutputsDumpDir,
146 const bool gpuProfilingEnabled,
147 Priority priority,
148 const bool prepareModelFromCache)
149 : m_NetworkId(networkId)
150 , m_Runtime(runtime)
151 , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir)
152 , m_GpuProfilingEnabled(gpuProfilingEnabled)
153 , m_ModelPriority(priority)
154 , m_PrepareFromCache(prepareModelFromCache)
155{
156 Init();
157}
158
159
160ErrorStatus ArmnnPreparedModel::PrepareMemoryForInputs(
161 armnn::InputTensors& inputs,
162 const Request& request,
163 const std::vector<android::nn::RunTimePoolInfo>& memPools) const
164{
165 inputs.reserve(request.inputs.size());
166 for (unsigned int i = 0; i < request.inputs.size(); i++)
167 {
168 const auto& inputArg = request.inputs[i];
169
170 armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
171 // inputs (of type InputTensors) is composed of a vector of ConstTensors.
172 // Therefore, set all TensorInfo isConstant parameters of input Tensors to true.
173 inputTensorInfo.SetConstant();
174 const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, memPools);
175
176 if (inputTensor.GetMemoryArea() == nullptr)
177 {
178 VLOG(DRIVER) << "Cannot execute request. Error converting request input " << i << "to tensor.";
179 return ErrorStatus::GENERAL_FAILURE;
180 }
181 inputs.emplace_back(i, inputTensor);
182 }
183
184 return ErrorStatus::NONE;
185}
186
187ErrorStatus ArmnnPreparedModel::PrepareMemoryForOutputs(
188 armnn::OutputTensors& outputs,
189 std::vector<OutputShape> &outputShapes,
190 const Request& request,
191 const std::vector<android::nn::RunTimePoolInfo>& memPools) const
192{
193 outputs.reserve(request.outputs.size());
194 for (unsigned int i = 0; i < request.outputs.size(); i++)
195 {
196 auto& outputArg = request.outputs[i];
197
198 armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
199 armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, memPools);
200 if (outputTensor.GetMemoryArea() == nullptr)
201 {
202 VLOG(DRIVER) << "Cannot execute request. Error converting request output " << i << "to tensor.";
203 return ErrorStatus::GENERAL_FAILURE;
204 }
205
206 const size_t outputSize = outputTensorInfo.GetNumBytes();
207
208 unsigned int count = 0;
209 std::for_each(outputArg.dimensions.begin(), outputArg.dimensions.end(), [&](auto dim)
210 {
211 if (dim != 0)
212 {
213 outputTensorInfo.GetShape()[count] = dim;
214 }
215 else
216 {
217 outputTensorInfo.GetShape()[count] = outputArg.dimensions.size();
218 }
219
220 count++;
221 });
222
223 outputs.emplace_back(i, outputTensor);
224 outputShapes[i] = ComputeShape(outputTensorInfo);
225
226 if (outputArg.location.length < outputSize)
227 {
228 VLOG(DRIVER) << "ArmnnPreparedModel::Execute failed outputArg.location.length "
229 << std::to_string(outputArg.location.length).c_str()
230 << " < outputSize " << std::to_string(outputSize).c_str();
231 outputShapes[i].isSufficient = false;
232 return ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
233 }
234
235 //TODO: Need to check for Request::Argument::LifeTime::POINTER
236 if (outputArg.lifetime == Request::Argument::LifeTime::POOL)
237 {
238 size_t bufferSize = memPools.at(outputArg.location.poolIndex).getSize();
239 if (bufferSize < outputSize)
240 {
241 VLOG(DRIVER) << "ArmnnPreparedModel::Execute failed bufferSize "
242 << std::to_string(outputArg.location.length).c_str()
243 << " < outputSize " << std::to_string(outputSize).c_str();
244 outputShapes[i].isSufficient = false;
245 return ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
246 }
247 }
248 }
249 return ErrorStatus::NONE;
250}
251
252ErrorStatus ArmnnPreparedModel::PrepareMemoryForIO(armnn::InputTensors& inputs,
253 armnn::OutputTensors& outputs,
254 std::vector<android::nn::RunTimePoolInfo>& memPools,
255 const Request& request) const
256{
257 //Check memory pools are not empty
258 // add the inputs and outputs with their data
259 try
260 {
261 if (!setRunTimePoolInfosFromMemoryPools(&memPools, request.pools))
262 {
263 return ErrorStatus::INVALID_ARGUMENT;
264 }
265
266 if (PrepareMemoryForInputs(inputs, request, memPools) != ErrorStatus::NONE)
267 {
268 VLOG(DRIVER) << "Failed when preparing memory for Inputs";
269 return ErrorStatus::GENERAL_FAILURE;
270 }
271
272 std::vector<OutputShape> outputShapes(request.outputs.size());
273
274 auto errorStatus = PrepareMemoryForOutputs(outputs, outputShapes, request, memPools);
275 if (errorStatus != ErrorStatus::NONE)
276 {
277 return errorStatus;
278 }
279 }
280 catch (armnn::Exception& e)
281 {
282 VLOG(DRIVER) << "armnn::Exception caught while preparing for EnqueueWorkload: " << e.what();
283 return ErrorStatus::GENERAL_FAILURE;
284 }
285 catch (std::exception& e)
286 {
287 VLOG(DRIVER) << "std::exception caught while preparing for EnqueueWorkload: " << e.what();
288 return ErrorStatus::GENERAL_FAILURE;
289 }
290
291 return ErrorStatus::NONE;
292}
293
294ExecutionResult<std::pair<std::vector<OutputShape>, Timing>> ArmnnPreparedModel::execute(
295 const Request& request,
296 MeasureTiming measureTiming,
297 const OptionalTimePoint& deadline,
298 const OptionalDuration&,
299 const std::vector<android::nn::TokenValuePair>& hints,
300 const std::vector<android::nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const
301{
302 VLOG(DRIVER) << "CanonicalDriver::PreparedModel::execute()";
303
304 CanonicalExecutionContext ctx;
305 if (measureTiming == MeasureTiming::YES)
306 {
307 ctx.measureTimings = measureTiming;
308 ctx.driverStart = Clock::now();
309 }
310
311 if (!m_PrepareFromCache)
312 {
313 const auto modelRequest = validateRequestForModel(request, m_Model);
314 if (!modelRequest.ok())
315 {
316 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << modelRequest.error();
317 }
318 VLOG(DRIVER) << "ArmnnPreparedModel::execute(): " << GetModelSummary(m_Model).c_str();
319 }
320 if (hasDeadlinePassed(deadline)) {
321 return NN_ERROR(ErrorStatus::MISSED_DEADLINE_PERSISTENT);
322 }
323
324 // map the memory pool into shared pointers
325 // use a shared memory pools vector on the heap, as it is passed to the request thread
326 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
327
328 // allocate the tensors on the heap, as they are passed to the request thread
329 auto inputTensors = std::make_shared<armnn::InputTensors>();
330 auto outputTensors = std::make_shared<armnn::OutputTensors>();
331
332 ErrorStatus theErrorStatus = ErrorStatus::NONE;
333
334 auto isPointerTypeMemory = IsPointerTypeMemory(request);
335 nn::RequestRelocation relocation;
336 if (isPointerTypeMemory)
337 {
338 std::optional<nn::Request> maybeRequestInShared;
339 auto executionResult =
340 nn::convertRequestFromPointerToShared(
341 &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
342 &maybeRequestInShared, &relocation);
343 if(!executionResult.has_value())
344 {
345 VLOG(DRIVER) << "ArmnnPreparedModel::PrepareMemoryForIO::Failed to convertRequestFromPointerToShared.";
346 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
347 << "ArmnnPreparedModel convertRequestFromPointerToShared failed";
348 }
349 const nn::Request& requestInShared = std::move(executionResult).value();
350 if (relocation.input)
351 {
352 relocation.input->flush();
353 }
354
355 theErrorStatus = PrepareMemoryForIO(*inputTensors, *outputTensors, *memPools, requestInShared);
356 }
357 else
358 {
359 theErrorStatus = PrepareMemoryForIO(*inputTensors, *outputTensors, *memPools, request);
360 }
361
362 switch(theErrorStatus)
363 {
364 case ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
365 return NN_ERROR(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE);
366 case ErrorStatus::GENERAL_FAILURE:
367 return NN_ERROR(ErrorStatus::GENERAL_FAILURE);
368 case ErrorStatus::INVALID_ARGUMENT:
369 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT);
370 default:
371 {}
372 }
373
374 std::vector<OutputShape> outputShapes(outputTensors->size());
375 for (unsigned int i = 0; i < outputTensors->size(); i++)
376 {
377 std::pair<int, armnn::Tensor> outputTensorPair = (*outputTensors)[i];
378 const armnn::Tensor outputTensor = outputTensorPair.second;
379 const armnn::TensorInfo outputTensorInfo = outputTensor.GetInfo();
380
381 outputShapes[i] = ComputeShape(outputTensorInfo);
382 }
383 Timing theTiming;
384
385 VLOG(DRIVER) << "ArmnnPreparedModel::execute(...) before ExecuteGraph";
386 auto errorStatus = ExecuteGraph(memPools, *inputTensors, *outputTensors, ctx);
387 if (errorStatus != ErrorStatus::NONE)
388 {
389 return NN_ERROR(errorStatus) << "execute() failed";
390 }
391 VLOG(DRIVER) << "ArmnnPreparedModel::execute(...) after ExecuteGraph";
392 if (isPointerTypeMemory && relocation.output)
393 {
394 relocation.output->flush();
395 }
396
397 return std::make_pair(outputShapes, theTiming);
398}
399
400ErrorStatus ArmnnPreparedModel::ExecuteGraph(
401 std::shared_ptr<std::vector<android::nn::RunTimePoolInfo>>& pMemPools,
402 armnn::InputTensors& inputTensors,
403 armnn::OutputTensors& outputTensors,
404 CanonicalExecutionContext ctx) const
405{
406 VLOG(DRIVER) << "ArmnnPreparedModel::ExecuteGraph(...)";
407
408 DumpTensorsIfRequired("Input", inputTensors);
409
410 try
411 {
412 if (ctx.measureTimings == MeasureTiming::YES)
413 {
414 ctx.deviceStart = Clock::now();
415 }
416 armnn::Status status;
417 VLOG(DRIVER) << "ArmnnPreparedModel::ExecuteGraph m_AsyncModelExecutionEnabled false";
418
419 status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors);
420
421 if (ctx.measureTimings == MeasureTiming::YES)
422 {
423 ctx.deviceEnd = Clock::now();
424 }
425 if (status != armnn::Status::Success)
426 {
427 VLOG(DRIVER) << "ArmnnPreparedModel:ExecuteGraph EnqueueWorkload failed";
428 return ErrorStatus::GENERAL_FAILURE;
429 }
430 }
431 catch (armnn::Exception& e)
432 {
433 VLOG(DRIVER) << "armnn:Exception caught from EnqueueWorkload: " << e.what();
434 return ErrorStatus::GENERAL_FAILURE;
435 }
436 catch (std::exception& e)
437 {
438 VLOG(DRIVER) << "std::exception caught from EnqueueWorkload: " << e.what();
439 return ErrorStatus::GENERAL_FAILURE;
440 }
441
442 CommitPools(*pMemPools);
443 DumpTensorsIfRequired("Output", outputTensors);
444
445 if (ctx.measureTimings == MeasureTiming::YES)
446 {
447 ctx.driverEnd = Clock::now();
448 Timing timing;
449 timing.timeOnDevice = ctx.deviceEnd - ctx.deviceStart;
450 timing.timeInDriver = ctx.driverEnd - ctx.driverStart;
451 VLOG(DRIVER) << "ArmnnPreparedModel::execute timing - Device = "
452 << timing.timeOnDevice << "Driver = " << timing.timeInDriver;
453 }
454 return ErrorStatus::NONE;
455}
456
457Priority ArmnnPreparedModel::GetModelPriority() const
458{
459 return m_ModelPriority;
460}
461
462
463GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>> ArmnnPreparedModel::executeFenced(
464 const Request& request,
465 const std::vector<SyncFence>& waitFor,
466 MeasureTiming measureTiming,
467 const OptionalTimePoint& deadline,
468 const OptionalDuration&,
469 const OptionalDuration&,
470 const std::vector<android::nn::TokenValuePair>& hints,
471 const std::vector<android::nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const
472{
473 VLOG(DRIVER) << "ArmnnPreparedModel::executeFenced()";
474
475 if (!m_PrepareFromCache) {
476 const auto modelRequest = validateRequestForModel(request, m_Model);
477 if (!modelRequest.ok())
478 {
479 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << modelRequest.error();
480 }
481 VLOG(DRIVER) << "ArmnnPreparedModel::executeFenced(): " << GetModelSummary(m_Model).c_str();
482 }
483 if (hasDeadlinePassed(deadline))
484 {
485 return NN_ERROR(ErrorStatus::MISSED_DEADLINE_PERSISTENT);
486 }
487
488 CanonicalExecutionContext ctx;
489 if (measureTiming == MeasureTiming::YES)
490 {
491 ctx.measureTimings = measureTiming;
492 ctx.driverStart = Clock::now();
493 }
494
495 // Wait for the dependent events to signal
496 for (const auto& syncFence : waitFor)
497 {
498 if (!syncFence.getSharedHandle())
499 {
500 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT);
501 }
502 if (syncFence.syncWait({}) != SyncFence::FenceState::SIGNALED)
503 {
504 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "syncWait failed";
505 }
506 }
507
508 android::nn::TimePoint fenceExecutionStart;
509 if (measureTiming == MeasureTiming::YES)
510 {
511 fenceExecutionStart = Clock::now();
512 }
513
514 // map the memory pool into shared pointers
515 // use a shared memory pools vector on the heap, as it is passed to the request thread
516 auto memPools = std::make_shared<std::vector<android::nn::RunTimePoolInfo>>();
517
518 // allocate the tensors on the heap, as they are passed to the request thread
519 auto inputTensors = std::make_shared<armnn::InputTensors>();
520 auto outputTensors = std::make_shared<armnn::OutputTensors>();
521
522 ErrorStatus theErrorStatus = ErrorStatus::NONE;
523
524 auto isPointerTypeMemory = IsPointerTypeMemory(request);
525 nn::RequestRelocation relocation;
526 if (isPointerTypeMemory)
527 {
528 std::optional<nn::Request> maybeRequestInShared;
529 auto executionResult =
530 nn::convertRequestFromPointerToShared(
531 &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
532 &maybeRequestInShared, &relocation);
533 if(!executionResult.has_value())
534 {
535 VLOG(DRIVER) << "ArmnnPreparedModel::PrepareMemoryForIO::Failed to convertRequestFromPointerToShared.";
536 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
537 << "ArmnnPreparedModel convertRequestFromPointerToShared failed";
538 }
539 const nn::Request& requestInShared = std::move(executionResult).value();
540 if (relocation.input)
541 {
542 relocation.input->flush();
543 }
544
545 theErrorStatus = PrepareMemoryForIO(*inputTensors, *outputTensors, *memPools, requestInShared);
546 }
547 else
548 {
549 theErrorStatus = PrepareMemoryForIO(*inputTensors, *outputTensors, *memPools, request);
550 }
551
552 if (theErrorStatus != ErrorStatus::NONE)
553 {
554 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "executeFenced() failed";
555 }
556
557 Timing timingSinceLaunch = {};
558 Timing timingAfterFence = {};
559 if (measureTiming == MeasureTiming::YES)
560 {
561 timingAfterFence.timeOnDevice = ctx.deviceEnd - ctx.deviceStart;
562 timingAfterFence.timeInDriver = ctx.driverEnd - fenceExecutionStart;
563 VLOG(DRIVER) << "executeFenced timingSinceLaunch = " << timingAfterFence.timeOnDevice;
564 VLOG(DRIVER) << "executeFenced timingAfterFence = " << timingAfterFence.timeInDriver;
565 }
566
567 VLOG(DRIVER) << "ArmnnCanonicalPreparedModel::executeFenced(...) before ExecuteGraph";
568 auto errorStatus = ExecuteGraph(memPools, *inputTensors, *outputTensors, ctx);
569 VLOG(DRIVER) << "ArmnnCanonicalPreparedModel::executeFenced(...) after ExecuteGraph";
570 if (isPointerTypeMemory && relocation.output)
571 {
572 relocation.output->flush();
573 }
574
575 ExecuteFencedInfoCallback armnnFencedExecutionCallback =
576 [timingSinceLaunch, timingAfterFence, errorStatus]() {
577
578 GeneralResult<std::pair<Timing, Timing>> result;
579
580 switch(errorStatus)
581 {
582 case ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
583 result.error().code = (ErrorStatus::OUTPUT_INSUFFICIENT_SIZE);
584 case ErrorStatus::GENERAL_FAILURE:
585 result.error().code = (ErrorStatus::GENERAL_FAILURE);
586 case ErrorStatus::INVALID_ARGUMENT:
587 result.error().code = (ErrorStatus::INVALID_ARGUMENT);
588 default:
589 {
590 result.value() = std::make_pair(timingSinceLaunch, timingAfterFence);
591 }
592 }
593 return result;
594 };
595 return std::make_pair(SyncFence::createAsSignaled(), std::move(armnnFencedExecutionCallback ));
596}
597
598GeneralResult<SharedExecution> ArmnnPreparedModel::createReusableExecution(
599 const Request& request,
600 MeasureTiming measureTiming,
601 const OptionalDuration& loopTimeoutDuration,
602 const std::vector<android::nn::TokenValuePair>& hints,
603 const std::vector<android::nn::ExtensionNameAndPrefix>& extensionNameToPrefix) const
604{
605 VLOG(DRIVER) << "ArmnnPreparedModel::createReusableExecution()";
606 return std::make_shared<DefaultExecution>(shared_from_this(),
607 request,
608 measureTiming,
609 loopTimeoutDuration);
610}
611
612GeneralResult<SharedBurst> ArmnnPreparedModel::configureExecutionBurst() const
613{
614 // TODO: Implement BURST
615 return nullptr;
616}
617
618std::any ArmnnPreparedModel::getUnderlyingResource() const
619{
620 return &m_Model;
621}
622
623template<typename TensorBindingCollection>
624void ArmnnPreparedModel::DumpTensorsIfRequired(char const* tensorNamePrefix,
625 const TensorBindingCollection& tensorBindings) const
626{
627 if (!m_RequestInputsAndOutputsDumpDir.empty())
628 {
629 const std::string requestName = std::to_string(m_NetworkId) + ".dump";
630 for (std::size_t i = 0u; i < tensorBindings.size(); ++i)
631 {
632 DumpTensor(m_RequestInputsAndOutputsDumpDir,
633 requestName,
634 BuildTensorName(tensorNamePrefix, i),
635 tensorBindings[i].second);
636 }
637 }
638}
639
640ArmnnPreparedModel::~ArmnnPreparedModel()
641{
642 VLOG(DRIVER) << "ArmnnPreparedModel::~ArmnnPreparedModel()";
643 // Get a hold of the profiler used by this model.
644 if (m_GpuProfilingEnabled)
645 {
646 auto profiler = m_Runtime->GetProfiler(m_NetworkId);
647 if (profiler)
648 {
649 // Dump the profiling info to a file if required.
650 DumpJsonProfilingIfRequired(m_GpuProfilingEnabled,
651 m_RequestInputsAndOutputsDumpDir,
652 m_NetworkId,
653 profiler.get());
654 }
655 }
656 // Unload the network associated with this model
657 m_Runtime->UnloadNetwork(m_NetworkId);
658}
659
660bool ArmnnPreparedModel::ExecuteWithDummyInputs(unsigned int numInputs, unsigned int numOutputs) const
661{
662 std::vector<std::vector<char>> storage;
663 armnn::InputTensors inputTensors;
664 for (unsigned int i = 0; i < numInputs; i++)
665 {
666 armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i);
667 // pInputTensors (of type InputTensors) is composed of a vector of ConstTensors.
668 // Therefore, set all TensorInfo isConstant parameters of input Tensors to true.
669 inputTensorInfo.SetConstant();
670 storage.emplace_back(inputTensorInfo.GetNumBytes());
671 const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data());
672
673 inputTensors.emplace_back(i, inputTensor);
674 }
675
676 armnn::OutputTensors outputTensors;
677 for (unsigned int i = 0; i < numOutputs; i++)
678 {
679 const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i);
680 storage.emplace_back(outputTensorInfo.GetNumBytes());
681 const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data());
682
683 outputTensors.emplace_back(i, outputTensor);
684 }
685 CanonicalExecutionContext ctx;
686 ctx.measureTimings = MeasureTiming::NO;
687 auto memPools = std::make_shared<std::vector<::android::nn::RunTimePoolInfo>>();
688
689 auto errorStatus = ExecuteGraph(memPools,
690 inputTensors,
691 outputTensors,
692 ctx);
693
694 return errorStatus == ErrorStatus::NONE;
695}
696
697} // namespace armnn_driver