1 //
2 // Copyright © 2017, 2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "ArmNNProfilingServiceInitialiser.hpp"
7 #include "Runtime.hpp"
8
9 #include <ProfilingOptionsConverter.hpp>
10
11 #include <armnn/Version.hpp>
12 #include <armnn/BackendRegistry.hpp>
13 #include <armnn/BackendHelper.hpp>
14 #include <armnn/Logging.hpp>
15
16 #include <armnn/backends/IBackendContext.hpp>
17
18 #include <armnn/profiling/ArmNNProfiling.hpp>
19
20 #include <armnn/utility/PolymorphicDowncast.hpp>
21 #include <armnn/utility/Timer.hpp>
22
23 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
24 #include <backendsCommon/DynamicBackendUtils.hpp>
25 #endif
26
27 #include <backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyLibrary.hpp>
28
29 #include <client/include/backends/IBackendProfiling.hpp>
30
31 #include <common/include/LabelsAndEventClasses.hpp>
32
33 #include <iostream>
34
35
36 using namespace armnn;
37 using namespace std;
38
39 namespace armnn
40 {
IRuntime()41 IRuntime::IRuntime() : pRuntimeImpl( new RuntimeImpl(armnn::IRuntime::CreationOptions())) {}
42
IRuntime(const IRuntime::CreationOptions & options)43 IRuntime::IRuntime(const IRuntime::CreationOptions& options) : pRuntimeImpl(new RuntimeImpl(options)) {}
44
45 IRuntime::~IRuntime() = default;
46
CreateRaw(const CreationOptions & options)47 IRuntime* IRuntime::CreateRaw(const CreationOptions& options)
48 {
49 return new IRuntime(options);
50 }
51
Create(const CreationOptions & options)52 IRuntimePtr IRuntime::Create(const CreationOptions& options)
53 {
54 return IRuntimePtr(CreateRaw(options), &IRuntime::Destroy);
55 }
56
Destroy(IRuntime * runtime)57 void IRuntime::Destroy(IRuntime* runtime)
58 {
59 delete runtime;
60 }
61
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network)62 Status IRuntime::LoadNetwork(NetworkId& networkIdOut, IOptimizedNetworkPtr network)
63 {
64 return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network));
65 }
66
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network,std::string & errorMessage)67 Status IRuntime::LoadNetwork(NetworkId& networkIdOut,
68 IOptimizedNetworkPtr network,
69 std::string& errorMessage)
70 {
71 return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network), errorMessage);
72 }
73
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr network,std::string & errorMessage,const INetworkProperties & networkProperties)74 Status IRuntime::LoadNetwork(NetworkId& networkIdOut,
75 IOptimizedNetworkPtr network,
76 std::string& errorMessage,
77 const INetworkProperties& networkProperties)
78 {
79 return pRuntimeImpl->LoadNetwork(networkIdOut, std::move(network), errorMessage, networkProperties);
80 }
81
GetInputTensorInfo(NetworkId networkId,LayerBindingId layerId) const82 armnn::TensorInfo IRuntime::GetInputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
83 {
84 return pRuntimeImpl->GetInputTensorInfo(networkId, layerId);
85 }
86
GetOutputTensorInfo(NetworkId networkId,LayerBindingId layerId) const87 armnn::TensorInfo IRuntime::GetOutputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
88 {
89 return pRuntimeImpl->GetOutputTensorInfo(networkId, layerId);
90 }
91
ImportInputs(NetworkId networkId,const InputTensors & inputTensors,MemorySource forceImportMemorySource)92 std::vector<ImportedInputId> IRuntime::ImportInputs(NetworkId networkId, const InputTensors& inputTensors,
93 MemorySource forceImportMemorySource)
94 {
95 return pRuntimeImpl->ImportInputs(networkId, inputTensors, forceImportMemorySource);
96 }
97
ImportOutputs(NetworkId networkId,const OutputTensors & outputTensors,MemorySource forceImportMemorySource)98 std::vector<ImportedOutputId> IRuntime::ImportOutputs(NetworkId networkId, const OutputTensors& outputTensors,
99 MemorySource forceImportMemorySource)
100 {
101 return pRuntimeImpl->ImportOutputs(networkId, outputTensors, forceImportMemorySource);
102 }
103
ClearImportedInputs(NetworkId networkId,const std::vector<ImportedInputId> inputIds)104 void IRuntime::ClearImportedInputs(NetworkId networkId, const std::vector<ImportedInputId> inputIds)
105 {
106 return pRuntimeImpl->ClearImportedInputs(networkId, inputIds);
107 }
ClearImportedOutputs(NetworkId networkId,const std::vector<ImportedOutputId> outputIds)108 void IRuntime::ClearImportedOutputs(NetworkId networkId, const std::vector<ImportedOutputId> outputIds)
109 {
110 return pRuntimeImpl->ClearImportedOutputs(networkId, outputIds);
111 }
112
EnqueueWorkload(NetworkId networkId,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputIds,std::vector<ImportedOutputId> preImportedOutputIds)113 Status IRuntime::EnqueueWorkload(NetworkId networkId,
114 const InputTensors& inputTensors,
115 const OutputTensors& outputTensors,
116 std::vector<ImportedInputId> preImportedInputIds,
117 std::vector<ImportedOutputId> preImportedOutputIds)
118 {
119 return pRuntimeImpl->EnqueueWorkload(networkId, inputTensors, outputTensors,
120 preImportedInputIds, preImportedOutputIds);
121 }
122
Execute(IWorkingMemHandle & workingMemHandle,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputs,std::vector<ImportedOutputId> preImportedOutputs)123 Status IRuntime::Execute(IWorkingMemHandle& workingMemHandle,
124 const InputTensors& inputTensors,
125 const OutputTensors& outputTensors,
126 std::vector<ImportedInputId> preImportedInputs,
127 std::vector<ImportedOutputId> preImportedOutputs)
128 {
129 return pRuntimeImpl->Execute(workingMemHandle,
130 inputTensors,
131 outputTensors,
132 preImportedInputs,
133 preImportedOutputs);
134 }
135
UnloadNetwork(NetworkId networkId)136 Status IRuntime::UnloadNetwork(NetworkId networkId)
137 {
138 return pRuntimeImpl->UnloadNetwork(networkId);
139 }
140
GetDeviceSpec() const141 const IDeviceSpec& IRuntime::GetDeviceSpec() const
142 {
143 return pRuntimeImpl->GetDeviceSpec();
144 }
145
CreateWorkingMemHandle(NetworkId networkId)146 std::unique_ptr<IWorkingMemHandle> IRuntime::CreateWorkingMemHandle(NetworkId networkId)
147 {
148 return pRuntimeImpl->CreateWorkingMemHandle(networkId);
149 }
150
GetProfiler(NetworkId networkId) const151 const std::shared_ptr<IProfiler> IRuntime::GetProfiler(NetworkId networkId) const
152 {
153 return pRuntimeImpl->GetProfiler(networkId);
154 }
155
RegisterDebugCallback(NetworkId networkId,const DebugCallbackFunction & func)156 void IRuntime::RegisterDebugCallback(NetworkId networkId, const DebugCallbackFunction& func)
157 {
158 return pRuntimeImpl->RegisterDebugCallback(networkId, func);
159 }
160
GenerateNetworkId()161 int RuntimeImpl::GenerateNetworkId()
162 {
163 return m_NetworkIdCounter++;
164 }
165
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork)166 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut, IOptimizedNetworkPtr inNetwork)
167 {
168 std::string ignoredErrorMessage;
169 return LoadNetwork(networkIdOut, std::move(inNetwork), ignoredErrorMessage);
170 }
171
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage)172 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut,
173 IOptimizedNetworkPtr inNetwork,
174 std::string& errorMessage)
175 {
176 INetworkProperties networkProperties(
177 false, MemorySource::Undefined, MemorySource::Undefined);
178 return LoadNetwork(networkIdOut, std::move(inNetwork), errorMessage, networkProperties);
179 }
180
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage,const INetworkProperties & networkProperties)181 Status RuntimeImpl::LoadNetwork(NetworkId& networkIdOut,
182 IOptimizedNetworkPtr inNetwork,
183 std::string& errorMessage,
184 const INetworkProperties& networkProperties)
185 {
186 // Register the profiler
187 auto profiler = inNetwork->GetProfiler();
188 ProfilerManager::GetInstance().RegisterProfiler(profiler.get());
189
190 IOptimizedNetwork* rawNetwork = inNetwork.release();
191
192 networkIdOut = GenerateNetworkId();
193
194 for (auto&& context : m_BackendContexts)
195 {
196 context.second->BeforeLoadNetwork(networkIdOut);
197 }
198
199 unique_ptr<LoadedNetwork> loadedNetwork = LoadedNetwork::MakeLoadedNetwork(
200 std::unique_ptr<IOptimizedNetwork>(rawNetwork),
201 errorMessage,
202 networkProperties,
203 m_ProfilingService.get());
204
205 if (!loadedNetwork)
206 {
207 return Status::Failure;
208 }
209
210 {
211 #if !defined(ARMNN_DISABLE_THREADS)
212 std::lock_guard<std::mutex> lockGuard(m_Mutex);
213 #endif
214
215 // Stores the network
216 m_LoadedNetworks[networkIdOut] = std::move(loadedNetwork);
217 }
218
219 for (auto&& context : m_BackendContexts)
220 {
221 context.second->AfterLoadNetwork(networkIdOut);
222 }
223
224 if (m_ProfilingService->IsProfilingEnabled())
225 {
226 m_ProfilingService->IncrementCounterValue(arm::pipe::NETWORK_LOADS);
227 }
228
229 return Status::Success;
230 }
231
UnloadNetwork(NetworkId networkId)232 Status RuntimeImpl::UnloadNetwork(NetworkId networkId)
233 {
234 bool unloadOk = true;
235 for (auto&& context : m_BackendContexts)
236 {
237 unloadOk &= context.second->BeforeUnloadNetwork(networkId);
238 }
239
240 if (!unloadOk)
241 {
242 ARMNN_LOG(warning) << "RuntimeImpl::UnloadNetwork(): failed to unload "
243 "network with ID:" << networkId << " because BeforeUnloadNetwork failed";
244 return Status::Failure;
245 }
246
247 std::unique_ptr<arm::pipe::TimelineUtilityMethods> timelineUtils =
248 arm::pipe::TimelineUtilityMethods::GetTimelineUtils(*m_ProfilingService.get());
249 {
250 #if !defined(ARMNN_DISABLE_THREADS)
251 std::lock_guard<std::mutex> lockGuard(m_Mutex);
252 #endif
253
254 // If timeline recording is on mark the Network end of life
255 if (timelineUtils)
256 {
257 auto search = m_LoadedNetworks.find(networkId);
258 if (search != m_LoadedNetworks.end())
259 {
260 arm::pipe::ProfilingGuid networkGuid = search->second->GetNetworkGuid();
261 timelineUtils->RecordEvent(networkGuid,
262 arm::pipe::LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
263 }
264 }
265
266 if (m_LoadedNetworks.erase(networkId) == 0)
267 {
268 ARMNN_LOG(warning) << "WARNING: RuntimeImpl::UnloadNetwork(): " << networkId << " not found!";
269 return Status::Failure;
270 }
271
272 if (m_ProfilingService->IsProfilingEnabled())
273 {
274 m_ProfilingService->IncrementCounterValue(arm::pipe::NETWORK_UNLOADS);
275 }
276 }
277
278 for (auto&& context : m_BackendContexts)
279 {
280 context.second->AfterUnloadNetwork(networkId);
281 }
282
283 // Unregister the profiler
284 ProfilerManager::GetInstance().RegisterProfiler(nullptr);
285
286 ARMNN_LOG(debug) << "RuntimeImpl::UnloadNetwork(): Unloaded network with ID: " << networkId;
287 return Status::Success;
288 }
289
GetProfiler(NetworkId networkId) const290 const std::shared_ptr<IProfiler> RuntimeImpl::GetProfiler(NetworkId networkId) const
291 {
292 auto it = m_LoadedNetworks.find(networkId);
293 if (it != m_LoadedNetworks.end())
294 {
295 auto& loadedNetwork = it->second;
296 return loadedNetwork->GetProfiler();
297 }
298
299 return nullptr;
300 }
301
ReportStructure(arm::pipe::IProfilingService & profilingService)302 void RuntimeImpl::ReportStructure(arm::pipe::IProfilingService& profilingService)
303 {
304 if (profilingService.IsProfilingEnabled())
305 {
306 LoadedNetworks::iterator it = m_LoadedNetworks.begin();
307 while (it != m_LoadedNetworks.end())
308 {
309 auto& loadedNetwork = it->second;
310 loadedNetwork->SendNetworkStructure(profilingService);
311 // Increment the Iterator to point to next entry
312 it++;
313 }
314 }
315 }
316
InitialiseProfilingService(arm::pipe::IProfilingService & profilingService)317 void RuntimeImpl::InitialiseProfilingService(arm::pipe::IProfilingService& profilingService)
318 {
319 ArmNNProfilingServiceInitialiser initialiser;
320 initialiser.InitialiseProfilingService(profilingService);
321 }
322
RuntimeImpl(const IRuntime::CreationOptions & options)323 RuntimeImpl::RuntimeImpl(const IRuntime::CreationOptions& options)
324 : m_NetworkIdCounter(0)
325 {
326 m_ProfilingService = arm::pipe::IProfilingService::CreateProfilingService(
327 arm::pipe::MAX_ARMNN_COUNTER,
328 *this,
329 arm::pipe::ARMNN_SOFTWARE_INFO,
330 arm::pipe::ARMNN_SOFTWARE_VERSION,
331 arm::pipe::ARMNN_HARDWARE_VERSION,
332 *this);
333 const auto start_time = armnn::GetTimeNow();
334 ARMNN_LOG(info) << "ArmNN v" << ARMNN_VERSION;
335 if ( options.m_ProfilingOptions.m_TimelineEnabled && !options.m_ProfilingOptions.m_EnableProfiling )
336 {
337 throw RuntimeException(
338 "It is not possible to enable timeline reporting without profiling being enabled");
339 }
340 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
341 // Load any available/compatible dynamic backend before the runtime
342 // goes through the backend registry
343 LoadDynamicBackends(options.m_DynamicBackendsPath);
344 #endif
345 armnn::BackendIdSet supportedBackends;
346 for (const auto& id : BackendRegistryInstance().GetBackendIds())
347 {
348 // Store backend contexts for the supported ones
349 try {
350 auto factoryFun = BackendRegistryInstance().GetFactory(id);
351 ARMNN_ASSERT(factoryFun != nullptr);
352 auto backend = factoryFun();
353 ARMNN_ASSERT(backend != nullptr);
354 ARMNN_ASSERT(backend.get() != nullptr);
355
356 auto customAllocatorMapIterator = options.m_CustomAllocatorMap.find(id);
357 if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end() &&
358 customAllocatorMapIterator->second == nullptr)
359 {
360 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
361 // We need to manually clean up the dynamic backends before throwing an exception.
362 DynamicBackendUtils::DeregisterDynamicBackends(m_DeviceSpec.GetDynamicBackends());
363 m_DeviceSpec.ClearDynamicBackends();
364 #endif
365 throw armnn::Exception("Allocator associated with id " + id.Get() + " is null");
366 }
367
368 // If the runtime is created in protected mode only add backends that support this mode
369 if (options.m_ProtectedMode)
370 {
371 // check if backend supports ProtectedMode
372 using BackendCapability = BackendOptions::BackendOption;
373 BackendCapability protectedContentCapability {"ProtectedContentAllocation", true};
374 if (!HasCapability(protectedContentCapability, id))
375 {
376 // Protected Content Allocation is not supported by the backend
377 // backend should not be registered
378 ARMNN_LOG(warning) << "Backend "
379 << id
380 << " is not registered as does not support protected content allocation.";
381 continue;
382 }
383 // The user is responsible to provide a custom memory allocator which allows to allocate
384 // protected memory
385 if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end())
386 {
387 std::string err;
388 if (customAllocatorMapIterator->second->GetMemorySourceType()
389 == armnn::MemorySource::DmaBufProtected)
390 {
391 if (!backend->UseCustomMemoryAllocator(customAllocatorMapIterator->second, err))
392 {
393 ARMNN_LOG(error) << "The backend "
394 << id
395 << " reported an error when entering protected mode. Backend won't be"
396 << " used. ErrorMsg: " << err;
397 continue;
398 }
399 // No errors so register the Custom Allocator with the BackendRegistry
400 BackendRegistryInstance().RegisterAllocator(id, customAllocatorMapIterator->second);
401 m_AllocatorsAddedByThisRuntime.emplace(id);
402 }
403 else
404 {
405 ARMNN_LOG(error) << "The CustomAllocator provided with the runtime options doesn't support "
406 "protected memory. Protected mode can't be activated. The backend "
407 << id
408 << " is not going to be used. MemorySource must be MemorySource::DmaBufProtected";
409 continue;
410 }
411 }
412 else
413 {
414 ARMNN_LOG(error) << "Protected mode can't be activated for backend: "
415 << id
416 << " no custom allocator was provided to the runtime options.";
417 continue;
418 }
419 }
420 else
421 {
422 // If a custom memory allocator is provided make the backend use that instead of the default
423 if (customAllocatorMapIterator != options.m_CustomAllocatorMap.end())
424 {
425 std::string err;
426 if (!backend->UseCustomMemoryAllocator(customAllocatorMapIterator->second, err))
427 {
428 ARMNN_LOG(error) << "The backend "
429 << id
430 << " reported an error when trying to use the provided custom allocator."
431 " Backend won't be used."
432 << " ErrorMsg: " << err;
433 continue;
434 }
435 // No errors so register the Custom Allocator with the BackendRegistry
436 BackendRegistryInstance().RegisterAllocator(id, customAllocatorMapIterator->second);
437 m_AllocatorsAddedByThisRuntime.emplace(id);
438 }
439 }
440
441 // check if custom memory optimizer strategy map is set
442 if (!options.m_MemoryOptimizerStrategyMap.empty())
443 {
444 auto customMemoryOptimizerStrategyMapIterator = options.m_MemoryOptimizerStrategyMap.find(id);
445 // if a memory optimizer strategy is provided make the backend use that instead of the default
446 if (customMemoryOptimizerStrategyMapIterator != options.m_MemoryOptimizerStrategyMap.end())
447 {
448 // no errors.. register the memory optimizer strategy with the BackendRegistry
449 BackendRegistryInstance().RegisterMemoryOptimizerStrategy(
450 id, customMemoryOptimizerStrategyMapIterator->second);
451
452 ARMNN_LOG(info) << "MemoryOptimizerStrategy "
453 << customMemoryOptimizerStrategyMapIterator->second->GetName()
454 << " set for the backend " << id << ".";
455 }
456 }
457 else
458 {
459 // check if to use one of the existing memory optimizer strategies is set
460 std::string memoryOptimizerStrategyName = "";
461 ParseOptions(options.m_BackendOptions, id, [&](std::string name, const BackendOptions::Var& value)
462 {
463 if (name == "MemoryOptimizerStrategy")
464 {
465 memoryOptimizerStrategyName = ParseStringBackendOption(value, "");
466 }
467 });
468 if (memoryOptimizerStrategyName != "")
469 {
470 std::shared_ptr<IMemoryOptimizerStrategy> strategy =
471 GetMemoryOptimizerStrategy(memoryOptimizerStrategyName);
472
473 if (!strategy)
474 {
475 ARMNN_LOG(warning) << "MemoryOptimizerStrategy: " << memoryOptimizerStrategyName
476 << " was not found.";
477 }
478 else
479 {
480 using BackendCapability = BackendOptions::BackendOption;
481 auto strategyType = GetMemBlockStrategyTypeName(strategy->GetMemBlockStrategyType());
482 BackendCapability memOptimizeStrategyCapability {strategyType, true};
483 if (HasCapability(memOptimizeStrategyCapability, id))
484 {
485 BackendRegistryInstance().RegisterMemoryOptimizerStrategy(id, strategy);
486
487 ARMNN_LOG(info) << "MemoryOptimizerStrategy: "
488 << memoryOptimizerStrategyName << " set for the backend " << id << ".";
489 }
490 else
491 {
492 ARMNN_LOG(warning) << "Backend "
493 << id
494 << " does not have multi-axis packing capability and cannot support"
495 << "MemoryOptimizerStrategy: " << memoryOptimizerStrategyName << ".";
496 }
497 }
498 }
499 }
500
501 auto context = backend->CreateBackendContext(options);
502
503 // backends are allowed to return nullptrs if they
504 // don't wish to create a backend specific context
505 if (context)
506 {
507 m_BackendContexts.emplace(std::make_pair(id, std::move(context)));
508 }
509 supportedBackends.emplace(id);
510
511 unique_ptr<arm::pipe::IBackendProfiling> profilingIface =
512 arm::pipe::IBackendProfiling::CreateBackendProfiling(
513 arm::pipe::ConvertExternalProfilingOptions(options.m_ProfilingOptions),
514 *m_ProfilingService.get(),
515 id.Get());
516
517 // Backends may also provide a profiling context. Ask for it now.
518 auto profilingContext = backend->CreateBackendProfilingContext(options, profilingIface);
519 // Backends that don't support profiling will return a null profiling context.
520 if (profilingContext)
521 {
522 // Pass the context onto the profiling service.
523 m_ProfilingService->AddBackendProfilingContext(id, profilingContext);
524 }
525 }
526 catch (const BackendUnavailableException&)
527 {
528 // Ignore backends which are unavailable
529 }
530 }
531
532 BackendRegistryInstance().SetProfilingService(*m_ProfilingService.get());
533 // pass configuration info to the profiling service
534 m_ProfilingService->ConfigureProfilingService(
535 arm::pipe::ConvertExternalProfilingOptions(options.m_ProfilingOptions));
536 if (options.m_ProfilingOptions.m_EnableProfiling)
537 {
538 // try to wait for the profiling service to initialise
539 m_ProfilingService->WaitForProfilingServiceActivation(3000);
540 }
541
542 m_DeviceSpec.AddSupportedBackends(supportedBackends);
543
544 ARMNN_LOG(info) << "Initialization time: " << std::setprecision(2)
545 << std::fixed << armnn::GetTimeDuration(start_time).count() << " ms.";
546 }
547
~RuntimeImpl()548 RuntimeImpl::~RuntimeImpl()
549 {
550 const auto startTime = armnn::GetTimeNow();
551 std::vector<int> networkIDs;
552 try
553 {
554 // Coverity fix: The following code may throw an exception of type std::length_error.
555 std::transform(m_LoadedNetworks.begin(), m_LoadedNetworks.end(),
556 std::back_inserter(networkIDs),
557 [](const auto &pair) { return pair.first; });
558 }
559 catch (const std::exception& e)
560 {
561 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
562 // exception of type std::length_error.
563 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
564 std::cerr << "WARNING: An error has occurred when getting the IDs of the networks to unload: " << e.what()
565 << "\nSome of the loaded networks may not be unloaded" << std::endl;
566 }
567 // We then proceed to unload all the networks which IDs have been appended to the list
568 // up to the point the exception was thrown (if any).
569
570 for (auto networkID : networkIDs)
571 {
572 try
573 {
574 // Coverity fix: UnloadNetwork() may throw an exception of type std::length_error,
575 // boost::log::v2s_mt_posix::odr_violation or boost::log::v2s_mt_posix::system_error
576 UnloadNetwork(networkID);
577 }
578 catch (const std::exception& e)
579 {
580 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
581 // exception of type std::length_error.
582 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
583 std::cerr << "WARNING: An error has occurred when unloading network " << networkID << ": " << e.what()
584 << std::endl;
585 }
586 }
587 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
588 // Clear all dynamic backends.
589 DynamicBackendUtils::DeregisterDynamicBackends(m_DeviceSpec.GetDynamicBackends());
590 m_DeviceSpec.ClearDynamicBackends();
591 #endif
592 m_BackendContexts.clear();
593
594 BackendRegistryInstance().SetProfilingService(armnn::EmptyOptional());
595 // Remove custom allocators that this runtime has added.
596 // Note: that as backends can be per process and there can be many instances of a runtime in a process an allocator
597 // may have been overwritten by another runtime.
598 for_each(m_AllocatorsAddedByThisRuntime.begin(), m_AllocatorsAddedByThisRuntime.end(),
599 [](BackendId id) {BackendRegistryInstance().DeregisterAllocator(id);});
600
601 ARMNN_LOG(info) << "Shutdown time: " << std::setprecision(2)
602 << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
603 }
604
GetLoadedNetworkPtr(NetworkId networkId) const605 LoadedNetwork* RuntimeImpl::GetLoadedNetworkPtr(NetworkId networkId) const
606 {
607 #if !defined(ARMNN_DISABLE_THREADS)
608 std::lock_guard<std::mutex> lockGuard(m_Mutex);
609 #endif
610 return m_LoadedNetworks.at(networkId).get();
611 }
612
GetInputTensorInfo(NetworkId networkId,LayerBindingId layerId) const613 TensorInfo RuntimeImpl::GetInputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
614 {
615 return GetLoadedNetworkPtr(networkId)->GetInputTensorInfo(layerId);
616 }
617
GetOutputTensorInfo(NetworkId networkId,LayerBindingId layerId) const618 TensorInfo RuntimeImpl::GetOutputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
619 {
620 return GetLoadedNetworkPtr(networkId)->GetOutputTensorInfo(layerId);
621 }
622
ImportInputs(NetworkId networkId,const InputTensors & inputTensors,MemorySource forceImportMemorySource)623 std::vector<ImportedInputId> RuntimeImpl::ImportInputs(NetworkId networkId, const InputTensors& inputTensors,
624 MemorySource forceImportMemorySource)
625 {
626 return GetLoadedNetworkPtr(networkId)->ImportInputs(inputTensors, forceImportMemorySource);
627 }
628
ImportOutputs(NetworkId networkId,const OutputTensors & outputTensors,MemorySource forceImportMemorySource)629 std::vector<ImportedOutputId> RuntimeImpl::ImportOutputs(NetworkId networkId, const OutputTensors& outputTensors,
630 MemorySource forceImportMemorySource)
631 {
632 return GetLoadedNetworkPtr(networkId)->ImportOutputs(outputTensors, forceImportMemorySource);
633 }
634
ClearImportedInputs(NetworkId networkId,const std::vector<ImportedInputId> inputIds)635 void RuntimeImpl::ClearImportedInputs(NetworkId networkId, const std::vector<ImportedInputId> inputIds)
636 {
637 return GetLoadedNetworkPtr(networkId)->ClearImportedInputs(inputIds);
638 }
ClearImportedOutputs(NetworkId networkId,const std::vector<ImportedOutputId> outputIds)639 void RuntimeImpl::ClearImportedOutputs(NetworkId networkId, const std::vector<ImportedOutputId> outputIds)
640 {
641 return GetLoadedNetworkPtr(networkId)->ClearImportedOutputs(outputIds);
642 }
643
EnqueueWorkload(NetworkId networkId,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputIds,std::vector<ImportedOutputId> preImportedOutputIds)644 Status RuntimeImpl::EnqueueWorkload(NetworkId networkId,
645 const InputTensors& inputTensors,
646 const OutputTensors& outputTensors,
647 std::vector<ImportedInputId> preImportedInputIds,
648 std::vector<ImportedOutputId> preImportedOutputIds)
649 {
650 const auto startTime = armnn::GetTimeNow();
651
652 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
653
654 if (!loadedNetwork)
655 {
656 ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
657 return Status::Failure;
658 }
659 if (loadedNetwork->IsAsyncEnabled())
660 {
661 ARMNN_LOG(error) << "Network " << networkId << " is async enabled.";
662 return Status::Failure;
663 }
664 ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
665
666 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "EnqueueWorkload");
667
668 static thread_local NetworkId lastId = networkId;
669 if (lastId != networkId)
670 {
671 LoadedNetworkFuncSafe(lastId, [](LoadedNetwork* network)
672 {
673 network->FreeWorkingMemory();
674 });
675 }
676 lastId=networkId;
677
678 auto status = loadedNetwork->EnqueueWorkload(inputTensors, outputTensors,
679 preImportedInputIds, preImportedOutputIds);
680
681
682 // Check if we imported, if not there's no need to call the After EnqueueWorkload events
683 if (!preImportedInputIds.empty() || !preImportedOutputIds.empty())
684 {
685 // Call After EnqueueWorkload events
686 for (auto&& context : m_BackendContexts)
687 {
688 context.second->AfterEnqueueWorkload(networkId);
689 }
690 }
691 ARMNN_LOG(info) << "Execution time: " << std::setprecision(2)
692 << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
693 return status;
694 }
695
Execute(IWorkingMemHandle & iWorkingMemHandle,const InputTensors & inputTensors,const OutputTensors & outputTensors,std::vector<ImportedInputId> preImportedInputs,std::vector<ImportedOutputId> preImportedOutputs)696 Status RuntimeImpl::Execute(IWorkingMemHandle& iWorkingMemHandle,
697 const InputTensors& inputTensors,
698 const OutputTensors& outputTensors,
699 std::vector<ImportedInputId> preImportedInputs,
700 std::vector<ImportedOutputId> preImportedOutputs)
701 {
702 const auto startTime = armnn::GetTimeNow();
703
704 NetworkId networkId = iWorkingMemHandle.GetNetworkId();
705 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
706
707 if (!loadedNetwork)
708 {
709 ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
710 return Status::Failure;
711 }
712 if (!loadedNetwork->IsAsyncEnabled())
713 {
714 ARMNN_LOG(error) << "Attempting execute " << networkId << " when it is not async enabled.";
715 return Status::Failure;
716 }
717 ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
718
719 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Execute");
720
721 auto status = loadedNetwork->Execute(inputTensors,
722 outputTensors,
723 iWorkingMemHandle,
724 preImportedInputs,
725 preImportedOutputs);
726
727 ARMNN_LOG(info) << "Execution time: " << std::setprecision(2)
728 << std::fixed << armnn::GetTimeDuration(startTime).count() << " ms.";
729
730 return status;
731 }
732
733 /// Create a new unique WorkingMemHandle object. Create multiple handles if you wish to have
734 /// overlapped Execution by calling this function from different threads.
CreateWorkingMemHandle(NetworkId networkId)735 std::unique_ptr<IWorkingMemHandle> RuntimeImpl::CreateWorkingMemHandle(NetworkId networkId)
736 {
737 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
738
739 if (!loadedNetwork)
740 {
741 ARMNN_LOG(error) << "A Network with an id of " << networkId << " does not exist.";
742 return nullptr;
743 }
744 if (!loadedNetwork->IsAsyncEnabled())
745 {
746 ARMNN_LOG(error) << "Network " << networkId << " is not async enabled.";
747 return nullptr;
748 }
749 ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
750
751 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "CreateWorkingMemHandle");
752
753 static thread_local NetworkId lastId = networkId;
754 if (lastId != networkId)
755 {
756 LoadedNetworkFuncSafe(lastId, [](LoadedNetwork* network)
757 {
758 network->FreeWorkingMemory();
759 });
760 }
761 lastId=networkId;
762
763 return loadedNetwork->CreateWorkingMemHandle(networkId);
764 }
765
RegisterDebugCallback(NetworkId networkId,const DebugCallbackFunction & func)766 void RuntimeImpl::RegisterDebugCallback(NetworkId networkId, const DebugCallbackFunction& func)
767 {
768 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
769 loadedNetwork->RegisterDebugCallback(func);
770 }
771
772 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
LoadDynamicBackends(const std::string & overrideBackendPath)773 void RuntimeImpl::LoadDynamicBackends(const std::string& overrideBackendPath)
774 {
775 // Get the paths where to load the dynamic backends from
776 std::vector<std::string> backendPaths = DynamicBackendUtils::GetBackendPaths(overrideBackendPath);
777
778 // Get the shared objects to try to load as dynamic backends
779 std::vector<std::string> sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths);
780
781 // Create a list of dynamic backends
782 m_DynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects);
783
784 // Register the dynamic backends in the backend registry
785 armnn::BackendIdSet registeredBackendIds = DynamicBackendUtils::RegisterDynamicBackends(m_DynamicBackends);
786
787 // Add the registered dynamic backend ids to the list of supported backends
788 m_DeviceSpec.AddSupportedBackends(registeredBackendIds, true);
789 }
790 #endif
791 } // namespace armnn
792