blob: 8a17b532a222f0938c82f37d753acfd0da411023 [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"
Jim Flynnf2e175c2019-12-12 15:11:30 +00009#include "Half.hpp"
telsoa015307bc12018-03-09 13:51:08 +000010
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000011#include <armnnUtils/Permute.hpp>
12
Derek Lambertid00ad912020-01-22 15:55:16 +000013#include <armnn/Utils.hpp>
14
telsoa015307bc12018-03-09 13:51:08 +000015#include <cassert>
Jim Flynn829ad302019-12-13 14:43:24 +000016#include <cerrno>
telsoa015307bc12018-03-09 13:51:08 +000017#include <cinttypes>
Jim Flynn829ad302019-12-13 14:43:24 +000018#include <sstream>
19#include <cstdio>
20#include <time.h>
21
22
telsoa015307bc12018-03-09 13:51:08 +000023
24using namespace android;
telsoa01ce3e84a2018-08-31 09:31:35 +010025using namespace android::hardware;
telsoa015307bc12018-03-09 13:51:08 +000026using namespace android::hidl::memory::V1_0;
27
28namespace armnn_driver
29{
30const armnn::PermutationVector g_DontPermute{};
31
32namespace
33{
34
telsoa015307bc12018-03-09 13:51:08 +000035void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorShape& inTensorShape, const void* input,
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000036 void* output, size_t dataTypeSize, const armnn::PermutationVector& mappings)
telsoa015307bc12018-03-09 13:51:08 +000037{
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000038 assert(inTensorShape.GetNumDimensions() == 4U);
telsoa015307bc12018-03-09 13:51:08 +000039
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000040 armnnUtils::Permute(armnnUtils::Permuted(inTensorShape, mappings), mappings, input, output, dataTypeSize);
telsoa015307bc12018-03-09 13:51:08 +000041}
42
43} // anonymous namespace
44
45void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorInfo& tensor, const void* input, void* output,
46 const armnn::PermutationVector& mappings)
47{
48 assert(tensor.GetNumDimensions() == 4U);
49
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000050 armnn::DataType dataType = tensor.GetDataType();
51 switch (dataType)
telsoa015307bc12018-03-09 13:51:08 +000052 {
Mike Kelly3c673942019-07-25 09:26:06 +010053 case armnn::DataType::Float16:
telsoa015307bc12018-03-09 13:51:08 +000054 case armnn::DataType::Float32:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000055 case armnn::DataType::QAsymmU8:
Derek Lambertid00ad912020-01-22 15:55:16 +000056 case armnn::DataType::QSymmS8:
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000057 SwizzleAndroidNn4dTensorToArmNn(tensor.GetShape(), input, output, armnn::GetDataTypeSize(dataType), mappings);
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000058 break;
telsoa015307bc12018-03-09 13:51:08 +000059 default:
60 ALOGW("Unknown armnn::DataType for swizzling");
61 assert(0);
62 }
63}
64
65void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
66{
67 // find the location within the pool
68 assert(location.poolIndex < memPools.size());
69
surmeh01deb3bdb2018-07-05 12:06:04 +010070 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
71
surmeh01deb3bdb2018-07-05 12:06:04 +010072 uint8_t* memPoolBuffer = memPool.getBuffer();
surmeh01deb3bdb2018-07-05 12:06:04 +010073
74 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000075
76 return memory;
77}
78
Matthew Bentham912b3622019-05-03 15:49:14 +010079armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000080{
81 armnn::DataType type;
82
83 switch (operand.type)
84 {
Matthew Bentham912b3622019-05-03 15:49:14 +010085 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +000086 type = armnn::DataType::Float32;
87 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010088 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000089 type = armnn::DataType::QAsymmU8;
telsoa015307bc12018-03-09 13:51:08 +000090 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010091 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +000092 type = armnn::DataType::Signed32;
93 break;
94 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +010095 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +000096 }
97
98 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
99
100 ret.SetQuantizationScale(operand.scale);
101 ret.SetQuantizationOffset(operand.zeroPoint);
102
103 return ret;
104}
105
Kevin May42477c12020-03-26 13:34:14 +0000106#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)// Using ::android::hardware::neuralnetworks::V1_2
Mike Kellyb5fdf382019-06-11 16:35:25 +0100107
108armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
109{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000110 using namespace armnn;
Derek Lambertid00ad912020-01-22 15:55:16 +0000111 bool perChannel = false;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100112
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000113 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100114 switch (operand.type)
115 {
Sadik Armagan793a70c2020-03-19 13:54:04 +0000116 case V1_2::OperandType::TENSOR_BOOL8:
117 type = armnn::DataType::Boolean;
118 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100119 case V1_2::OperandType::TENSOR_FLOAT32:
120 type = armnn::DataType::Float32;
121 break;
Mike Kelly3c673942019-07-25 09:26:06 +0100122 case V1_2::OperandType::TENSOR_FLOAT16:
123 type = armnn::DataType::Float16;
124 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100125 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000126 type = armnn::DataType::QAsymmU8;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100127 break;
Derek Lambertid00ad912020-01-22 15:55:16 +0000128 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
129 perChannel=true;
130 ARMNN_FALLTHROUGH;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000131 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
FinnWilliamsArm624fe9f2019-12-06 17:12:42 +0000132 type = armnn::DataType::QSymmS8;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000133 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100134 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000135 type = armnn::DataType::QSymmS16;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100136 break;
137 case V1_2::OperandType::TENSOR_INT32:
138 type = armnn::DataType::Signed32;
139 break;
140 default:
141 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
142 }
143
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000144 TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
Derek Lambertid00ad912020-01-22 15:55:16 +0000145 if (perChannel)
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000146 {
147 // ExtraParams is expected to be of type channelQuant
148 BOOST_ASSERT(operand.extraParams.getDiscriminator() ==
149 V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100150
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000151 auto perAxisQuantParams = operand.extraParams.channelQuant();
152
153 ret.SetQuantizationScales(perAxisQuantParams.scales);
154 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
155 }
156 else
157 {
158 ret.SetQuantizationScale(operand.scale);
159 ret.SetQuantizationOffset(operand.zeroPoint);
160 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100161
162 return ret;
163}
164
165#endif
166
Kevin May42477c12020-03-26 13:34:14 +0000167#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
168
169armnn::TensorInfo GetTensorInfoForOperand(const V1_3::Operand& operand)
170{
171 using namespace armnn;
172 bool perChannel = false;
173
174 DataType type;
175 switch (operand.type)
176 {
177 case V1_3::OperandType::TENSOR_FLOAT32:
178 type = armnn::DataType::Float32;
179 break;
180 case V1_3::OperandType::TENSOR_FLOAT16:
181 type = armnn::DataType::Float16;
182 break;
183 case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
184 type = armnn::DataType::QAsymmU8;
185 break;
186 case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
187 perChannel=true;
188 ARMNN_FALLTHROUGH;
189 case V1_3::OperandType::TENSOR_QUANT8_SYMM:
190 type = armnn::DataType::QSymmS8;
191 break;
192 case V1_3::OperandType::TENSOR_QUANT16_SYMM:
193 type = armnn::DataType::QSymmS16;
194 break;
195 case V1_3::OperandType::TENSOR_INT32:
196 type = armnn::DataType::Signed32;
197 break;
198 case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
199 type = armnn::DataType::QAsymmS8;
200 break;
201 default:
202 throw UnsupportedOperand<V1_3::OperandType>(operand.type);
203 }
204
205 TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
206 if (perChannel)
207 {
208 // ExtraParams is expected to be of type channelQuant
209 BOOST_ASSERT(operand.extraParams.getDiscriminator() ==
210 V1_3::Operand::ExtraParams::hidl_discriminator::channelQuant);
211
212 auto perAxisQuantParams = operand.extraParams.channelQuant();
213
214 ret.SetQuantizationScales(perAxisQuantParams.scales);
215 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
216 }
217 else
218 {
219 ret.SetQuantizationScale(operand.scale);
220 ret.SetQuantizationOffset(operand.zeroPoint);
221 }
222
223 return ret;
224}
225
226#endif
227
Matthew Bentham912b3622019-05-03 15:49:14 +0100228std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000229{
230 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
231 toString(operand.type);
232}
233
Kevin May42477c12020-03-26 13:34:14 +0000234#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3) // Using ::android::hardware::neuralnetworks::V1_2
Mike Kellyb5fdf382019-06-11 16:35:25 +0100235
236std::string GetOperandSummary(const V1_2::Operand& operand)
237{
238 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
239 toString(operand.type);
240}
241
242#endif
243
Kevin May42477c12020-03-26 13:34:14 +0000244#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
245
246std::string GetOperandSummary(const V1_3::Operand& operand)
247{
248 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
249 toString(operand.type);
250}
251
252#endif
253
telsoa015307bc12018-03-09 13:51:08 +0000254using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
255 unsigned int elementIndex,
256 std::ofstream& fileStream);
257
258namespace
259{
260template <typename ElementType, typename PrintableType = ElementType>
261void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
262{
263 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
264 fileStream << static_cast<PrintableType>(elements[elementIndex]) << ",";
265}
266
267constexpr const char* MemoryLayoutString(const armnn::ConstTensor& tensor)
268{
269 const char* str = "";
270
271 switch (tensor.GetNumDimensions())
272 {
273 case 4: { str = "(BHWC) "; break; }
274 case 3: { str = "(HWC) "; break; }
275 case 2: { str = "(HW) "; break; }
276 default: { str = ""; break; }
277 }
278
279 return str;
280}
281} // namespace
282
283void DumpTensor(const std::string& dumpDir,
284 const std::string& requestName,
285 const std::string& tensorName,
286 const armnn::ConstTensor& tensor)
287{
288 // The dump directory must exist in advance.
289 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.dump") % dumpDir % requestName % tensorName);
290
291 std::ofstream fileStream;
292 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
293
294 if (!fileStream.good())
295 {
296 ALOGW("Could not open file %s for writing", fileName.c_str());
297 return;
298 }
299
300 DumpElementFunction dumpElementFunction = nullptr;
301
302 switch (tensor.GetDataType())
303 {
304 case armnn::DataType::Float32:
305 {
306 dumpElementFunction = &DumpTensorElement<float>;
307 break;
308 }
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000309 case armnn::DataType::QAsymmU8:
telsoa015307bc12018-03-09 13:51:08 +0000310 {
311 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
312 break;
313 }
314 case armnn::DataType::Signed32:
315 {
316 dumpElementFunction = &DumpTensorElement<int32_t>;
317 break;
318 }
Jim Flynnf2e175c2019-12-12 15:11:30 +0000319 case armnn::DataType::Float16:
320 {
321 dumpElementFunction = &DumpTensorElement<armnn::Half>;
322 break;
323 }
telsoa015307bc12018-03-09 13:51:08 +0000324 default:
325 {
326 dumpElementFunction = nullptr;
327 }
328 }
329
330 if (dumpElementFunction != nullptr)
331 {
332 const unsigned int numDimensions = tensor.GetNumDimensions();
333
334 const unsigned int batch = (numDimensions == 4) ? tensor.GetShape()[numDimensions - 4] : 1;
335
336 const unsigned int height = (numDimensions >= 3)
337 ? tensor.GetShape()[numDimensions - 3]
338 : (numDimensions >= 2) ? tensor.GetShape()[numDimensions - 2] : 1;
339
340 const unsigned int width = (numDimensions >= 3)
341 ? tensor.GetShape()[numDimensions - 2]
342 : (numDimensions >= 1) ? tensor.GetShape()[numDimensions - 1] : 0;
343
344 const unsigned int channels = (numDimensions >= 3) ? tensor.GetShape()[numDimensions - 1] : 1;
345
346 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
347 fileStream << "# Dimensions " << MemoryLayoutString(tensor);
348 fileStream << "[" << tensor.GetShape()[0];
349 for (unsigned int d = 1; d < numDimensions; d++)
350 {
351 fileStream << "," << tensor.GetShape()[d];
352 }
353 fileStream << "]" << std::endl;
354
355 for (unsigned int e = 0, b = 0; b < batch; ++b)
356 {
357 if (numDimensions >= 4)
358 {
359 fileStream << "# Batch " << b << std::endl;
360 }
361 for (unsigned int c = 0; c < channels; c++)
362 {
363 if (numDimensions >= 3)
364 {
365 fileStream << "# Channel " << c << std::endl;
366 }
367 for (unsigned int h = 0; h < height; h++)
368 {
369 for (unsigned int w = 0; w < width; w++, e += channels)
370 {
371 (*dumpElementFunction)(tensor, e, fileStream);
372 }
373 fileStream << std::endl;
374 }
375 e -= channels - 1;
376 if (c < channels)
377 {
378 e -= ((height * width) - 1) * channels;
379 }
380 }
381 fileStream << std::endl;
382 }
383 fileStream << std::endl;
384 }
385 else
386 {
387 fileStream << "Cannot dump tensor elements: Unsupported data type "
388 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
389 }
390
391 if (!fileStream.good())
392 {
393 ALOGW("An error occurred when writing to file %s", fileName.c_str());
394 }
395}
396
telsoa01ce3e84a2018-08-31 09:31:35 +0100397void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
398 const std::string& dumpDir,
399 armnn::NetworkId networkId,
400 const armnn::IProfiler* profiler)
401{
402 // Check if profiling is required.
403 if (!gpuProfilingEnabled)
404 {
405 return;
406 }
407
408 // The dump directory must exist in advance.
409 if (dumpDir.empty())
410 {
411 return;
412 }
413
414 BOOST_ASSERT(profiler);
415
416 // Set the name of the output profiling file.
417 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.json")
418 % dumpDir
419 % std::to_string(networkId)
420 % "profiling");
421
422 // Open the ouput file for writing.
423 std::ofstream fileStream;
424 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
425
426 if (!fileStream.good())
427 {
428 ALOGW("Could not open file %s for writing", fileName.c_str());
429 return;
430 }
431
432 // Write the profiling info to a JSON file.
433 profiler->Print(fileStream);
434}
435
Jim Flynn829ad302019-12-13 14:43:24 +0000436std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
437 const std::string& dumpDir)
438{
439 std::string fileName;
440 // The dump directory must exist in advance.
441 if (dumpDir.empty())
442 {
443 return fileName;
444 }
445
446 std::string timestamp = GetFileTimestamp();
447 if (timestamp.empty())
448 {
449 return fileName;
450 }
451
452 // Set the name of the output .dot file.
453 fileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
454 % dumpDir
455 % timestamp);
456
457 ALOGV("Exporting the optimized network graph to file: %s", fileName.c_str());
458
459 // Write the network graph to a dot file.
460 std::ofstream fileStream;
461 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
462
463 if (!fileStream.good())
464 {
465 ALOGW("Could not open file %s for writing", fileName.c_str());
466 return fileName;
467 }
468
469 if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success)
470 {
471 ALOGW("An error occurred when writing to file %s", fileName.c_str());
472 }
473 return fileName;
474}
475
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100476bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
477{
478 // Dynamic tensors have at least one 0-sized dimension
479 return outputInfo.GetNumElements() == 0u;
480}
481
Jim Flynn829ad302019-12-13 14:43:24 +0000482std::string GetFileTimestamp()
483{
484 // used to get a timestamp to name diagnostic files (the ArmNN serialized graph
485 // and getSupportedOperations.txt files)
486 timespec ts;
487 int iRet = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
488 std::stringstream ss;
489 if (iRet == 0)
490 {
491 ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec);
492 }
493 else
494 {
495 ALOGW("clock_gettime failed with errno %s : %s", std::to_string(errno).c_str(), std::strerror(errno));
496 }
497 return ss.str();
498}
499
500void RenameGraphDotFile(const std::string& oldName, const std::string& dumpDir, const armnn::NetworkId networkId)
501{
502 if (dumpDir.empty())
503 {
504 return;
505 }
506 if (oldName.empty())
507 {
508 return;
509 }
510 const std::string newFileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
511 % dumpDir
512 % std::to_string(networkId));
513 int iRet = rename(oldName.c_str(), newFileName.c_str());
514 if (iRet != 0)
515 {
516 std::stringstream ss;
517 ss << "rename of [" << oldName << "] to [" << newFileName << "] failed with errno " << std::to_string(errno)
518 << " : " << std::strerror(errno);
519 ALOGW(ss.str().c_str());
520 }
521}
522
Kevin May42477c12020-03-26 13:34:14 +0000523void CommitPools(std::vector<::android::nn::RunTimePoolInfo>& memPools)
524{
525 if (memPools.empty())
526 {
527 return;
528 }
529 // Commit output buffers.
530 // Note that we update *all* pools, even if they aren't actually used as outputs -
531 // this is simpler and is what the CpuExecutor does.
532 for (auto& pool : memPools)
533 {
534 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
535 // update() has been removed and flush() added.
536#if defined(ARMNN_ANDROID_R) // Use the new Android implementation.
537 pool.flush();
538#else
539 pool.update();
540#endif
541 }
542}
543
Jim Flynn829ad302019-12-13 14:43:24 +0000544
545
telsoa015307bc12018-03-09 13:51:08 +0000546} // namespace armnn_driver