blob: d3d62a022047f0c72b5f3c260b90c656963ec50c [file] [log] [blame]
telsoa015307bc12018-03-09 13:51:08 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beck93e48982018-09-05 13:05:09 +01003// SPDX-License-Identifier: MIT
telsoa015307bc12018-03-09 13:51:08 +00004//
5
6#define LOG_TAG "ArmnnDriver"
7
8#include "Utils.hpp"
9
10#include <Permute.hpp>
11
telsoa015307bc12018-03-09 13:51:08 +000012#include <cassert>
13#include <cinttypes>
telsoa015307bc12018-03-09 13:51:08 +000014
15using namespace android;
telsoa01ce3e84a2018-08-31 09:31:35 +010016using namespace android::hardware;
telsoa015307bc12018-03-09 13:51:08 +000017using namespace android::hidl::memory::V1_0;
18
19namespace armnn_driver
20{
21const armnn::PermutationVector g_DontPermute{};
22
23namespace
24{
25
26template <typename T>
27void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorShape& inTensorShape, const void* input,
28 void* output, const armnn::PermutationVector& mappings)
29{
30 const auto inputData = static_cast<const T*>(input);
31 const auto outputData = static_cast<T*>(output);
32
Matteo Martincigh2c444fc2019-01-07 10:18:47 +000033 armnnUtils::Permute(armnnUtils::Permuted(inTensorShape, mappings), mappings, inputData, outputData, sizeof(T));
telsoa015307bc12018-03-09 13:51:08 +000034}
35
36} // anonymous namespace
37
38void SwizzleAndroidNn4dTensorToArmNn(const armnn::TensorInfo& tensor, const void* input, void* output,
39 const armnn::PermutationVector& mappings)
40{
41 assert(tensor.GetNumDimensions() == 4U);
42
43 switch(tensor.GetDataType())
44 {
45 case armnn::DataType::Float32:
46 SwizzleAndroidNn4dTensorToArmNn<float>(tensor.GetShape(), input, output, mappings);
47 break;
48 case armnn::DataType::QuantisedAsymm8:
49 SwizzleAndroidNn4dTensorToArmNn<uint8_t>(tensor.GetShape(), input, output, mappings);
50 break;
51 default:
52 ALOGW("Unknown armnn::DataType for swizzling");
53 assert(0);
54 }
55}
56
57void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::RunTimePoolInfo>& memPools)
58{
59 // find the location within the pool
60 assert(location.poolIndex < memPools.size());
61
surmeh01deb3bdb2018-07-05 12:06:04 +010062 const android::nn::RunTimePoolInfo& memPool = memPools[location.poolIndex];
63
64 // Type android::nn::RunTimePoolInfo has changed between Android O and Android P, where
65 // "buffer" has been made private and must be accessed via the accessor method "getBuffer".
Mike Kellyb5fdf382019-06-11 16:35:25 +010066#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q) // Use the new Android implementation.
surmeh01deb3bdb2018-07-05 12:06:04 +010067 uint8_t* memPoolBuffer = memPool.getBuffer();
68#else // Fallback to the old Android O implementation.
69 uint8_t* memPoolBuffer = memPool.buffer;
70#endif
71
72 uint8_t* memory = memPoolBuffer + location.offset;
telsoa015307bc12018-03-09 13:51:08 +000073
74 return memory;
75}
76
Matthew Bentham912b3622019-05-03 15:49:14 +010077armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +000078{
79 armnn::DataType type;
80
81 switch (operand.type)
82 {
Matthew Bentham912b3622019-05-03 15:49:14 +010083 case V1_0::OperandType::TENSOR_FLOAT32:
telsoa015307bc12018-03-09 13:51:08 +000084 type = armnn::DataType::Float32;
85 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010086 case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
telsoa015307bc12018-03-09 13:51:08 +000087 type = armnn::DataType::QuantisedAsymm8;
88 break;
Matthew Bentham912b3622019-05-03 15:49:14 +010089 case V1_0::OperandType::TENSOR_INT32:
telsoa015307bc12018-03-09 13:51:08 +000090 type = armnn::DataType::Signed32;
91 break;
92 default:
Mike Kellyb5fdf382019-06-11 16:35:25 +010093 throw UnsupportedOperand<V1_0::OperandType>(operand.type);
telsoa015307bc12018-03-09 13:51:08 +000094 }
95
96 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
97
98 ret.SetQuantizationScale(operand.scale);
99 ret.SetQuantizationOffset(operand.zeroPoint);
100
101 return ret;
102}
103
Mike Kellyb5fdf382019-06-11 16:35:25 +0100104#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
105
106armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
107{
108 armnn::DataType type;
109
110 switch (operand.type)
111 {
112 case V1_2::OperandType::TENSOR_FLOAT32:
113 type = armnn::DataType::Float32;
114 break;
115 case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
116 type = armnn::DataType::QuantisedAsymm8;
117 break;
118 case V1_2::OperandType::TENSOR_QUANT16_SYMM:
119 type = armnn::DataType::QuantisedSymm16;
120 break;
121 case V1_2::OperandType::TENSOR_INT32:
122 type = armnn::DataType::Signed32;
123 break;
124 default:
125 throw UnsupportedOperand<V1_2::OperandType>(operand.type);
126 }
127
128 armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
129
130 ret.SetQuantizationScale(operand.scale);
131 ret.SetQuantizationOffset(operand.zeroPoint);
132
133 return ret;
134}
135
136#endif
137
Matthew Bentham912b3622019-05-03 15:49:14 +0100138std::string GetOperandSummary(const V1_0::Operand& operand)
telsoa015307bc12018-03-09 13:51:08 +0000139{
140 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
141 toString(operand.type);
142}
143
Mike Kellyb5fdf382019-06-11 16:35:25 +0100144#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
145
146std::string GetOperandSummary(const V1_2::Operand& operand)
147{
148 return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
149 toString(operand.type);
150}
151
152#endif
153
telsoa015307bc12018-03-09 13:51:08 +0000154using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
155 unsigned int elementIndex,
156 std::ofstream& fileStream);
157
158namespace
159{
160template <typename ElementType, typename PrintableType = ElementType>
161void DumpTensorElement(const armnn::ConstTensor& tensor, unsigned int elementIndex, std::ofstream& fileStream)
162{
163 const ElementType* elements = reinterpret_cast<const ElementType*>(tensor.GetMemoryArea());
164 fileStream << static_cast<PrintableType>(elements[elementIndex]) << ",";
165}
166
167constexpr const char* MemoryLayoutString(const armnn::ConstTensor& tensor)
168{
169 const char* str = "";
170
171 switch (tensor.GetNumDimensions())
172 {
173 case 4: { str = "(BHWC) "; break; }
174 case 3: { str = "(HWC) "; break; }
175 case 2: { str = "(HW) "; break; }
176 default: { str = ""; break; }
177 }
178
179 return str;
180}
181} // namespace
182
183void DumpTensor(const std::string& dumpDir,
184 const std::string& requestName,
185 const std::string& tensorName,
186 const armnn::ConstTensor& tensor)
187{
188 // The dump directory must exist in advance.
189 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.dump") % dumpDir % requestName % tensorName);
190
191 std::ofstream fileStream;
192 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
193
194 if (!fileStream.good())
195 {
196 ALOGW("Could not open file %s for writing", fileName.c_str());
197 return;
198 }
199
200 DumpElementFunction dumpElementFunction = nullptr;
201
202 switch (tensor.GetDataType())
203 {
204 case armnn::DataType::Float32:
205 {
206 dumpElementFunction = &DumpTensorElement<float>;
207 break;
208 }
209 case armnn::DataType::QuantisedAsymm8:
210 {
211 dumpElementFunction = &DumpTensorElement<uint8_t, uint32_t>;
212 break;
213 }
214 case armnn::DataType::Signed32:
215 {
216 dumpElementFunction = &DumpTensorElement<int32_t>;
217 break;
218 }
219 default:
220 {
221 dumpElementFunction = nullptr;
222 }
223 }
224
225 if (dumpElementFunction != nullptr)
226 {
227 const unsigned int numDimensions = tensor.GetNumDimensions();
228
229 const unsigned int batch = (numDimensions == 4) ? tensor.GetShape()[numDimensions - 4] : 1;
230
231 const unsigned int height = (numDimensions >= 3)
232 ? tensor.GetShape()[numDimensions - 3]
233 : (numDimensions >= 2) ? tensor.GetShape()[numDimensions - 2] : 1;
234
235 const unsigned int width = (numDimensions >= 3)
236 ? tensor.GetShape()[numDimensions - 2]
237 : (numDimensions >= 1) ? tensor.GetShape()[numDimensions - 1] : 0;
238
239 const unsigned int channels = (numDimensions >= 3) ? tensor.GetShape()[numDimensions - 1] : 1;
240
241 fileStream << "# Number of elements " << tensor.GetNumElements() << std::endl;
242 fileStream << "# Dimensions " << MemoryLayoutString(tensor);
243 fileStream << "[" << tensor.GetShape()[0];
244 for (unsigned int d = 1; d < numDimensions; d++)
245 {
246 fileStream << "," << tensor.GetShape()[d];
247 }
248 fileStream << "]" << std::endl;
249
250 for (unsigned int e = 0, b = 0; b < batch; ++b)
251 {
252 if (numDimensions >= 4)
253 {
254 fileStream << "# Batch " << b << std::endl;
255 }
256 for (unsigned int c = 0; c < channels; c++)
257 {
258 if (numDimensions >= 3)
259 {
260 fileStream << "# Channel " << c << std::endl;
261 }
262 for (unsigned int h = 0; h < height; h++)
263 {
264 for (unsigned int w = 0; w < width; w++, e += channels)
265 {
266 (*dumpElementFunction)(tensor, e, fileStream);
267 }
268 fileStream << std::endl;
269 }
270 e -= channels - 1;
271 if (c < channels)
272 {
273 e -= ((height * width) - 1) * channels;
274 }
275 }
276 fileStream << std::endl;
277 }
278 fileStream << std::endl;
279 }
280 else
281 {
282 fileStream << "Cannot dump tensor elements: Unsupported data type "
283 << static_cast<unsigned int>(tensor.GetDataType()) << std::endl;
284 }
285
286 if (!fileStream.good())
287 {
288 ALOGW("An error occurred when writing to file %s", fileName.c_str());
289 }
290}
291
telsoa01ce3e84a2018-08-31 09:31:35 +0100292void DumpJsonProfilingIfRequired(bool gpuProfilingEnabled,
293 const std::string& dumpDir,
294 armnn::NetworkId networkId,
295 const armnn::IProfiler* profiler)
296{
297 // Check if profiling is required.
298 if (!gpuProfilingEnabled)
299 {
300 return;
301 }
302
303 // The dump directory must exist in advance.
304 if (dumpDir.empty())
305 {
306 return;
307 }
308
309 BOOST_ASSERT(profiler);
310
311 // Set the name of the output profiling file.
312 const std::string fileName = boost::str(boost::format("%1%/%2%_%3%.json")
313 % dumpDir
314 % std::to_string(networkId)
315 % "profiling");
316
317 // Open the ouput file for writing.
318 std::ofstream fileStream;
319 fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc);
320
321 if (!fileStream.good())
322 {
323 ALOGW("Could not open file %s for writing", fileName.c_str());
324 return;
325 }
326
327 // Write the profiling info to a JSON file.
328 profiler->Print(fileStream);
329}
330
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100331bool IsDynamicTensor(const armnn::TensorInfo& outputInfo)
332{
333 // Dynamic tensors have at least one 0-sized dimension
334 return outputInfo.GetNumElements() == 0u;
335}
336
telsoa015307bc12018-03-09 13:51:08 +0000337} // namespace armnn_driver