blob: 6350c2542cb3fedf6139d32c5ba7635ebea094a8 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
Colm Donelana9bea1a2023-04-24 21:51:44 +01002// Copyright © 2017, 2023 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5
6#pragma once
7
Matteo Martincighe5b8eb92019-11-28 15:45:42 +00008#include <armnn/backends/ITensorHandle.hpp>
Colm Donelan0c479742021-12-10 12:43:54 +00009#include <armnn/backends/TensorHandle.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000010#include <armnn/Tensor.hpp>
Jan Eilersbb446e52020-04-02 13:56:54 +010011#include <armnn/utility/PolymorphicDowncast.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000012#include <armnnUtils/Permute.hpp>
13
Kevin May665a964a2019-08-21 16:53:50 +010014#include <Half.hpp>
Matteo Martincigh747ef822018-12-18 09:26:39 +000015#include <Profiling.hpp>
Matteo Martincigh747ef822018-12-18 09:26:39 +000016
telsoa01c577f2c2018-08-31 09:22:23 +010017
18namespace armnn
19{
20namespace
21{
Matteo Martincigh747ef822018-12-18 09:26:39 +000022
Kevin May665a964a2019-08-21 16:53:50 +010023template <typename ArrayType, typename Arg>
telsoa01c577f2c2018-08-31 09:22:23 +010024void AssignValues(unsigned int num, unsigned int& idx, const ArrayType& array, Arg& arg)
25{
Matteo Martincigh747ef822018-12-18 09:26:39 +000026 if (idx >= num)
27 {
28 return;
29 }
telsoa01c577f2c2018-08-31 09:22:23 +010030
Matteo Martincigh747ef822018-12-18 09:26:39 +000031 arg = array[(num - 1) - idx];
32 idx++;
33}
telsoa01c577f2c2018-08-31 09:22:23 +010034
Kevin May665a964a2019-08-21 16:53:50 +010035template <typename T, typename ArrayType, typename... Args>
36void AssignValues(unsigned int num, unsigned int idx, const ArrayType& array, T& assignee, Args&... args)
telsoa01c577f2c2018-08-31 09:22:23 +010037{
Matteo Martincigh747ef822018-12-18 09:26:39 +000038 AssignValues(num, idx, array, assignee);
telsoa01c577f2c2018-08-31 09:22:23 +010039
Matteo Martincigh747ef822018-12-18 09:26:39 +000040 AssignValues(num, idx, array, args...);
telsoa01c577f2c2018-08-31 09:22:23 +010041}
Matteo Martincigh747ef822018-12-18 09:26:39 +000042
Kevin May665a964a2019-08-21 16:53:50 +010043} // anonymous namespace
telsoa01c577f2c2018-08-31 09:22:23 +010044
Kevin May665a964a2019-08-21 16:53:50 +010045template <typename CopyFunc>
telsoa01c577f2c2018-08-31 09:22:23 +010046void CopyTensorContentsGeneric(const ITensorHandle* srcTensor, ITensorHandle* dstTensor, CopyFunc copy)
47{
Matthew Benthamefdbca62019-09-14 23:35:28 +010048 // For ease of understanding, names are assigned to the dimensions
49 // of the tensor as if NHWC, however this routine works with any 5D tensor
Matthew Jacksondba634f2019-08-15 15:14:18 +010050 static_assert(MaxNumOfTensorDimensions == 5, "Please update CopyTensorContents");
telsoa01c577f2c2018-08-31 09:22:23 +010051
Kevin May665a964a2019-08-21 16:53:50 +010052 TensorShape srcStrides = srcTensor->GetStrides();
telsoa01c577f2c2018-08-31 09:22:23 +010053 const TensorShape& srcShape = srcTensor->GetShape();
Rob Hughes9934e4e2019-11-27 14:27:11 +000054 const auto srcSize = srcTensor->GetStrides()[0] * srcShape[0];
Kevin May665a964a2019-08-21 16:53:50 +010055 TensorShape dstStrides = dstTensor->GetStrides();
telsoa01c577f2c2018-08-31 09:22:23 +010056 const TensorShape& dstShape = dstTensor->GetShape();
Rob Hughes9934e4e2019-11-27 14:27:11 +000057 const auto dstSize = dstTensor->GetStrides()[0] * dstShape[0];
telsoa01c577f2c2018-08-31 09:22:23 +010058
Kevin May665a964a2019-08-21 16:53:50 +010059 size_t srcDepth = 1;
60 size_t srcBatches = 1;
Kevin May665a964a2019-08-21 16:53:50 +010061 size_t srcHeight = 1;
62 size_t srcWidth = 1;
Matthew Benthamefdbca62019-09-14 23:35:28 +010063 size_t srcChannels = 1;
Kevin May665a964a2019-08-21 16:53:50 +010064 AssignValues(srcShape.GetNumDimensions(),
65 0,
66 srcShape,
Matthew Benthamefdbca62019-09-14 23:35:28 +010067 srcChannels,
telsoa01c577f2c2018-08-31 09:22:23 +010068 srcWidth,
69 srcHeight,
Matthew Jacksondba634f2019-08-15 15:14:18 +010070 srcBatches,
71 srcDepth);
telsoa01c577f2c2018-08-31 09:22:23 +010072
Kevin May665a964a2019-08-21 16:53:50 +010073 size_t srcDepthStride = 0;
74 size_t srcBatchStride = 0;
Kevin May665a964a2019-08-21 16:53:50 +010075 size_t srcHeightStride = 0;
76 size_t srcWidthStride = 0;
Matthew Benthamefdbca62019-09-14 23:35:28 +010077 size_t srcChannelStride = 0;
Kevin May665a964a2019-08-21 16:53:50 +010078 AssignValues(srcStrides.GetNumDimensions(),
79 0,
80 srcStrides,
Matthew Benthamefdbca62019-09-14 23:35:28 +010081 srcChannelStride,
telsoa01c577f2c2018-08-31 09:22:23 +010082 srcWidthStride,
83 srcHeightStride,
Matthew Jacksondba634f2019-08-15 15:14:18 +010084 srcBatchStride,
85 srcDepthStride);
telsoa01c577f2c2018-08-31 09:22:23 +010086
Kevin May665a964a2019-08-21 16:53:50 +010087 size_t dstDepth = 1;
88 size_t dstBatches = 1;
Kevin May665a964a2019-08-21 16:53:50 +010089 size_t dstHeight = 1;
90 size_t dstWidth = 1;
Matthew Benthamefdbca62019-09-14 23:35:28 +010091 size_t dstChannels = 1;
Kevin May665a964a2019-08-21 16:53:50 +010092 AssignValues(dstShape.GetNumDimensions(),
93 0,
94 dstShape,
Matthew Benthamefdbca62019-09-14 23:35:28 +010095 dstChannels,
telsoa01c577f2c2018-08-31 09:22:23 +010096 dstWidth,
97 dstHeight,
Matthew Jacksondba634f2019-08-15 15:14:18 +010098 dstBatches,
99 dstDepth);
telsoa01c577f2c2018-08-31 09:22:23 +0100100
Kevin May665a964a2019-08-21 16:53:50 +0100101 size_t dstDepthStride = 0;
102 size_t dstBatchStride = 0;
Kevin May665a964a2019-08-21 16:53:50 +0100103 size_t dstHeightStride = 0;
104 size_t dstWidthStride = 0;
Matthew Benthamefdbca62019-09-14 23:35:28 +0100105 size_t dstChannelStride = 0;
Kevin May665a964a2019-08-21 16:53:50 +0100106 AssignValues(dstStrides.GetNumDimensions(),
107 0,
108 dstStrides,
Matthew Benthamefdbca62019-09-14 23:35:28 +0100109 dstChannelStride,
telsoa01c577f2c2018-08-31 09:22:23 +0100110 dstWidthStride,
111 dstHeightStride,
Matthew Jacksondba634f2019-08-15 15:14:18 +0100112 dstBatchStride,
113 dstDepthStride);
telsoa01c577f2c2018-08-31 09:22:23 +0100114
Rob Hughes9934e4e2019-11-27 14:27:11 +0000115 const unsigned char* srcDataStart;
116 unsigned char* dstDataStart;
Sadik Armaganbf86d512018-12-24 09:01:31 +0000117 {
118 ARMNN_SCOPED_PROFILING_EVENT(Compute::Undefined, "Synchronize buffers");
Rob Hughes9934e4e2019-11-27 14:27:11 +0000119 srcDataStart = static_cast<const uint8_t*>(srcTensor->Map());
120 dstDataStart = static_cast<uint8_t*>(dstTensor->Map());
Sadik Armaganbf86d512018-12-24 09:01:31 +0000121 }
Colm Donelana9bea1a2023-04-24 21:51:44 +0100122 if (srcDataStart == nullptr)
123 {
124 throw MemoryValidationException("The source tensor is null.");
125 }
126 if (dstDataStart == nullptr)
127 {
128 throw MemoryValidationException("The destination tensor is null.");
129 }
telsoa01c577f2c2018-08-31 09:22:23 +0100130
Rob Hughes9934e4e2019-11-27 14:27:11 +0000131 size_t copyLength = std::min(srcChannels * srcChannelStride, dstChannels * dstChannelStride);
Matthew Benthamefdbca62019-09-14 23:35:28 +0100132 size_t copyWidth = std::min(srcWidth, dstWidth);
133 size_t copyHeight = std::min(srcHeight, dstHeight);
134 size_t copyBatches = std::min(srcBatches, dstBatches);
135 size_t copyDepth = std::min(srcDepth, dstDepth);
telsoa01c577f2c2018-08-31 09:22:23 +0100136
Matthew Bentham019c4b12019-09-15 00:06:05 +0100137 // Coalesce inner dimensions where possible
138 // to reduce overheard calling copy() and to
139 // allow for memory bandwidth optimisations
140 if (copyLength == srcWidthStride &&
141 copyLength == dstWidthStride)
142 {
143 // There is no special padding between rows,
144 // and sizes are compatible, so copy whole rows
145 copyLength *= copyWidth;
146 copyWidth = 1;
147
148 if (copyLength == srcHeightStride &&
149 copyLength == dstHeightStride)
150 {
151 // There is no special padding between batches
152 // and sizes are compatible so copy whole batches
153 copyLength *= copyHeight;
154 copyHeight = 1;
155 }
156 }
157
Rob Hughes9934e4e2019-11-27 14:27:11 +0000158 const unsigned char* srcData = srcDataStart;
159 unsigned char* dstData = dstDataStart;
Kevin May665a964a2019-08-21 16:53:50 +0100160 for (unsigned int d = 0; d < copyDepth; ++d)
telsoa01c577f2c2018-08-31 09:22:23 +0100161 {
Matthew Jacksondba634f2019-08-15 15:14:18 +0100162 auto srcPtrDepth = srcData;
163 auto dstPtrDepth = dstData;
Kevin May665a964a2019-08-21 16:53:50 +0100164 for (unsigned int b = 0; b < copyBatches; ++b)
telsoa01c577f2c2018-08-31 09:22:23 +0100165 {
Matthew Jacksondba634f2019-08-15 15:14:18 +0100166 auto srcPtrBatch = srcData;
167 auto dstPtrBatch = dstData;
Matthew Benthamefdbca62019-09-14 23:35:28 +0100168 for (unsigned int h = 0; h < copyHeight; ++h)
telsoa01c577f2c2018-08-31 09:22:23 +0100169 {
Matthew Jacksondba634f2019-08-15 15:14:18 +0100170 auto srcPtrChannel = srcData;
171 auto dstPtrChannel = dstData;
Matthew Benthamefdbca62019-09-14 23:35:28 +0100172 for (unsigned int w = 0; w < copyWidth; ++w)
Matthew Jacksondba634f2019-08-15 15:14:18 +0100173 {
Colm Donelana9bea1a2023-04-24 21:51:44 +0100174 // Sanity check the memory area we've been asked to copy from and to.
175 if (copyLength > srcSize)
176 {
177 throw MemoryValidationException(
178 "The source tensor size does not match the size of the allocated tensor.");
179 }
180 if (copyLength > dstSize)
181 {
182 throw MemoryValidationException(
183 "The destination tensor size will overrun the destination tensor.");
184 }
Matthew Jacksondba634f2019-08-15 15:14:18 +0100185 copy(dstData, srcData, copyLength);
Matthew Benthamefdbca62019-09-14 23:35:28 +0100186 dstData += dstWidthStride;
187 srcData += srcWidthStride;
Matthew Jacksondba634f2019-08-15 15:14:18 +0100188 }
Matthew Benthamefdbca62019-09-14 23:35:28 +0100189 dstData += (static_cast<long>(dstHeightStride) - (dstData - dstPtrChannel));
190 srcData += (static_cast<long>(srcHeightStride) - (srcData - srcPtrChannel));
telsoa01c577f2c2018-08-31 09:22:23 +0100191 }
Kevin May665a964a2019-08-21 16:53:50 +0100192 dstData += (static_cast<long>(dstBatchStride) - (dstData - dstPtrBatch));
193 srcData += (static_cast<long>(srcBatchStride) - (srcData - srcPtrBatch));
telsoa01c577f2c2018-08-31 09:22:23 +0100194 }
Kevin May665a964a2019-08-21 16:53:50 +0100195 dstData += (static_cast<long>(dstDepthStride) - (dstData - dstPtrDepth));
196 srcData += (static_cast<long>(srcDepthStride) - (srcData - srcPtrDepth));
telsoa01c577f2c2018-08-31 09:22:23 +0100197 }
198
199 srcTensor->Unmap();
200 dstTensor->Unmap();
201}
202
203template <typename SrcTensorHandleType, typename DstTensorHandleType, typename DescriptorType>
204void GatherTensorHandlePairs(const DescriptorType& descriptor,
205 std::vector<std::pair<SrcTensorHandleType*, DstTensorHandleType*>>& tensorHandlePairs)
206{
207 const unsigned int numInputs = static_cast<unsigned int>(descriptor.m_Inputs.size());
208 tensorHandlePairs.reserve(numInputs);
209
210 for (unsigned int i = 0; i < numInputs; ++i)
211 {
Kevin May665a964a2019-08-21 16:53:50 +0100212 SrcTensorHandleType* const srcTensorHandle =
Jan Eilersbb446e52020-04-02 13:56:54 +0100213 PolymorphicDowncast<SrcTensorHandleType*>(descriptor.m_Inputs[i]);
Kevin May665a964a2019-08-21 16:53:50 +0100214 DstTensorHandleType* const dstTensorHandle =
Jan Eilersbb446e52020-04-02 13:56:54 +0100215 PolymorphicDowncast<DstTensorHandleType*>(descriptor.m_Outputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +0100216
217 tensorHandlePairs.emplace_back(srcTensorHandle, dstTensorHandle);
218 }
219}
220
Francis Murtaghec33a912019-11-05 14:26:23 +0000221int32_t ConvertMaskToACLFormat(int32_t mask, int32_t numDim);
222
James Conroy1f58f032021-04-27 17:13:27 +0100223armnn::ConstTensor PermuteTensor(const ConstTensorHandle* tensor,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000224 const PermutationVector& permutationVector,
225 void* permuteBuffer);
226
227void ReshapeWeightsForAcl(TensorInfo& weightInfo, DataLayout dataLayout);
228
229TensorInfo ConvertWeightTensorInfoFromArmnnToAcl(const TensorInfo& weightInfo, DataLayout dataLayout);
230
Jan Eilers53ef7952021-06-02 12:01:25 +0100231/// Weights for depthwise have a datalayout of [1,H,W,O] = [1,H,W,I*M]
232/// This function coverts a TensorInfo from [1,H,W,I*M] to [1,I*M,H,W] (if NCHW) or keeps it at [1,H,W,I*M] (if NHWC)
233/// as required by the compute library
234/// Returns a tuple of converted weights tensor info and depth multiplier
235std::tuple<TensorInfo, unsigned int> Convert1HWOTensorInfoToAcl(const TensorInfo& weightInfo,
236 const TensorInfo& inputInfo,
237 const DataLayout dataLayout);
238
James Conroy1f58f032021-04-27 17:13:27 +0100239armnn::ConstTensor ConvertWeightTensorFromArmnnToAcl(const ConstTensorHandle* weightTensor,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000240 DataLayout dataLayout,
241 void* permuteBuffer);
242
Jan Eilers53ef7952021-06-02 12:01:25 +0100243/// Weights for depthwise have a datalayout of [1,H,W,O] = [1,H,W,I*M]
244/// This function coverts a ConstCpuTensorHandle from [1,H,W,I*M] to [1,I*M,H,W] (if NCHW) or
245/// keeps it at [1,H,W,I*M] (if NHWC) as required by the compute library
246///
247/// \param weightTensor - ConstTensorHandle of weights tensor
248/// \param inputInfo - TensorInfo of input tensor
249/// \param dataLayout - DataLayout of the input tensor
250/// \param permuteBuffer - Pointer to memory with the size of tensor. Used for the permutation
251/// \return tuple of transformed weights-ConstTensor and depthwise multiplier
252std::tuple<ConstTensor, unsigned int> Convert1HWOTensorToAcl(const ConstTensorHandle* weightTensor,
253 const TensorInfo& inputInfo,
254 const DataLayout dataLayout,
255 void* permuteBuffer);
256
257/// Converts a (weights) tensor from [1, H, W, I*M] = [1, H, W, O] to [M, I, H, W]
258///
259/// \param weightTensor - ConstTensorHandle of the weight tensor that should be converted
260/// \param inputInfo - TensorInfo of the corresponding input tensor
261/// \param dataLayout - DataLayout of the input tensor e.g. NHWC or NCHW
262/// \param permuteBuffer - Memory location with the same size as the weight tensor to write converted data to
263/// \return - A tuple of ConstTensor and unsigned int which is the converted weightTensor and the depthMultiplier
264std::tuple<ConstTensor, unsigned int> Convert1HWOtoMIHW(const ConstTensorHandle* weightTensor,
265 const TensorInfo& inputInfo,
266 const DataLayout& dataLayout,
267 void* permuteBuffer);
268
Teresa Charlinb2d3ec52022-04-12 22:07:09 +0100269/// Calculates the key index values needed for GatherNd: N, ND, K, W, C (N is always 1)
270///
271/// \param inputInfo0 - TensorInfo of the corresponding input tensor: params
272/// \param inputInfo1 - TensorInfo of the corresponding input tensor: indices
273/// \return - A map with names and values for N, ND, K, W, C
274std::map<std::string, unsigned int> CalculateGatherNdKeyIndices(TensorInfo inputInfo0, TensorInfo inputInfo1);
275
Teresa Charlin0f86ecf2022-10-13 15:47:08 +0100276/// Generates a permutation vector of size rank that permutes the 2 most right dimensions
277///
278/// \param rank - Tensor rank, i.e. number of dimensions in the tensors
279/// \return - A permutation vector that permutes the 2 last dimensions
280armnn::PermutationVector GeneratePermutationVectorOnLastTwoDimensions(unsigned int rank);
281
Kevin May665a964a2019-08-21 16:53:50 +0100282} //namespace armnn