blob: c6a562f0e5174ea9b0789c5973c0f3581f49c923 [file] [log] [blame]
Narumol Prangnawarat1a268962020-07-27 15:52:13 +01001//
2// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Sadik Armagan76615a52020-08-04 14:01:05 +01005
6#include <Graph.hpp>
7#include <Network.hpp>
8
Narumol Prangnawarat1a268962020-07-27 15:52:13 +01009#include <neon/NeonTensorHandle.hpp>
10#include <neon/NeonTensorHandleFactory.hpp>
11
Sadik Armagan76615a52020-08-04 14:01:05 +010012#include <armnn/utility/PolymorphicDowncast.hpp>
13
14#include <test/GraphUtils.hpp>
Narumol Prangnawaratb8d771a2020-08-14 11:51:12 +010015#include <arm_compute/runtime/Allocator.h>
Sadik Armagan76615a52020-08-04 14:01:05 +010016
Narumol Prangnawarat1a268962020-07-27 15:52:13 +010017#include <boost/test/unit_test.hpp>
18
19BOOST_AUTO_TEST_SUITE(NeonTensorHandleTests)
20using namespace armnn;
21
22BOOST_AUTO_TEST_CASE(NeonTensorHandleGetCapabilitiesNoPadding)
23{
24 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>();
25 NeonTensorHandleFactory handleFactory(memoryManager);
26
27 INetworkPtr network(INetwork::Create());
28
29 // Add the layers
30 IConnectableLayer* input = network->AddInputLayer(0);
31 SoftmaxDescriptor descriptor;
32 descriptor.m_Beta = 1.0f;
33 IConnectableLayer* softmax = network->AddSoftmaxLayer(descriptor);
34 IConnectableLayer* output = network->AddOutputLayer(2);
35
36 // Establish connections
37 input->GetOutputSlot(0).Connect(softmax->GetInputSlot(0));
38 softmax->GetOutputSlot(0).Connect(output->GetInputSlot(0));
39
40 // No padding required for input
41 std::vector<Capability> capabilities = handleFactory.GetCapabilities(input,
42 softmax,
43 CapabilityClass::PaddingRequired);
44 BOOST_TEST(capabilities.empty());
45
46 // No padding required for Softmax
47 capabilities = handleFactory.GetCapabilities(softmax, output, CapabilityClass::PaddingRequired);
48 BOOST_TEST(capabilities.empty());
49
50 // No padding required for output
51 capabilities = handleFactory.GetCapabilities(output, nullptr, CapabilityClass::PaddingRequired);
52 BOOST_TEST(capabilities.empty());
53}
54
55BOOST_AUTO_TEST_CASE(NeonTensorHandleGetCapabilitiesPadding)
56{
57 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>();
58 NeonTensorHandleFactory handleFactory(memoryManager);
59
60 INetworkPtr network(INetwork::Create());
61
62 // Add the layers
63 IConnectableLayer* input = network->AddInputLayer(0);
64 Pooling2dDescriptor descriptor;
65 IConnectableLayer* pooling = network->AddPooling2dLayer(descriptor);
66 IConnectableLayer* output = network->AddOutputLayer(2);
67
68 // Establish connections
69 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
70 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
71
72 // No padding required for input
73 std::vector<Capability> capabilities = handleFactory.GetCapabilities(input,
74 pooling,
75 CapabilityClass::PaddingRequired);
76 BOOST_TEST(capabilities.empty());
77
78 // No padding required for output
79 capabilities = handleFactory.GetCapabilities(output, nullptr, CapabilityClass::PaddingRequired);
80 BOOST_TEST(capabilities.empty());
81
82 // Padding required for Pooling2d
83 capabilities = handleFactory.GetCapabilities(pooling, output, CapabilityClass::PaddingRequired);
84 BOOST_TEST(capabilities.size() == 1);
85 BOOST_TEST((capabilities[0].m_CapabilityClass == CapabilityClass::PaddingRequired));
86 BOOST_TEST(capabilities[0].m_Value);
87}
88
Sadik Armagan76615a52020-08-04 14:01:05 +010089BOOST_AUTO_TEST_CASE(ConcatOnXorYSubTensorsNoPaddinRequiredTest)
90{
91 armnn::INetworkPtr net(armnn::INetwork::Create());
92
93 // Set up tensor infos
94 const armnn::TensorInfo inputInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
95 const armnn::TensorInfo intermediateInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
96 const armnn::TensorInfo outputInfo = armnn::TensorInfo({2, 3, 4, 2}, armnn::DataType::Float32);
97
98 armnn::ElementwiseUnaryDescriptor descriptor(armnn::UnaryOperation::Abs);
99
100 // Create the network
101 armnn::IConnectableLayer* const input0Layer = net->AddInputLayer(0, "input_0");
102 input0Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
103 armnn::IConnectableLayer* elementwiseUnaryLayer0 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseUnary_0");
104 elementwiseUnaryLayer0->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
105 input0Layer->GetOutputSlot(0).Connect(elementwiseUnaryLayer0->GetInputSlot(0));
106
107 armnn::IConnectableLayer* const input1Layer = net->AddInputLayer(1, "input_1");
108 input1Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
109 armnn::IConnectableLayer* elementwiseUnaryLayer1 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseUnary_1");
110 elementwiseUnaryLayer1->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
111 input1Layer->GetOutputSlot(0).Connect(elementwiseUnaryLayer1->GetInputSlot(0));
112
113 std::array<armnn::TensorShape, 2> concatInputShapes = { intermediateInfo.GetShape(), intermediateInfo.GetShape() };
114 armnn::IConnectableLayer* const concatLayer = net->AddConcatLayer(armnn::CreateDescriptorForConcatenation(
115 concatInputShapes.begin(), concatInputShapes.end(), 2), "concatenation");
116 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
117 elementwiseUnaryLayer0->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
118 elementwiseUnaryLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
119
120 armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output");
121 concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
122
123 armnn::IRuntime::CreationOptions options;
124 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
125
126 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
127 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
128
129 const armnn::Graph& theGraph = static_cast<armnn::OptimizedNetwork*>(optimizedNet.get())->GetGraph();
130
131 // Load graph into runtime
132 armnn::NetworkId networkIdentifier;
133 runtime->LoadNetwork(networkIdentifier, std::move(optimizedNet));
134
135 // now check the concat how many sub-tensors it is using..
136 auto TraceSubTensorHandleAncestry = [](armnn::ITensorHandle* const subTensorHandle)
137 {
138 if (subTensorHandle && subTensorHandle->GetParent())
139 {
140 return true;
141 }
142 return false;
143 };
144
145 for (auto&& layer : theGraph)
146 {
147 if(layer->GetType() == armnn::LayerType::Concat)
148 {
149 unsigned int numberOfSubTensors = 0;
150 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
151 {
152 const armnn::OutputSlot* slot = layer->GetInputSlot(i).GetConnectedOutputSlot();
153 if (TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()))
154 {
155 ++numberOfSubTensors;
156 }
157 }
158 // sub-tensors should be supported in this configuration
159 BOOST_CHECK(numberOfSubTensors > 0);
160 }
161 }
162}
163
Narumol Prangnawaratb8d771a2020-08-14 11:51:12 +0100164BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryMemoryManaged)
165{
166 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
167 std::make_unique<arm_compute::Allocator>(),
168 BaseMemoryManager::MemoryAffinity::Offset);
169 NeonTensorHandleFactory handleFactory(memoryManager);
170 TensorInfo info({ 1, 1, 2, 1 }, DataType::Float32);
171
172 // create TensorHandle with memory managed
173 auto handle = handleFactory.CreateTensorHandle(info, true);
174 handle->Manage();
175 handle->Allocate();
176
177 memoryManager->Acquire();
178 {
179 float* buffer = reinterpret_cast<float*>(handle->Map());
180 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer
181 buffer[0] = 1.5f;
182 buffer[1] = 2.5f;
183 BOOST_CHECK(buffer[0] == 1.5f); // Memory is writable and readable
184 BOOST_CHECK(buffer[1] == 2.5f); // Memory is writable and readable
185 }
186 memoryManager->Release();
187
188 memoryManager->Acquire();
189 {
190 float* buffer = reinterpret_cast<float*>(handle->Map());
191 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer
192 buffer[0] = 3.5f;
193 buffer[1] = 4.5f;
194 BOOST_CHECK(buffer[0] == 3.5f); // Memory is writable and readable
195 BOOST_CHECK(buffer[1] == 4.5f); // Memory is writable and readable
196 }
197 memoryManager->Release();
198
199 float testPtr[2] = { 2.5f, 5.5f };
200 // Cannot import as import is disabled
201 BOOST_CHECK(!handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc));
202}
203
204BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryImport)
205{
206 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
207 std::make_unique<arm_compute::Allocator>(),
208 BaseMemoryManager::MemoryAffinity::Offset);
209 NeonTensorHandleFactory handleFactory(memoryManager);
210 TensorInfo info({ 1, 1, 2, 1 }, DataType::Float32);
211
212 // create TensorHandle without memory managed
213 auto handle = handleFactory.CreateTensorHandle(info, false);
214 handle->Manage();
215 handle->Allocate();
216 memoryManager->Acquire();
217
218 // No buffer allocated when import is enabled
219 BOOST_CHECK((PolymorphicDowncast<NeonTensorHandle*>(handle.get()))->GetTensor().buffer() == nullptr);
220
221 float testPtr[2] = { 2.5f, 5.5f };
222 // Correctly import
223 BOOST_CHECK(handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc));
224 float* buffer = reinterpret_cast<float*>(handle->Map());
225 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer after import
226 BOOST_CHECK(buffer == testPtr); // buffer is pointing to testPtr
227 // Memory is writable and readable with correct value
228 BOOST_CHECK(buffer[0] == 2.5f);
229 BOOST_CHECK(buffer[1] == 5.5f);
230 buffer[0] = 3.5f;
231 buffer[1] = 10.0f;
232 BOOST_CHECK(buffer[0] == 3.5f);
233 BOOST_CHECK(buffer[1] == 10.0f);
234 memoryManager->Release();
235}
236
Narumol Prangnawarat1a268962020-07-27 15:52:13 +0100237BOOST_AUTO_TEST_SUITE_END()