blob: 00d61c7bc6bf743c3b7d1dd3c01fb4b4fea2ccfb [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:
Sadik Armagan1153d1e2020-04-01 15:09:39 +010057 case armnn::DataType::QAsymmS8:
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000058 SwizzleAndroidNn4dTensorToArmNn(tensor.GetShape(), input, output, armnn::GetDataTypeSize(dataType), mappings);
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000059 break;
telsoa015307bc12018-03-09 13:51:08 +000060 default:
61 ALOGW("Unknown armnn::DataType for swizzling");
62 assert(0);
63 }
64}
65
66void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
67{
68 // find the location within the pool
69 assert(location.poolIndex < memPools.size());
70
surmeh01deb3bdb2018-07-05 12:06:04 +010071 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
72
surmeh01deb3bdb2018-07-05 12:06:04 +010073 uint8_t* memPoolBuffer = memPool.getBuffer();
surmeh01deb3bdb2018-07-05 12:06:04 +010074
75 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000076
77 return memory;
78}
79
Matthew Bentham912b3622019-05-03 15:49:14 +010080armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000081{
82 armnn::DataType type;
83
84 switch (operand.type)
85 {
Matthew Bentham912b3622019-05-03 15:49:14 +010086 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +000087 type = armnn::DataType::Float32;
88 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010089 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000090 type = armnn::DataType::QAsymmU8;
telsoa015307bc12018-03-09 13:51:08 +000091 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010092 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +000093 type = armnn::DataType::Signed32;
94 break;
95 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +010096 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +000097 }
98
99 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
100
101 ret.SetQuantizationScale(operand.scale);
102 ret.SetQuantizationOffset(operand.zeroPoint);
103
104 return ret;
105}
106
Kevin May42477c12020-03-26 13:34:14 +0000107#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 +0100108
109armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
110{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000111 using namespace armnn;
Derek Lambertid00ad912020-01-22 15:55:16 +0000112 bool perChannel = false;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100113
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000114 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100115 switch (operand.type)
116 {
Sadik Armagan793a70c2020-03-19 13:54:04 +0000117 case V1_2::OperandType::TENSOR_BOOL8:
118 type = armnn::DataType::Boolean;
119 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100120 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;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100126 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000127 type = armnn::DataType::QAsymmU8;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100128 break;
Derek Lambertid00ad912020-01-22 15:55:16 +0000129 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
130 perChannel=true;
131 ARMNN_FALLTHROUGH;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000132 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
FinnWilliamsArm624fe9f2019-12-06 17:12:42 +0000133 type = armnn::DataType::QSymmS8;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000134 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100135 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000136 type = armnn::DataType::QSymmS16;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100137 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);
Derek Lambertid00ad912020-01-22 15:55:16 +0000146 if (perChannel)
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000147 {
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
Kevin May42477c12020-03-26 13:34:14 +0000168#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
169
170armnn::TensorInfo GetTensorInfoForOperand(const V1_3::Operand& operand)
171{
172 using namespace armnn;
173 bool perChannel = false;
174
175 DataType type;
176 switch (operand.type)
177 {
Sadik Armagan51ba2c62020-03-31 15:36:25 +0100178 case V1_3::OperandType::TENSOR_BOOL8:
179 type = armnn::DataType::Boolean;
180 break;
Kevin May42477c12020-03-26 13:34:14 +0000181 case V1_3::OperandType::TENSOR_FLOAT32:
182 type = armnn::DataType::Float32;
183 break;
184 case V1_3::OperandType::TENSOR_FLOAT16:
185 type = armnn::DataType::Float16;
186 break;
187 case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
188 type = armnn::DataType::QAsymmU8;
189 break;
190 case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
191 perChannel=true;
192 ARMNN_FALLTHROUGH;
193 case V1_3::OperandType::TENSOR_QUANT8_SYMM:
194 type = armnn::DataType::QSymmS8;
195 break;
196 case V1_3::OperandType::TENSOR_QUANT16_SYMM:
197 type = armnn::DataType::QSymmS16;
198 break;
199 case V1_3::OperandType::TENSOR_INT32:
200 type = armnn::DataType::Signed32;
201 break;
202 case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
203 type = armnn::DataType::QAsymmS8;
204 break;
205 default:
206 throw UnsupportedOperand<V1_3::OperandType>(operand.type);
207 }
208
209 TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
210 if (perChannel)
211 {
212 // ExtraParams is expected to be of type channelQuant
213 BOOST_ASSERT(operand.extraParams.getDiscriminator() ==
Kevin May352d8382020-03-31 15:03:42 +0100214 V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant);
Kevin May42477c12020-03-26 13:34:14 +0000215
216 auto perAxisQuantParams = operand.extraParams.channelQuant();
217
218 ret.SetQuantizationScales(perAxisQuantParams.scales);
219 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
220 }
221 else
222 {
223 ret.SetQuantizationScale(operand.scale);
224 ret.SetQuantizationOffset(operand.zeroPoint);
225 }
226
227 return ret;
228}
229
230#endif
231
Matthew Bentham912b3622019-05-03 15:49:14 +0100232std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000233{
234 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
235 toString(operand.type);
236}
237
Kevin May42477c12020-03-26 13:34:14 +0000238#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 +0100239
240std::string GetOperandSummary(const V1_2::Operand& operand)
241{
242 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
243 toString(operand.type);
244}
245
246#endif
247
Kevin May42477c12020-03-26 13:34:14 +0000248#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
249
250std::string GetOperandSummary(const V1_3::Operand& operand)
251{
252 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
253 toString(operand.type);
254}
255
256#endif
257
telsoa015307bc12018-03-09 13:51:08 +0000258using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
259 unsigned int elementIndex,
260 std::ofstream& fileStream);
261
262namespace
263{
264template <typename ElementType, typename PrintableType = ElementType>
265void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
266{
267 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
268 fileStream << static_cast<PrintableType>(elements[elementIndex]) << ",";
269}
270
271constexpr const char* MemoryLayoutString(const armnn::ConstTensor& tensor)
272{
273 const char* str = "";
274
275 switch (tensor.GetNumDimensions())
276 {
277 case 4: { str = "(BHWC) "; break; }
278 case 3: { str = "(HWC) "; break; }
279 case 2: { str = "(HW) "; break; }
280 default: { str = ""; break; }
281 }
282
283 return str;
284}
285} // namespace
286
287void DumpTensor(const std::string& dumpDir,
288 const std::string& requestName,
289 const std::string& tensorName,
290 const armnn::ConstTensor& tensor)
291{
292 // The dump directory must exist in advance.
293 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.dump") % dumpDir % requestName % tensorName);
294
295 std::ofstream fileStream;
296 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
297
298 if (!fileStream.good())
299 {
300 ALOGW("Could not open file %s for writing", fileName.c_str());
301 return;
302 }
303
304 DumpElementFunction dumpElementFunction = nullptr;
305
306 switch (tensor.GetDataType())
307 {
308 case armnn::DataType::Float32:
309 {
310 dumpElementFunction = &DumpTensorElement<float>;
311 break;
312 }
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000313 case armnn::DataType::QAsymmU8:
telsoa015307bc12018-03-09 13:51:08 +0000314 {
315 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
316 break;
317 }
318 case armnn::DataType::Signed32:
319 {
320 dumpElementFunction = &DumpTensorElement<int32_t>;
321 break;
322 }
Jim Flynnf2e175c2019-12-12 15:11:30 +0000323 case armnn::DataType::Float16:
324 {
325 dumpElementFunction = &DumpTensorElement<armnn::Half>;
326 break;
327 }
telsoa015307bc12018-03-09 13:51:08 +0000328 default:
329 {
330 dumpElementFunction = nullptr;
331 }
332 }
333
334 if (dumpElementFunction != nullptr)
335 {
336 const unsigned int numDimensions = tensor.GetNumDimensions();
337
338 const unsigned int batch = (numDimensions == 4) ? tensor.GetShape()[numDimensions - 4] : 1;
339
340 const unsigned int height = (numDimensions >= 3)
341 ? tensor.GetShape()[numDimensions - 3]
342 : (numDimensions >= 2) ? tensor.GetShape()[numDimensions - 2] : 1;
343
344 const unsigned int width = (numDimensions >= 3)
345 ? tensor.GetShape()[numDimensions - 2]
346 : (numDimensions >= 1) ? tensor.GetShape()[numDimensions - 1] : 0;
347
348 const unsigned int channels = (numDimensions >= 3) ? tensor.GetShape()[numDimensions - 1] : 1;
349
350 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
351 fileStream << "# Dimensions " << MemoryLayoutString(tensor);
352 fileStream << "[" << tensor.GetShape()[0];
353 for (unsigned int d = 1; d < numDimensions; d++)
354 {
355 fileStream << "," << tensor.GetShape()[d];
356 }
357 fileStream << "]" << std::endl;
358
359 for (unsigned int e = 0, b = 0; b < batch; ++b)
360 {
361 if (numDimensions >= 4)
362 {
363 fileStream << "# Batch " << b << std::endl;
364 }
365 for (unsigned int c = 0; c < channels; c++)
366 {
367 if (numDimensions >= 3)
368 {
369 fileStream << "# Channel " << c << std::endl;
370 }
371 for (unsigned int h = 0; h < height; h++)
372 {
373 for (unsigned int w = 0; w < width; w++, e += channels)
374 {
375 (*dumpElementFunction)(tensor, e, fileStream);
376 }
377 fileStream << std::endl;
378 }
379 e -= channels - 1;
380 if (c < channels)
381 {
382 e -= ((height * width) - 1) * channels;
383 }
384 }
385 fileStream << std::endl;
386 }
387 fileStream << std::endl;
388 }
389 else
390 {
391 fileStream << "Cannot dump tensor elements: Unsupported data type "
392 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
393 }
394
395 if (!fileStream.good())
396 {
397 ALOGW("An error occurred when writing to file %s", fileName.c_str());
398 }
399}
400
telsoa01ce3e84a2018-08-31 09:31:35 +0100401void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
402 const std::string& dumpDir,
403 armnn::NetworkId networkId,
404 const armnn::IProfiler* profiler)
405{
406 // Check if profiling is required.
407 if (!gpuProfilingEnabled)
408 {
409 return;
410 }
411
412 // The dump directory must exist in advance.
413 if (dumpDir.empty())
414 {
415 return;
416 }
417
418 BOOST_ASSERT(profiler);
419
420 // Set the name of the output profiling file.
421 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.json")
422 % dumpDir
423 % std::to_string(networkId)
424 % "profiling");
425
426 // Open the ouput file for writing.
427 std::ofstream fileStream;
428 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
429
430 if (!fileStream.good())
431 {
432 ALOGW("Could not open file %s for writing", fileName.c_str());
433 return;
434 }
435
436 // Write the profiling info to a JSON file.
437 profiler->Print(fileStream);
438}
439
Jim Flynn829ad302019-12-13 14:43:24 +0000440std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
441 const std::string& dumpDir)
442{
443 std::string fileName;
444 // The dump directory must exist in advance.
445 if (dumpDir.empty())
446 {
447 return fileName;
448 }
449
450 std::string timestamp = GetFileTimestamp();
451 if (timestamp.empty())
452 {
453 return fileName;
454 }
455
456 // Set the name of the output .dot file.
457 fileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
458 % dumpDir
459 % timestamp);
460
461 ALOGV("Exporting the optimized network graph to file: %s", fileName.c_str());
462
463 // Write the network graph to a dot file.
464 std::ofstream fileStream;
465 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
466
467 if (!fileStream.good())
468 {
469 ALOGW("Could not open file %s for writing", fileName.c_str());
470 return fileName;
471 }
472
473 if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success)
474 {
475 ALOGW("An error occurred when writing to file %s", fileName.c_str());
476 }
477 return fileName;
478}
479
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100480bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
481{
482 // Dynamic tensors have at least one 0-sized dimension
483 return outputInfo.GetNumElements() == 0u;
484}
485
Jim Flynn829ad302019-12-13 14:43:24 +0000486std::string GetFileTimestamp()
487{
488 // used to get a timestamp to name diagnostic files (the ArmNN serialized graph
489 // and getSupportedOperations.txt files)
490 timespec ts;
491 int iRet = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
492 std::stringstream ss;
493 if (iRet == 0)
494 {
495 ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec);
496 }
497 else
498 {
499 ALOGW("clock_gettime failed with errno %s : %s", std::to_string(errno).c_str(), std::strerror(errno));
500 }
501 return ss.str();
502}
503
504void RenameGraphDotFile(const std::string& oldName, const std::string& dumpDir, const armnn::NetworkId networkId)
505{
506 if (dumpDir.empty())
507 {
508 return;
509 }
510 if (oldName.empty())
511 {
512 return;
513 }
514 const std::string newFileName = boost::str(boost::format("%1%/%2%_networkgraph.dot")
515 % dumpDir
516 % std::to_string(networkId));
517 int iRet = rename(oldName.c_str(), newFileName.c_str());
518 if (iRet != 0)
519 {
520 std::stringstream ss;
521 ss << "rename of [" << oldName << "] to [" << newFileName << "] failed with errno " << std::to_string(errno)
522 << " : " << std::strerror(errno);
523 ALOGW(ss.str().c_str());
524 }
525}
526
Kevin May42477c12020-03-26 13:34:14 +0000527void CommitPools(std::vector<::android::nn::RunTimePoolInfo>& memPools)
528{
529 if (memPools.empty())
530 {
531 return;
532 }
533 // Commit output buffers.
534 // Note that we update *all* pools, even if they aren't actually used as outputs -
535 // this is simpler and is what the CpuExecutor does.
536 for (auto& pool : memPools)
537 {
538 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
539 // update() has been removed and flush() added.
540#if defined(ARMNN_ANDROID_R) // Use the new Android implementation.
541 pool.flush();
542#else
543 pool.update();
544#endif
545 }
546}
547
Jim Flynn829ad302019-12-13 14:43:24 +0000548
549
telsoa015307bc12018-03-09 13:51:08 +0000550} // namespace armnn_driver