ArmNN
 22.02
WorkloadUtils.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
7 
8 #include <armnn/Utils.hpp>
11 
12 #include <fmt/format.h>
13 
14 namespace armnn
15 {
16 
18  const PermutationVector& permutationVector, void* permuteBuffer)
19 {
20  ARMNN_ASSERT_MSG(tensor, "Invalid input tensor");
21  ARMNN_ASSERT_MSG(permuteBuffer, "Invalid permute buffer");
22 
23  TensorInfo tensorInfo = tensor->GetTensorInfo();
24 
25  if (permutationVector.GetSize() > 0)
26  {
27  tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector);
28  armnnUtils::Permute(tensorInfo.GetShape(), permutationVector,
29  tensor->GetConstTensor<void>(), permuteBuffer,
30  GetDataTypeSize(tensorInfo.GetDataType()));
31  }
32  else
33  {
34  ::memcpy(permuteBuffer, tensor->GetConstTensor<void>(), tensorInfo.GetNumBytes());
35  }
36  tensorInfo.SetConstant(true);
37  return ConstTensor(tensorInfo, permuteBuffer);
38 }
39 
40 void ReshapeWeightsForAcl(TensorInfo& weightInfo, DataLayout dataLayout)
41 {
42  // Reshape the weights in-place
43  const TensorShape& weightShape = weightInfo.GetShape();
44  switch (dataLayout)
45  {
46  case DataLayout::NHWC:
47  // The data layout is NHWC, reshape from [ H, W, I, M ] to [ 1, H, W, I * M ]
48  weightInfo.SetShape({ 1,
49  weightShape[0],
50  weightShape[1],
51  weightShape[2] * weightShape[3] });
52  weightInfo.SetShape({ 1,
53  weightShape[0] * weightShape[1],
54  weightShape[2],
55  weightShape[3] });
56  break;
57  case DataLayout::NCHW:
58  default:
59  // The data layout is NCHW, reshape from [ M, I, H, W ] to [ 1, I * M, H, W, ]
60  weightInfo.SetShape({ 1, weightShape[0] * weightShape[1], weightShape[2], weightShape[3] });
61  break;
62  }
63 }
64 
65 template <typename DataType>
66 ConstTensor ReorderWeightChannelsForAcl(const ConstTensor& weightHandle, DataLayout dataLayout, void* permuteBuffer)
67 {
68  DataType* weight = static_cast<DataType*>(permuteBuffer);
69  const TensorShape& weightShape = weightHandle.GetShape();
70  unsigned int multiplier;
71  unsigned int height;
72  unsigned int width;
73  unsigned int inputChannels;
74  switch (dataLayout)
75  {
76  case DataLayout::NHWC: //It actually is [ H, W, I, M ]
77  height = weightShape[0];
78  width = weightShape[1];
79  inputChannels = weightShape[2];
80  multiplier = weightShape[3];
81  break;
82  case DataLayout::NCHW: //It actually is [ M, I, H, W ]
83  default:
84  height = weightShape[2];
85  width = weightShape[3];
86  inputChannels = weightShape[1];
87  multiplier = weightShape[0];
88  break;
89  }
90 
91  std::vector<DataType> weightAclOrder(height*width*inputChannels*multiplier);
92  unsigned int destinationWeightsChannel;
93  unsigned int totalChannels = inputChannels * multiplier;
94  unsigned int channelSize = height * width;
95  unsigned int inputChannel = 0;
96 
97  for (unsigned int originWeightsChannel = 0; originWeightsChannel < totalChannels; originWeightsChannel++)
98  {
99  inputChannel = originWeightsChannel % inputChannels;
100  destinationWeightsChannel = (originWeightsChannel - inputChannel) / inputChannels + multiplier * inputChannel;
101 
102  for (unsigned int i = 0; i < channelSize; i++)
103  {
104  weightAclOrder[i + destinationWeightsChannel * channelSize] =
105  weight[i + originWeightsChannel * channelSize];
106  }
107  }
108 
109  ::memcpy(permuteBuffer, weightAclOrder.data(), weightHandle.GetInfo().GetNumBytes());
110  return ConstTensor(weightHandle.GetInfo(), permuteBuffer);
111 }
112 
113 
115 {
116  // Convert the weight format from ArmNN's [ M, I, H, W ] (does NOT depend on the data layout) to either
117  // [ 1, H, W, I * M ] (if NHWC) or [ 1, I * M, H, W ] (if NCHW), as required by the compute library
118 
119  // 1. Permute the weights if necessary
120  // If the data layout is NCHW no permutation is necessary, as a reshape to [ 1, I * M, H, W ] can be better done
121  // starting from the current shape of [ M, I, H, W ]
122  TensorInfo weightPermutedInfo(weightInfo);
123  if (dataLayout == DataLayout::NHWC)
124  {
125  // The data layout is NHWC, then permute the weights from [ M, I, H, W ] to [ H, W, I, M ]
126  PermutationVector permutationVector{ 3, 2, 0, 1 };
127  weightPermutedInfo = armnnUtils::Permuted(weightInfo, permutationVector);
128  }
129 
130  // 2. Reshape the weights
131  ReshapeWeightsForAcl(weightPermutedInfo, dataLayout);
132 
133  // 3. Return the permuted weight info
134  return weightPermutedInfo;
135 }
136 
137 
138 std::tuple<ConstTensor, unsigned int> Convert1HWOTensorToAcl(const ConstTensorHandle* weightTensor,
139  const TensorInfo& inputInfo,
140  const DataLayout dataLayout,
141  void* permuteBuffer)
142 {
143  TensorInfo weightsInfo = weightTensor->GetTensorInfo();
144  unsigned int depthMultiplier = 1;
145  PermutationVector permutationVector{};
146  if (dataLayout == armnn::DataLayout::NHWC)
147  {
148  // No permutation required. Data layouts are the same.
149 
150  depthMultiplier = weightsInfo.GetShape()[3] / inputInfo.GetShape()[3];
151  }
152  else if (dataLayout == armnn::DataLayout::NCHW)
153  {
154  // [ 1, H, W, I*M] --> [ 1, I * M, H, W ]
155  depthMultiplier = weightsInfo.GetShape()[3] / inputInfo.GetShape()[1];
156  permutationVector = { 0, 2, 3, 1 };
157  }
158  else
159  {
160  throw InvalidArgumentException(fmt::format("Unknown data layout for tensor conversion: {}",
161  GetDataLayoutName(dataLayout)));
162  }
163 
164  ConstTensor weightsPermuted = PermuteTensor(weightTensor, permutationVector, permuteBuffer);
165 
166  return std::make_tuple(weightsPermuted, depthMultiplier);
167 }
168 
169 std::tuple<TensorInfo, unsigned int> Convert1HWOTensorInfoToAcl(const TensorInfo& weightInfo,
170  const TensorInfo& inputInfo,
171  const DataLayout dataLayout)
172 {
173  unsigned int aclDepthMultiplier = 1;
174  TensorInfo weightsPermuted;
175  if (dataLayout == armnn::DataLayout::NHWC)
176  {
177  // No permutation required. Data layouts are the same.
178  aclDepthMultiplier = weightInfo.GetShape()[3] / inputInfo.GetShape()[3];
179  weightsPermuted = weightInfo;
180  }
181  else if (dataLayout == armnn::DataLayout::NCHW)
182  {
183  // [ 1, H, W, I*M] --> [ 1, I * M, H, W ]
184  aclDepthMultiplier = weightInfo.GetShape()[3] / inputInfo.GetShape()[1];
185  PermutationVector permutationVector{ 0, 2, 3, 1 };
186  weightsPermuted = armnnUtils::Permuted(weightInfo, permutationVector);
187  }
188  else
189  {
190  throw InvalidArgumentException(fmt::format("Unknown data layout for tensor info conversion: {}",
191  GetDataLayoutName(dataLayout)));
192  }
193 
194  return std::make_tuple(weightsPermuted, aclDepthMultiplier);
195 }
196 
197 
198 std::tuple<ConstTensor, unsigned int> Convert1HWOtoMIHW(const ConstTensorHandle* weightTensor,
199  const TensorInfo& inputInfo,
200  const DataLayout& dataLayout,
201  void* permuteBuffer)
202 {
203  TensorInfo weightsInfo = weightTensor->GetTensorInfo();
204 
205  if (weightsInfo.HasPerAxisQuantization())
206  {
207  throw InvalidArgumentException("Can't convert tensor from [1,H,W,Cout] to [M,Cin,H,W] when per channel "
208  "quantization is applied.");
209  }
210 
211  // Reshape weights [ 1, H, W, I*M ] --> [ H, W, I, M ]
212  auto weightsShape = weightsInfo.GetShape();
213  auto channelIndex = armnnUtils::DataLayoutIndexed(dataLayout).GetChannelsIndex();
214  unsigned int depthMultiplier = weightsShape[3] / inputInfo.GetShape()[channelIndex];
215  weightsInfo.SetShape({ weightsShape[1],
216  weightsShape[2],
217  inputInfo.GetShape()[channelIndex],
218  depthMultiplier});
219 
220  // Permute [ H, W, I, M ] --> [ M, I, H, W ]
221  PermutationVector permutationVector = { 2, 3, 1, 0 };
222  ConstTensor weightsPermuted = PermuteTensor(weightTensor, permutationVector, permuteBuffer);
223 
224  return std::make_tuple(weightsPermuted, depthMultiplier);
225 }
226 
228  DataLayout dataLayout,
229  void* permuteBuffer)
230 {
231  ARMNN_ASSERT_MSG(weightTensor, "Invalid input tensor");
232  ARMNN_ASSERT_MSG(permuteBuffer, "Invalid permute buffer");
233 
234  auto multiplier = weightTensor->GetTensorInfo().GetShape()[0];
235  auto inputChannels = weightTensor->GetTensorInfo().GetShape()[1];
236 
237  // Convert the weight format from ArmNN's [ M, I, H, W ] (does NOT depend on the data layout) to either
238  // [ 1, H, W, I * M ] (if NHWC) or [ 1, I * M, H, W ] (if NCHW), as required by the compute library
239 
240  // 1. Permute the weights if necessary
241  // If the data layout is NCHW no permutation is necessary, as a reshape to [ 1, I * M, H, W ] can be better done
242  // starting from the current shape of [ M, I, H, W ]
243  // If no permutation is necessary, leave the permutation vector empty
244  PermutationVector permutationVector{};
245  if (dataLayout == DataLayout::NHWC)
246  {
247  // The data layout is NHWC, then permute the weights from [ M, I, H, W ] to [ H, W, I, M ]
248  permutationVector = { 3, 2, 0, 1 };
249  }
250  ConstTensor weightPermuted = PermuteTensor(weightTensor, permutationVector, permuteBuffer);
251 
252  // Shuffle the weights data to obtain the channel order needed used by Acl
253  if (multiplier > 1 && inputChannels > 1 && dataLayout == DataLayout::NCHW)
254  {
255  switch (weightPermuted.GetDataType())
256  {
257  case DataType::Float32:
258  weightPermuted = ReorderWeightChannelsForAcl<float>(weightPermuted, dataLayout, permuteBuffer);
259  break;
260  case DataType::Float16:
261  weightPermuted =
262  ReorderWeightChannelsForAcl<half_float::half>(weightPermuted, dataLayout, permuteBuffer);
263  break;
264  case DataType::QAsymmS8:
265  case DataType::QAsymmU8:
266  weightPermuted = ReorderWeightChannelsForAcl<uint8_t>(weightPermuted, dataLayout, permuteBuffer);
267  break;
268  case DataType::QSymmS8:
269  weightPermuted = ReorderWeightChannelsForAcl<int8_t>(weightPermuted, dataLayout, permuteBuffer);
270  break;
271  default:
272  break;
273  }
274  }
275 
276  // 2. Reshape the weights
277  ReshapeWeightsForAcl(weightPermuted.GetInfo(), dataLayout);
278 
279  // 3. Return both the tensor and the allocated storage to ensure that the data stays alive
280  return weightPermuted;
281 }
282 
283 int32_t ConvertMaskToACLFormat(int32_t mask, int32_t numDim)
284 {
285  int32_t reversedMask = 0;
286  for (unsigned int i = 0; i < armnn::numeric_cast<unsigned int>(numDim); ++i)
287  {
288  // Check if bit set in mask for each dimension
289  int32_t bit = (mask & 1 << i) != 0;
290  // Increment the new mask with the bits reversed
291  reversedMask += (bit << std::max(numDim-(armnn::numeric_cast<int>(i)+1), 0));
292  }
293 
294  return reversedMask;
295 }
296 
297 } // namespace armnn
armnn::ConstTensor PermuteTensor(const ConstTensorHandle *tensor, const PermutationVector &permutationVector, void *permuteBuffer)
constexpr const char * GetDataLayoutName(DataLayout dataLayout)
Definition: TypesUtils.hpp:222
DataLayout
Definition: Types.hpp:49
const TensorShape & GetShape() const
Definition: Tensor.hpp:191
TensorInfo ConvertWeightTensorInfoFromArmnnToAcl(const TensorInfo &weightInfo, DataLayout dataLayout)
bool HasPerAxisQuantization() const
Definition: Tensor.cpp:448
armnn::ConstTensor ConvertWeightTensorFromArmnnToAcl(const ConstTensorHandle *weightTensor, DataLayout dataLayout, void *permuteBuffer)
const TensorShape & GetShape() const
Definition: Tensor.hpp:297
unsigned int GetNumBytes() const
Definition: Tensor.cpp:429
Copyright (c) 2021 ARM Limited and Contributors.
SizeType GetSize() const
Definition: Types.hpp:325
ConstTensor ReorderWeightChannelsForAcl(const ConstTensor &weightHandle, DataLayout dataLayout, void *permuteBuffer)
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:193
const TensorInfo & GetTensorInfo() const
std::tuple< ConstTensor, unsigned int > Convert1HWOTensorToAcl(const ConstTensorHandle *weightTensor, const TensorInfo &inputInfo, const DataLayout dataLayout, void *permuteBuffer)
Weights for depthwise have a datalayout of [1,H,W,O] = [1,H,W,I*M] This function coverts a ConstCpuTe...
DataType
Definition: Types.hpp:35
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout...
DataType GetDataType() const
Definition: Tensor.hpp:198
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
std::tuple< TensorInfo, unsigned int > Convert1HWOTensorInfoToAcl(const TensorInfo &weightInfo, const TensorInfo &inputInfo, const DataLayout dataLayout)
Weights for depthwise have a datalayout of [1,H,W,O] = [1,H,W,I*M] This function coverts a TensorInfo...
const TensorInfo & GetInfo() const
Definition: Tensor.hpp:295
int32_t ConvertMaskToACLFormat(int32_t mask, int32_t numDim)
std::tuple< ConstTensor, unsigned int > Convert1HWOtoMIHW(const ConstTensorHandle *weightTensor, const TensorInfo &inputInfo, const DataLayout &dataLayout, void *permuteBuffer)
Converts a (weights) tensor from [1, H, W, I*M] = [1, H, W, O] to [M, I, H, W].
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition: Tensor.cpp:516
DataType GetDataType() const
Definition: Tensor.hpp:300
void ReshapeWeightsForAcl(TensorInfo &weightInfo, DataLayout dataLayout)
unsigned int GetChannelsIndex() const
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition: Permute.cpp:98
const T * GetConstTensor() const
constexpr unsigned int GetDataTypeSize(DataType dataType)
Definition: TypesUtils.hpp:151