1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include "Runtime.hpp"
6
7 #include <armnn/Version.hpp>
8 #include <armnn/BackendRegistry.hpp>
9 #include <LabelsAndEventClasses.hpp>
10 #include <armnn/Logging.hpp>
11 #include <armnn/utility/Timer.hpp>
12
13 #include <armnn/backends/IBackendContext.hpp>
14 #include <backendsCommon/DynamicBackendUtils.hpp>
15 #include <armnn/utility/PolymorphicDowncast.hpp>
16
17 #include <iostream>
18
19 #include <backends/BackendProfiling.hpp>
20
21 using namespace armnn;
22 using namespace std;
23
24 namespace armnn
25 {
26
CreateRaw(const CreationOptions & options)27 IRuntime* IRuntime::CreateRaw(const CreationOptions& options)
28 {
29 return new Runtime(options);
30 }
31
Create(const CreationOptions & options)32 IRuntimePtr IRuntime::Create(const CreationOptions& options)
33 {
34 return IRuntimePtr(CreateRaw(options), &IRuntime::Destroy);
35 }
36
Destroy(IRuntime * runtime)37 void IRuntime::Destroy(IRuntime* runtime)
38 {
39 delete PolymorphicDowncast<Runtime*>(runtime);
40 }
41
GenerateNetworkId()42 int Runtime::GenerateNetworkId()
43 {
44 return m_NetworkIdCounter++;
45 }
46
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork)47 Status Runtime::LoadNetwork(NetworkId& networkIdOut, IOptimizedNetworkPtr inNetwork)
48 {
49 std::string ignoredErrorMessage;
50 return LoadNetwork(networkIdOut, std::move(inNetwork), ignoredErrorMessage);
51 }
52
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage)53 Status Runtime::LoadNetwork(NetworkId& networkIdOut,
54 IOptimizedNetworkPtr inNetwork,
55 std::string& errorMessage)
56 {
57 INetworkProperties networkProperties;
58 return LoadNetwork(networkIdOut, std::move(inNetwork), errorMessage, networkProperties);
59 }
60
LoadNetwork(NetworkId & networkIdOut,IOptimizedNetworkPtr inNetwork,std::string & errorMessage,const INetworkProperties & networkProperties)61 Status Runtime::LoadNetwork(NetworkId& networkIdOut,
62 IOptimizedNetworkPtr inNetwork,
63 std::string& errorMessage,
64 const INetworkProperties& networkProperties)
65 {
66 IOptimizedNetwork* rawNetwork = inNetwork.release();
67
68 networkIdOut = GenerateNetworkId();
69
70 for (auto&& context : m_BackendContexts)
71 {
72 context.second->BeforeLoadNetwork(networkIdOut);
73 }
74
75 unique_ptr<LoadedNetwork> loadedNetwork = LoadedNetwork::MakeLoadedNetwork(
76 std::unique_ptr<OptimizedNetwork>(PolymorphicDowncast<OptimizedNetwork*>(rawNetwork)),
77 errorMessage,
78 networkProperties,
79 m_ProfilingService);
80
81 if (!loadedNetwork)
82 {
83 return Status::Failure;
84 }
85
86 {
87 std::lock_guard<std::mutex> lockGuard(m_Mutex);
88
89 // Stores the network
90 m_LoadedNetworks[networkIdOut] = std::move(loadedNetwork);
91 }
92
93 for (auto&& context : m_BackendContexts)
94 {
95 context.second->AfterLoadNetwork(networkIdOut);
96 }
97
98 if (m_ProfilingService.IsProfilingEnabled())
99 {
100 m_ProfilingService.IncrementCounterValue(armnn::profiling::NETWORK_LOADS);
101 }
102
103 return Status::Success;
104 }
105
UnloadNetwork(NetworkId networkId)106 Status Runtime::UnloadNetwork(NetworkId networkId)
107 {
108 bool unloadOk = true;
109 for (auto&& context : m_BackendContexts)
110 {
111 unloadOk &= context.second->BeforeUnloadNetwork(networkId);
112 }
113
114 if (!unloadOk)
115 {
116 ARMNN_LOG(warning) << "Runtime::UnloadNetwork(): failed to unload "
117 "network with ID:" << networkId << " because BeforeUnloadNetwork failed";
118 return Status::Failure;
119 }
120
121 std::unique_ptr<profiling::TimelineUtilityMethods> timelineUtils =
122 profiling::TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
123 {
124 std::lock_guard<std::mutex> lockGuard(m_Mutex);
125
126 // If timeline recording is on mark the Network end of life
127 if (timelineUtils)
128 {
129 auto search = m_LoadedNetworks.find(networkId);
130 if (search != m_LoadedNetworks.end())
131 {
132 profiling::ProfilingGuid networkGuid = search->second->GetNetworkGuid();
133 timelineUtils->RecordEvent(networkGuid,
134 profiling::LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
135 }
136 }
137 if (m_LoadedNetworks.erase(networkId) == 0)
138 {
139 ARMNN_LOG(warning) << "WARNING: Runtime::UnloadNetwork(): " << networkId << " not found!";
140 return Status::Failure;
141 }
142
143 if (m_ProfilingService.IsProfilingEnabled())
144 {
145 m_ProfilingService.IncrementCounterValue(armnn::profiling::NETWORK_UNLOADS);
146 }
147 }
148
149 for (auto&& context : m_BackendContexts)
150 {
151 context.second->AfterUnloadNetwork(networkId);
152 }
153
154 ARMNN_LOG(debug) << "Runtime::UnloadNetwork(): Unloaded network with ID: " << networkId;
155 return Status::Success;
156 }
157
GetProfiler(NetworkId networkId) const158 const std::shared_ptr<IProfiler> Runtime::GetProfiler(NetworkId networkId) const
159 {
160 auto it = m_LoadedNetworks.find(networkId);
161 if (it != m_LoadedNetworks.end())
162 {
163 auto& loadedNetwork = it->second;
164 return loadedNetwork->GetProfiler();
165 }
166
167 return nullptr;
168 }
169
ReportStructure()170 void Runtime::ReportStructure() // armnn::profiling::IProfilingService& profilingService as param
171 {
172 // No-op for the time being, but this may be useful in future to have the profilingService available
173 // if (profilingService.IsProfilingEnabled()){}
174
175 LoadedNetworks::iterator it = m_LoadedNetworks.begin();
176 while (it != m_LoadedNetworks.end())
177 {
178 auto& loadedNetwork = it->second;
179 loadedNetwork->SendNetworkStructure();
180 // Increment the Iterator to point to next entry
181 it++;
182 }
183 }
184
Runtime(const CreationOptions & options)185 Runtime::Runtime(const CreationOptions& options)
186 : m_NetworkIdCounter(0),
187 m_ProfilingService(*this)
188 {
189 const auto start_time = armnn::GetTimeNow();
190 ARMNN_LOG(info) << "ArmNN v" << ARMNN_VERSION << "\n";
191
192 if ( options.m_ProfilingOptions.m_TimelineEnabled && !options.m_ProfilingOptions.m_EnableProfiling )
193 {
194 throw RuntimeException("It is not possible to enable timeline reporting without profiling being enabled");
195 }
196
197 // Load any available/compatible dynamic backend before the runtime
198 // goes through the backend registry
199 LoadDynamicBackends(options.m_DynamicBackendsPath);
200
201 BackendIdSet supportedBackends;
202 for (const auto& id : BackendRegistryInstance().GetBackendIds())
203 {
204 // Store backend contexts for the supported ones
205 try {
206 auto factoryFun = BackendRegistryInstance().GetFactory(id);
207 auto backend = factoryFun();
208 ARMNN_ASSERT(backend.get() != nullptr);
209
210 auto context = backend->CreateBackendContext(options);
211
212 // backends are allowed to return nullptrs if they
213 // don't wish to create a backend specific context
214 if (context)
215 {
216 m_BackendContexts.emplace(std::make_pair(id, std::move(context)));
217 }
218 supportedBackends.emplace(id);
219
220 unique_ptr<armnn::profiling::IBackendProfiling> profilingIface =
221 std::make_unique<armnn::profiling::BackendProfiling>(armnn::profiling::BackendProfiling(
222 options, m_ProfilingService, id));
223
224 // Backends may also provide a profiling context. Ask for it now.
225 auto profilingContext = backend->CreateBackendProfilingContext(options, profilingIface);
226 // Backends that don't support profiling will return a null profiling context.
227 if (profilingContext)
228 {
229 // Pass the context onto the profiling service.
230 m_ProfilingService.AddBackendProfilingContext(id, profilingContext);
231 }
232 }
233 catch (const BackendUnavailableException&)
234 {
235 // Ignore backends which are unavailable
236 }
237 }
238
239 BackendRegistryInstance().SetProfilingService(m_ProfilingService);
240 // pass configuration info to the profiling service
241 m_ProfilingService.ConfigureProfilingService(options.m_ProfilingOptions);
242 if (options.m_ProfilingOptions.m_EnableProfiling)
243 {
244 // try to wait for the profiling service to initialise
245 m_ProfilingService.WaitForProfilingServiceActivation(3000);
246 }
247
248 m_DeviceSpec.AddSupportedBackends(supportedBackends);
249
250 ARMNN_LOG(info) << "Initialization time: " << std::setprecision(2)
251 << std::fixed << armnn::GetTimeDuration(start_time).count() << " ms\n";
252 }
253
~Runtime()254 Runtime::~Runtime()
255 {
256 const auto start_time = armnn::GetTimeNow();
257 std::vector<int> networkIDs;
258 try
259 {
260 // Coverity fix: The following code may throw an exception of type std::length_error.
261 std::transform(m_LoadedNetworks.begin(), m_LoadedNetworks.end(),
262 std::back_inserter(networkIDs),
263 [](const auto &pair) { return pair.first; });
264 }
265 catch (const std::exception& e)
266 {
267 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
268 // exception of type std::length_error.
269 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
270 std::cerr << "WARNING: An error has occurred when getting the IDs of the networks to unload: " << e.what()
271 << "\nSome of the loaded networks may not be unloaded" << std::endl;
272 }
273 // We then proceed to unload all the networks which IDs have been appended to the list
274 // up to the point the exception was thrown (if any).
275
276 for (auto networkID : networkIDs)
277 {
278 try
279 {
280 // Coverity fix: UnloadNetwork() may throw an exception of type std::length_error,
281 // boost::log::v2s_mt_posix::odr_violation or boost::log::v2s_mt_posix::system_error
282 UnloadNetwork(networkID);
283 }
284 catch (const std::exception& e)
285 {
286 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
287 // exception of type std::length_error.
288 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
289 std::cerr << "WARNING: An error has occurred when unloading network " << networkID << ": " << e.what()
290 << std::endl;
291 }
292 }
293
294 // Clear all dynamic backends.
295 DynamicBackendUtils::DeregisterDynamicBackends(m_DeviceSpec.GetDynamicBackends());
296 m_DeviceSpec.ClearDynamicBackends();
297 m_BackendContexts.clear();
298
299 BackendRegistryInstance().SetProfilingService(armnn::EmptyOptional());
300 ARMNN_LOG(info) << "Shutdown time: " << std::setprecision(2)
301 << std::fixed << armnn::GetTimeDuration(start_time).count() << " ms\n";
302 }
303
GetLoadedNetworkPtr(NetworkId networkId) const304 LoadedNetwork* Runtime::GetLoadedNetworkPtr(NetworkId networkId) const
305 {
306 std::lock_guard<std::mutex> lockGuard(m_Mutex);
307 return m_LoadedNetworks.at(networkId).get();
308 }
309
GetInputTensorInfo(NetworkId networkId,LayerBindingId layerId) const310 TensorInfo Runtime::GetInputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
311 {
312 return GetLoadedNetworkPtr(networkId)->GetInputTensorInfo(layerId);
313 }
314
GetOutputTensorInfo(NetworkId networkId,LayerBindingId layerId) const315 TensorInfo Runtime::GetOutputTensorInfo(NetworkId networkId, LayerBindingId layerId) const
316 {
317 return GetLoadedNetworkPtr(networkId)->GetOutputTensorInfo(layerId);
318 }
319
320
EnqueueWorkload(NetworkId networkId,const InputTensors & inputTensors,const OutputTensors & outputTensors)321 Status Runtime::EnqueueWorkload(NetworkId networkId,
322 const InputTensors& inputTensors,
323 const OutputTensors& outputTensors)
324 {
325 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
326 ProfilerManager::GetInstance().RegisterProfiler(loadedNetwork->GetProfiler().get());
327
328 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "EnqueueWorkload");
329
330 static thread_local NetworkId lastId = networkId;
331 if (lastId != networkId)
332 {
333 LoadedNetworkFuncSafe(lastId, [](LoadedNetwork* network)
334 {
335 network->FreeWorkingMemory();
336 });
337 }
338 lastId=networkId;
339
340 return loadedNetwork->EnqueueWorkload(inputTensors, outputTensors);
341 }
342
RegisterDebugCallback(NetworkId networkId,const DebugCallbackFunction & func)343 void Runtime::RegisterDebugCallback(NetworkId networkId, const DebugCallbackFunction& func)
344 {
345 LoadedNetwork* loadedNetwork = GetLoadedNetworkPtr(networkId);
346 loadedNetwork->RegisterDebugCallback(func);
347 }
348
LoadDynamicBackends(const std::string & overrideBackendPath)349 void Runtime::LoadDynamicBackends(const std::string& overrideBackendPath)
350 {
351 // Get the paths where to load the dynamic backends from
352 std::vector<std::string> backendPaths = DynamicBackendUtils::GetBackendPaths(overrideBackendPath);
353
354 // Get the shared objects to try to load as dynamic backends
355 std::vector<std::string> sharedObjects = DynamicBackendUtils::GetSharedObjects(backendPaths);
356
357 // Create a list of dynamic backends
358 m_DynamicBackends = DynamicBackendUtils::CreateDynamicBackends(sharedObjects);
359
360 // Register the dynamic backends in the backend registry
361 BackendIdSet registeredBackendIds = DynamicBackendUtils::RegisterDynamicBackends(m_DynamicBackends);
362
363 // Add the registered dynamic backend ids to the list of supported backends
364 m_DeviceSpec.AddSupportedBackends(registeredBackendIds, true);
365 }
366
367 } // namespace armnn
368