blob: f910cd495edda2d9076af65eb657bb97f96fc062 [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
Mike Kellye2d611e2021-10-14 12:35:58 +01002// Copyright © 2017 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
Sadik Armaganb3021432021-01-13 15:56:51 +000011#include <armnnSerializer/ISerializer.hpp>
Rob Hughes083be702021-07-19 15:29:47 +010012#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>
23
telsoa015307bc12018-03-09 13:51:08 +000024using 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
Jan Eilersa71c0632021-04-12 13:12:19 +010032void SwizzleAndroidNn4dTensorToArmNn(armnn::TensorInfo& tensorInfo, const void* input, void* output,
telsoa015307bc12018-03-09 13:51:08 +000033 const armnn::PermutationVector& mappings)
34{
Mike Kellye2d611e2021-10-14 12:35:58 +010035 if (tensorInfo.GetNumDimensions() != 4U)
36 {
37 throw armnn::InvalidArgumentException("NumDimensions must be 4");
38 }
Jan Eilersa71c0632021-04-12 13:12:19 +010039 armnn::DataType dataType = tensorInfo.GetDataType();
Matteo Martincighbf19d2a2019-11-29 11:46:50 +000040 switch (dataType)
telsoa015307bc12018-03-09 13:51:08 +000041 {
Mike Kelly3c673942019-07-25 09:26:06 +010042 case armnn::DataType::Float16:
telsoa015307bc12018-03-09 13:51:08 +000043 case armnn::DataType::Float32:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000044 case armnn::DataType::QAsymmU8:
Mike Kellye2d611e2021-10-14 12:35:58 +010045 case armnn::DataType::QSymmS16:
Derek Lambertid00ad912020-01-22 15:55:16 +000046 case armnn::DataType::QSymmS8:
Sadik Armagan1153d1e2020-04-01 15:09:39 +010047 case armnn::DataType::QAsymmS8:
Jan Eilersa71c0632021-04-12 13:12:19 +010048 // First swizzle tensor info
49 tensorInfo = armnnUtils::Permuted(tensorInfo, mappings);
50 // Then swizzle tensor data
51 armnnUtils::Permute(tensorInfo.GetShape(), mappings, input, output, armnn::GetDataTypeSize(dataType));
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +000052 break;
telsoa015307bc12018-03-09 13:51:08 +000053 default:
Mike Kellye2d611e2021-10-14 12:35:58 +010054 throw armnn::InvalidArgumentException("Unknown DataType for swizzling");
telsoa015307bc12018-03-09 13:51:08 +000055 }
56}
57
Sadik Armagan188675f2021-02-12 17:16:42 +000058void* GetMemoryFromPool(V1_0::DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
telsoa015307bc12018-03-09 13:51:08 +000059{
60 // find the location within the pool
Mike Kellye2d611e2021-10-14 12:35:58 +010061 if (location.poolIndex >= memPools.size())
62 {
63 throw armnn::InvalidArgumentException("The poolIndex is greater than the memPools size.");
64 }
telsoa015307bc12018-03-09 13:51:08 +000065
surmeh01deb3bdb2018-07-05 12:06:04 +010066 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
67
surmeh01deb3bdb2018-07-05 12:06:04 +010068 uint8_t* memPoolBuffer = memPool.getBuffer();
surmeh01deb3bdb2018-07-05 12:06:04 +010069
70 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000071
72 return memory;
73}
74
Matthew Bentham912b3622019-05-03 15:49:14 +010075armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000076{
Finn Williamsa4983ce2020-07-23 12:55:12 +010077 using namespace armnn;
78 DataType type;
telsoa015307bc12018-03-09 13:51:08 +000079
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:
Derek Lamberti1a38cda2020-01-10 17:28:20 +000086 type = armnn::DataType::QAsymmU8;
telsoa015307bc12018-03-09 13:51:08 +000087 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
Finn Williamsa4983ce2020-07-23 12:55:12 +010095 TensorInfo ret;
96 if (operand.dimensions.size() == 0)
97 {
98 TensorShape tensorShape(Dimensionality::NotSpecified);
99 ret = TensorInfo(tensorShape, type);
100 }
101 else
102 {
103 bool dimensionsSpecificity[5] = { true, true, true, true, true };
104 int count = 0;
105 std::for_each(operand.dimensions.data(),
106 operand.dimensions.data() + operand.dimensions.size(),
107 [&](const unsigned int val)
108 {
109 if (val == 0)
110 {
111 dimensionsSpecificity[count] = false;
112 }
113 count++;
114 });
115
116 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity);
117 ret = TensorInfo(tensorShape, type);
118 }
telsoa015307bc12018-03-09 13:51:08 +0000119
120 ret.SetQuantizationScale(operand.scale);
121 ret.SetQuantizationOffset(operand.zeroPoint);
122
123 return ret;
124}
125
Kevin May42477c12020-03-26 13:34:14 +0000126#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 +0100127
128armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
129{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000130 using namespace armnn;
Derek Lambertid00ad912020-01-22 15:55:16 +0000131 bool perChannel = false;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100132
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000133 DataType type;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100134 switch (operand.type)
135 {
Sadik Armagan793a70c2020-03-19 13:54:04 +0000136 case V1_2::OperandType::TENSOR_BOOL8:
137 type = armnn::DataType::Boolean;
138 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100139 case V1_2::OperandType::TENSOR_FLOAT32:
140 type = armnn::DataType::Float32;
141 break;
Mike Kelly3c673942019-07-25 09:26:06 +0100142 case V1_2::OperandType::TENSOR_FLOAT16:
143 type = armnn::DataType::Float16;
144 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100145 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000146 type = armnn::DataType::QAsymmU8;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100147 break;
Derek Lambertid00ad912020-01-22 15:55:16 +0000148 case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
149 perChannel=true;
150 ARMNN_FALLTHROUGH;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000151 case V1_2::OperandType::TENSOR_QUANT8_SYMM:
FinnWilliamsArm624fe9f2019-12-06 17:12:42 +0000152 type = armnn::DataType::QSymmS8;
Mike Kelly0e2e31b2019-11-19 09:16:00 +0000153 break;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100154 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000155 type = armnn::DataType::QSymmS16;
Mike Kellyb5fdf382019-06-11 16:35:25 +0100156 break;
157 case V1_2::OperandType::TENSOR_INT32:
158 type = armnn::DataType::Signed32;
159 break;
160 default:
161 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
162 }
163
Finn Williamsa4983ce2020-07-23 12:55:12 +0100164 TensorInfo ret;
165 if (operand.dimensions.size() == 0)
166 {
167 TensorShape tensorShape(Dimensionality::NotSpecified);
168 ret = TensorInfo(tensorShape, type);
169 }
170 else
171 {
172 bool dimensionsSpecificity[5] = { true, true, true, true, true };
173 int count = 0;
174 std::for_each(operand.dimensions.data(),
175 operand.dimensions.data() + operand.dimensions.size(),
176 [&](const unsigned int val)
177 {
178 if (val == 0)
179 {
180 dimensionsSpecificity[count] = false;
181 }
182 count++;
183 });
184
185 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity);
186 ret = TensorInfo(tensorShape, type);
187 }
188
Derek Lambertid00ad912020-01-22 15:55:16 +0000189 if (perChannel)
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000190 {
Mike Kellye2d611e2021-10-14 12:35:58 +0100191 if (operand.extraParams.getDiscriminator() != V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant)
192 {
193 throw armnn::InvalidArgumentException("ExtraParams is expected to be of type channelQuant");
194 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100195
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000196 auto perAxisQuantParams = operand.extraParams.channelQuant();
197
198 ret.SetQuantizationScales(perAxisQuantParams.scales);
199 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
200 }
201 else
202 {
203 ret.SetQuantizationScale(operand.scale);
204 ret.SetQuantizationOffset(operand.zeroPoint);
205 }
Mike Kellyb5fdf382019-06-11 16:35:25 +0100206
207 return ret;
208}
209
210#endif
211
Kevin May42477c12020-03-26 13:34:14 +0000212#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
213
214armnn::TensorInfo GetTensorInfoForOperand(const V1_3::Operand& operand)
215{
216 using namespace armnn;
217 bool perChannel = false;
Teresa Charlin896572b2020-07-15 12:37:51 +0100218 bool isScalar = false;
Kevin May42477c12020-03-26 13:34:14 +0000219
220 DataType type;
221 switch (operand.type)
222 {
Sadik Armagan51ba2c62020-03-31 15:36:25 +0100223 case V1_3::OperandType::TENSOR_BOOL8:
224 type = armnn::DataType::Boolean;
225 break;
Kevin May42477c12020-03-26 13:34:14 +0000226 case V1_3::OperandType::TENSOR_FLOAT32:
227 type = armnn::DataType::Float32;
228 break;
229 case V1_3::OperandType::TENSOR_FLOAT16:
230 type = armnn::DataType::Float16;
231 break;
232 case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
233 type = armnn::DataType::QAsymmU8;
234 break;
235 case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
236 perChannel=true;
237 ARMNN_FALLTHROUGH;
238 case V1_3::OperandType::TENSOR_QUANT8_SYMM:
239 type = armnn::DataType::QSymmS8;
240 break;
241 case V1_3::OperandType::TENSOR_QUANT16_SYMM:
242 type = armnn::DataType::QSymmS16;
243 break;
244 case V1_3::OperandType::TENSOR_INT32:
245 type = armnn::DataType::Signed32;
246 break;
Finn Williamsfc884b42020-06-11 17:35:44 +0100247 case V1_3::OperandType::INT32:
248 type = armnn::DataType::Signed32;
Teresa Charlin896572b2020-07-15 12:37:51 +0100249 isScalar = true;
Finn Williamsfc884b42020-06-11 17:35:44 +0100250 break;
Kevin May42477c12020-03-26 13:34:14 +0000251 case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
252 type = armnn::DataType::QAsymmS8;
253 break;
254 default:
255 throw UnsupportedOperand<V1_3::OperandType>(operand.type);
256 }
257
Finn Williamsfc884b42020-06-11 17:35:44 +0100258 TensorInfo ret;
Teresa Charlin896572b2020-07-15 12:37:51 +0100259 if (isScalar)
Finn Williamsfc884b42020-06-11 17:35:44 +0100260 {
Teresa Charlin896572b2020-07-15 12:37:51 +0100261 ret = TensorInfo(TensorShape(armnn::Dimensionality::Scalar), type);
Finn Williamsfc884b42020-06-11 17:35:44 +0100262 }
263 else
264 {
Finn Williamsa4983ce2020-07-23 12:55:12 +0100265 if (operand.dimensions.size() == 0)
266 {
267 TensorShape tensorShape(Dimensionality::NotSpecified);
268 ret = TensorInfo(tensorShape, type);
269 }
270 else
271 {
272 bool dimensionsSpecificity[5] = { true, true, true, true, true };
273 int count = 0;
274 std::for_each(operand.dimensions.data(),
275 operand.dimensions.data() + operand.dimensions.size(),
276 [&](const unsigned int val)
277 {
278 if (val == 0)
279 {
280 dimensionsSpecificity[count] = false;
281 }
282 count++;
283 });
284
285 TensorShape tensorShape(operand.dimensions.size(), operand.dimensions.data(), dimensionsSpecificity);
286 ret = TensorInfo(tensorShape, type);
287 }
Finn Williamsfc884b42020-06-11 17:35:44 +0100288 }
289
Kevin May42477c12020-03-26 13:34:14 +0000290 if (perChannel)
291 {
292 // ExtraParams is expected to be of type channelQuant
Mike Kellye2d611e2021-10-14 12:35:58 +0100293 if (operand.extraParams.getDiscriminator() != V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant)
294 {
295 throw armnn::InvalidArgumentException("ExtraParams is expected to be of type channelQuant");
296 }
Kevin May42477c12020-03-26 13:34:14 +0000297 auto perAxisQuantParams = operand.extraParams.channelQuant();
298
299 ret.SetQuantizationScales(perAxisQuantParams.scales);
300 ret.SetQuantizationDim(MakeOptional<unsigned int>(perAxisQuantParams.channelDim));
301 }
302 else
303 {
304 ret.SetQuantizationScale(operand.scale);
305 ret.SetQuantizationOffset(operand.zeroPoint);
306 }
Kevin May42477c12020-03-26 13:34:14 +0000307 return ret;
308}
309
310#endif
311
Matthew Bentham912b3622019-05-03 15:49:14 +0100312std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000313{
314 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
315 toString(operand.type);
316}
317
Kevin May42477c12020-03-26 13:34:14 +0000318#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 +0100319
320std::string GetOperandSummary(const V1_2::Operand& operand)
321{
322 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
323 toString(operand.type);
324}
325
326#endif
327
Kevin May42477c12020-03-26 13:34:14 +0000328#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
329
330std::string GetOperandSummary(const V1_3::Operand& operand)
331{
332 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
333 toString(operand.type);
334}
335
336#endif
337
telsoa015307bc12018-03-09 13:51:08 +0000338using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
339 unsigned int elementIndex,
340 std::ofstream& fileStream);
341
342namespace
343{
344template <typename ElementType, typename PrintableType = ElementType>
345void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
346{
347 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000348 fileStream << static_cast<PrintableType>(elements[elementIndex]) << " ";
telsoa015307bc12018-03-09 13:51:08 +0000349}
350
telsoa015307bc12018-03-09 13:51:08 +0000351} // namespace
352
353void DumpTensor(const std::string& dumpDir,
354 const std::string& requestName,
355 const std::string& tensorName,
356 const armnn::ConstTensor& tensor)
357{
358 // The dump directory must exist in advance.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100359 fs::path dumpPath = dumpDir;
360 const fs::path fileName = dumpPath / (requestName + "_" + tensorName + ".dump");
telsoa015307bc12018-03-09 13:51:08 +0000361
362 std::ofstream fileStream;
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100363 fileStream.open(fileName.c_str(), std::ofstream::out | std::ofstream::trunc);
telsoa015307bc12018-03-09 13:51:08 +0000364
365 if (!fileStream.good())
366 {
367 ALOGW("Could not open file %s for writing", fileName.c_str());
368 return;
369 }
370
371 DumpElementFunction dumpElementFunction = nullptr;
372
373 switch (tensor.GetDataType())
374 {
375 case armnn::DataType::Float32:
376 {
377 dumpElementFunction = &DumpTensorElement<float>;
378 break;
379 }
Derek Lamberti1a38cda2020-01-10 17:28:20 +0000380 case armnn::DataType::QAsymmU8:
telsoa015307bc12018-03-09 13:51:08 +0000381 {
382 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
383 break;
384 }
385 case armnn::DataType::Signed32:
386 {
387 dumpElementFunction = &DumpTensorElement<int32_t>;
388 break;
389 }
Jim Flynnf2e175c2019-12-12 15:11:30 +0000390 case armnn::DataType::Float16:
391 {
392 dumpElementFunction = &DumpTensorElement<armnn::Half>;
393 break;
394 }
Teresa Charlinb248ec12020-04-30 11:06:34 +0100395 case armnn::DataType::QAsymmS8:
396 {
397 dumpElementFunction = &DumpTensorElement<int8_t, int32_t>;
398 break;
399 }
400 case armnn::DataType::Boolean:
401 {
402 dumpElementFunction = &DumpTensorElement<bool>;
403 break;
404 }
telsoa015307bc12018-03-09 13:51:08 +0000405 default:
406 {
407 dumpElementFunction = nullptr;
408 }
409 }
410
411 if (dumpElementFunction != nullptr)
412 {
413 const unsigned int numDimensions = tensor.GetNumDimensions();
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000414 const armnn::TensorShape shape = tensor.GetShape();
telsoa015307bc12018-03-09 13:51:08 +0000415
Mike Kelly7780e602021-04-26 21:54:55 +0100416 if (!shape.AreAllDimensionsSpecified())
417 {
418 fileStream << "Cannot dump tensor elements: not all dimensions are specified" << std::endl;
419 return;
420 }
telsoa015307bc12018-03-09 13:51:08 +0000421 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
Mike Kelly7780e602021-04-26 21:54:55 +0100422
423 if (numDimensions == 0)
424 {
425 fileStream << "# Shape []" << std::endl;
426 return;
427 }
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000428 fileStream << "# Shape [" << shape[0];
429 for (unsigned int d = 1; d < numDimensions; ++d)
telsoa015307bc12018-03-09 13:51:08 +0000430 {
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000431 fileStream << "," << shape[d];
telsoa015307bc12018-03-09 13:51:08 +0000432 }
433 fileStream << "]" << std::endl;
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000434 fileStream << "Each line contains the data of each of the elements of dimension0. In NCHW and NHWC, each line"
435 " will be a batch" << std::endl << std::endl;
telsoa015307bc12018-03-09 13:51:08 +0000436
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000437 // Split will create a new line after all elements of the first dimension
438 // (in a 4, 3, 2, 3 tensor, there will be 4 lines of 18 elements)
439 unsigned int split = 1;
440 if (numDimensions == 1)
telsoa015307bc12018-03-09 13:51:08 +0000441 {
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000442 split = shape[0];
443 }
444 else
445 {
446 for (unsigned int i = 1; i < numDimensions; ++i)
telsoa015307bc12018-03-09 13:51:08 +0000447 {
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000448 split *= shape[i];
telsoa015307bc12018-03-09 13:51:08 +0000449 }
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000450 }
451
452 // Print all elements in the tensor
453 for (unsigned int elementIndex = 0; elementIndex < tensor.GetNumElements(); ++elementIndex)
454 {
455 (*dumpElementFunction)(tensor, elementIndex, fileStream);
456
457 if ( (elementIndex + 1) % split == 0 )
telsoa015307bc12018-03-09 13:51:08 +0000458 {
Teresa Charlin32fe97e2021-03-08 19:28:24 +0000459 fileStream << std::endl;
telsoa015307bc12018-03-09 13:51:08 +0000460 }
telsoa015307bc12018-03-09 13:51:08 +0000461 }
462 fileStream << std::endl;
463 }
464 else
465 {
466 fileStream << "Cannot dump tensor elements: Unsupported data type "
467 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
468 }
469
470 if (!fileStream.good())
471 {
472 ALOGW("An error occurred when writing to file %s", fileName.c_str());
473 }
474}
475
telsoa01ce3e84a2018-08-31 09:31:35 +0100476void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
477 const std::string& dumpDir,
478 armnn::NetworkId networkId,
479 const armnn::IProfiler* profiler)
480{
481 // Check if profiling is required.
482 if (!gpuProfilingEnabled)
483 {
484 return;
485 }
486
487 // The dump directory must exist in advance.
488 if (dumpDir.empty())
489 {
490 return;
491 }
492
Mike Kellye2d611e2021-10-14 12:35:58 +0100493 if (!profiler)
494 {
495 ALOGW("profiler was null");
496 return;
497 }
telsoa01ce3e84a2018-08-31 09:31:35 +0100498
499 // Set the name of the output profiling file.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100500 fs::path dumpPath = dumpDir;
501 const fs::path fileName = dumpPath / (std::to_string(networkId) + "_profiling.json");
telsoa01ce3e84a2018-08-31 09:31:35 +0100502
503 // Open the ouput file for writing.
504 std::ofstream fileStream;
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100505 fileStream.open(fileName.c_str(), std::ofstream::out | std::ofstream::trunc);
telsoa01ce3e84a2018-08-31 09:31:35 +0100506
507 if (!fileStream.good())
508 {
509 ALOGW("Could not open file %s for writing", fileName.c_str());
510 return;
511 }
512
513 // Write the profiling info to a JSON file.
514 profiler->Print(fileStream);
515}
516
Jim Flynn829ad302019-12-13 14:43:24 +0000517std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
518 const std::string& dumpDir)
519{
520 std::string fileName;
521 // The dump directory must exist in advance.
522 if (dumpDir.empty())
523 {
524 return fileName;
525 }
526
527 std::string timestamp = GetFileTimestamp();
528 if (timestamp.empty())
529 {
530 return fileName;
531 }
532
533 // Set the name of the output .dot file.
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100534 fs::path dumpPath = dumpDir;
535 fs::path tempFilePath = dumpPath / (timestamp + "_networkgraph.dot");
536 fileName = tempFilePath.string();
Jim Flynn829ad302019-12-13 14:43:24 +0000537
538 ALOGV("Exporting the optimized network graph to file: %s", fileName.c_str());
539
540 // Write the network graph to a dot file.
541 std::ofstream fileStream;
542 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
543
544 if (!fileStream.good())
545 {
546 ALOGW("Could not open file %s for writing", fileName.c_str());
547 return fileName;
548 }
549
550 if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success)
551 {
552 ALOGW("An error occurred when writing to file %s", fileName.c_str());
553 }
554 return fileName;
555}
556
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100557std::string SerializeNetwork(const armnn::INetwork& network,
558 const std::string& dumpDir,
559 std::vector<uint8_t>& dataCacheData,
560 bool dataCachingActive)
Sadik Armaganb3021432021-01-13 15:56:51 +0000561{
562 std::string fileName;
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100563 bool bSerializeToFile = true;
Sadik Armaganb3021432021-01-13 15:56:51 +0000564 if (dumpDir.empty())
565 {
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100566 bSerializeToFile = false;
Sadik Armaganb3021432021-01-13 15:56:51 +0000567 }
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100568 else
569 {
570 std::string timestamp = GetFileTimestamp();
571 if (timestamp.empty())
572 {
573 bSerializeToFile = false;
574 }
575 }
576 if (!bSerializeToFile && !dataCachingActive)
Sadik Armaganb3021432021-01-13 15:56:51 +0000577 {
578 return fileName;
579 }
580
581 auto serializer(armnnSerializer::ISerializer::Create());
Sadik Armaganb3021432021-01-13 15:56:51 +0000582 // Serialize the Network
583 serializer->Serialize(network);
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100584 if (dataCachingActive)
Sadik Armaganb3021432021-01-13 15:56:51 +0000585 {
Sadik Armagan0a2dfab2021-10-06 16:41:44 +0100586 std::stringstream stream;
587 auto serialized = serializer->SaveSerializedToStream(stream);
588 if (serialized)
589 {
590 std::string const serializedString{stream.str()};
591 std::copy(serializedString.begin(), serializedString.end(), std::back_inserter(dataCacheData));
592 }
593 }
594
595 if (bSerializeToFile)
596 {
597 // Set the name of the output .armnn file.
598 fs::path dumpPath = dumpDir;
599 std::string timestamp = GetFileTimestamp();
600 fs::path tempFilePath = dumpPath / (timestamp + "_network.armnn");
601 fileName = tempFilePath.string();
602
603 // Save serialized network to a file
604 std::ofstream serializedFile(fileName, std::ios::out | std::ios::binary);
605 auto serialized = serializer->SaveSerializedToStream(serializedFile);
606 if (!serialized)
607 {
608 ALOGW("An error occurred when serializing to file %s", fileName.c_str());
609 }
Sadik Armaganb3021432021-01-13 15:56:51 +0000610 }
611 return fileName;
612}
613
Finn Williamsa4983ce2020-07-23 12:55:12 +0100614bool IsDynamicTensor(const armnn::TensorInfo& tensorInfo)
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100615{
Finn Williamsa4983ce2020-07-23 12:55:12 +0100616 if (tensorInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified)
617 {
618 return true;
619 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100620 // Account for the usage of the TensorShape empty constructor
621 if (tensorInfo.GetNumDimensions() == 0)
622 {
623 return true;
624 }
Finn Williamsa4983ce2020-07-23 12:55:12 +0100625 return !tensorInfo.GetShape().AreAllDimensionsSpecified();
626}
627
628bool AreDynamicTensorsSupported()
629{
630#if defined(ARMNN_ANDROID_NN_V1_3)
631 return true;
632#else
633 return false;
634#endif
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100635}
636
Teresa Charlind3381d52021-06-02 18:35:16 +0100637bool isQuantizedOperand(const V1_0::OperandType& operandType)
638{
639 if (operandType == V1_0::OperandType::TENSOR_QUANT8_ASYMM)
640 {
641 return true;
642 }
643 else
644 {
645 return false;
646 }
647}
648
649#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)// Using ::android::hardware::neuralnetworks::V1_2
650bool isQuantizedOperand(const V1_2::OperandType& operandType)
651{
652 if (operandType == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
653 operandType == V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
654 operandType == V1_2::OperandType::TENSOR_QUANT8_SYMM ||
655 operandType == V1_2::OperandType::TENSOR_QUANT16_SYMM )
656 {
657 return true;
658 }
659 else
660 {
661 return false;
662 }
663}
664#endif
665
666#ifdef ARMNN_ANDROID_NN_V1_3 // Using ::android::hardware::neuralnetworks::V1_3
667bool isQuantizedOperand(const V1_3::OperandType& operandType)
668{
669 if (operandType == V1_3::OperandType::TENSOR_QUANT8_ASYMM ||
670 operandType == V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
671 operandType == V1_3::OperandType::TENSOR_QUANT8_SYMM ||
672 operandType == V1_3::OperandType::TENSOR_QUANT16_SYMM ||
673 operandType == V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED)
674 {
675 return true;
676 }
677 else
678 {
679 return false;
680 }
681}
682#endif
683
Jim Flynn829ad302019-12-13 14:43:24 +0000684std::string GetFileTimestamp()
685{
686 // used to get a timestamp to name diagnostic files (the ArmNN serialized graph
687 // and getSupportedOperations.txt files)
688 timespec ts;
689 int iRet = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
690 std::stringstream ss;
691 if (iRet == 0)
692 {
693 ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec);
694 }
695 else
696 {
697 ALOGW("clock_gettime failed with errno %s : %s", std::to_string(errno).c_str(), std::strerror(errno));
698 }
699 return ss.str();
700}
701
Sadik Armaganb3021432021-01-13 15:56:51 +0000702void RenameExportedFiles(const std::string& existingSerializedFileName,
703 const std::string& existingDotFileName,
704 const std::string& dumpDir,
705 const armnn::NetworkId networkId)
Jim Flynn829ad302019-12-13 14:43:24 +0000706{
707 if (dumpDir.empty())
708 {
709 return;
710 }
Sadik Armaganb3021432021-01-13 15:56:51 +0000711 RenameFile(existingSerializedFileName, std::string("_network.armnn"), dumpDir, networkId);
712 RenameFile(existingDotFileName, std::string("_networkgraph.dot"), dumpDir, networkId);
713}
714
715void RenameFile(const std::string& existingName,
716 const std::string& extension,
717 const std::string& dumpDir,
718 const armnn::NetworkId networkId)
719{
720 if (existingName.empty() || dumpDir.empty())
Jim Flynn829ad302019-12-13 14:43:24 +0000721 {
722 return;
723 }
Colm Donelan08d9a1c2020-09-09 17:56:55 +0100724
Sadik Armaganb3021432021-01-13 15:56:51 +0000725 fs::path dumpPath = dumpDir;
726 const fs::path newFileName = dumpPath / (std::to_string(networkId) + extension);
727 int iRet = rename(existingName.c_str(), newFileName.c_str());
Jim Flynn829ad302019-12-13 14:43:24 +0000728 if (iRet != 0)
729 {
730 std::stringstream ss;
Sadik Armaganb3021432021-01-13 15:56:51 +0000731 ss << "rename of [" << existingName << "] to [" << newFileName << "] failed with errno "
732 << std::to_string(errno) << " : " << std::strerror(errno);
Jim Flynn829ad302019-12-13 14:43:24 +0000733 ALOGW(ss.str().c_str());
734 }
735}
736
Kevin May42477c12020-03-26 13:34:14 +0000737void CommitPools(std::vector<::android::nn::RunTimePoolInfo>& memPools)
738{
739 if (memPools.empty())
740 {
741 return;
742 }
743 // Commit output buffers.
744 // Note that we update *all* pools, even if they aren't actually used as outputs -
745 // this is simpler and is what the CpuExecutor does.
746 for (auto& pool : memPools)
747 {
748 // Type android::nn::RunTimePoolInfo has changed between Android P & Q and Android R, where
749 // update() has been removed and flush() added.
Sadik Armagan188675f2021-02-12 17:16:42 +0000750#if defined(ARMNN_ANDROID_R) || defined(ARMNN_ANDROID_S) // Use the new Android implementation.
Kevin May42477c12020-03-26 13:34:14 +0000751 pool.flush();
752#else
753 pool.update();
754#endif
755 }
756}
telsoa015307bc12018-03-09 13:51:08 +0000757} // namespace armnn_driver