blob: 86e1182703386c5e922d69ea3a68bffb10f47f8b [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>
13#include <cl/ClBackend.hpp>
14
15#include <doctest/doctest.h>
16
17// Contains the OpenCl interfaces for mapping memory in the Gpu Page Tables
18// Requires the OpenCl backend to be included (GpuAcc)
19#include <arm_compute/core/CL/CLKernelLibrary.h>
20#include <CL/cl_ext.h>
21#include <arm_compute/runtime/CL/CLScheduler.h>
22
Jan Eilersc1c872f2021-07-22 13:17:04 +010023/** Sample implementation of ICustomAllocator for use with the ClBackend.
24 * Note: any memory allocated must be host accessible with write access to allow for weights and biases
25 * to be passed in. Read access is not required.. */
26class SampleClBackendCustomAllocator : public armnn::ICustomAllocator
27{
28public:
29 SampleClBackendCustomAllocator() = default;
30
31 void* allocate(size_t size, size_t alignment)
32 {
33 // If alignment is 0 just use the CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE for alignment
34 if (alignment == 0)
35 {
36 alignment = arm_compute::CLKernelLibrary::get().get_device().getInfo<CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE>();
37 }
38 size_t space = size + alignment + alignment;
39 auto allocatedMemPtr = std::malloc(space * sizeof(size_t));
40
41 if (std::align(alignment, size, allocatedMemPtr, space) == nullptr)
42 {
43 throw armnn::Exception("SampleClBackendCustomAllocator::Alignment failed");
44 }
45 return allocatedMemPtr;
46 }
47
48 /** Interface to be implemented by the child class to free the allocated tensor */
49 void free(void* ptr)
50 {
51 std::free(ptr);
52 }
53
54 armnn::MemorySource GetMemorySourceType()
55 {
56 return armnn::MemorySource::Malloc;
57 }
58};
59
Francis Murtagh62573b62021-08-12 11:55:21 +010060armnn::INetworkPtr CreateTestNetwork(armnn::TensorInfo& inputTensorInfo)
Jan Eilersc1c872f2021-07-22 13:17:04 +010061{
62 using namespace armnn;
Jan Eilersc1c872f2021-07-22 13:17:04 +010063 INetworkPtr myNetwork = INetwork::Create();
64
65 armnn::FullyConnectedDescriptor fullyConnectedDesc;
66 float weightsData[] = {1.0f}; // Identity
67 TensorInfo weightsInfo(TensorShape({1, 1}), DataType::Float32);
68 weightsInfo.SetConstant(true);
69 armnn::ConstTensor weights(weightsInfo, weightsData);
70
71 ARMNN_NO_DEPRECATE_WARN_BEGIN
72 IConnectableLayer* fullyConnected = myNetwork->AddFullyConnectedLayer(fullyConnectedDesc,
73 weights,
74 EmptyOptional(),
75 "fully connected");
76 ARMNN_NO_DEPRECATE_WARN_END
77 IConnectableLayer* InputLayer = myNetwork->AddInputLayer(0);
78 IConnectableLayer* OutputLayer = myNetwork->AddOutputLayer(0);
79 InputLayer->GetOutputSlot(0).Connect(fullyConnected->GetInputSlot(0));
80 fullyConnected->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
81
Francis Murtagh62573b62021-08-12 11:55:21 +010082 //Set the tensors in the network.
83
84 InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
85
86 TensorInfo outputTensorInfo(TensorShape({1, 1}), DataType::Float32);
87 fullyConnected->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
88
89 return myNetwork;
90}
91
92TEST_SUITE("ClCustomAllocatorTests")
93{
94
95// This is a copy of the SimpleSample app modified to use a custom
96// allocator for the clbackend. It creates a FullyConnected network with a single layer
97// taking a single number as an input
98TEST_CASE("ClCustomAllocatorTest")
99{
100 using namespace armnn;
101
102 float number = 3;
103
104 // Construct ArmNN network
105 armnn::NetworkId networkIdentifier;
106
107 TensorInfo inputTensorInfo(TensorShape({1, 1}), DataType::Float32);
108
109 INetworkPtr myNetwork = CreateTestNetwork(inputTensorInfo);
Jan Eilersc1c872f2021-07-22 13:17:04 +0100110
111 // Create ArmNN runtime
112 IRuntime::CreationOptions options; // default options
113 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
114 options.m_CustomAllocatorMap = {{"GpuAcc", std::move(customAllocator)}};
115 IRuntimePtr run = IRuntime::Create(options);
116
Jan Eilersc1c872f2021-07-22 13:17:04 +0100117 // Optimise ArmNN network
118 OptimizerOptions optOptions;
119 optOptions.m_ImportEnabled = true;
120 armnn::IOptimizedNetworkPtr optNet = Optimize(*myNetwork, {"GpuAcc"}, run->GetDeviceSpec(), optOptions);
121 CHECK(optNet);
122
123 // Load graph into runtime
124 std::string ignoredErrorMessage;
125 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
126 run->LoadNetwork(networkIdentifier, std::move(optNet), ignoredErrorMessage, networkProperties);
127
128 // Creates structures for input & output
129 unsigned int numElements = inputTensorInfo.GetNumElements();
130 size_t totalBytes = numElements * sizeof(float);
131
132 const size_t alignment =
133 arm_compute::CLKernelLibrary::get().get_device().getInfo<CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE>();
134
135 void* alignedInputPtr = options.m_CustomAllocatorMap["GpuAcc"]->allocate(totalBytes, alignment);
136
137 // Input with negative values
138 auto* inputPtr = reinterpret_cast<float*>(alignedInputPtr);
139 std::fill_n(inputPtr, numElements, number);
140
141 void* alignedOutputPtr = options.m_CustomAllocatorMap["GpuAcc"]->allocate(totalBytes, alignment);
142 auto* outputPtr = reinterpret_cast<float*>(alignedOutputPtr);
143 std::fill_n(outputPtr, numElements, -10.0f);
144
145 armnn::InputTensors inputTensors
146 {
147 {0, armnn::ConstTensor(run->GetInputTensorInfo(networkIdentifier, 0), alignedInputPtr)},
148 };
149 armnn::OutputTensors outputTensors
150 {
151 {0, armnn::Tensor(run->GetOutputTensorInfo(networkIdentifier, 0), alignedOutputPtr)}
152 };
153
154 // Execute network
155 run->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
156 run->UnloadNetwork(networkIdentifier);
157
158
159 // Tell the CLBackend to sync memory so we can read the output.
160 arm_compute::CLScheduler::get().sync();
161 auto* outputResult = reinterpret_cast<float*>(alignedOutputPtr);
162
163 run->UnloadNetwork(networkIdentifier);
164 CHECK(outputResult[0] == number);
165 auto& backendRegistry = armnn::BackendRegistryInstance();
166 backendRegistry.DeregisterAllocator(ClBackend::GetIdStatic());
167}
168
Francis Murtagh62573b62021-08-12 11:55:21 +0100169TEST_CASE("ClCustomAllocatorCpuAccNegativeTest")
170{
171 using namespace armnn;
172
173 // Create ArmNN runtime
174 IRuntime::CreationOptions options; // default options
175 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
176 options.m_CustomAllocatorMap = {{"CpuAcc", std::move(customAllocator)}};
177 IRuntimePtr run = IRuntime::Create(options);
178
179 TensorInfo inputTensorInfo(TensorShape({1, 1}), DataType::Float32);
180 INetworkPtr myNetwork = CreateTestNetwork(inputTensorInfo);
181
182 // Optimise ArmNN network
183 OptimizerOptions optOptions;
184 optOptions.m_ImportEnabled = true;
185 IOptimizedNetworkPtr optNet(nullptr, nullptr);
186 std::vector<std::string> errMessages;
187
188 try
189 {
190 optNet = Optimize(*myNetwork, {"CpuAcc"}, run->GetDeviceSpec(), optOptions, errMessages);
191 FAIL("Should have thrown an exception as GetAvailablePreferredBackends() should be empty in Optimize().");
192 }
193 catch (const armnn::InvalidArgumentException& e)
194 {
195 // Different exceptions are thrown on different backends
196 }
197 CHECK(errMessages.size() > 0);
198
199 auto& backendRegistry = armnn::BackendRegistryInstance();
200 backendRegistry.DeregisterAllocator(ClBackend::GetIdStatic());
201}
202
203TEST_CASE("ClCustomAllocatorGpuAccNullptrTest")
204{
205 using namespace armnn;
206
207 // Create ArmNN runtime
208 IRuntime::CreationOptions options; // default options
209 auto customAllocator = std::make_shared<SampleClBackendCustomAllocator>();
210 options.m_CustomAllocatorMap = {{"GpuAcc", nullptr}};
211
212 try
213 {
214 IRuntimePtr run = IRuntime::Create(options);
215 FAIL("Should have thrown an exception in RuntimeImpl::RuntimeImpl().");
216 }
217 catch (const armnn::Exception& e)
218 {
219 // Caught successfully
220 }
221
222 auto& backendRegistry = armnn::BackendRegistryInstance();
223 backendRegistry.DeregisterAllocator(ClBackend::GetIdStatic());
224}
225
Jan Eilersc1c872f2021-07-22 13:17:04 +0100226} // test suite ClCustomAllocatorTests