blob: 139e688dc2f922256ae44706a14f47cd6dcdb65a [file] [log] [blame]
Jan Eilersc1c872f2021-07-22 13:17:04 +01001//
2// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include <armnn/backends/ICustomAllocator.hpp>
7#include <armnn/Descriptors.hpp>
8#include <armnn/Exceptions.hpp>
9#include <armnn/INetwork.hpp>
10#include <armnn/IRuntime.hpp>
11#include <armnn/Utils.hpp>
12#include <armnn/BackendRegistry.hpp>
Francis Murtaghe8d7ccb2021-10-14 17:30:24 +010013
Jan Eilersc1c872f2021-07-22 13:17:04 +010014#include <cl/ClBackend.hpp>
Francis Murtaghb80eaff2021-08-16 11:59:53 +010015#if defined(ARMCOMPUTENEON_ENABLED)
16#include <neon/NeonBackend.hpp>
17#endif
Jan Eilersc1c872f2021-07-22 13:17:04 +010018#include <doctest/doctest.h>
Francis Murtaghe8d7ccb2021-10-14 17:30:24 +010019#include <armnn/utility/IgnoreUnused.hpp>
Jan Eilersc1c872f2021-07-22 13:17:04 +010020// Contains the OpenCl interfaces for mapping memory in the Gpu Page Tables
21// Requires the OpenCl backend to be included (GpuAcc)
22#include <arm_compute/core/CL/CLKernelLibrary.h>
23#include <CL/cl_ext.h>
24#include <arm_compute/runtime/CL/CLScheduler.h>
25
Jan Eilersc1c872f2021-07-22 13:17:04 +010026/** Sample implementation of ICustomAllocator for use with the ClBackend.
27 * Note: any memory allocated must be host accessible with write access to allow for weights and biases
28 * to be passed in. Read access is not required.. */
29class SampleClBackendCustomAllocator : public armnn::ICustomAllocator
30{
31public:
32 SampleClBackendCustomAllocator() = default;
33
Francis Murtaghe8d7ccb2021-10-14 17:30:24 +010034 void* allocate(size_t size, size_t alignment) override
Jan Eilersc1c872f2021-07-22 13:17:04 +010035 {
36 // If alignment is 0 just use the CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE for alignment
37 if (alignment == 0)
38 {
39 alignment = arm_compute::CLKernelLibrary::get().get_device().getInfo<CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE>();
40 }
41 size_t space = size + alignment + alignment;
42 auto allocatedMemPtr = std::malloc(space * sizeof(size_t));
Jan Eilersc1c872f2021-07-22 13:17:04 +010043 if (std::align(alignment, size, allocatedMemPtr, space) == nullptr)
44 {
45 throw armnn::Exception("SampleClBackendCustomAllocator::Alignment failed");
46 }
47 return allocatedMemPtr;
48 }
49
50 /** Interface to be implemented by the child class to free the allocated tensor */
Francis Murtaghe8d7ccb2021-10-14 17:30:24 +010051 void free(void* ptr) override
Jan Eilersc1c872f2021-07-22 13:17:04 +010052 {
53 std::free(ptr);
54 }
55
Francis Murtaghe8d7ccb2021-10-14 17:30:24 +010056 armnn::MemorySource GetMemorySourceType() override
Jan Eilersc1c872f2021-07-22 13:17:04 +010057 {
58 return armnn::MemorySource::Malloc;
59 }
60};
61
Francis Murtagh62573b62021-08-12 11:55:21 +010062armnn::INetworkPtr CreateTestNetwork(armnn::TensorInfo& inputTensorInfo)
Jan Eilersc1c872f2021-07-22 13:17:04 +010063{
64 using namespace armnn;
Jan Eilersc1c872f2021-07-22 13:17:04 +010065
66 armnn::FullyConnectedDescriptor fullyConnectedDesc;
67 float weightsData[] = {1.0f}; // Identity
Cathal Corbett5b8093c2021-10-22 11:12:07 +010068 TensorInfo weightsInfo(TensorShape({1, 1}), DataType::Float32, 0.0f, 0, true);
Jan Eilersc1c872f2021-07-22 13:17:04 +010069 weightsInfo.SetConstant(true);
70 armnn::ConstTensor weights(weightsInfo, weightsData);
71
Francis Murtaghbb6c6492022-02-09 15:13:38 +000072 armnn::INetworkPtr network = armnn::INetwork::Create();
73 armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
74 armnn::IConnectableLayer* const weightsLayer = network->AddConstantLayer(weights, "Weights");
75 armnn::IConnectableLayer* const fullyConnectedLayer =
76 network->AddFullyConnectedLayer(fullyConnectedDesc, "fully connected");
77 armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
78
79 inputLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(0));
80 weightsLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1));
81 fullyConnectedLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
82
83 weightsLayer->GetOutputSlot(0).SetTensorInfo(weightsInfo);
Jan Eilersc1c872f2021-07-22 13:17:04 +010084
Francis Murtagh62573b62021-08-12 11:55:21 +010085 //Set the tensors in the network.
86
Francis Murtaghbb6c6492022-02-09 15:13:38 +000087 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
Francis Murtagh62573b62021-08-12 11:55:21 +010088
89 TensorInfo outputTensorInfo(TensorShape({1, 1}), DataType::Float32);
Francis Murtaghbb6c6492022-02-09 15:13:38 +000090 fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Francis Murtagh62573b62021-08-12 11:55:21 +010091
Francis Murtaghbb6c6492022-02-09 15:13:38 +000092 return network;
Francis Murtagh62573b62021-08-12 11:55:21 +010093}
94
95TEST_SUITE("ClCustomAllocatorTests")
96{
97
98// This is a copy of the SimpleSample app modified to use a custom
99// allocator for the clbackend. It creates a FullyConnected network with a single layer
100// taking a single number as an input
101TEST_CASE("ClCustomAllocatorTest")
102{
103 using namespace armnn;
104
105 float number = 3;
106
107 // Construct ArmNN network
108 armnn::NetworkId networkIdentifier;
109
110 TensorInfo inputTensorInfo(TensorShape({1, 1}), DataType::Float32);
111
112 INetworkPtr myNetwork = CreateTestNetwork(inputTensorInfo);
Jan Eilersc1c872f2021-07-22 13:17:04 +0100113
114 // Create ArmNN runtime
115 IRuntime::CreationOptions options; // default options
116 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
117 options.m_CustomAllocatorMap = {{"GpuAcc", std::move(customAllocator)}};
118 IRuntimePtr run = IRuntime::Create(options);
119
Jan Eilersc1c872f2021-07-22 13:17:04 +0100120 // Optimise ArmNN network
121 OptimizerOptions optOptions;
122 optOptions.m_ImportEnabled = true;
123 armnn::IOptimizedNetworkPtr optNet = Optimize(*myNetwork, {"GpuAcc"}, run->GetDeviceSpec(), optOptions);
124 CHECK(optNet);
125
126 // Load graph into runtime
127 std::string ignoredErrorMessage;
128 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
129 run->LoadNetwork(networkIdentifier, std::move(optNet), ignoredErrorMessage, networkProperties);
130
131 // Creates structures for input & output
132 unsigned int numElements = inputTensorInfo.GetNumElements();
133 size_t totalBytes = numElements * sizeof(float);
134
135 const size_t alignment =
136 arm_compute::CLKernelLibrary::get().get_device().getInfo<CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE>();
137
138 void* alignedInputPtr = options.m_CustomAllocatorMap["GpuAcc"]->allocate(totalBytes, alignment);
139
140 // Input with negative values
141 auto* inputPtr = reinterpret_cast<float*>(alignedInputPtr);
142 std::fill_n(inputPtr, numElements, number);
143
144 void* alignedOutputPtr = options.m_CustomAllocatorMap["GpuAcc"]->allocate(totalBytes, alignment);
145 auto* outputPtr = reinterpret_cast<float*>(alignedOutputPtr);
146 std::fill_n(outputPtr, numElements, -10.0f);
147
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100148 armnn::TensorInfo inputTensorInfo2 = run->GetInputTensorInfo(networkIdentifier, 0);
149 inputTensorInfo2.SetConstant(true);
Jan Eilersc1c872f2021-07-22 13:17:04 +0100150 armnn::InputTensors inputTensors
151 {
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100152 {0, armnn::ConstTensor(inputTensorInfo2, alignedInputPtr)},
Jan Eilersc1c872f2021-07-22 13:17:04 +0100153 };
154 armnn::OutputTensors outputTensors
155 {
156 {0, armnn::Tensor(run->GetOutputTensorInfo(networkIdentifier, 0), alignedOutputPtr)}
157 };
158
159 // Execute network
160 run->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
161 run->UnloadNetwork(networkIdentifier);
162
163
164 // Tell the CLBackend to sync memory so we can read the output.
165 arm_compute::CLScheduler::get().sync();
166 auto* outputResult = reinterpret_cast<float*>(alignedOutputPtr);
167
168 run->UnloadNetwork(networkIdentifier);
169 CHECK(outputResult[0] == number);
170 auto& backendRegistry = armnn::BackendRegistryInstance();
171 backendRegistry.DeregisterAllocator(ClBackend::GetIdStatic());
172}
173
Francis Murtaghb80eaff2021-08-16 11:59:53 +0100174// Only run this test if NEON is enabled
175#if defined(ARMCOMPUTENEON_ENABLED)
176
Francis Murtagh62573b62021-08-12 11:55:21 +0100177TEST_CASE("ClCustomAllocatorCpuAccNegativeTest")
178{
179 using namespace armnn;
180
181 // Create ArmNN runtime
182 IRuntime::CreationOptions options; // default options
183 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
184 options.m_CustomAllocatorMap = {{"CpuAcc", std::move(customAllocator)}};
185 IRuntimePtr run = IRuntime::Create(options);
Francis Murtagh62573b62021-08-12 11:55:21 +0100186 TensorInfo inputTensorInfo(TensorShape({1, 1}), DataType::Float32);
187 INetworkPtr myNetwork = CreateTestNetwork(inputTensorInfo);
188
189 // Optimise ArmNN network
190 OptimizerOptions optOptions;
191 optOptions.m_ImportEnabled = true;
192 IOptimizedNetworkPtr optNet(nullptr, nullptr);
193 std::vector<std::string> errMessages;
194
Colm Donelan380c1a02021-08-17 00:52:23 +0100195 CHECK_THROWS_AS_MESSAGE(Optimize(*myNetwork, {"CpuAcc"}, run->GetDeviceSpec(), optOptions, errMessages),
196 armnn::InvalidArgumentException,
197 "Expected an exception as GetAvailablePreferredBackends() should be empty in Optimize().");
Francis Murtagh62573b62021-08-12 11:55:21 +0100198
199 auto& backendRegistry = armnn::BackendRegistryInstance();
Francis Murtaghb80eaff2021-08-16 11:59:53 +0100200 backendRegistry.DeregisterAllocator(NeonBackend::GetIdStatic());
Francis Murtagh62573b62021-08-12 11:55:21 +0100201}
202
Francis Murtaghb80eaff2021-08-16 11:59:53 +0100203#endif
Francis Murtagh62573b62021-08-12 11:55:21 +0100204
Colm Donelan380c1a02021-08-17 00:52:23 +0100205TEST_CASE("ClCustomAllocatorGpuAccNullptrTest")
206{
207 using namespace armnn;
208
209 // Create ArmNN runtime
210 IRuntime::CreationOptions options; // default options
211 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
212 options.m_CustomAllocatorMap = {{"GpuAcc", nullptr}};
213
214 CHECK_THROWS_AS_MESSAGE(IRuntimePtr run = IRuntime::Create(options),
215 armnn::Exception,
216 "Expected exception in RuntimeImpl::RuntimeImpl() as allocator was nullptr.");
217}
218
Francis Murtaghbb6c6492022-02-09 15:13:38 +0000219} // test suite ClCustomAllocatorTests