blob: 0211e92a66064525a98a5c0e5d816bdbec8adf22 [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beck93e48982018-09-05 13:05:09 +01003// SPDX-License-Identifier: MIT
telsoa015307bc12018-03-09 13:51:08 +00004//
5
6#define LOG_TAG "ArmnnDriver"
7
8#include "Utils.hpp"
9
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000010#include <armnnUtils/Permute.hpp>
11
telsoa015307bc12018-03-09 13:51:08 +000012#include <cassert>
13#include <cinttypes>
telsoa015307bc12018-03-09 13:51:08 +000014
15using namespace android;
telsoa01ce3e84a2018-08-31 09:31:35 +010016using namespace android::hardware;
telsoa015307bc12018-03-09 13:51:08 +000017using namespace android::hidl::memory::V1_0;
18
19namespace armnn_driver
20{
21const armnn::PermutationVector g_DontPermute{};
22
23namespace
24{
25
telsoa015307bc12018-03-09 13:51:08 +000026void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorShape& inTensorShape, const void* input,
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000027 void* output, size_t dataTypeSize, const armnn::PermutationVector& mappings)
telsoa015307bc12018-03-09 13:51:08 +000028{
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000029 assert(inTensorShape.GetNumDimensions() == 4U);
telsoa015307bc12018-03-09 13:51:08 +000030
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000031 armnnUtils::Permute(armnnUtils::Permuted(inTensorShape, mappings), mappings, input, output, dataTypeSize);
telsoa015307bc12018-03-09 13:51:08 +000032}
33
34} // anonymous namespace
35
36void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorInfo& tensor, const void* input, void* output,
37 const armnn::PermutationVector& mappings)
38{
39 assert(tensor.GetNumDimensions() == 4U);
40
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000041 armnn::DataType dataType = tensor.GetDataType();
42 switch (dataType)
telsoa015307bc12018-03-09 13:51:08 +000043 {
Mike Kelly3c673942019-07-25 09:26:06 +010044 case armnn::DataType::Float16:
telsoa015307bc12018-03-09 13:51:08 +000045 case armnn::DataType::Float32:
telsoa015307bc12018-03-09 13:51:08 +000046 case armnn::DataType::QuantisedAsymm8:
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000047 case armnn::DataType::QuantizedSymm8PerAxis:
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000048 SwizzleAndroidNn4dTensorToArmNn(tensor.GetShape(), input, output, armnn::GetDataTypeSize(dataType), mappings);
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000049 break;
telsoa015307bc12018-03-09 13:51:08 +000050 default:
51 ALOGW("Unknown armnn::DataType for swizzling");
52 assert(0);
53 }
54}
55
56void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
57{
58 // find the location within the pool
59 assert(location.poolIndex < memPools.size());
60
surmeh01deb3bdb2018-07-05 12:06:04 +010061 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
62
63 // Type android::nn::RunTimePoolInfo has changed between Android O and Android P, where
64 // "buffer" has been made private and must be accessed via the accessor method "getBuffer".
Mike Kellyb5fdf382019-06-11 16:35:25 +010065#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q) // Use the new Android implementation.
surmeh01deb3bdb2018-07-05 12:06:04 +010066 uint8_t* memPoolBuffer = memPool.getBuffer();
67#else // Fallback to the old Android O implementation.
68 uint8_t* memPoolBuffer = memPool.buffer;
69#endif
70
71 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000072
73 return memory;
74}
75
Matthew Bentham912b3622019-05-03 15:49:14 +010076armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000077{
78 armnn::DataType type;
79
80 switch (operand.type)
81 {
Matthew Bentham912b3622019-05-03 15:49:14 +010082 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +000083 type = armnn::DataType::Float32;
84 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010085 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
telsoa015307bc12018-03-09 13:51:08 +000086 type = armnn::DataType::QuantisedAsymm8;
87 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010088 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +000089 type = armnn::DataType::Signed32;
90 break;
91 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +010092 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +000093 }
94
95 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
96
97 ret.SetQuantizationScale(operand.scale);
98 ret.SetQuantizationOffset(operand.zeroPoint);
99
100 return ret;
101}
102
Mike Kellyb5fdf382019-06-11 16:35:25 +0100103#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
104
105armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
106{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000107 using namespace armnn;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100108
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000109 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100110 switch (operand.type)
111 {
112 case V1_2::OperandType::TENSOR_FLOAT32:
113 type = armnn::DataType::Float32;
114 break;
Mike Kelly3c673942019-07-25 09:26:06 +0100115 case V1_2::OperandType::TENSOR_FLOAT16:
116 type = armnn::DataType::Float16;
117 break;
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000118 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
119 type = armnn::DataType::QuantizedSymm8PerAxis;
120 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100121 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
122 type = armnn::DataType::QuantisedAsymm8;
123 break;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000124 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
FinnWilliamsArm624fe9f2019-12-06 17:12:42 +0000125 type = armnn::DataType::QSymmS8;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000126 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100127 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
128 type = armnn::DataType::QuantisedSymm16;
129 break;
130 case V1_2::OperandType::TENSOR_INT32:
131 type = armnn::DataType::Signed32;
132 break;
133 default:
134 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
135 }
136
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000137 TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
138 if (type == DataType::QuantizedSymm8PerAxis)
139 {
140 // ExtraParams is expected to be of type channelQuant
141 BOOST_ASSERT(operand.extraParams.getDiscriminator() ==
142 V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100143
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000144 auto perAxisQuantParams = operand.extraParams.channelQuant();
145
146 ret.SetQuantizationScales(perAxisQuantParams.scales);
147 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
148 }
149 else
150 {
151 ret.SetQuantizationScale(operand.scale);
152 ret.SetQuantizationOffset(operand.zeroPoint);
153 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100154
155 return ret;
156}
157
158#endif
159
Matthew Bentham912b3622019-05-03 15:49:14 +0100160std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000161{
162 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
163 toString(operand.type);
164}
165
Mike Kellyb5fdf382019-06-11 16:35:25 +0100166#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
167
168std::string GetOperandSummary(const V1_2::Operand& operand)
169{
170 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
171 toString(operand.type);
172}
173
174#endif
175
telsoa015307bc12018-03-09 13:51:08 +0000176using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
177 unsigned int elementIndex,
178 std::ofstream& fileStream);
179
180namespace
181{
182template <typename ElementType, typename PrintableType = ElementType>
183void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
184{
185 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
186 fileStream << static_cast<PrintableType>(elements[elementIndex]) << ",";
187}
188
189constexpr const char* MemoryLayoutString(const armnn::ConstTensor& tensor)
190{
191 const char* str = "";
192
193 switch (tensor.GetNumDimensions())
194 {
195 case 4: { str = "(BHWC) "; break; }
196 case 3: { str = "(HWC) "; break; }
197 case 2: { str = "(HW) "; break; }
198 default: { str = ""; break; }
199 }
200
201 return str;
202}
203} // namespace
204
205void DumpTensor(const std::string& dumpDir,
206 const std::string& requestName,
207 const std::string& tensorName,
208 const armnn::ConstTensor& tensor)
209{
210 // The dump directory must exist in advance.
211 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.dump") % dumpDir % requestName % tensorName);
212
213 std::ofstream fileStream;
214 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
215
216 if (!fileStream.good())
217 {
218 ALOGW("Could not open file %s for writing", fileName.c_str());
219 return;
220 }
221
222 DumpElementFunction dumpElementFunction = nullptr;
223
224 switch (tensor.GetDataType())
225 {
226 case armnn::DataType::Float32:
227 {
228 dumpElementFunction = &DumpTensorElement<float>;
229 break;
230 }
231 case armnn::DataType::QuantisedAsymm8:
232 {
233 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
234 break;
235 }
236 case armnn::DataType::Signed32:
237 {
238 dumpElementFunction = &DumpTensorElement<int32_t>;
239 break;
240 }
241 default:
242 {
243 dumpElementFunction = nullptr;
244 }
245 }
246
247 if (dumpElementFunction != nullptr)
248 {
249 const unsigned int numDimensions = tensor.GetNumDimensions();
250
251 const unsigned int batch = (numDimensions == 4) ? tensor.GetShape()[numDimensions - 4] : 1;
252
253 const unsigned int height = (numDimensions >= 3)
254 ? tensor.GetShape()[numDimensions - 3]
255 : (numDimensions >= 2) ? tensor.GetShape()[numDimensions - 2] : 1;
256
257 const unsigned int width = (numDimensions >= 3)
258 ? tensor.GetShape()[numDimensions - 2]
259 : (numDimensions >= 1) ? tensor.GetShape()[numDimensions - 1] : 0;
260
261 const unsigned int channels = (numDimensions >= 3) ? tensor.GetShape()[numDimensions - 1] : 1;
262
263 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
264 fileStream << "# Dimensions " << MemoryLayoutString(tensor);
265 fileStream << "[" << tensor.GetShape()[0];
266 for (unsigned int d = 1; d < numDimensions; d++)
267 {
268 fileStream << "," << tensor.GetShape()[d];
269 }
270 fileStream << "]" << std::endl;
271
272 for (unsigned int e = 0, b = 0; b < batch; ++b)
273 {
274 if (numDimensions >= 4)
275 {
276 fileStream << "# Batch " << b << std::endl;
277 }
278 for (unsigned int c = 0; c < channels; c++)
279 {
280 if (numDimensions >= 3)
281 {
282 fileStream << "# Channel " << c << std::endl;
283 }
284 for (unsigned int h = 0; h < height; h++)
285 {
286 for (unsigned int w = 0; w < width; w++, e += channels)
287 {
288 (*dumpElementFunction)(tensor, e, fileStream);
289 }
290 fileStream << std::endl;
291 }
292 e -= channels - 1;
293 if (c < channels)
294 {
295 e -= ((height * width) - 1) * channels;
296 }
297 }
298 fileStream << std::endl;
299 }
300 fileStream << std::endl;
301 }
302 else
303 {
304 fileStream << "Cannot dump tensor elements: Unsupported data type "
305 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
306 }
307
308 if (!fileStream.good())
309 {
310 ALOGW("An error occurred when writing to file %s", fileName.c_str());
311 }
312}
313
telsoa01ce3e84a2018-08-31 09:31:35 +0100314void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
315 const std::string& dumpDir,
316 armnn::NetworkId networkId,
317 const armnn::IProfiler* profiler)
318{
319 // Check if profiling is required.
320 if (!gpuProfilingEnabled)
321 {
322 return;
323 }
324
325 // The dump directory must exist in advance.
326 if (dumpDir.empty())
327 {
328 return;
329 }
330
331 BOOST_ASSERT(profiler);
332
333 // Set the name of the output profiling file.
334 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.json")
335 % dumpDir
336 % std::to_string(networkId)
337 % "profiling");
338
339 // Open the ouput file for writing.
340 std::ofstream fileStream;
341 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
342
343 if (!fileStream.good())
344 {
345 ALOGW("Could not open file %s for writing", fileName.c_str());
346 return;
347 }
348
349 // Write the profiling info to a JSON file.
350 profiler->Print(fileStream);
351}
352
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100353bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
354{
355 // Dynamic tensors have at least one 0-sized dimension
356 return outputInfo.GetNumElements() == 0u;
357}
358
telsoa015307bc12018-03-09 13:51:08 +0000359} // namespace armnn_driver