blob: 48f06a92b7892dbaec15f96229624fe4ce47b68f [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
Renato Grottesi77a0fb02023-05-08 12:55:03 +00002// Copyright © 2017-2021,2023 Arm Ltd and Contributors. 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
Renato Grottesi77a0fb02023-05-08 12:55:03 +000011#include <armnnSerializer/ISerializer.hpp>
12#include <armnnUtils/Filesystem.hpp>
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000013#include <armnnUtils/Permute.hpp>
14
Derek Lambertid00ad912020-01-22 15:55:16 +000015#include <armnn/Utils.hpp>
Colm Donelan08d9a1c2020-09-09 17:56:55 +010016#include <log/log.h>
Derek Lambertid00ad912020-01-22 15:55:16 +000017
Jim Flynn829ad302019-12-13 14:43:24 +000018#include <cerrno>
telsoa015307bc12018-03-09 13:51:08 +000019#include <cinttypes>
Jim Flynn829ad302019-12-13 14:43:24 +000020#include <sstream>
21#include <cstdio>
22#include <time.h>
Guus Sliepen8921bdd2023-06-27 15:20:19 +000023#include <string>
24#include <span>
Jim Flynn829ad302019-12-13 14:43:24 +000025
telsoa015307bc12018-03-09 13:51:08 +000026using namespace android;
telsoa01ce3e84a2018-08-31 09:31:35 +010027using namespace android::hardware;
telsoa015307bc12018-03-09 13:51:08 +000028using namespace android::hidl::memory::V1_0;
29
30namespace armnn_driver
31{
32const armnn::PermutationVector g_DontPermute{};
33
Renato Grottesi77a0fb02023-05-08 12:55:03 +000034void SwizzleAndroidNn4dTensorToArmNn(armnn::TensorInfo& tensorInfo, const void* input, void* output,
telsoa015307bc12018-03-09 13:51:08 +000035 const armnn::PermutationVector& mappings)
36{
Renato Grottesi77a0fb02023-05-08 12:55:03 +000037 if (tensorInfo.GetNumDimensions() != 4U)
38 {
39 throw armnn::InvalidArgumentException("NumDimensions must be 4");
40 }
41 armnn::DataType dataType = tensorInfo.GetDataType();
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000042 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:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000046 case armnn::DataType::QAsymmU8:
Renato Grottesi77a0fb02023-05-08 12:55:03 +000047 case armnn::DataType::QSymmS16:
Derek Lambertid00ad912020-01-22 15:55:16 +000048 case armnn::DataType::QSymmS8:
Sadik Armagan1153d1e2020-04-01 15:09:39 +010049 case armnn::DataType::QAsymmS8:
Renato Grottesi77a0fb02023-05-08 12:55:03 +000050 // First swizzle tensor info
51 tensorInfo = armnnUtils::Permuted(tensorInfo, mappings);
52 // Then swizzle tensor data
53 armnnUtils::Permute(tensorInfo.GetShape(), mappings, input, output, armnn::GetDataTypeSize(dataType));
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000054 break;
telsoa015307bc12018-03-09 13:51:08 +000055 default:
Renato Grottesi77a0fb02023-05-08 12:55:03 +000056 throw armnn::InvalidArgumentException("Unknown DataType for swizzling");
telsoa015307bc12018-03-09 13:51:08 +000057 }
58}
59
Guus Sliepen8921bdd2023-06-27 15:20:19 +000060template<typename Dimensions>
61auto GetDimensionsSpecificity(const Dimensions& dimensions)
62{
63 // We can't use std::vector<bool> since that is a specialization that packs
64 // bits, so use a string of bools instead. This also has the benefit of
65 // using small string optimization.
66 std::basic_string<bool> specificity(dimensions.size(), false);
67
68 for (std::size_t i = 0; i < dimensions.size(); ++i)
69 {
70 specificity[i] = dimensions.data()[i] != 0;
71 }
72
73 return specificity;
74}
75
Sadik Armagan188675f2021-02-12 17:16:42 +000076void* GetMemoryFromPool(V1_0::DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
telsoa015307bc12018-03-09 13:51:08 +000077{
78 // find the location within the pool
Renato Grottesi77a0fb02023-05-08 12:55:03 +000079 if (location.poolIndex >= memPools.size())
80 {
81 throw armnn::InvalidArgumentException("The poolIndex is greater than the memPools size.");
82 }
telsoa015307bc12018-03-09 13:51:08 +000083
surmeh01deb3bdb2018-07-05 12:06:04 +010084 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
85
surmeh01deb3bdb2018-07-05 12:06:04 +010086 uint8_t* memPoolBuffer = memPool.getBuffer();
surmeh01deb3bdb2018-07-05 12:06:04 +010087
88 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000089
90 return memory;
91}
92
Matthew Bentham912b3622019-05-03 15:49:14 +010093armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000094{
Finn Williamsa4983ce2020-07-23 12:55:12 +010095 using namespace armnn;
96 DataType type;
telsoa015307bc12018-03-09 13:51:08 +000097
98 switch (operand.type)
99 {
Matthew Bentham912b3622019-05-03 15:49:14 +0100100 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +0000101 type = armnn::DataType::Float32;
102 break;
Matthew Bentham912b3622019-05-03 15:49:14 +0100103 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000104 type = armnn::DataType::QAsymmU8;
telsoa015307bc12018-03-09 13:51:08 +0000105 break;
Matthew Bentham912b3622019-05-03 15:49:14 +0100106 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +0000107 type = armnn::DataType::Signed32;
108 break;
109 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +0100110 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +0000111 }
112
Finn Williamsa4983ce2020-07-23 12:55:12 +0100113 TensorInfo ret;
114 if (operand.dimensions.size() == 0)
115 {
116 TensorShape tensorShape(Dimensionality::NotSpecified);
117 ret = TensorInfo(tensorShape, type);
118 }
119 else
120 {
Guus Sliepen8921bdd2023-06-27 15:20:19 +0000121 auto dimensionsSpecificity = GetDimensionsSpecificity(operand.dimensions);
122 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity.data());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100123 ret = TensorInfo(tensorShape, type);
124 }
telsoa015307bc12018-03-09 13:51:08 +0000125
126 ret.SetQuantizationScale(operand.scale);
127 ret.SetQuantizationOffset(operand.zeroPoint);
128
129 return ret;
130}
131
Kevin May42477c12020-03-26 13:34:14 +0000132#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 +0100133
134armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
135{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000136 using namespace armnn;
Derek Lambertid00ad912020-01-22 15:55:16 +0000137 bool perChannel = false;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100138
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000139 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100140 switch (operand.type)
141 {
Sadik Armagan793a70c2020-03-19 13:54:04 +0000142 case V1_2::OperandType::TENSOR_BOOL8:
143 type = armnn::DataType::Boolean;
144 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100145 case V1_2::OperandType::TENSOR_FLOAT32:
146 type = armnn::DataType::Float32;
147 break;
Mike Kelly3c673942019-07-25 09:26:06 +0100148 case V1_2::OperandType::TENSOR_FLOAT16:
149 type = armnn::DataType::Float16;
150 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100151 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000152 type = armnn::DataType::QAsymmU8;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100153 break;
Derek Lambertid00ad912020-01-22 15:55:16 +0000154 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
155 perChannel=true;
156 ARMNN_FALLTHROUGH;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000157 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
FinnWilliamsArm624fe9f2019-12-06 17:12:42 +0000158 type = armnn::DataType::QSymmS8;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000159 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100160 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000161 type = armnn::DataType::QSymmS16;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100162 break;
163 case V1_2::OperandType::TENSOR_INT32:
164 type = armnn::DataType::Signed32;
165 break;
166 default:
167 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
168 }
169
Finn Williamsa4983ce2020-07-23 12:55:12 +0100170 TensorInfo ret;
171 if (operand.dimensions.size() == 0)
172 {
173 TensorShape tensorShape(Dimensionality::NotSpecified);
174 ret = TensorInfo(tensorShape, type);
175 }
176 else
177 {
Guus Sliepen8921bdd2023-06-27 15:20:19 +0000178 auto dimensionsSpecificity = GetDimensionsSpecificity(operand.dimensions);
179 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity.data());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100180 ret = TensorInfo(tensorShape, type);
181 }
182
Derek Lambertid00ad912020-01-22 15:55:16 +0000183 if (perChannel)
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000184 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000185 if (operand.extraParams.getDiscriminator() != V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant)
186 {
187 throw armnn::InvalidArgumentException("ExtraParams is expected to be of type channelQuant");
188 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100189
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000190 auto perAxisQuantParams = operand.extraParams.channelQuant();
191
192 ret.SetQuantizationScales(perAxisQuantParams.scales);
193 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
194 }
195 else
196 {
197 ret.SetQuantizationScale(operand.scale);
198 ret.SetQuantizationOffset(operand.zeroPoint);
199 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100200
201 return ret;
202}
203
204#endif
205
Kevin May42477c12020-03-26 13:34:14 +0000206#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
207
208armnn::TensorInfo GetTensorInfoForOperand(const V1_3::Operand& operand)
209{
210 using namespace armnn;
211 bool perChannel = false;
Teresa Charlin896572b2020-07-15 12:37:51 +0100212 bool isScalar = false;
Kevin May42477c12020-03-26 13:34:14 +0000213
214 DataType type;
215 switch (operand.type)
216 {
Sadik Armagan51ba2c62020-03-31 15:36:25 +0100217 case V1_3::OperandType::TENSOR_BOOL8:
218 type = armnn::DataType::Boolean;
219 break;
Kevin May42477c12020-03-26 13:34:14 +0000220 case V1_3::OperandType::TENSOR_FLOAT32:
221 type = armnn::DataType::Float32;
222 break;
223 case V1_3::OperandType::TENSOR_FLOAT16:
224 type = armnn::DataType::Float16;
225 break;
226 case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
227 type = armnn::DataType::QAsymmU8;
228 break;
229 case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
230 perChannel=true;
231 ARMNN_FALLTHROUGH;
232 case V1_3::OperandType::TENSOR_QUANT8_SYMM:
233 type = armnn::DataType::QSymmS8;
234 break;
235 case V1_3::OperandType::TENSOR_QUANT16_SYMM:
236 type = armnn::DataType::QSymmS16;
237 break;
238 case V1_3::OperandType::TENSOR_INT32:
239 type = armnn::DataType::Signed32;
240 break;
Finn Williamsfc884b42020-06-11 17:35:44 +0100241 case V1_3::OperandType::INT32:
242 type = armnn::DataType::Signed32;
Teresa Charlin896572b2020-07-15 12:37:51 +0100243 isScalar = true;
Finn Williamsfc884b42020-06-11 17:35:44 +0100244 break;
Kevin May42477c12020-03-26 13:34:14 +0000245 case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
246 type = armnn::DataType::QAsymmS8;
247 break;
248 default:
249 throw UnsupportedOperand<V1_3::OperandType>(operand.type);
250 }
251
Finn Williamsfc884b42020-06-11 17:35:44 +0100252 TensorInfo ret;
Teresa Charlin896572b2020-07-15 12:37:51 +0100253 if (isScalar)
Finn Williamsfc884b42020-06-11 17:35:44 +0100254 {
Teresa Charlin896572b2020-07-15 12:37:51 +0100255 ret = TensorInfo(TensorShape(armnn::Dimensionality::Scalar), type);
Finn Williamsfc884b42020-06-11 17:35:44 +0100256 }
257 else
258 {
Finn Williamsa4983ce2020-07-23 12:55:12 +0100259 if (operand.dimensions.size() == 0)
260 {
261 TensorShape tensorShape(Dimensionality::NotSpecified);
262 ret = TensorInfo(tensorShape, type);
263 }
264 else
265 {
Guus Sliepen8921bdd2023-06-27 15:20:19 +0000266 auto dimensionsSpecificity = GetDimensionsSpecificity(operand.dimensions);
267 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity.data());
Finn Williamsa4983ce2020-07-23 12:55:12 +0100268 ret = TensorInfo(tensorShape, type);
269 }
Finn Williamsfc884b42020-06-11 17:35:44 +0100270 }
271
Kevin May42477c12020-03-26 13:34:14 +0000272 if (perChannel)
273 {
274 // ExtraParams is expected to be of type channelQuant
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000275 if (operand.extraParams.getDiscriminator() != V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant)
276 {
277 throw armnn::InvalidArgumentException("ExtraParams is expected to be of type channelQuant");
278 }
Kevin May42477c12020-03-26 13:34:14 +0000279 auto perAxisQuantParams = operand.extraParams.channelQuant();
280
281 ret.SetQuantizationScales(perAxisQuantParams.scales);
282 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
283 }
284 else
285 {
286 ret.SetQuantizationScale(operand.scale);
287 ret.SetQuantizationOffset(operand.zeroPoint);
288 }
Kevin May42477c12020-03-26 13:34:14 +0000289 return ret;
290}
291
292#endif
293
Matthew Bentham912b3622019-05-03 15:49:14 +0100294std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000295{
296 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
297 toString(operand.type);
298}
299
Kevin May42477c12020-03-26 13:34:14 +0000300#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 +0100301
302std::string GetOperandSummary(const V1_2::Operand& operand)
303{
304 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
305 toString(operand.type);
306}
307
308#endif
309
Kevin May42477c12020-03-26 13:34:14 +0000310#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
311
312std::string GetOperandSummary(const V1_3::Operand& operand)
313{
314 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
315 toString(operand.type);
316}
317
318#endif
319
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000320template <typename TensorType>
321using DumpElementFunction = void (*)(const TensorType& tensor,
telsoa015307bc12018-03-09 13:51:08 +0000322 unsigned int elementIndex,
323 std::ofstream& fileStream);
324
325namespace
326{
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000327template <typename TensorType, typename ElementType, typename PrintableType = ElementType>
328void DumpTensorElement(const TensorType& tensor, unsigned int elementIndex, std::ofstream& fileStream)
telsoa015307bc12018-03-09 13:51:08 +0000329{
330 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000331 fileStream << static_cast<PrintableType>(elements[elementIndex]) << " ";
telsoa015307bc12018-03-09 13:51:08 +0000332}
333
telsoa015307bc12018-03-09 13:51:08 +0000334} // namespace
335
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000336template <typename TensorType>
telsoa015307bc12018-03-09 13:51:08 +0000337void DumpTensor(const std::string& dumpDir,
338 const std::string& requestName,
339 const std::string& tensorName,
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000340 const TensorType& tensor)
telsoa015307bc12018-03-09 13:51:08 +0000341{
342 // The dump directory must exist in advance.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100343 fs::path dumpPath = dumpDir;
344 const fs::path fileName = dumpPath / (requestName + "_" + tensorName + ".dump");
telsoa015307bc12018-03-09 13:51:08 +0000345
346 std::ofstream fileStream;
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100347 fileStream.open(fileName.c_str(), std::ofstream::out | std::ofstream::trunc);
telsoa015307bc12018-03-09 13:51:08 +0000348
349 if (!fileStream.good())
350 {
351 ALOGW("Could not open file %s for writing", fileName.c_str());
352 return;
353 }
354
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000355 DumpElementFunction<TensorType> dumpElementFunction = nullptr;
telsoa015307bc12018-03-09 13:51:08 +0000356
357 switch (tensor.GetDataType())
358 {
359 case armnn::DataType::Float32:
360 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000361 dumpElementFunction = &DumpTensorElement<TensorType, float>;
telsoa015307bc12018-03-09 13:51:08 +0000362 break;
363 }
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000364 case armnn::DataType::QAsymmU8:
telsoa015307bc12018-03-09 13:51:08 +0000365 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000366 dumpElementFunction = &DumpTensorElement<TensorType, uint8_t, uint32_t>;
telsoa015307bc12018-03-09 13:51:08 +0000367 break;
368 }
369 case armnn::DataType::Signed32:
370 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000371 dumpElementFunction = &DumpTensorElement<TensorType, int32_t>;
telsoa015307bc12018-03-09 13:51:08 +0000372 break;
373 }
Jim Flynnf2e175c2019-12-12 15:11:30 +0000374 case armnn::DataType::Float16:
375 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000376 dumpElementFunction = &DumpTensorElement<TensorType, armnn::Half>;
Jim Flynnf2e175c2019-12-12 15:11:30 +0000377 break;
378 }
Teresa Charlinb248ec12020-04-30 11:06:34 +0100379 case armnn::DataType::QAsymmS8:
380 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000381 dumpElementFunction = &DumpTensorElement<TensorType, int8_t, int32_t>;
Teresa Charlinb248ec12020-04-30 11:06:34 +0100382 break;
383 }
384 case armnn::DataType::Boolean:
385 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000386 dumpElementFunction = &DumpTensorElement<TensorType, bool>;
Teresa Charlinb248ec12020-04-30 11:06:34 +0100387 break;
388 }
telsoa015307bc12018-03-09 13:51:08 +0000389 default:
390 {
391 dumpElementFunction = nullptr;
392 }
393 }
394
395 if (dumpElementFunction != nullptr)
396 {
397 const unsigned int numDimensions = tensor.GetNumDimensions();
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000398 const armnn::TensorShape shape = tensor.GetShape();
telsoa015307bc12018-03-09 13:51:08 +0000399
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000400 if (!shape.AreAllDimensionsSpecified())
Mike Kelly7780e602021-04-26 21:54:55 +0100401 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000402 fileStream << "Cannot dump tensor elements: not all dimensions are specified" << std::endl;
403 return;
404 }
405 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
406
407 if (numDimensions == 0)
408 {
409 fileStream << "# Shape []" << std::endl;
410 return;
411 }
412 fileStream << "# Shape [" << shape[0];
413 for (unsigned int d = 1; d < numDimensions; ++d)
414 {
415 fileStream << "," << shape[d];
telsoa015307bc12018-03-09 13:51:08 +0000416 }
417 fileStream << "]" << std::endl;
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000418 fileStream << "Each line contains the data of each of the elements of dimension0. In NCHW and NHWC, each line"
419 " will be a batch" << std::endl << std::endl;
telsoa015307bc12018-03-09 13:51:08 +0000420
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000421 // Split will create a new line after all elements of the first dimension
422 // (in a 4, 3, 2, 3 tensor, there will be 4 lines of 18 elements)
423 unsigned int split = 1;
424 if (numDimensions == 1)
telsoa015307bc12018-03-09 13:51:08 +0000425 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000426 split = shape[0];
427 }
428 else
429 {
430 for (unsigned int i = 1; i < numDimensions; ++i)
telsoa015307bc12018-03-09 13:51:08 +0000431 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000432 split *= shape[i];
telsoa015307bc12018-03-09 13:51:08 +0000433 }
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000434 }
435
436 // Print all elements in the tensor
437 for (unsigned int elementIndex = 0; elementIndex < tensor.GetNumElements(); ++elementIndex)
438 {
439 (*dumpElementFunction)(tensor, elementIndex, fileStream);
440
441 if ( (elementIndex + 1) % split == 0 )
telsoa015307bc12018-03-09 13:51:08 +0000442 {
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000443 fileStream << std::endl;
telsoa015307bc12018-03-09 13:51:08 +0000444 }
telsoa015307bc12018-03-09 13:51:08 +0000445 }
446 fileStream << std::endl;
447 }
448 else
449 {
450 fileStream << "Cannot dump tensor elements: Unsupported data type "
451 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
452 }
453
454 if (!fileStream.good())
455 {
456 ALOGW("An error occurred when writing to file %s", fileName.c_str());
457 }
458}
459
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000460
461template void DumpTensor<armnn::ConstTensor>(const std::string& dumpDir,
462 const std::string& requestName,
463 const std::string& tensorName,
464 const armnn::ConstTensor& tensor);
465
466template void DumpTensor<armnn::Tensor>(const std::string& dumpDir,
467 const std::string& requestName,
468 const std::string& tensorName,
469 const armnn::Tensor& tensor);
470
telsoa01ce3e84a2018-08-31 09:31:35 +0100471void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
472 const std::string& dumpDir,
473 armnn::NetworkId networkId,
474 const armnn::IProfiler* profiler)
475{
476 // Check if profiling is required.
477 if (!gpuProfilingEnabled)
478 {
479 return;
480 }
481
482 // The dump directory must exist in advance.
483 if (dumpDir.empty())
484 {
485 return;
486 }
487
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000488 if (!profiler)
489 {
490 ALOGW("profiler was null");
491 return;
492 }
telsoa01ce3e84a2018-08-31 09:31:35 +0100493
494 // Set the name of the output profiling file.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100495 fs::path dumpPath = dumpDir;
496 const fs::path fileName = dumpPath / (std::to_string(networkId) + "_profiling.json");
telsoa01ce3e84a2018-08-31 09:31:35 +0100497
498 // Open the ouput file for writing.
499 std::ofstream fileStream;
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100500 fileStream.open(fileName.c_str(), std::ofstream::out | std::ofstream::trunc);
telsoa01ce3e84a2018-08-31 09:31:35 +0100501
502 if (!fileStream.good())
503 {
504 ALOGW("Could not open file %s for writing", fileName.c_str());
505 return;
506 }
507
508 // Write the profiling info to a JSON file.
509 profiler->Print(fileStream);
510}
511
Jim Flynn829ad302019-12-13 14:43:24 +0000512std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
513 const std::string& dumpDir)
514{
515 std::string fileName;
516 // The dump directory must exist in advance.
517 if (dumpDir.empty())
518 {
519 return fileName;
520 }
521
522 std::string timestamp = GetFileTimestamp();
523 if (timestamp.empty())
524 {
525 return fileName;
526 }
527
528 // Set the name of the output .dot file.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100529 fs::path dumpPath = dumpDir;
530 fs::path tempFilePath = dumpPath / (timestamp + "_networkgraph.dot");
531 fileName = tempFilePath.string();
Jim Flynn829ad302019-12-13 14:43:24 +0000532
533 ALOGV("Exporting the optimized network graph to file: %s", fileName.c_str());
534
535 // Write the network graph to a dot file.
536 std::ofstream fileStream;
537 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
538
539 if (!fileStream.good())
540 {
541 ALOGW("Could not open file %s for writing", fileName.c_str());
542 return fileName;
543 }
544
545 if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success)
546 {
547 ALOGW("An error occurred when writing to file %s", fileName.c_str());
548 }
549 return fileName;
550}
551
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000552std::string SerializeNetwork(const armnn::INetwork& network,
553 const std::string& dumpDir,
554 std::vector<uint8_t>& dataCacheData,
555 bool dataCachingActive)
556{
557 std::string fileName;
558 bool bSerializeToFile = true;
559 if (dumpDir.empty())
560 {
561 bSerializeToFile = false;
562 }
563 else
564 {
565 std::string timestamp = GetFileTimestamp();
566 if (timestamp.empty())
567 {
568 bSerializeToFile = false;
569 }
570 }
571 if (!bSerializeToFile && !dataCachingActive)
572 {
573 return fileName;
574 }
575
576 auto serializer(armnnSerializer::ISerializer::Create());
577 // Serialize the Network
578 serializer->Serialize(network);
579 if (dataCachingActive)
580 {
581 std::stringstream stream;
582 auto serialized = serializer->SaveSerializedToStream(stream);
583 if (serialized)
584 {
585 std::string const serializedString{stream.str()};
586 std::copy(serializedString.begin(), serializedString.end(), std::back_inserter(dataCacheData));
587 }
588 }
589
590 if (bSerializeToFile)
591 {
592 // Set the name of the output .armnn file.
593 fs::path dumpPath = dumpDir;
594 std::string timestamp = GetFileTimestamp();
595 fs::path tempFilePath = dumpPath / (timestamp + "_network.armnn");
596 fileName = tempFilePath.string();
597
598 // Save serialized network to a file
599 std::ofstream serializedFile(fileName, std::ios::out | std::ios::binary);
600 auto serialized = serializer->SaveSerializedToStream(serializedFile);
601 if (!serialized)
602 {
603 ALOGW("An error occurred when serializing to file %s", fileName.c_str());
604 }
605 }
606 return fileName;
607}
608
Finn Williamsa4983ce2020-07-23 12:55:12 +0100609bool IsDynamicTensor(const armnn::TensorInfo& tensorInfo)
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100610{
Finn Williamsa4983ce2020-07-23 12:55:12 +0100611 if (tensorInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified)
612 {
613 return true;
614 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100615 // Account for the usage of the TensorShape empty constructor
616 if (tensorInfo.GetNumDimensions() == 0)
617 {
618 return true;
619 }
Finn Williamsa4983ce2020-07-23 12:55:12 +0100620 return !tensorInfo.GetShape().AreAllDimensionsSpecified();
621}
622
623bool AreDynamicTensorsSupported()
624{
625#if defined(ARMNN_ANDROID_NN_V1_3)
626 return true;
627#else
628 return false;
629#endif
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100630}
631
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000632bool isQuantizedOperand(const V1_0::OperandType& operandType)
633{
634 if (operandType == V1_0::OperandType::TENSOR_QUANT8_ASYMM)
635 {
636 return true;
637 }
638 else
639 {
640 return false;
641 }
642}
643
644#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)// Using ::android::hardware::neuralnetworks::V1_2
645bool isQuantizedOperand(const V1_2::OperandType& operandType)
646{
647 if (operandType == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
648 operandType == V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
649 operandType == V1_2::OperandType::TENSOR_QUANT8_SYMM ||
650 operandType == V1_2::OperandType::TENSOR_QUANT16_SYMM )
651 {
652 return true;
653 }
654 else
655 {
656 return false;
657 }
658}
659#endif
660
661#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
662bool isQuantizedOperand(const V1_3::OperandType& operandType)
663{
664 if (operandType == V1_3::OperandType::TENSOR_QUANT8_ASYMM ||
665 operandType == V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
666 operandType == V1_3::OperandType::TENSOR_QUANT8_SYMM ||
667 operandType == V1_3::OperandType::TENSOR_QUANT16_SYMM ||
668 operandType == V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED)
669 {
670 return true;
671 }
672 else
673 {
674 return false;
675 }
676}
677#endif
678
Jim Flynn829ad302019-12-13 14:43:24 +0000679std::string GetFileTimestamp()
680{
681 // used to get a timestamp to name diagnostic files (the ArmNN serialized graph
682 // and getSupportedOperations.txt files)
683 timespec ts;
684 int iRet = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
685 std::stringstream ss;
686 if (iRet == 0)
687 {
688 ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec);
689 }
690 else
691 {
692 ALOGW("clock_gettime failed with errno %s : %s", std::to_string(errno).c_str(), std::strerror(errno));
693 }
694 return ss.str();
695}
696
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000697void RenameExportedFiles(const std::string& existingSerializedFileName,
698 const std::string& existingDotFileName,
699 const std::string& dumpDir,
700 const armnn::NetworkId networkId)
Jim Flynn829ad302019-12-13 14:43:24 +0000701{
702 if (dumpDir.empty())
703 {
704 return;
705 }
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000706 RenameFile(existingSerializedFileName, std::string("_network.armnn"), dumpDir, networkId);
707 RenameFile(existingDotFileName, std::string("_networkgraph.dot"), dumpDir, networkId);
708}
709
710void RenameFile(const std::string& existingName,
711 const std::string& extension,
712 const std::string& dumpDir,
713 const armnn::NetworkId networkId)
714{
715 if (existingName.empty() || dumpDir.empty())
Jim Flynn829ad302019-12-13 14:43:24 +0000716 {
717 return;
718 }
Renato Grottesi1b2abb72023-05-08 06:58:44 +0000719
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000720 fs::path dumpPath = dumpDir;
721 const fs::path newFileName = dumpPath / (std::to_string(networkId) + extension);
722 int iRet = rename(existingName.c_str(), newFileName.c_str());
Jim Flynn829ad302019-12-13 14:43:24 +0000723 if (iRet != 0)
724 {
725 std::stringstream ss;
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000726 ss << "rename of [" << existingName << "] to [" << newFileName << "] failed with errno "
727 << std::to_string(errno) << " : " << std::strerror(errno);
Jim Flynn829ad302019-12-13 14:43:24 +0000728 ALOGW(ss.str().c_str());
729 }
730}
731
Kevin May42477c12020-03-26 13:34:14 +0000732void CommitPools(std::vector<::android::nn::RunTimePoolInfo>& memPools)
733{
734 if (memPools.empty())
735 {
736 return;
737 }
738 // Commit output buffers.
739 // Note that we update *all* pools, even if they aren't actually used as outputs -
740 // this is simpler and is what the CpuExecutor does.
741 for (auto& pool : memPools)
742 {
743 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
744 // update() has been removed and flush() added.
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000745#if defined(ARMNN_ANDROID_R) || defined(ARMNN_ANDROID_S) // Use the new Android implementation.
Kevin May42477c12020-03-26 13:34:14 +0000746 pool.flush();
747#else
748 pool.update();
749#endif
750 }
751}
Renato Grottesi77a0fb02023-05-08 12:55:03 +0000752
753size_t GetSize(const V1_0::Request& request, const V1_0::RequestArgument& requestArgument)
754{
755 return request.pools[requestArgument.location.poolIndex].size();
756}
757
758#ifdef ARMNN_ANDROID_NN_V1_3
759size_t GetSize(const V1_3::Request& request, const V1_0::RequestArgument& requestArgument)
760{
761 if (request.pools[requestArgument.location.poolIndex].getDiscriminator() ==
762 V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory)
763 {
764 return request.pools[requestArgument.location.poolIndex].hidlMemory().size();
765 }
766 else
767 {
768 return 0;
769 }
770}
771#endif
772
773template <typename ErrorStatus, typename Request>
774ErrorStatus ValidateRequestArgument(const Request& request,
775 const armnn::TensorInfo& tensorInfo,
776 const V1_0::RequestArgument& requestArgument,
777 std::string descString)
778{
779 if (requestArgument.location.poolIndex >= request.pools.size())
780 {
781 std::string err = fmt::format("Invalid {} pool at index {} the pool index is greater than the number "
782 "of available pools {}",
783 descString, requestArgument.location.poolIndex, request.pools.size());
784 ALOGE(err.c_str());
785 return ErrorStatus::GENERAL_FAILURE;
786 }
787 const size_t size = GetSize(request, requestArgument);
788 size_t totalLength = tensorInfo.GetNumBytes();
789
790 if (static_cast<size_t>(requestArgument.location.offset) + totalLength > size)
791 {
792 std::string err = fmt::format("Invalid {} pool at index {} the offset {} and length {} are greater "
793 "than the pool size {}", descString, requestArgument.location.poolIndex,
794 requestArgument.location.offset, totalLength, size);
795 ALOGE(err.c_str());
796 return ErrorStatus::GENERAL_FAILURE;
797 }
798 return ErrorStatus::NONE;
799}
800
801template V1_0::ErrorStatus ValidateRequestArgument<V1_0::ErrorStatus, V1_0::Request>(
802 const V1_0::Request& request,
803 const armnn::TensorInfo& tensorInfo,
804 const V1_0::RequestArgument& requestArgument,
805 std::string descString);
806
807#ifdef ARMNN_ANDROID_NN_V1_3
808template V1_3::ErrorStatus ValidateRequestArgument<V1_3::ErrorStatus, V1_3::Request>(
809 const V1_3::Request& request,
810 const armnn::TensorInfo& tensorInfo,
811 const V1_0::RequestArgument& requestArgument,
812 std::string descString);
813#endif
814
telsoa015307bc12018-03-09 13:51:08 +0000815} // namespace armnn_driver