blob: 229f35b6948c13fa1777a012f4edc75020636d3e [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
Mike Kelly3c673942019-07-25 09:26:06 +010010#include <Half.hpp>
telsoa015307bc12018-03-09 13:51:08 +000011#include <Permute.hpp>
12
telsoa015307bc12018-03-09 13:51:08 +000013#include <cassert>
14#include <cinttypes>
telsoa015307bc12018-03-09 13:51:08 +000015
16using namespace android;
telsoa01ce3e84a2018-08-31 09:31:35 +010017using namespace android::hardware;
telsoa015307bc12018-03-09 13:51:08 +000018using namespace android::hidl::memory::V1_0;
19
20namespace armnn_driver
21{
22const armnn::PermutationVector g_DontPermute{};
23
24namespace
25{
26
27template <typename T>
28void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorShape& inTensorShape, const void* input,
29 void* output, const armnn::PermutationVector& mappings)
30{
31 const auto inputData = static_cast<const T*>(input);
32 const auto outputData = static_cast<T*>(output);
33
Matteo Martincigh2c444fc2019-01-07 10:18:47 +000034 armnnUtils::Permute(armnnUtils::Permuted(inTensorShape, mappings), mappings, inputData, outputData, sizeof(T));
telsoa015307bc12018-03-09 13:51:08 +000035}
36
37} // anonymous namespace
38
39void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorInfo& tensor, const void* input, void* output,
40 const armnn::PermutationVector& mappings)
41{
42 assert(tensor.GetNumDimensions() == 4U);
43
44 switch(tensor.GetDataType())
45 {
Mike Kelly3c673942019-07-25 09:26:06 +010046 case armnn::DataType::Float16:
47 SwizzleAndroidNn4dTensorToArmNn<armnn::Half>(tensor.GetShape(), input, output, mappings);
48 break;
telsoa015307bc12018-03-09 13:51:08 +000049 case armnn::DataType::Float32:
50 SwizzleAndroidNn4dTensorToArmNn<float>(tensor.GetShape(), input, output, mappings);
51 break;
52 case armnn::DataType::QuantisedAsymm8:
53 SwizzleAndroidNn4dTensorToArmNn<uint8_t>(tensor.GetShape(), input, output, mappings);
54 break;
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000055 case armnn::DataType::QuantizedSymm8PerAxis:
56 SwizzleAndroidNn4dTensorToArmNn<int8_t>(tensor.GetShape(), input, output, mappings);
57 break;
telsoa015307bc12018-03-09 13:51:08 +000058 default:
59 ALOGW("Unknown armnn::DataType for swizzling");
60 assert(0);
61 }
62}
63
64void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
65{
66 // find the location within the pool
67 assert(location.poolIndex < memPools.size());
68
surmeh01deb3bdb2018-07-05 12:06:04 +010069 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
70
71 // Type android::nn::RunTimePoolInfo has changed between Android O and Android P, where
72 // "buffer" has been made private and must be accessed via the accessor method "getBuffer".
Mike Kellyb5fdf382019-06-11 16:35:25 +010073#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q) // Use the new Android implementation.
surmeh01deb3bdb2018-07-05 12:06:04 +010074 uint8_t* memPoolBuffer = memPool.getBuffer();
75#else // Fallback to the old Android O implementation.
76 uint8_t* memPoolBuffer = memPool.buffer;
77#endif
78
79 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000080
81 return memory;
82}
83
Matthew Bentham912b3622019-05-03 15:49:14 +010084armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000085{
86 armnn::DataType type;
87
88 switch (operand.type)
89 {
Matthew Bentham912b3622019-05-03 15:49:14 +010090 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +000091 type = armnn::DataType::Float32;
92 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010093 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
telsoa015307bc12018-03-09 13:51:08 +000094 type = armnn::DataType::QuantisedAsymm8;
95 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010096 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +000097 type = armnn::DataType::Signed32;
98 break;
99 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +0100100 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +0000101 }
102
103 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
104
105 ret.SetQuantizationScale(operand.scale);
106 ret.SetQuantizationOffset(operand.zeroPoint);
107
108 return ret;
109}
110
Mike Kellyb5fdf382019-06-11 16:35:25 +0100111#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
112
113armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
114{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000115 using namespace armnn;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100116
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000117 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100118 switch (operand.type)
119 {
120 case V1_2::OperandType::TENSOR_FLOAT32:
121 type = armnn::DataType::Float32;
122 break;
Mike Kelly3c673942019-07-25 09:26:06 +0100123 case V1_2::OperandType::TENSOR_FLOAT16:
124 type = armnn::DataType::Float16;
125 break;
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000126 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
127 type = armnn::DataType::QuantizedSymm8PerAxis;
128 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100129 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
130 type = armnn::DataType::QuantisedAsymm8;
131 break;
Mike Kellyd7de1652019-11-19 09:16:00 +0000132 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
133 type = armnn::DataType::QuantisedSymm8;
134 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100135 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
136 type = armnn::DataType::QuantisedSymm16;
137 break;
138 case V1_2::OperandType::TENSOR_INT32:
139 type = armnn::DataType::Signed32;
140 break;
141 default:
142 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
143 }
144
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000145 TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
146 if (type == DataType::QuantizedSymm8PerAxis)
147 {
148 // ExtraParams is expected to be of type channelQuant
149 BOOST_ASSERT(operand.extraParams.getDiscriminator() ==
150 V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100151
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000152 auto perAxisQuantParams = operand.extraParams.channelQuant();
153
154 ret.SetQuantizationScales(perAxisQuantParams.scales);
155 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
156 }
157 else
158 {
159 ret.SetQuantizationScale(operand.scale);
160 ret.SetQuantizationOffset(operand.zeroPoint);
161 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100162
163 return ret;
164}
165
166#endif
167
Matthew Bentham912b3622019-05-03 15:49:14 +0100168std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000169{
170 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
171 toString(operand.type);
172}
173
Mike Kellyb5fdf382019-06-11 16:35:25 +0100174#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
175
176std::string GetOperandSummary(const V1_2::Operand& operand)
177{
178 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
179 toString(operand.type);
180}
181
182#endif
183
telsoa015307bc12018-03-09 13:51:08 +0000184using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
185 unsigned int elementIndex,
186 std::ofstream& fileStream);
187
188namespace
189{
190template <typename ElementType, typename PrintableType = ElementType>
191void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
192{
193 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
194 fileStream << static_cast<PrintableType>(elements[elementIndex]) << ",";
195}
196
197constexpr const char* MemoryLayoutString(const armnn::ConstTensor& tensor)
198{
199 const char* str = "";
200
201 switch (tensor.GetNumDimensions())
202 {
203 case 4: { str = "(BHWC) "; break; }
204 case 3: { str = "(HWC) "; break; }
205 case 2: { str = "(HW) "; break; }
206 default: { str = ""; break; }
207 }
208
209 return str;
210}
211} // namespace
212
213void DumpTensor(const std::string& dumpDir,
214 const std::string& requestName,
215 const std::string& tensorName,
216 const armnn::ConstTensor& tensor)
217{
218 // The dump directory must exist in advance.
219 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.dump") % dumpDir % requestName % tensorName);
220
221 std::ofstream fileStream;
222 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
223
224 if (!fileStream.good())
225 {
226 ALOGW("Could not open file %s for writing", fileName.c_str());
227 return;
228 }
229
230 DumpElementFunction dumpElementFunction = nullptr;
231
232 switch (tensor.GetDataType())
233 {
234 case armnn::DataType::Float32:
235 {
236 dumpElementFunction = &DumpTensorElement<float>;
237 break;
238 }
239 case armnn::DataType::QuantisedAsymm8:
240 {
241 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
242 break;
243 }
244 case armnn::DataType::Signed32:
245 {
246 dumpElementFunction = &DumpTensorElement<int32_t>;
247 break;
248 }
249 default:
250 {
251 dumpElementFunction = nullptr;
252 }
253 }
254
255 if (dumpElementFunction != nullptr)
256 {
257 const unsigned int numDimensions = tensor.GetNumDimensions();
258
259 const unsigned int batch = (numDimensions == 4) ? tensor.GetShape()[numDimensions - 4] : 1;
260
261 const unsigned int height = (numDimensions >= 3)
262 ? tensor.GetShape()[numDimensions - 3]
263 : (numDimensions >= 2) ? tensor.GetShape()[numDimensions - 2] : 1;
264
265 const unsigned int width = (numDimensions >= 3)
266 ? tensor.GetShape()[numDimensions - 2]
267 : (numDimensions >= 1) ? tensor.GetShape()[numDimensions - 1] : 0;
268
269 const unsigned int channels = (numDimensions >= 3) ? tensor.GetShape()[numDimensions - 1] : 1;
270
271 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
272 fileStream << "# Dimensions " << MemoryLayoutString(tensor);
273 fileStream << "[" << tensor.GetShape()[0];
274 for (unsigned int d = 1; d < numDimensions; d++)
275 {
276 fileStream << "," << tensor.GetShape()[d];
277 }
278 fileStream << "]" << std::endl;
279
280 for (unsigned int e = 0, b = 0; b < batch; ++b)
281 {
282 if (numDimensions >= 4)
283 {
284 fileStream << "# Batch " << b << std::endl;
285 }
286 for (unsigned int c = 0; c < channels; c++)
287 {
288 if (numDimensions >= 3)
289 {
290 fileStream << "# Channel " << c << std::endl;
291 }
292 for (unsigned int h = 0; h < height; h++)
293 {
294 for (unsigned int w = 0; w < width; w++, e += channels)
295 {
296 (*dumpElementFunction)(tensor, e, fileStream);
297 }
298 fileStream << std::endl;
299 }
300 e -= channels - 1;
301 if (c < channels)
302 {
303 e -= ((height * width) - 1) * channels;
304 }
305 }
306 fileStream << std::endl;
307 }
308 fileStream << std::endl;
309 }
310 else
311 {
312 fileStream << "Cannot dump tensor elements: Unsupported data type "
313 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
314 }
315
316 if (!fileStream.good())
317 {
318 ALOGW("An error occurred when writing to file %s", fileName.c_str());
319 }
320}
321
telsoa01ce3e84a2018-08-31 09:31:35 +0100322void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
323 const std::string& dumpDir,
324 armnn::NetworkId networkId,
325 const armnn::IProfiler* profiler)
326{
327 // Check if profiling is required.
328 if (!gpuProfilingEnabled)
329 {
330 return;
331 }
332
333 // The dump directory must exist in advance.
334 if (dumpDir.empty())
335 {
336 return;
337 }
338
339 BOOST_ASSERT(profiler);
340
341 // Set the name of the output profiling file.
342 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.json")
343 % dumpDir
344 % std::to_string(networkId)
345 % "profiling");
346
347 // Open the ouput file for writing.
348 std::ofstream fileStream;
349 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
350
351 if (!fileStream.good())
352 {
353 ALOGW("Could not open file %s for writing", fileName.c_str());
354 return;
355 }
356
357 // Write the profiling info to a JSON file.
358 profiler->Print(fileStream);
359}
360
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100361bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
362{
363 // Dynamic tensors have at least one 0-sized dimension
364 return outputInfo.GetNumElements() == 0u;
365}
366
telsoa015307bc12018-03-09 13:51:08 +0000367} // namespace armnn_driver