1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
5 #include <vpu/model/data_desc.hpp>
12 #include <unordered_map>
13 #include <unordered_set>
16 #include <precision_utils.h>
18 #include <vpu/model/edges.hpp>
19 #include <vpu/utils/ie_helpers.hpp>
20 #include <vpu/utils/numeric.hpp>
30 const StorageOrder64 ORDER_MASK = static_cast<StorageOrder64>(-1ull) >> (std::numeric_limits<StorageOrder64>::digits / 4 - MAX_DIMS_64);
34 StorageOrder64 maskOrder(StorageOrder64 fullOrder, int size) {
35 StorageOrder64 mask = ~ORDER_MASK | ~(static_cast<StorageOrder64>(-1ull) << (size * 4));
36 return fullOrder & mask;
39 DimsOrder DimsOrder::C = DimsOrder::fromCode(0x3);
40 DimsOrder DimsOrder::NC = DimsOrder::fromCode(0x43);
41 DimsOrder DimsOrder::CHW = DimsOrder::fromCode(0x321);
42 DimsOrder DimsOrder::HWC = DimsOrder::fromCode(0x213);
43 DimsOrder DimsOrder::HCW = DimsOrder::fromCode(0x231);
44 DimsOrder DimsOrder::NCHW = DimsOrder::fromCode(0x4321);
45 DimsOrder DimsOrder::NHWC = DimsOrder::fromCode(0x4213);
46 DimsOrder DimsOrder::NHCW = DimsOrder::fromCode(0x4231);
47 DimsOrder DimsOrder::NCDHW = DimsOrder::fromCode(0x43521);
48 DimsOrder DimsOrder::NDHWC = DimsOrder::fromCode(0x45213);
52 bool isOrderCodeValid(StorageOrder64 order) {
57 std::unordered_set<int> usedDims;
59 auto orderCopy = order;
63 for (int i = 0; i < MAX_DIMS_64; i++) {
64 int digit = orderCopy & 0xF;
71 // Dimension is used more than once
72 if (usedDims.count(digit) > 0) {
75 usedDims.insert(digit);
82 orderCopy = order >> (4 * length);
84 // All digits on positions upper or equal to the order length should be UNDEF
85 for (int i = length; i < MAX_DIMS_64; i++) {
86 int digit = orderCopy & 0xF;
99 DimsOrder DimsOrder::fromCode(StorageOrder64 code) {
100 IE_ASSERT(isOrderCodeValid(code));
106 DimsOrder DimsOrder::fromNumDims(int numDims) {
107 static const StorageOrder64 FULL_ORDER_DEFAULT =
108 maskOrder(static_cast<StorageOrder64>(0x0fedcba987654321ull), MAX_DIMS_64);
112 } else if (numDims == 2) {
113 return DimsOrder::NC;
114 } else if (numDims == 3) {
115 return DimsOrder::CHW;
116 } else if (numDims == 4) {
117 return DimsOrder::NCHW;
118 } else if (numDims == 5) {
119 return DimsOrder::NCDHW;
121 return DimsOrder::fromCode(maskOrder(FULL_ORDER_DEFAULT, numDims));
125 DimsOrder DimsOrder::fromPermutation(const DimVector& perm) {
126 StorageOrder64 code = 0;
128 for (int sh = 0, i = 0; i < perm.size(); i++, sh += 4) {
129 code += (((static_cast<StorageOrder64>(perm[i]) + 1ull) & 0xFull) << sh);
132 return DimsOrder::fromCode(code);
135 DimsOrder DimsOrder::fromLayout(ie::Layout const& layout) {
137 case ie::Layout::C : return DimsOrder::C;
138 case ie::Layout::NC : return DimsOrder::NC;
139 case ie::Layout::CHW : return DimsOrder::CHW;
140 case ie::Layout::NCHW : return DimsOrder::NCHW;
141 case ie::Layout::NHWC : return DimsOrder::NHWC;
142 case ie::Layout::NCDHW : return DimsOrder::NCDHW;
143 case ie::Layout::NDHWC : return DimsOrder::NDHWC;
145 VPU_THROW_EXCEPTION << "Unsupported layout " << layout;
149 int DimsOrder::numDims() const {
154 for (int i = 0; i < MAX_DIMS_64; i++) {
155 auto digit = code & 0xF;
167 bool DimsOrder::hasDim(Dim d) const {
168 auto dimDigit = static_cast<int>(d) + 1;
172 for (int i = 0; i < MAX_DIMS_64; i++) {
173 auto digit = code & 0xF;
177 if (digit == dimDigit) {
187 int DimsOrder::dimInd(Dim d) const {
188 auto dimDigit = static_cast<int>(d) + 1;
192 for (int i = 0; i < MAX_DIMS_64; i++) {
193 auto digit = code & 0xF;
197 if (digit == dimDigit) {
204 VPU_THROW_EXCEPTION << "Dim " << d << " is not avaialble in layout " << toString(*this);
207 SmallVector<Dim, MAX_DIMS_64> DimsOrder::toPermutation() const {
208 SmallVector<Dim, MAX_DIMS_64> out;
212 for (int i = 0; i < MAX_DIMS_64; i++) {
213 auto digit = code & 0xF;
217 auto d = static_cast<Dim>(digit - 1);
226 DimValues DimsOrder::toIndices() const {
231 for (int i = 0; i < MAX_DIMS_64; i++) {
232 auto digit = code & 0xF;
236 auto d = static_cast<Dim>(digit - 1);
245 void DimsOrder::moveDim(Dim dim, int newPos) {
246 IE_ASSERT(newPos >= 0 && newPos < numDims());
248 int oldPos = dimInd(dim);
249 if (oldPos == newPos)
252 auto step = (oldPos > newPos) ? -1 : 1;
254 auto perm = toPermutation();
255 IE_ASSERT(newPos < perm.size());
257 for (int i = oldPos; i != newPos; i += step) {
258 perm[i] = perm[i + step];
263 _code = fromPermutation(perm).code();
266 DimsOrder DimsOrder::createMovedDim(Dim dim, int newPos) const {
268 copy.moveDim(dim, newPos);
272 bool isOrdersCompatible(DimsOrder order1, DimsOrder order2) {
273 auto vec1 = order1.toPermutation();
274 auto vec2 = order2.toPermutation();
276 std::sort(vec1.begin(), vec1.end());
277 std::sort(vec2.begin(), vec2.end());
282 void printTo(std::ostream& os, DimsOrder order) {
283 static std::unordered_map<int, char> DIM_NAMES({
291 auto code = order.code();
293 int i = MAX_DIMS_64 - 1;
295 for (; i >= 0; i--) {
296 auto curDim = (code >> (i * 4)) & 0xF;
302 for (; i >= 0; i--) {
303 auto curDim = (code >> (i * 4)) & 0xF;
305 auto it = DIM_NAMES.find(curDim);
306 if (it != DIM_NAMES.end()) {
318 int dimToIeInd(vpu::Dim const& dim, int numDims) {
319 IE_ASSERT(1 <= numDims && numDims <= 8);
320 auto dimsOrder = DimsOrder::fromNumDims(numDims);
321 int dimInd = dimsOrder.dimInd(dim);
322 return (numDims - 1) - dimInd;
329 DataDesc::DataDesc(const ie::TensorDesc& ieDesc) {
334 _type = fromIEPrecision(ieDesc.getPrecision());
337 // Parse dimensions and layout
340 const auto& ieDims = ieDesc.getDims();
341 IE_ASSERT(!ieDims.empty());
343 _dimsOrder = DimsOrder::fromNumDims(ieDims.size());
345 auto perm = _dimsOrder.toPermutation();
347 for (int i = 0; i < perm.size(); ++i) {
348 _dims.set(perm[i], ieDims[ieDims.size() - 1 - i]);
352 DataDesc::DataDesc(DataType type, DimsOrder dimsOrder, const DimValues& dims) :
353 _type(type), _dimsOrder(dimsOrder), _dims(dims) {
354 IE_ASSERT(_dimsOrder.numDims() == _dims.size());
355 for (const auto& p : _dims) {
356 IE_ASSERT(_dimsOrder.hasDim(p.first));
360 int DataDesc::elemSize() const {
363 return sizeof(uint8_t);
365 return sizeof(int8_t);
367 return sizeof(fp16_t);
369 return sizeof(float);
371 return sizeof(int32_t);
373 VPU_THROW_EXCEPTION << "Unknown data type " << _type;
377 void DataDesc::setDim(Dim d, int val) {
378 IE_ASSERT(_dimsOrder.hasDim(d));
382 int DataDesc::totalDimSize() const {
385 auto perm = _dimsOrder.toPermutation();
386 for (auto d : perm) {
393 void DataDesc::reorder(DimsOrder dimsOrder) {
394 IE_ASSERT(isOrdersCompatible(_dimsOrder, dimsOrder));
395 _dimsOrder = dimsOrder;
398 ie::TensorDesc DataDesc::toTensorDesc() const {
401 switch (this->type()) {
403 desc.setPrecision(ie::Precision::FP16);
406 desc.setPrecision(ie::Precision::FP32);
409 desc.setPrecision(ie::Precision::I8);
412 desc.setPrecision(ie::Precision::U8);
415 desc.setPrecision(ie::Precision::I32);
418 desc.setPrecision(ie::Precision::UNSPECIFIED);
421 ie::SizeVector dims{};
423 DataDesc descCopy = *this;
424 descCopy.reorder(DimsOrder::fromNumDims(this->numDims()));
425 auto perm = descCopy.dimsOrder().toPermutation();
426 std::reverse(perm.begin(), perm.end());
427 for (auto &p : perm) {
428 dims.push_back(descCopy.dim(p));
433 if (DimsOrder::C == this->dimsOrder()) {
434 desc.setLayout(ie::Layout::C);
435 } else if (DimsOrder::NC == this->dimsOrder()) {
436 desc.setLayout(ie::Layout::NC);
437 } else if (DimsOrder::CHW == this->dimsOrder()) {
438 desc.setLayout(ie::Layout::CHW);
439 } else if (DimsOrder::NCHW == this->dimsOrder()) {
440 desc.setLayout(ie::Layout::NCHW);
441 } else if (DimsOrder::NHWC == this->dimsOrder()) {
442 desc.setLayout(ie::Layout::NHWC);
443 } else if (DimsOrder::NCDHW == this->dimsOrder()) {
444 desc.setLayout(ie::Layout::NCDHW);
445 } else if (DimsOrder::NDHWC == this->dimsOrder()) {
446 desc.setLayout(ie::Layout::NDHWC);
448 desc.setLayout(ie::Layout::BLOCKED);
454 void printTo(std::ostream& os, const DataDesc& desc) {
455 os << "[" << std::endl;
458 printTo(os, desc.type());
462 printTo(os, desc.dimsOrder());
466 printTo(os, desc.dims());
472 void printTo(DotLabel& lbl, const DataDesc& desc) {
473 DotLabel subLbl(lbl);
474 subLbl.appendPair("type", desc.type());
475 subLbl.appendPair("dimsOrder", desc.dimsOrder());
476 subLbl.appendPair("dims", desc.dims());
480 // StridesRequirement
483 StridesRequirement StridesRequirement::compact() {
484 StridesRequirement reqs;
485 for (int i = 0; i < MAX_DIMS_64; ++i) {
486 reqs.add(i, DimStride::Compact);
491 StridesRequirement StridesRequirement::fixed(const std::vector<int>& strides, const DataDesc& desc) {
492 StridesRequirement reqs;
494 const auto dims = desc.dims();
495 const auto dimsOrder = desc.dimsOrder();
496 const auto dimOrderVec = dimsOrder.toPermutation();
497 auto setStride = [&] (Dim d, int val) {
498 IE_ASSERT(dimsOrder.hasDim(d));
500 auto perm = dimsOrder.toPermutation();
501 auto idx = dimsOrder.dimInd(d);
503 auto minStrideVal = idx == 0 ? desc.elemSize() : reqs._fixedStrides[perm[idx - 1]] * dims[perm[idx - 1]];
504 IE_ASSERT(val >= minStrideVal);
506 reqs._fixedStrides.set(d, val);
509 for (const auto& dim : dimOrderVec) {
510 const auto idx = dimToIeInd(dim, dims.size());
511 setStride(dim, strides[idx]);
514 for (int i = 0; i < MAX_DIMS_64; ++i) {
515 reqs.add(i, DimStride::Fixed);
520 void printTo(std::ostream& os, const StridesRequirement& reqs) {
521 os << "[" << std::endl;
523 for (int i = 0; i < MAX_DIMS_64; ++i) {
524 auto req = reqs.get(i);
525 if (req != DimStride::Any) {
536 void printTo(DotLabel& lbl, const StridesRequirement& reqs) {
537 DotLabel subLbl(lbl);
538 for (int i = 0; i < MAX_DIMS_64; ++i) {
539 auto req = reqs.get(i);
540 if (req != DimStride::Any) {
541 subLbl.appendPair(i, req);
548 int applyStrideRequirement(int origStride, int index, const StridesRequirement& reqs) {
549 auto req = reqs.get(index);
551 if (req == DimStride::Any || req == DimStride::Compact) {
553 } else if (req == DimStride::Aligned) {
554 return alignVal(origStride, STRIDE_ALIGNMENT);
556 VPU_THROW_EXCEPTION << "Unknown stride requirement : " << req;
562 DimValues calcStrides(const DataDesc& desc, const StridesRequirement& reqs) {
565 auto perm = desc.dimsOrder().toPermutation();
566 IE_ASSERT(!perm.empty());
568 strides = reqs.fixedStrides();
570 if (strides.empty()) {
571 strides.set(perm[0], desc.elemSize());
572 strides.set(perm[0], applyStrideRequirement(strides[perm[0]], 0, reqs));
574 for (std::size_t i = 1; i < perm.size(); i++) {
575 strides.set(perm[i], strides[perm[i - 1]] * desc.dim(perm[i - 1]));
576 strides.set(perm[i], applyStrideRequirement(strides[perm[i]], i, reqs));
584 const DimValues& strides,
585 const DataDesc& desc,
587 const StridesRequirement& reqs) {
588 const auto req = reqs.get(ind);
589 if (req == DimStride::Any) {
593 auto perm = desc.dimsOrder().toPermutation();
594 IE_ASSERT(!perm.empty());
596 auto strideVal = strides[perm[ind]];
598 if (req == DimStride::Compact) {
600 if (strideVal != desc.elemSize()) {
604 if (strides[perm[ind]] != strides[perm[ind - 1]] * desc.dim(perm[ind - 1])) {
608 } else if (req == DimStride::Aligned) {
609 if (strideVal % STRIDE_ALIGNMENT != 0) {
612 } else if (req == DimStride::Fixed) {
613 if (strideVal != reqs.getFixedStride(perm[ind])) {
617 VPU_THROW_EXCEPTION << "Unsupported stride requirement : " << req;
624 const DataDesc& desc,
625 const DimValues& strides,
626 const StridesRequirement& reqs) {
627 auto perm = desc.dimsOrder().toPermutation();
628 IE_ASSERT(!perm.empty());
630 for (int i = 0; i < perm.size(); i++) {
631 if (!checkStride(strides, desc, i, reqs)) {
639 int calcTotalByteSize(const DataDesc& desc, const DimValues& strides) {
640 auto perm = desc.dimsOrder().toPermutation();
641 return strides[perm.back()] * desc.dim(perm.back());
644 DataType fromIEPrecision(const InferenceEngine::Precision& precision) {
646 case InferenceEngine::Precision::U8: return DataType::U8;
647 case InferenceEngine::Precision::I8: return DataType::I8;
648 case InferenceEngine::Precision::I32: return DataType::S32;
649 case InferenceEngine::Precision::FP16: return DataType::FP16;
650 case InferenceEngine::Precision::FP32: return DataType::FP32;
651 default: VPU_THROW_EXCEPTION << precision << " isn't supported";