blob: 3d7173b9a653cc38c6d8cf9c59d2b5aabd1309d7 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Jim Flynn6398a982020-05-27 17:05:21 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5
6#include "LoadedNetwork.hpp"
7#include "Layer.hpp"
telsoa014fcda012018-03-09 14:13:49 +00008#include "Graph.hpp"
Jim Flynnf7713212020-07-14 09:50:59 +01009#include <Processes.hpp>
telsoa014fcda012018-03-09 14:13:49 +000010#include "Profiling.hpp"
surmeh013537c2c2018-05-18 16:31:43 +010011#include "HeapProfiling.hpp"
Mike Kelly55a8ffd2021-04-07 20:10:49 +010012#include "WorkingMemHandle.hpp"
telsoa014fcda012018-03-09 14:13:49 +000013
Matteo Martincighc601aa62019-10-29 15:03:22 +000014#include <armnn/BackendRegistry.hpp>
Matthew Benthamf48afc62020-01-15 17:55:08 +000015#include <armnn/Logging.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010016#include <armnn/utility/Assert.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000017
James Conroy1f58f032021-04-27 17:13:27 +010018#include <backendsCommon/TensorHandle.hpp>
Matteo Martincighe5b8eb92019-11-28 15:45:42 +000019#include <armnn/backends/IMemoryManager.hpp>
Derek Lambertif674aa02019-08-01 15:56:25 +010020#include <backendsCommon/MemCopyWorkload.hpp>
21#include <backendsCommon/MemSyncWorkload.hpp>
Finn Williamsdbf5f312021-08-26 11:08:01 +010022#include <armnn/BackendHelper.hpp>
Matteo Martincighe5b8eb92019-11-28 15:45:42 +000023
Colm Donelan5b5c2222020-09-09 12:48:16 +010024#include <fmt/format.h>
telsoa014fcda012018-03-09 14:13:49 +000025
26namespace armnn
27{
28
29using namespace std;
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +000030using namespace armnn::profiling;
telsoa014fcda012018-03-09 14:13:49 +000031
telsoa01c577f2c2018-08-31 09:22:23 +010032namespace
33{
34
35template <typename ExceptionType>
36std::string ToErrorMessage(const char * prefix, const ExceptionType & error)
37{
38 std::stringstream ss;
39 ss << prefix << " " << error.what();
40 return ss.str();
41}
42
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +000043void AddLayerStructure(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
44 const Layer& layer,
45 ProfilingGuid networkGuid)
46{
47 // Add layer to the post-optimisation network structure
48 std::string layerName = layer.GetNameStr().empty() ? "<Unnamed>" : layer.GetNameStr();
49 timelineUtils->CreateNamedTypedChildEntity(layer.GetGuid(),
50 networkGuid,
51 layerName,
52 LabelsAndEventClasses::LAYER_GUID);
53 for (auto&& input : layer.GetInputSlots())
54 {
55 const IOutputSlot* source = input.GetConnectedOutputSlot();
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010056 ARMNN_ASSERT(source != NULL);
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +000057 timelineUtils->CreateConnectionRelationship(ProfilingRelationshipType::RetentionLink,
58 source->GetOwningLayerGuid(),
59 layer.GetGuid());
60 }
61}
62
63void AddWorkloadStructure(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
64 std::unique_ptr<IWorkload>& workload,
65 const Layer& layer)
66{
67 // Add workload to the post-optimisation network structure
68 timelineUtils->CreateTypedEntity(workload->GetGuid(), LabelsAndEventClasses::WORKLOAD_GUID);
69 timelineUtils->MarkEntityWithLabel(workload->GetGuid(),
70 layer.GetBackendId().Get(),
71 LabelsAndEventClasses::BACKENDID_GUID);
72
73 // Link the workload to the layer
74 timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
75 layer.GetGuid(),
Jim Flynn6398a982020-05-27 17:05:21 +010076 workload->GetGuid(),
77 LabelsAndEventClasses::CHILD_GUID);
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +000078}
79
telsoa01c577f2c2018-08-31 09:22:23 +010080} // anonymous
81
Francis Murtagh3d2b4b22021-02-15 18:23:17 +000082std::unique_ptr<LoadedNetwork> LoadedNetwork::MakeLoadedNetwork(std::unique_ptr<IOptimizedNetwork> net,
David Monahan4f1e8e42019-09-04 09:22:10 +010083 std::string& errorMessage,
Sadik Armagan3184c902020-03-18 10:57:30 +000084 const INetworkProperties& networkProperties,
Finn Williamsf364d532021-06-09 17:07:33 +010085 profiling::ProfilingService& profilingService)
telsoa014fcda012018-03-09 14:13:49 +000086{
87 std::unique_ptr<LoadedNetwork> loadedNetwork;
88
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +010089 auto Fail = [&](const std::exception& error) -> std::unique_ptr<LoadedNetwork>
90 {
91 errorMessage = ToErrorMessage("An error occurred when preparing the network workloads: ", error);
Derek Lamberti08446972019-11-26 16:38:31 +000092 ARMNN_LOG(error) << errorMessage;
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +010093
94 return std::unique_ptr<LoadedNetwork>();
95 };
96
telsoa014fcda012018-03-09 14:13:49 +000097 try
98 {
Finn Williamsf364d532021-06-09 17:07:33 +010099 loadedNetwork.reset(new LoadedNetwork(std::move(net), networkProperties, profilingService));
telsoa014fcda012018-03-09 14:13:49 +0000100 }
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100101 catch (const armnn::RuntimeException& error)
telsoa014fcda012018-03-09 14:13:49 +0000102 {
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100103 return Fail(error);
telsoa014fcda012018-03-09 14:13:49 +0000104 }
105 catch (const armnn::Exception& error)
106 {
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100107 return Fail(error);
telsoa014fcda012018-03-09 14:13:49 +0000108 }
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100109 catch (const std::runtime_error& error)
telsoa014fcda012018-03-09 14:13:49 +0000110 {
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100111 return Fail(error);
telsoa014fcda012018-03-09 14:13:49 +0000112 }
telsoa014fcda012018-03-09 14:13:49 +0000113
114 return loadedNetwork;
115}
116
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000117LoadedNetwork::LoadedNetwork(std::unique_ptr<IOptimizedNetwork> net,
Sadik Armagan3184c902020-03-18 10:57:30 +0000118 const INetworkProperties& networkProperties,
Finn Williamsf364d532021-06-09 17:07:33 +0100119 profiling::ProfilingService& profilingService) :
David Monahan4f1e8e42019-09-04 09:22:10 +0100120 m_OptimizedNetwork(std::move(net)),
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100121 m_NetworkProperties(networkProperties),
Narumol Prangnawarat549cb7a2020-07-10 17:50:53 +0100122 m_TensorHandleFactoryRegistry(),
Sadik Armagan3184c902020-03-18 10:57:30 +0000123 m_ProfilingService(profilingService)
telsoa014fcda012018-03-09 14:13:49 +0000124{
telsoa01c577f2c2018-08-31 09:22:23 +0100125 // Create a profiler and register it for the current thread.
Francis Murtagh33199c22021-02-15 10:11:28 +0000126 m_Profiler = std::make_shared<IProfiler>();
telsoa01c577f2c2018-08-31 09:22:23 +0100127 ProfilerManager::GetInstance().RegisterProfiler(m_Profiler.get());
128
Keith Davis554fa092021-07-20 11:25:22 +0100129 m_Profiler->EnableProfiling(networkProperties.m_ProfilingEnabled);
130
Keith Davis4914d0c2021-08-18 17:14:05 +0100131 m_Profiler->EnableNetworkDetailsToStdOut(networkProperties.m_OutputNetworkDetailsMethod);
Keith Davisf4874862021-08-09 16:49:18 +0100132
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000133 Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
David Beck29c75de2018-10-23 13:35:58 +0100134 //First create tensor handlers, backends and workload factories.
telsoa01c577f2c2018-08-31 09:22:23 +0100135 //Handlers are created before workloads are.
136 //Because workload creation can modify some of the handlers,
Jim Flynne242f2d2019-05-22 14:24:13 +0100137 //(for example the splitter and concat layers).
telsoa014fcda012018-03-09 14:13:49 +0000138 for (auto&& layer : order)
139 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100140 auto const& backendId = layer->GetBackendId();
141 if (m_Backends.count(backendId) == 0)
David Beck29c75de2018-10-23 13:35:58 +0100142 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100143 auto createBackend = BackendRegistryInstance().GetFactory(backendId);
144 auto it = m_Backends.emplace(std::make_pair(backendId, createBackend()));
Aron Virginas-Tar56055192018-11-12 18:10:43 +0000145
Derek Lamberti84da38b2019-06-13 11:40:08 +0100146 IBackendInternal* backend = it.first->second.get();
Aron Virginas-Tar56055192018-11-12 18:10:43 +0000147
Finn Williamsdbf5f312021-08-26 11:08:01 +0100148 if (networkProperties.m_AsyncEnabled &&
Finn Williamsf37b9702021-09-01 18:06:04 +0100149 !HasCapability(BackendOptions::BackendOption{"AsyncExecution", true}, backend->GetCapabilities()))
Finn Williamsdbf5f312021-08-26 11:08:01 +0100150 {
151 std::string er = backend->GetId();
152 er += " does not support AsyncExecution";
153 throw BackendCapabilityException(er);
154 }
155
Derek Lamberti84da38b2019-06-13 11:40:08 +0100156 if (backend->SupportsTensorAllocatorAPI())
157 {
Sadik Armagan04a72972020-09-14 15:44:18 +0100158 auto workloadFactory = backend->CreateWorkloadFactory(
Finn Williamsf37b9702021-09-01 18:06:04 +0100159 m_TensorHandleFactoryRegistry,
160 m_OptimizedNetwork->pOptimizedNetworkImpl->GetModelOptions(),
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100161 static_cast<MemorySourceFlags>(m_NetworkProperties.m_InputSource),
162 static_cast<MemorySourceFlags>(m_NetworkProperties.m_OutputSource));
Derek Lamberti84da38b2019-06-13 11:40:08 +0100163 m_WorkloadFactories.emplace(
164 std::make_pair(backendId, std::make_pair(std::move(workloadFactory), nullptr)));
165 }
166 else
167 {
168 IBackendInternal::IMemoryManagerSharedPtr memoryManager = backend->CreateMemoryManager();
Sadik Armagan04a72972020-09-14 15:44:18 +0100169 auto workloadFactory = backend->CreateWorkloadFactory(
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000170 memoryManager, m_OptimizedNetwork->pOptimizedNetworkImpl->GetModelOptions());
Derek Lamberti84da38b2019-06-13 11:40:08 +0100171
172 m_WorkloadFactories.emplace(
173 std::make_pair(backendId, std::make_pair(std::move(workloadFactory), memoryManager)));
174 }
David Beck29c75de2018-10-23 13:35:58 +0100175 }
Derek Lamberti84da38b2019-06-13 11:40:08 +0100176 }
Keith Davise813d672021-04-22 10:10:34 +0100177
Finn Williams01097942021-04-26 12:06:34 +0100178 if (!networkProperties.m_AsyncEnabled)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100179 {
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100180 for (auto&& layer : order)
David Monahan3fb7e102019-08-20 11:25:29 +0100181 {
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100182 auto& workloadFactory = GetWorkloadFactory(*layer);
Finn Williams01097942021-04-26 12:06:34 +0100183
184 switch (layer->GetType())
David Monahan3fb7e102019-08-20 11:25:29 +0100185 {
Finn Williams01097942021-04-26 12:06:34 +0100186 case LayerType::Input:
187 case LayerType::MemImport:
Ferran Balaguer83239f92019-09-19 11:49:25 +0100188 {
Finn Williams01097942021-04-26 12:06:34 +0100189 // If IsImportEnabled is true then we need to set IsMemoryManaged
190 // to false when creating TensorHandles
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100191 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry,
192 workloadFactory,
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100193 !m_NetworkProperties.m_ImportEnabled);
Finn Williams01097942021-04-26 12:06:34 +0100194 break;
Ferran Balaguer83239f92019-09-19 11:49:25 +0100195 }
Finn Williams01097942021-04-26 12:06:34 +0100196 default:
Ferran Balaguer83239f92019-09-19 11:49:25 +0100197 {
Finn Williams01097942021-04-26 12:06:34 +0100198 // Look for a layer with 1 OutputSlot which has 1 connection and that connection is an Output Layer
199 // If Export is enabled disable memory management so we can export, otherwise we do a copy
200 if ((layer->GetNumOutputSlots() == 1) &&
201 (layer->GetOutputSlots()[0].GetNumConnections() == 1) &&
202 (layer->GetOutputSlots()[0].GetConnection(0)->GetOwningLayer().GetType() == LayerType::Output))
203 {
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100204 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry,
205 workloadFactory,
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100206 !m_NetworkProperties.m_ExportEnabled);
Finn Williams01097942021-04-26 12:06:34 +0100207 }
208 else
209 {
210 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry, workloadFactory);
211 }
Ferran Balaguer83239f92019-09-19 11:49:25 +0100212 }
David Monahan3fb7e102019-08-20 11:25:29 +0100213 }
214 }
telsoa014fcda012018-03-09 14:13:49 +0000215 }
216
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000217 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
Sadik Armagan3184c902020-03-18 10:57:30 +0000218 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
219 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000220 if (timelineUtils)
221 {
222 timelineUtils->CreateTypedEntity(networkGuid, LabelsAndEventClasses::NETWORK_GUID);
Jim Flynnf7713212020-07-14 09:50:59 +0100223 // Mark the network with a start of life event
224 timelineUtils->RecordEvent(networkGuid, LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
225 // and with the process ID
226 int processID = armnnUtils::Processes::GetCurrentId();
227 std::stringstream ss;
228 ss << processID;
229 timelineUtils->MarkEntityWithLabel(networkGuid, ss.str(), LabelsAndEventClasses::PROCESS_ID_GUID);
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000230 }
231
telsoa01c577f2c2018-08-31 09:22:23 +0100232 //Then create workloads.
telsoa014fcda012018-03-09 14:13:49 +0000233 for (auto&& layer : order)
234 {
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000235 if (timelineUtils)
236 {
237 // Add layer to the post-optimisation network structure
238 AddLayerStructure(timelineUtils, *layer, networkGuid);
239 }
240
surmeh013537c2c2018-05-18 16:31:43 +0100241 const IWorkloadFactory& workloadFactory = GetWorkloadFactory(*layer);
telsoa014fcda012018-03-09 14:13:49 +0000242
243 switch (layer->GetType())
244 {
245 case LayerType::Input:
246 case LayerType::Output:
247 {
telsoa01c577f2c2018-08-31 09:22:23 +0100248 // Inputs and outputs are treated in a special way - see EnqueueInput() and EnqueueOutput().
telsoa014fcda012018-03-09 14:13:49 +0000249 break;
250 }
251 default:
252 {
Derek Lamberti94a88d22019-12-10 21:12:59 +0000253 auto workload = layer->CreateWorkload(workloadFactory);
telsoa014fcda012018-03-09 14:13:49 +0000254
255 if (!workload)
256 {
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000257 const char* const layerName =
258 layer->GetNameStr().length() != 0 ? layer->GetName() : "<Unnamed>";
Colm Donelan5b5c2222020-09-09 12:48:16 +0100259 throw InvalidArgumentException(
260 fmt::format("No workload created for layer (name: '{0}' type: '{1}') (compute '{2}')",
261 layerName, static_cast<int>(layer->GetType()), layer->GetBackendId().Get()
telsoa014fcda012018-03-09 14:13:49 +0000262 ));
263 }
264
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000265 if (timelineUtils)
266 {
267 // Add workload to the post-optimisation network structure
268 AddWorkloadStructure(timelineUtils, workload, *layer);
269 }
270
Finn Williams01097942021-04-26 12:06:34 +0100271 // For async networks ConstantWorkloads are managed exclusively by LoadedNetwork
272 // and are separated out from the other workloads
273 if (networkProperties.m_AsyncEnabled && layer->GetType() == LayerType::Constant)
274 {
275 m_ConstantWorkloads[layer->GetGuid()] = std::move(workload);
276 }
277 else
278 {
279 m_WorkloadQueue.push_back(move(workload));
280 }
281
telsoa01c577f2c2018-08-31 09:22:23 +0100282 // release the constant data in the layer..
283 layer->ReleaseConstantData();
telsoa014fcda012018-03-09 14:13:49 +0000284 break;
285 }
286 }
287 }
288
Sadik Armagandea8fb62020-11-26 10:38:11 +0000289 for (auto&& workloadFactory : m_WorkloadFactories)
290 {
291 workloadFactory.second.first->AfterWorkloadsCreated();
292 }
293
Narumol Prangnawaratdf31cfe2019-11-22 11:26:06 +0000294 if (timelineUtils)
295 {
296 // Commit to send the post-optimisation network structure
297 timelineUtils->Commit();
298 }
299
Finn Williams01097942021-04-26 12:06:34 +0100300 if (!networkProperties.m_AsyncEnabled)
Derek Lambertif30f7d32019-04-09 10:25:02 +0100301 {
Finn Williams01097942021-04-26 12:06:34 +0100302 // Set up memory.
303 m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().AllocateDynamicBuffers();
304
305 // Now that the intermediate tensor memory has been set-up,
306 // do any post allocation configuration for each workload.
307 for (auto &workload : m_WorkloadQueue)
308 {
309 workload->PostAllocationConfigure();
310 }
311 }
312 else
313 {
314 AllocateAndExecuteConstantWorkloads();
Derek Lambertif30f7d32019-04-09 10:25:02 +0100315 }
telsoa014fcda012018-03-09 14:13:49 +0000316}
317
Finn Williams01097942021-04-26 12:06:34 +0100318void LoadedNetwork::AllocateAndExecuteConstantWorkloads()
319{
320 Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
321 for (auto&& layer : order)
322 {
323 if (layer->GetType() == LayerType::Constant)
324 {
325 const auto& outSlot = layer->GetOutputSlots()[0];
326 const auto factoryId = outSlot.GetTensorHandleFactoryId();
327 ARMNN_ASSERT(factoryId != ITensorHandleFactory::LegacyFactoryId);
328 auto& workloadFactory = GetWorkloadFactory(*layer);
329
330 layer->CreateTensorHandles(m_TensorHandleFactoryRegistry, workloadFactory);
331 ITensorHandle* tensorHandle = outSlot.GetOutputHandler().GetData();
332
333 m_ConstantTensorHandles[layer->GetGuid()] = tensorHandle;
334 tensorHandle->Allocate();
335
336 WorkingMemDescriptor memDesc;
337 memDesc.m_Outputs.push_back(tensorHandle);
338 m_ConstantWorkloads[layer->GetGuid()]->ExecuteAsync(memDesc);
339 }
340 }
341}
342
343
Keith Davis33ed2212020-03-30 10:43:41 +0100344void LoadedNetwork::SendNetworkStructure()
345{
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000346 Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
Keith Davis33ed2212020-03-30 10:43:41 +0100347 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
348
349 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
350 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
351
352 timelineUtils->CreateTypedEntity(networkGuid, LabelsAndEventClasses::NETWORK_GUID);
353
354 for (auto&& layer : order)
355 {
356 // Add layer to the post-optimisation network structure
357 AddLayerStructure(timelineUtils, *layer, networkGuid);
358 switch (layer->GetType())
359 {
360 case LayerType::Input:
361 case LayerType::Output:
362 {
363 // Inputs and outputs are treated in a special way - see EnqueueInput() and EnqueueOutput().
364 break;
365 }
366 default:
367 {
368 for (auto& workload : m_WorkloadQueue)
369 {
370 // Add workload to the post-optimisation network structure
371 AddWorkloadStructure(timelineUtils, workload, *layer);
372 }
373 break;
374 }
375 }
376 }
377 // Commit to send the post-optimisation network structure
378 timelineUtils->Commit();
379}
380
Jim Flynnf7713212020-07-14 09:50:59 +0100381profiling::ProfilingGuid LoadedNetwork::GetNetworkGuid()
382{
383 return m_OptimizedNetwork->GetGuid();
384}
385
telsoa014fcda012018-03-09 14:13:49 +0000386TensorInfo LoadedNetwork::GetInputTensorInfo(LayerBindingId layerId) const
387{
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000388 for (auto&& inputLayer : m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetInputLayers())
telsoa014fcda012018-03-09 14:13:49 +0000389 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100390 ARMNN_ASSERT_MSG(inputLayer->GetNumOutputSlots() == 1, "Input layer should have exactly 1 output slot");
telsoa014fcda012018-03-09 14:13:49 +0000391 if (inputLayer->GetBindingId() == layerId)
392 {
393 return inputLayer->GetOutputSlot(0).GetTensorInfo();
394 }
395 }
396
Colm Donelan5b5c2222020-09-09 12:48:16 +0100397 throw InvalidArgumentException(fmt::format("No input layer is associated with id {}", layerId));
telsoa014fcda012018-03-09 14:13:49 +0000398}
399
400TensorInfo LoadedNetwork::GetOutputTensorInfo(LayerBindingId layerId) const
401{
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000402 for (auto&& outputLayer : m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().GetOutputLayers())
telsoa014fcda012018-03-09 14:13:49 +0000403 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100404 ARMNN_ASSERT_MSG(outputLayer->GetNumInputSlots() == 1, "Output layer should have exactly 1 input slot");
405 ARMNN_ASSERT_MSG(outputLayer->GetInputSlot(0).GetConnection(), "Input slot on Output layer must be connected");
telsoa014fcda012018-03-09 14:13:49 +0000406 if (outputLayer->GetBindingId() == layerId)
407 {
408 return outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo();
409 }
410 }
411
Colm Donelan5b5c2222020-09-09 12:48:16 +0100412 throw InvalidArgumentException(fmt::format("No output layer is associated with id {}", layerId));
telsoa014fcda012018-03-09 14:13:49 +0000413}
414
surmeh013537c2c2018-05-18 16:31:43 +0100415const IWorkloadFactory& LoadedNetwork::GetWorkloadFactory(const Layer& layer) const
telsoa014fcda012018-03-09 14:13:49 +0000416{
surmeh013537c2c2018-05-18 16:31:43 +0100417 const IWorkloadFactory* workloadFactory = nullptr;
telsoa014fcda012018-03-09 14:13:49 +0000418
David Beck29c75de2018-10-23 13:35:58 +0100419 auto it = m_WorkloadFactories.find(layer.GetBackendId());
420 if (it == m_WorkloadFactories.end())
telsoa014fcda012018-03-09 14:13:49 +0000421 {
Colm Donelan5b5c2222020-09-09 12:48:16 +0100422 throw RuntimeException(fmt::format("No workload factory for {0} to be used for layer: {1}",
423 layer.GetBackendId().Get(),
424 layer.GetNameStr()),
425 CHECK_LOCATION());
David Beck33f0ae02018-10-18 15:13:56 +0100426 }
David Beck29c75de2018-10-23 13:35:58 +0100427
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000428 workloadFactory = it->second.first.get();
telsoa014fcda012018-03-09 14:13:49 +0000429
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100430 ARMNN_ASSERT_MSG(workloadFactory, "No workload factory");
telsoa014fcda012018-03-09 14:13:49 +0000431
432 std::string reasonIfUnsupported;
Sadik Armagan04a72972020-09-14 15:44:18 +0100433 ARMNN_ASSERT_MSG(IWorkloadFactory::IsLayerSupported(layer,
434 {},
435 reasonIfUnsupported,
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000436 m_OptimizedNetwork->pOptimizedNetworkImpl->GetModelOptions()),
David Beck29c75de2018-10-23 13:35:58 +0100437 "Factory does not support layer");
Jan Eilers8eb25602020-03-09 12:13:48 +0000438 IgnoreUnused(reasonIfUnsupported);
surmeh013537c2c2018-05-18 16:31:43 +0100439 return *workloadFactory;
telsoa014fcda012018-03-09 14:13:49 +0000440}
441
442namespace {
443
444// Non-copyable class owning accelerator-specific tensor data.
445class TensorPin
446{
447public:
448 TensorPin(std::unique_ptr<ITensorHandle> handle, const TensorInfo& info, LayerBindingId id)
449 : m_TensorHandle(std::move(handle))
450 , m_TensorInfo(info)
451 , m_Id(id)
452 {
453 }
454
455 ITensorHandle* GetTensorHandle() const { return m_TensorHandle.get(); }
456 const TensorInfo& GetTensorInfo() const { return m_TensorInfo; }
457 LayerBindingId GetBindingId() const { return m_Id; }
458
459private:
460 std::unique_ptr<ITensorHandle> m_TensorHandle;
461 TensorInfo m_TensorInfo;
462 LayerBindingId m_Id;
463};
464
465static const TensorPin& GetTensorPin(LayerBindingId id,
466 const std::vector<TensorPin>& pins,
467 char const* bindingPointDesc)
468{
469 auto it = std::find_if(pins.begin(), pins.end(),
470 [id](const TensorPin& pin)
471 {
472 return pin.GetBindingId() == id;
473 });
474
475 if (it != pins.end())
476 {
477 return *it;
478 }
479 else
480 {
Colm Donelan5b5c2222020-09-09 12:48:16 +0100481 throw InvalidArgumentException(fmt::format("No tensor supplied for {0} {1}", bindingPointDesc, id));
telsoa014fcda012018-03-09 14:13:49 +0000482 }
483}
484
485// Stores data that needs to be kept accessible for the entire execution of a workload.
486class WorkloadData
487{
488public:
489 WorkloadData(const InputTensors& inputTensors, const OutputTensors& outputTensors)
490 {
491 m_InputTensorPins.reserve(inputTensors.size());
492 m_OutputTensorPins.reserve(outputTensors.size());
493
494 for (auto inputTensorPair : inputTensors)
495 {
496 auto inputTensor = inputTensorPair.second;
497
498 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100499 std::make_unique<ConstPassthroughTensorHandle>(inputTensor.GetInfo(),inputTensor.GetMemoryArea());
telsoa014fcda012018-03-09 14:13:49 +0000500 LayerBindingId layerId = inputTensorPair.first;
501
502 m_InputTensorPins.emplace_back(std::move(tensorHandle), inputTensor.GetInfo(), layerId);
503 }
504
505 for (auto outputTensorPair : outputTensors)
506 {
507 auto outputTensor = outputTensorPair.second;
508
509 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100510 std::make_unique<PassthroughTensorHandle>(outputTensor.GetInfo(), outputTensor.GetMemoryArea());
telsoa014fcda012018-03-09 14:13:49 +0000511 LayerBindingId layerId = outputTensorPair.first;
512
513 m_OutputTensorPins.emplace_back(std::move(tensorHandle), outputTensor.GetInfo(), layerId);
514 }
515 }
516
517 const TensorPin& GetInputTensorPin(LayerBindingId id) const
518 {
519 return GetTensorPin(id, m_InputTensorPins, "input");
520 }
521
522 const TensorPin& GetOutputTensorPin(LayerBindingId id) const
523 {
524 return GetTensorPin(id, m_OutputTensorPins, "output");
525 }
526
527private:
528
529 std::vector<TensorPin> m_InputTensorPins;
530 std::vector<TensorPin> m_OutputTensorPins;
531};
532
533}
534
535Status LoadedNetwork::EnqueueWorkload(const InputTensors& inputTensors,
surmeh013537c2c2018-05-18 16:31:43 +0100536 const OutputTensors& outputTensors)
telsoa014fcda012018-03-09 14:13:49 +0000537{
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000538 const Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
telsoa014fcda012018-03-09 14:13:49 +0000539
telsoa01c577f2c2018-08-31 09:22:23 +0100540 // Walk graph to determine the order of execution.
telsoa014fcda012018-03-09 14:13:49 +0000541 if (graph.GetNumLayers() < 2)
542 {
Derek Lamberti08446972019-11-26 16:38:31 +0000543 ARMNN_LOG(warning) << "IRuntime::EnqueueWorkload()::Less than two nodes in graph";
telsoa014fcda012018-03-09 14:13:49 +0000544 return Status::Failure;
545 }
546
telsoa01c577f2c2018-08-31 09:22:23 +0100547 // Data that must be kept alive for the entire execution of the workload.
telsoa014fcda012018-03-09 14:13:49 +0000548 WorkloadData workloadData(inputTensors, outputTensors);
549
550 if (graph.GetNumInputs() != inputTensors.size())
551 {
552 throw InvalidArgumentException("Number of inputs provided does not match network.");
553 }
554
telsoa01c577f2c2018-08-31 09:22:23 +0100555 // For each input to the network, call EnqueueInput with the data passed by the user.
telsoa014fcda012018-03-09 14:13:49 +0000556 {
Derek Lambertia08d29b2020-06-19 14:33:05 +0100557 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareInputs");
558 m_InputQueue.clear();
559 m_InputQueue.reserve(graph.GetNumInputs());
560 for (const BindableLayer* inputLayer : graph.GetInputLayers())
561 {
562 const TensorPin& pin = workloadData.GetInputTensorPin(inputLayer->GetBindingId());
563 EnqueueInput(*inputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
564 }
telsoa014fcda012018-03-09 14:13:49 +0000565 }
566
telsoa01c577f2c2018-08-31 09:22:23 +0100567 // For each output to the network, call EnqueueOutput with the data passed by the user.
telsoa014fcda012018-03-09 14:13:49 +0000568 {
Derek Lambertia08d29b2020-06-19 14:33:05 +0100569 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareOutputs");
570 m_OutputQueue.clear();
571 m_OutputQueue.reserve(graph.GetNumOutputs());
572 for (const BindableLayer* outputLayer : graph.GetOutputLayers())
573 {
574 const TensorPin& pin = workloadData.GetOutputTensorPin(outputLayer->GetBindingId());
575 EnqueueOutput(*outputLayer, pin.GetTensorHandle(), pin.GetTensorInfo());
576 }
telsoa014fcda012018-03-09 14:13:49 +0000577 }
578
Sadik Armagan3184c902020-03-18 10:57:30 +0000579 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
580 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
581 ProfilingGuid inferenceGuid = m_ProfilingService.GetNextGuid();
David Monahan6198fe02019-12-02 08:35:43 +0000582 if (timelineUtils)
583 {
584 // Add inference timeline trace if profiling is enabled.
585 ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
586 timelineUtils->CreateTypedEntity(inferenceGuid, LabelsAndEventClasses::INFERENCE_GUID);
Jim Flynn6398a982020-05-27 17:05:21 +0100587 timelineUtils->CreateRelationship(ProfilingRelationshipType::RetentionLink,
588 networkGuid,
589 inferenceGuid,
590 LabelsAndEventClasses::EXECUTION_OF_GUID);
David Monahan6198fe02019-12-02 08:35:43 +0000591 timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
592 }
593
telsoa014fcda012018-03-09 14:13:49 +0000594 bool executionSucceeded = true;
595
596 {
Sadik Armagan3184c902020-03-18 10:57:30 +0000597 if (m_ProfilingService.IsProfilingEnabled())
Keith Davise394bd92019-12-02 15:12:19 +0000598 {
Sadik Armagan3184c902020-03-18 10:57:30 +0000599 m_ProfilingService.IncrementCounterValue(armnn::profiling::INFERENCES_RUN);
Keith Davise394bd92019-12-02 15:12:19 +0000600 }
telsoa014fcda012018-03-09 14:13:49 +0000601 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Execute");
surmeh013537c2c2018-05-18 16:31:43 +0100602 ARMNN_SCOPED_HEAP_PROFILING("Executing");
David Monahan6198fe02019-12-02 08:35:43 +0000603 executionSucceeded = Execute(timelineUtils, inferenceGuid);
telsoa014fcda012018-03-09 14:13:49 +0000604 }
605
David Monahan6198fe02019-12-02 08:35:43 +0000606 if (timelineUtils)
607 {
608 // Add end of life of the inference timeline if profiling is enabled.
609 timelineUtils->RecordEvent(inferenceGuid, LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
610 timelineUtils->Commit();
611 }
telsoa014fcda012018-03-09 14:13:49 +0000612 return executionSucceeded ? Status::Success : Status::Failure;
613}
614
surmeh013537c2c2018-05-18 16:31:43 +0100615void LoadedNetwork::EnqueueInput(const BindableLayer& layer, ITensorHandle* tensorHandle, const TensorInfo& tensorInfo)
telsoa014fcda012018-03-09 14:13:49 +0000616{
617 if (layer.GetType() != LayerType::Input)
618 {
619 throw InvalidArgumentException("EnqueueInput: given layer not an InputLayer");
620 }
621
622 if (tensorHandle == nullptr)
623 {
624 throw InvalidArgumentException("EnqueueInput: tensorHandle must not be NULL");
625 }
626
627 InputQueueDescriptor inputQueueDescriptor;
628 WorkloadInfo info;
629
630 inputQueueDescriptor.m_Inputs.push_back(tensorHandle);
631 info.m_InputTensorInfos.push_back(tensorInfo);
632
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100633 ARMNN_ASSERT_MSG(layer.GetNumOutputSlots() == 1, "Can only handle Input Layer with one output");
telsoa014fcda012018-03-09 14:13:49 +0000634 const OutputHandler& handler = layer.GetOutputHandler();
635 const TensorInfo& outputTensorInfo = handler.GetTensorInfo();
636 ITensorHandle* outputTensorHandle = handler.GetData();
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100637 ARMNN_ASSERT_MSG(outputTensorHandle != nullptr,
telsoa014fcda012018-03-09 14:13:49 +0000638 "Data should have been allocated.");
639 inputQueueDescriptor.m_Outputs.push_back(outputTensorHandle);
640 info.m_OutputTensorInfos.push_back(outputTensorInfo);
641
Derek Lambertif674aa02019-08-01 15:56:25 +0100642 MemorySourceFlags importFlags = outputTensorHandle->GetImportFlags();
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000643 bool needMemCopy = true;
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100644 if (m_NetworkProperties.m_ImportEnabled) // Try import the input tensor
Derek Lambertif674aa02019-08-01 15:56:25 +0100645 {
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100646 if(CheckFlag(importFlags, m_NetworkProperties.m_InputSource))
Derek Lambertif674aa02019-08-01 15:56:25 +0100647 {
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000648 needMemCopy = false;
Ferran Balaguer83239f92019-09-19 11:49:25 +0100649 // This assumes a CPU Tensor handle
650 void* mem = tensorHandle->Map(false);
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100651 if (outputTensorHandle->Import(mem, m_NetworkProperties.m_InputSource))
Ferran Balaguer83239f92019-09-19 11:49:25 +0100652 {
653 tensorHandle->Unmap();
654 return; // No need for a workload since the import has been done.
655 }
Derek Lambertif674aa02019-08-01 15:56:25 +0100656 tensorHandle->Unmap();
Ferran Balaguer83239f92019-09-19 11:49:25 +0100657 throw MemoryImportException("EnqueueInput: Memory Import failed");
Derek Lambertif674aa02019-08-01 15:56:25 +0100658 }
Derek Lambertif674aa02019-08-01 15:56:25 +0100659 }
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000660 if (needMemCopy)
David Monahan4f1e8e42019-09-04 09:22:10 +0100661 {
662 // Create a mem copy workload for input since we did not import
Narumol Prangnawarataa68e012019-11-29 17:17:43 +0000663 std::unique_ptr<IWorkload> inputWorkload = std::make_unique<CopyMemGenericWorkload>(inputQueueDescriptor, info);
Derek Lambertif674aa02019-08-01 15:56:25 +0100664
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100665 ARMNN_ASSERT_MSG(inputWorkload, "No input workload created");
Narumol Prangnawarataa68e012019-11-29 17:17:43 +0000666
Sadik Armagan3184c902020-03-18 10:57:30 +0000667 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
668 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
Narumol Prangnawarataa68e012019-11-29 17:17:43 +0000669 if (timelineUtils)
670 {
671 // Add Input Workload to the post-optimisation network structure
672 AddWorkloadStructure(timelineUtils, inputWorkload, layer);
673 timelineUtils->Commit();
674 }
675
David Monahan4f1e8e42019-09-04 09:22:10 +0100676 m_InputQueue.push_back(move(inputWorkload));
677 }
telsoa014fcda012018-03-09 14:13:49 +0000678}
679
surmeh013537c2c2018-05-18 16:31:43 +0100680void LoadedNetwork::EnqueueOutput(const BindableLayer& layer, ITensorHandle* tensorHandle, const TensorInfo& tensorInfo)
telsoa014fcda012018-03-09 14:13:49 +0000681{
682 if (layer.GetType() != LayerType::Output)
683 {
684 throw InvalidArgumentException("EnqueueOutput: given layer not an OutputLayer");
685 }
686
687 if (tensorHandle == nullptr)
688 {
689 throw InvalidArgumentException("EnqueueOutput: tensorHandle must not be NULL");
690 }
691
692 OutputQueueDescriptor outputQueueDescriptor;
693 WorkloadInfo info;
694
695 outputQueueDescriptor.m_Outputs.push_back(tensorHandle);
696 info.m_OutputTensorInfos.push_back(tensorInfo);
697
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100698 ARMNN_ASSERT_MSG(layer.GetNumInputSlots() == 1, "Output Layer should have exactly one input.");
telsoa014fcda012018-03-09 14:13:49 +0000699
telsoa01c577f2c2018-08-31 09:22:23 +0100700 // Gets the output handler from the previous node.
telsoa014fcda012018-03-09 14:13:49 +0000701 const OutputHandler& outputHandler = layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetOutputHandler();
702
703 const TensorInfo& inputTensorInfo = outputHandler.GetTensorInfo();
704 ITensorHandle* inputTensorHandle = outputHandler.GetData();
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100705 ARMNN_ASSERT_MSG(inputTensorHandle != nullptr, "Data should have been allocated.");
telsoa014fcda012018-03-09 14:13:49 +0000706
Derek Lambertif674aa02019-08-01 15:56:25 +0100707 // Try import the output tensor.
708 // Note: We can only import the output pointer if all of the following hold true:
709 // a) The imported pointer is aligned sufficiently
710 // b) The tensor has zero padding
711 // c) There is only one connection to the OutputSlot and it is to an OutputLayer.
712 // d) The output pointer is allocated via malloc. (Other types will be supported in a later release)
Ferran Balaguer83239f92019-09-19 11:49:25 +0100713 // e) m_IsExportEnabled must be set to true
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000714 bool needMemCopy = true;
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100715 if (m_NetworkProperties.m_ExportEnabled &&
716 (layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetNumConnections() == 1))
Derek Lambertif674aa02019-08-01 15:56:25 +0100717 {
Ferran Balaguer83239f92019-09-19 11:49:25 +0100718 if(layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetOwningLayer().GetType() != LayerType::Input)
Derek Lambertif674aa02019-08-01 15:56:25 +0100719 {
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100720 MemorySourceFlags importFlags = inputTensorHandle->GetImportFlags();
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100721 if (CheckFlag(importFlags, m_NetworkProperties.m_OutputSource))
Derek Lambertif674aa02019-08-01 15:56:25 +0100722 {
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000723 needMemCopy = false;
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100724 void *mem = tensorHandle->Map(false);
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100725 bool importOk = inputTensorHandle->Import(mem, m_NetworkProperties.m_OutputSource);
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100726 tensorHandle->Unmap();
Derek Lambertif674aa02019-08-01 15:56:25 +0100727
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100728 if (importOk)
729 {
730 // Insert synchronization workload
731 MemSyncQueueDescriptor syncDesc;
732 syncDesc.m_Inputs.push_back(inputTensorHandle);
733 info.m_InputTensorInfos.push_back(inputTensorInfo);
734 auto syncWorkload = std::make_unique<SyncMemGenericWorkload>(syncDesc, info);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100735 ARMNN_ASSERT_MSG(syncWorkload, "No sync workload created");
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100736 m_OutputQueue.push_back(move(syncWorkload));
Ferran Balaguerbfeb2712019-08-07 15:14:56 +0100737 }
David Monahan4f1e8e42019-09-04 09:22:10 +0100738 else
739 {
740 throw MemoryExportException("EnqueueOutput: Memory Export failed");
741 }
Derek Lambertif674aa02019-08-01 15:56:25 +0100742 }
743 }
744 }
Narumol Prangnawarat265e53e2020-10-30 16:06:55 +0000745 if (needMemCopy)
David Monahan4f1e8e42019-09-04 09:22:10 +0100746 {
Sadik Armagan23969e82020-11-18 14:17:04 +0000747 // If we got here then we didn't export the memory, so add an output workload which performs a memcopy.
748 outputQueueDescriptor.m_Inputs.push_back(inputTensorHandle);
749 info.m_InputTensorInfos.push_back(inputTensorInfo);
750
751 std::unique_ptr<IWorkload> outputWorkload =
752 std::make_unique<CopyMemGenericWorkload>(outputQueueDescriptor, info);
753 ARMNN_ASSERT_MSG(outputWorkload, "No output workload created");
754
755 std::unique_ptr<TimelineUtilityMethods> timelineUtils =
756 TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
757 if (timelineUtils)
Narumol Prangnawarataa68e012019-11-29 17:17:43 +0000758 {
Sadik Armagan23969e82020-11-18 14:17:04 +0000759 // Add Output Workload to the post-optimisation network structure
760 AddWorkloadStructure(timelineUtils, outputWorkload, layer);
761 timelineUtils->Commit();
Sadik Armagan890bf652020-09-29 15:12:36 +0100762 }
Sadik Armagan23969e82020-11-18 14:17:04 +0000763
764 m_OutputQueue.push_back(move(outputWorkload));
David Monahan4f1e8e42019-09-04 09:22:10 +0100765 }
Derek Lamberti03614f62018-10-02 15:52:46 +0100766}
767
Derek Lambertia08d29b2020-06-19 14:33:05 +0100768void LoadedNetwork::AllocateWorkingMemory(std::lock_guard<std::mutex>& lock)
Derek Lamberti03614f62018-10-02 15:52:46 +0100769{
Derek Lambertia08d29b2020-06-19 14:33:05 +0100770 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Working Memory Allocation");
771
772 // this unused parameter makes sure we can only call this function with a valid lock
773 IgnoreUnused(lock);
774
Derek Lamberti03614f62018-10-02 15:52:46 +0100775 if (m_IsWorkingMemAllocated)
776 {
777 return;
778 }
David Beck29c75de2018-10-23 13:35:58 +0100779 for (auto&& workloadFactory : m_WorkloadFactories)
780 {
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000781 IBackendInternal::IMemoryManagerSharedPtr memoryManager = workloadFactory.second.second;
782 if (memoryManager)
783 {
784 memoryManager->Acquire();
785 }
David Beck29c75de2018-10-23 13:35:58 +0100786 }
Narumol Prangnawarat11bd2612019-08-13 10:26:53 +0100787 m_TensorHandleFactoryRegistry.AquireMemory();
Derek Lamberti03614f62018-10-02 15:52:46 +0100788 m_IsWorkingMemAllocated = true;
789}
790
791void LoadedNetwork::FreeWorkingMemory()
792{
Matthew Bentham2a326b52019-03-19 10:11:01 +0000793 std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
Derek Lamberti03614f62018-10-02 15:52:46 +0100794 if (!m_IsWorkingMemAllocated)
795 {
796 return;
797 }
798 // Informs the memory managers to release memory in it's respective memory group
David Beck29c75de2018-10-23 13:35:58 +0100799 for (auto&& workloadFactory : m_WorkloadFactories)
800 {
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000801 IBackendInternal::IMemoryManagerSharedPtr memoryManager = workloadFactory.second.second;
802 if (memoryManager)
803 {
804 memoryManager->Release();
805 }
David Beck29c75de2018-10-23 13:35:58 +0100806 }
Narumol Prangnawarat11bd2612019-08-13 10:26:53 +0100807 m_TensorHandleFactoryRegistry.ReleaseMemory();
Derek Lamberti03614f62018-10-02 15:52:46 +0100808 m_IsWorkingMemAllocated = false;
telsoa014fcda012018-03-09 14:13:49 +0000809}
810
David Monahan6198fe02019-12-02 08:35:43 +0000811bool LoadedNetwork::Execute(std::unique_ptr<TimelineUtilityMethods>& timelineUtils,
812 profiling::ProfilingGuid inferenceGuid)
telsoa014fcda012018-03-09 14:13:49 +0000813{
814 bool success = true;
815
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100816 auto Fail = [&](const std::exception& error)
817 {
Derek Lamberti08446972019-11-26 16:38:31 +0000818 ARMNN_LOG(error) << "An error occurred attempting to execute a workload: " << error.what();
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100819 success = false;
820 };
821
telsoa014fcda012018-03-09 14:13:49 +0000822 try
823 {
Matthew Bentham2a326b52019-03-19 10:11:01 +0000824 std::lock_guard<std::mutex> lockGuard(m_WorkingMemMutex);
Derek Lambertia08d29b2020-06-19 14:33:05 +0100825 AllocateWorkingMemory(lockGuard);
Derek Lamberti03614f62018-10-02 15:52:46 +0100826
David Monahan6198fe02019-12-02 08:35:43 +0000827 ProfilingDynamicGuid workloadInferenceID(0);
Derek Lambertia08d29b2020-06-19 14:33:05 +0100828 auto ExecuteQueue = [&timelineUtils, &workloadInferenceID, &inferenceGuid](WorkloadQueue& queue)
telsoa014fcda012018-03-09 14:13:49 +0000829 {
Derek Lambertia08d29b2020-06-19 14:33:05 +0100830 for (auto& workload : queue)
David Monahan6198fe02019-12-02 08:35:43 +0000831 {
Derek Lambertia08d29b2020-06-19 14:33:05 +0100832 if(timelineUtils)
833 {
834 workloadInferenceID = timelineUtils->RecordWorkloadInferenceAndStartOfLifeEvent(workload->GetGuid(),
835 inferenceGuid);
836 }
837 workload->Execute();
838 if(timelineUtils)
839 {
840 timelineUtils->RecordEndOfLifeEvent(workloadInferenceID);
841 }
David Monahan6198fe02019-12-02 08:35:43 +0000842 }
Derek Lambertia08d29b2020-06-19 14:33:05 +0100843 };
Derek Lamberti03614f62018-10-02 15:52:46 +0100844
Derek Lambertia08d29b2020-06-19 14:33:05 +0100845 ExecuteQueue(m_InputQueue);
846 ExecuteQueue(m_WorkloadQueue);
847 ExecuteQueue(m_OutputQueue);
telsoa014fcda012018-03-09 14:13:49 +0000848 }
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100849 catch (const RuntimeException& error)
telsoa014fcda012018-03-09 14:13:49 +0000850 {
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100851 Fail(error);
telsoa014fcda012018-03-09 14:13:49 +0000852 }
telsoa014fcda012018-03-09 14:13:49 +0000853 catch (const std::runtime_error& error)
854 {
Aron Virginas-Tara8e06ed2018-10-19 16:46:15 +0100855 Fail(error);
telsoa014fcda012018-03-09 14:13:49 +0000856 }
857
telsoa014fcda012018-03-09 14:13:49 +0000858 return success;
859}
860
Finn Williamsf37b9702021-09-01 18:06:04 +0100861void LoadedNetwork::EnqueueInput(const ConstTensor& inputTensor,
862 ITensorHandle* inputTensorHandle)
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100863{
Finn Williamsf37b9702021-09-01 18:06:04 +0100864 MemorySourceFlags importFlags = inputTensorHandle->GetImportFlags();
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100865 if (m_NetworkProperties.m_ImportEnabled) // Try import the input tensor
866 {
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100867 if (CheckFlag(importFlags, m_NetworkProperties.m_InputSource) )
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100868 {
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100869 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100870 std::make_unique<ConstPassthroughTensorHandle>(inputTensor.GetInfo(),
Finn Williamsf37b9702021-09-01 18:06:04 +0100871 inputTensor.GetMemoryArea());
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100872 void* mem = tensorHandle->Map(false);
Finn Williamsf37b9702021-09-01 18:06:04 +0100873
874 if (inputTensorHandle->Import(mem, m_NetworkProperties.m_InputSource))
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100875 {
876 tensorHandle->Unmap();
877 return;
878 }
879 tensorHandle->Unmap();
880 throw MemoryImportException("EnqueueInput: Memory Import failed");
881 }
882 else
883 {
884 throw MemoryImportException("EnqueueInput: Memory Import failed, backend does not support Import");
885 }
886 }
887 else
888 {
889 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100890 std::make_unique<ConstPassthroughTensorHandle>(inputTensor.GetInfo(), inputTensor.GetMemoryArea());
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100891
892 auto copyFunc = [](void* dst, const void* src, size_t size)
893 {
894 memcpy(dst, src, size);
895 };
896
Finn Williamsf37b9702021-09-01 18:06:04 +0100897 CopyTensorContentsGeneric(tensorHandle.get(), inputTensorHandle, copyFunc);
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100898 }
899}
900
901void LoadedNetwork::EnqueueOutput(const BindableLayer& layer, const Tensor& outputTensor, WorkingMemHandle& handle)
902{
903 if (layer.GetType() != LayerType::Output)
904 {
905 throw InvalidArgumentException("EnqueueOutput: given layer not an OutputLayer");
906 }
907 ARMNN_ASSERT_MSG(layer.GetNumInputSlots() == 1, "Output Layer should have exactly one input.");
908
Finn Williams01097942021-04-26 12:06:34 +0100909 LayerGuid id = layer.GetGuid();
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100910 WorkingMemDescriptor descriptor = handle.GetWorkingMemDescriptor(id);
911
912 ITensorHandle* inputTensorHandle = descriptor.m_Inputs[0];
913 ARMNN_ASSERT_MSG(inputTensorHandle != nullptr, "Data should have been allocated.");
914
915 // Try import the output tensor.
916 // Note: We can only import the output pointer if all of the following hold true:
917 // a) The imported pointer is aligned sufficiently
918 // b) The tensor has zero padding
919 // c) There is only one connection to the OutputSlot and it is to an OutputLayer.
920 // d) The output pointer is allocated via malloc. (Other types will be supported in a later release)
921 // e) m_IsExportEnabled must be set to true
922 if (m_NetworkProperties.m_ExportEnabled &&
923 (layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetNumConnections() == 1))
924 {
925 if (layer.GetInputSlots()[0].GetConnectedOutputSlot()->GetOwningLayer().GetType() != LayerType::Input)
926 {
927 MemorySourceFlags importFlags = inputTensorHandle->GetImportFlags();
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100928 if (CheckFlag(importFlags, m_NetworkProperties.m_OutputSource))
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100929 {
930 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100931 std::make_unique<PassthroughTensorHandle>(outputTensor.GetInfo(),
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100932 outputTensor.GetMemoryArea());
933
934 void* mem = tensorHandle->Map(false);
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +0100935 bool importOk = inputTensorHandle->Import(mem, m_NetworkProperties.m_OutputSource);
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100936 tensorHandle->Unmap();
937
938 if (importOk)
939 {
940 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "SyncMemGeneric_Execute");
Finn Williams01097942021-04-26 12:06:34 +0100941 inputTensorHandle->Map(true);
942 inputTensorHandle->Unmap();
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100943 }
944 else
945 {
946 throw MemoryExportException("EnqueueOutput: Memory Export failed");
947 }
948 }
949 else
950 {
951 throw MemoryExportException("EnqueueOutput: Memory Export failed, backend does not support Export");
952 }
953 }
954 else
955 {
956 throw MemoryExportException("EnqueueOutput: Memory Export failed, attempting to export Input Layer");
957 }
958 }
959 else
960 {
961 auto copyFunc = [](void* dst, const void* src, size_t size)
962 {
963 memcpy(dst, src, size);
964 };
965
966 std::unique_ptr<ITensorHandle> tensorHandle =
James Conroy1f58f032021-04-27 17:13:27 +0100967 std::make_unique<PassthroughTensorHandle>(outputTensor.GetInfo(),
Finn Williams01097942021-04-26 12:06:34 +0100968 outputTensor.GetMemoryArea());
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100969
Finn Williams01097942021-04-26 12:06:34 +0100970 CopyTensorContentsGeneric(inputTensorHandle, tensorHandle.get(), copyFunc);
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100971 }
972}
973
Finn Williams01097942021-04-26 12:06:34 +0100974
975const armnn::ConstTensor GetInputTensor(const LayerBindingId layerId, const InputTensors& inputTensors)
976{
977 for (auto inputTensorPair : inputTensors)
978 {
979 LayerBindingId id = inputTensorPair.first;
980 if (id == layerId)
981 {
982 return inputTensorPair.second;
983 }
984 }
985 throw InvalidArgumentException("Input does not exist.");
986}
987
988const armnn::Tensor GetOutputTensor(const LayerBindingId layerId, const OutputTensors& outputTensors)
989{
990 for (auto outputTensorPair : outputTensors)
991 {
992 LayerBindingId id = outputTensorPair.first;
993 if (id == layerId)
994 {
995 return outputTensorPair.second;
996 }
997 }
998 throw InvalidArgumentException("Output does not exist.");
999}
1000
Finn Williamsf37b9702021-09-01 18:06:04 +01001001std::vector<ImportedInputId> LoadedNetwork::ImportInputs(const InputTensors& inputTensors)
1002{
1003 if (!m_NetworkProperties.m_ImportEnabled) // Try import the input tensor
1004 {
1005 throw MemoryImportException("ImportInputs: Memory Import failed, NetworkProperties.m_ImportEnabled");
1006 }
1007
1008 std::vector<ImportedInputId> importedInputs;
1009 Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph().TopologicalSort();
1010
1011 for (auto inputTensor : inputTensors)
1012 {
1013 auto layerBindingId = inputTensor.first;
1014 auto it = std::find_if(graph.GetInputLayers().begin(), graph.GetInputLayers().end(), [=](auto* layer)
1015 {
1016 return layer->GetBindingId() == layerBindingId;
1017 });
1018
1019 if (it == graph.GetInputLayers().end())
1020 {
1021 throw MemoryImportException("ImportInputs: Memory Import failed, backend does not support Import");
1022 }
1023
1024 const Layer* layer = *it;
1025 if (layer->GetType() != LayerType::Input)
1026 {
1027 throw InvalidArgumentException("ImportInputs: given layer not an InputLayer");
1028 }
1029
1030 const OutputSlot& outputSlot = layer->GetOutputSlots()[0];
1031
1032 ITensorHandleFactory::FactoryId factoryId = outputSlot.GetTensorHandleFactoryId();
1033 const TensorInfo& tensorInfo = outputSlot.GetTensorInfo();
1034
1035 ITensorHandleFactory* handleFactory = m_TensorHandleFactoryRegistry.GetFactory(factoryId);
1036 ARMNN_ASSERT(handleFactory);
1037
1038 m_PreImportedInputHandles.emplace_back(layerBindingId,
1039 handleFactory->CreateTensorHandle(tensorInfo, false));
1040
1041 ITensorHandle* tensorHandle = m_PreImportedInputHandles.back().m_TensorHandle.get();
1042
1043 if (!CheckFlag(tensorHandle->GetImportFlags(), m_NetworkProperties.m_InputSource))
1044 {
1045 throw MemoryImportException(
1046 fmt::format("ImportInputs: Memory Import failed, backend: {} does not support importing from source {}"
1047 , factoryId, m_NetworkProperties.m_InputSource));
1048 }
1049
1050 std::unique_ptr<ITensorHandle> passThroughTensorHandle =
1051 std::make_unique<ConstPassthroughTensorHandle>(inputTensor.second.GetInfo(),
1052 inputTensor.second.GetMemoryArea());
1053
1054 if (tensorHandle->Import(passThroughTensorHandle->Map(), m_NetworkProperties.m_InputSource))
1055 {
1056 importedInputs.push_back(m_CurImportedInputId++);
1057 passThroughTensorHandle->Unmap();
1058 }
1059 else
1060 {
1061 passThroughTensorHandle->Unmap();
1062 throw MemoryImportException("ImportInputs: Memory Import failed");
1063 }
1064 }
1065
1066 return importedInputs;
1067}
1068
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001069Status LoadedNetwork::Execute(const InputTensors& inputTensors,
1070 const OutputTensors& outputTensors,
Finn Williamsf37b9702021-09-01 18:06:04 +01001071 IWorkingMemHandle& iWorkingMemHandle,
1072 std::vector<ImportedInputId> preImportedInputs)
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001073{
1074 const Graph& graph = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
1075
1076 // Walk graph to determine the order of execution.
1077 if (graph.GetNumLayers() < 2)
1078 {
1079 ARMNN_LOG(warning) << "IRuntime::EnqueueWorkload()::Less than two nodes in graph";
1080 return Status::Failure;
1081 }
1082
Finn Williamsf37b9702021-09-01 18:06:04 +01001083 if (inputTensors.size() + preImportedInputs.size() != graph.GetNumInputs() )
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001084 {
Finn Williamsf37b9702021-09-01 18:06:04 +01001085 if (preImportedInputs.empty())
1086 {
1087 throw InvalidArgumentException("Number of inputs provided does not match network.");
1088 }
1089 else
1090 {
1091 throw InvalidArgumentException("Number of inputs + preImportedInputs provided does not match network.");
1092 }
1093 }
1094
1095 WorkingMemHandle& workingMemHandle = dynamic_cast<WorkingMemHandle&>(iWorkingMemHandle);
1096
1097 // This map is a quick way to check for duplicate or non-existing LayerBindingIds
1098 std::unordered_map<LayerBindingId, bool> validationMap = workingMemHandle.GetValidationMap();
1099 for (auto pair : inputTensors)
1100 {
1101 const LayerBindingId layerBindingId = pair.first;
1102
1103 try
1104 {
1105 bool& previouslyUsed = validationMap.at(pair.first);
1106 if (previouslyUsed)
1107 {
1108 throw InvalidArgumentException(fmt::format("Duplicate LayerbindingId: {} ", layerBindingId));
1109 }
1110 else
1111 {
1112 previouslyUsed = true;
1113 }
1114 }
1115 catch (std::out_of_range)
1116 {
1117 throw InvalidArgumentException(fmt::format("Unknown LayerBindingId id: {}", layerBindingId));
1118 }
1119 }
1120
1121 if (!preImportedInputs.empty())
1122 {
1123 const unsigned int maxPreImportedId = *std::max_element(preImportedInputs.begin(), preImportedInputs.end());
1124 if (maxPreImportedId > m_CurImportedInputId)
1125 {
1126 throw InvalidArgumentException(fmt::format("Invalid ImportedInputId: {}", maxPreImportedId));
1127 }
1128 for (ImportedInputId id : preImportedInputs)
1129 {
1130 const LayerBindingId layerBindingId = m_PreImportedInputHandles[id].m_LayerBindingId;
1131
1132 try
1133 {
1134 bool& previouslyUsed = validationMap.at(layerBindingId);
1135 if (previouslyUsed)
1136 {
1137 throw InvalidArgumentException(fmt::format("Duplicate LayerbindingId: {} ", layerBindingId));
1138 }
1139 else
1140 {
1141 previouslyUsed = true;
1142 }
1143 }
1144 catch (std::out_of_range)
1145 {
1146 throw InvalidArgumentException(fmt::format("Unknown LayerBindingId id: {}", layerBindingId));
1147 }
1148 }
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001149 }
1150
1151 std::unique_ptr<profiling::TimelineUtilityMethods> timelineUtils =
1152 profiling::TimelineUtilityMethods::GetTimelineUtils(m_ProfilingService);
1153 profiling::ProfilingGuid inferenceGuid = m_ProfilingService.GetNextGuid();
1154 if (timelineUtils)
1155 {
1156 // Add inference timeline trace if profiling is enabled.
1157 profiling::ProfilingGuid networkGuid = m_OptimizedNetwork->GetGuid();
1158 timelineUtils->CreateTypedEntity(inferenceGuid, profiling::LabelsAndEventClasses::INFERENCE_GUID);
1159 timelineUtils->CreateRelationship(profiling::ProfilingRelationshipType::RetentionLink,
1160 networkGuid,
1161 inferenceGuid,
1162 profiling::LabelsAndEventClasses::EXECUTION_OF_GUID);
1163 timelineUtils->RecordEvent(inferenceGuid, profiling::LabelsAndEventClasses::ARMNN_PROFILING_SOL_EVENT_CLASS);
1164 }
1165
1166 bool executionSucceeded = true;
1167
1168 if (timelineUtils)
1169 {
1170 // Add end of life of the inference timeline if profiling is enabled.
1171 timelineUtils->RecordEvent(inferenceGuid, profiling::LabelsAndEventClasses::ARMNN_PROFILING_EOL_EVENT_CLASS);
1172 timelineUtils->Commit();
1173 }
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001174
1175 if (!workingMemHandle.IsAllocated())
1176 {
1177 workingMemHandle.Allocate();
1178 }
1179
1180 {
1181 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareInputs");
Finn Williamsf37b9702021-09-01 18:06:04 +01001182 // Swap in the pre-imported inputs if any
1183 for (ImportedInputId id : preImportedInputs)
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001184 {
Finn Williamsf37b9702021-09-01 18:06:04 +01001185 const ImportedInputHandlePin& importedInputPin = m_PreImportedInputHandles[id];
1186
1187 const LayerBindingId layerBindingId = m_PreImportedInputHandles[id].m_LayerBindingId;
1188 ITensorHandle* preimportedHandle = importedInputPin.m_TensorHandle.get();
1189 auto inputConnections = workingMemHandle.GetInputConnections(layerBindingId);
1190 for (auto it : inputConnections)
1191 {
1192 *it = preimportedHandle;
1193 }
1194 }
1195
1196 for (auto pair : inputTensors)
1197 {
1198 EnqueueInput(pair.second, workingMemHandle.GetInputHandle(pair.first));
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001199 }
1200 }
1201
1202 auto Fail = [&](const std::exception& error)
1203 {
1204 ARMNN_LOG(error) << "An error occurred attempting to execute a workload: " << error.what();
1205 executionSucceeded = false;
1206 };
1207 profiling::ProfilingDynamicGuid workloadInferenceID(0);
1208
1209 try
1210 {
1211 for (unsigned int i = 0; i < m_WorkloadQueue.size(); ++i)
1212 {
1213 auto& workload = m_WorkloadQueue[i];
1214 if (timelineUtils)
1215 {
1216 workloadInferenceID = timelineUtils->RecordWorkloadInferenceAndStartOfLifeEvent(workload->GetGuid(),
1217 inferenceGuid);
1218 }
1219 workload->ExecuteAsync(workingMemHandle.GetWorkingMemDescriptorAt(i));
1220
1221 if (timelineUtils)
1222 {
1223 timelineUtils->RecordEndOfLifeEvent(workloadInferenceID);
1224 }
1225 }
1226 }
1227 catch (const RuntimeException& error)
1228 {
1229 Fail(error);
1230 }
1231 catch (const std::runtime_error& error)
1232 {
1233 Fail(error);
1234 }
1235 // For each output to the network, call EnqueueOutput with the data passed by the user.
1236 {
1237 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "PrepareOutputs");
Finn Williams01097942021-04-26 12:06:34 +01001238 for (const BindableLayer *outputLayer : graph.GetOutputLayers())
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001239 {
Finn Williams01097942021-04-26 12:06:34 +01001240 EnqueueOutput(*outputLayer, GetOutputTensor(outputLayer->GetBindingId(), outputTensors), workingMemHandle);
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001241 }
1242 }
Keith Davise813d672021-04-22 10:10:34 +01001243
Finn Williamsf37b9702021-09-01 18:06:04 +01001244 // Restore the workingMemHandle to its original state
1245 for (ImportedInputId id : preImportedInputs)
1246 {
1247 const LayerBindingId layerBindingId = m_PreImportedInputHandles[id].m_LayerBindingId;
1248
1249 auto inputHandle = workingMemHandle.GetInputHandle(layerBindingId);
1250 auto inputConnections = workingMemHandle.GetInputConnections(layerBindingId);
1251 for (auto it : inputConnections)
1252 {
1253 *it = inputHandle;
1254 }
1255 }
1256
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001257 return executionSucceeded ? Status::Success : Status::Failure;
1258}
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001259
1260/// Create a new unique WorkingMemHandle object. Create multiple handles if you wish to have
1261/// overlapped Execution by calling this function from different threads.
1262std::unique_ptr<IWorkingMemHandle> LoadedNetwork::CreateWorkingMemHandle(NetworkId networkId)
1263{
1264 Graph& order = m_OptimizedNetwork->pOptimizedNetworkImpl->GetGraph();
Finn Williams01097942021-04-26 12:06:34 +01001265 std::unordered_map<LayerGuid, std::vector<std::unique_ptr<ITensorHandle> > > tensorHandleMap;
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001266 std::vector<WorkingMemDescriptor> workingMemDescriptors;
1267 std::unordered_map<LayerGuid, WorkingMemDescriptor> workingMemDescriptorMap;
Finn Williams01097942021-04-26 12:06:34 +01001268 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
1269 WorkloadFactoryMap workloadFactoryMap;
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001270
Finn Williams01097942021-04-26 12:06:34 +01001271 std::vector<std::shared_ptr<IMemoryManager>> memoryManagers;
1272
1273 for (auto const& backend : m_Backends)
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001274 {
Finn Williams01097942021-04-26 12:06:34 +01001275 if (backend.second->SupportsTensorAllocatorAPI())
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001276 {
Narumol Prangnawarate5f0b242021-05-07 17:52:36 +01001277 backend.second->RegisterTensorHandleFactories(
1278 tensorHandleFactoryRegistry,
1279 static_cast<MemorySourceFlags>(m_NetworkProperties.m_InputSource),
1280 static_cast<MemorySourceFlags>(m_NetworkProperties.m_OutputSource));
Finn Williams01097942021-04-26 12:06:34 +01001281 memoryManagers.emplace_back(tensorHandleFactoryRegistry.GetMemoryManagers().back());
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001282 }
1283 else
1284 {
Finn Williams01097942021-04-26 12:06:34 +01001285 std::shared_ptr<IMemoryManager> memoryManager = backend.second->CreateMemoryManager();
1286 auto workloadFactory = backend.second->CreateWorkloadFactory(
1287 memoryManager, m_OptimizedNetwork->pOptimizedNetworkImpl->GetModelOptions());
1288
1289 workloadFactoryMap.emplace(
1290 std::make_pair(backend.first, std::make_pair(std::move(workloadFactory), memoryManager)));
1291 memoryManagers.emplace_back(memoryManager);
1292 }
1293 }
1294
1295 auto GetTensorHandle = [&](Layer* layer, const OutputSlot& outputSlot, bool isMemoryManaged)
1296 {
1297 ITensorHandleFactory::FactoryId factoryId = outputSlot.GetTensorHandleFactoryId();
1298 const TensorInfo& tensorInfo = outputSlot.GetTensorInfo();
1299
1300 if (factoryId == ITensorHandleFactory::LegacyFactoryId)
1301 {
1302 BackendId id = layer->GetBackendId();
1303 ARMNN_NO_DEPRECATE_WARN_BEGIN
1304 return workloadFactoryMap.at(id).first->CreateTensorHandle(tensorInfo, isMemoryManaged);
1305 ARMNN_NO_DEPRECATE_WARN_END
1306 }
1307 else
1308 {
1309 ITensorHandleFactory* handleFactory = tensorHandleFactoryRegistry.GetFactory(factoryId);
1310 ARMNN_ASSERT(handleFactory);
1311 return handleFactory->CreateTensorHandle(tensorInfo, isMemoryManaged);
1312 }
1313 };
1314
Finn Williamsf37b9702021-09-01 18:06:04 +01001315 struct HandleInfo
1316 {
1317 unsigned int m_ReferenceCount = 0;
1318 bool isInputLayer = false;
1319 bool isOutputLayer = false;
1320 LayerBindingId m_LayerBindingId = -1;
1321 };
1322
1323 std::vector<WorkingMemHandle::InputConnectionInfo> inputConnections;
1324 std::vector<std::pair<LayerBindingId, LayerGuid>> inputIndexes;
1325
1326 std::unordered_map<const ITensorHandle*, HandleInfo> handleReferenceCounts;
1327
1328 unsigned int workingMemDescriptorIndex = 0;
Finn Williams01097942021-04-26 12:06:34 +01001329 for (auto&& layer : order)
1330 {
1331 WorkingMemDescriptor workingMemDescriptor;
1332
1333 // Constant layers execution and management is handled during loaded network construction
1334 if (layer->GetType() == LayerType::Constant)
1335 {
1336 continue;
1337 }
1338 bool isMemoryManaged = true;
Finn Williamsf37b9702021-09-01 18:06:04 +01001339 bool isInputLayer = false;
Finn Williams01097942021-04-26 12:06:34 +01001340 // Look for the layer with 1 OutputSlot which has 1 connection and that connection is an Output Layer
1341 // If Export is enabled disable memory management so we can export, otherwise we do a copy
1342 if ((layer->GetNumOutputSlots() == 1) &&
1343 (layer->GetOutputSlots()[0].GetNumConnections() == 1) &&
1344 (layer->GetOutputSlots()[0].GetConnection(0)->GetOwningLayer().GetType() == LayerType::Output))
1345 {
1346 isMemoryManaged = !m_NetworkProperties.m_ExportEnabled;
1347 }
1348 else if (layer->GetType() == LayerType::Input || layer->GetType() == LayerType::MemImport)
1349 {
1350 // Input layers/workloads will not be executed so the descriptor is not added to workingMemDescriptors
1351 // However we will still need to manage the tensorHandle
Finn Williamsf37b9702021-09-01 18:06:04 +01001352 isInputLayer = true;
1353 isMemoryManaged = !m_NetworkProperties.m_ImportEnabled;
Finn Williams01097942021-04-26 12:06:34 +01001354 }
1355
1356 // Create a tensor handle for each output slot of a layer
1357 // Once we create it, we start managing its lifetime
1358 for (auto& slot : layer->GetOutputSlots())
1359 {
1360 tensorHandleMap[layer->GetGuid()].emplace_back(GetTensorHandle(layer, slot, isMemoryManaged));
1361 ITensorHandle* tensorHandle = tensorHandleMap[layer->GetGuid()].back().get();
1362
1363 workingMemDescriptor.m_Outputs.push_back(tensorHandle);
1364 tensorHandle->Manage();
1365 unsigned int numConnections = slot.GetNumConnections();
1366 ARMNN_ASSERT(numConnections != 0);
1367
Finn Williamsf37b9702021-09-01 18:06:04 +01001368 handleReferenceCounts[tensorHandle].m_ReferenceCount = numConnections;
1369
1370 if (isInputLayer)
1371 {
1372 handleReferenceCounts[tensorHandle].isInputLayer = true;
1373 LayerBindingId bindingId = static_cast<BindableLayer*>(layer)->GetBindingId();
1374
1375 handleReferenceCounts[tensorHandle].m_LayerBindingId = bindingId;
1376
1377 inputIndexes.emplace_back(std::make_pair(bindingId, layer->GetGuid()));
1378 }
Finn Williams01097942021-04-26 12:06:34 +01001379 }
1380 // Loop through the input slots in the same layer and decrement the reference counter associated
1381 // to each tensor handle we encounter.
1382 // Once it reaches zero, the lifetime of the tensor handle has ended, and we mark it's memory as available
1383 // so that the next tensor handle with a non overlapping lifetime can share it's memory.
1384 for (auto& slot : layer->GetInputSlots())
1385 {
1386 ARMNN_ASSERT(slot.GetConnection());
1387 auto outputSlot = slot.GetConnectedOutputSlot();
1388 auto key = outputSlot->GetOwningLayer().GetGuid();
1389
1390 // Constant layers execution and management is handled during loaded network construction
1391 auto found = m_ConstantTensorHandles.find(key);
1392 if (found != m_ConstantTensorHandles.end())
1393 {
1394 workingMemDescriptor.m_Inputs.push_back(found->second);
1395 continue;
1396 }
1397
1398 auto search = tensorHandleMap.find(key);
1399 unsigned int index = outputSlot->CalculateIndexOnOwner();
1400 ITensorHandle* inputTensorHandle = search->second[index].get();
1401 workingMemDescriptor.m_Inputs.push_back(inputTensorHandle);
Finn Williamsf37b9702021-09-01 18:06:04 +01001402
1403 HandleInfo& handleInfo = handleReferenceCounts.at(inputTensorHandle);
1404
1405 // Store the iterator to the
1406 if (handleInfo.isInputLayer)
1407 {
1408 inputConnections.emplace_back(WorkingMemHandle::InputConnectionInfo{
1409 handleInfo.m_LayerBindingId, workingMemDescriptorIndex, slot.GetSlotIndex()});
1410 }
1411
1412 --handleInfo.m_ReferenceCount;
1413 if (handleInfo.m_ReferenceCount == 0u)
Finn Williams01097942021-04-26 12:06:34 +01001414 {
1415 // Stop managing lifetime of tensor handle
1416 inputTensorHandle->Allocate();
1417 handleReferenceCounts.erase(inputTensorHandle);
1418 }
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001419 }
1420 workingMemDescriptorMap.insert({layer->GetGuid(), workingMemDescriptor});
Finn Williams01097942021-04-26 12:06:34 +01001421
1422 // Input layers/workloads will not be executed, so the descriptor is not added to workingMemDescriptors
1423 // However we will still need to manage the tensorHandle
Finn Williamsf37b9702021-09-01 18:06:04 +01001424 if (!isInputLayer)
Finn Williams01097942021-04-26 12:06:34 +01001425 {
1426 workingMemDescriptors.push_back(workingMemDescriptor);
Finn Williamsf37b9702021-09-01 18:06:04 +01001427 workingMemDescriptorIndex++;
Finn Williams01097942021-04-26 12:06:34 +01001428 }
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001429 }
Finn Williams01097942021-04-26 12:06:34 +01001430
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001431 return std::make_unique<WorkingMemHandle>(networkId,
Finn Williamsf37b9702021-09-01 18:06:04 +01001432 inputIndexes,
1433 inputConnections,
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001434 workingMemDescriptors,
Finn Williams01097942021-04-26 12:06:34 +01001435 workingMemDescriptorMap,
1436 memoryManagers,
1437 std::move(tensorHandleMap));
Mike Kelly55a8ffd2021-04-07 20:10:49 +01001438}
1439
Nattapat Chaimanowong6e948202019-03-22 14:01:46 +00001440void LoadedNetwork::RegisterDebugCallback(const DebugCallbackFunction& func)
1441{
1442 for (auto&& workloadPtr: m_WorkloadQueue)
1443 {
1444 workloadPtr.get()->RegisterDebugCallback(func);
1445 }
1446}
1447
telsoa014fcda012018-03-09 14:13:49 +00001448}