/******************************************************************************* * * MIT License * * Copyright (c) 2017 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #ifndef GUARD_MIOPEN_TENSOR_DRIVER_HPP #define GUARD_MIOPEN_TENSOR_DRIVER_HPP #define UNPACK_VEC4(v) (v[0]), (v[1]), (v[2]), (v[3]) #include #include #include #include #include #include #include #include inline miopenTensorLayout_t StringToLayoutType(std::string layout_str) { miopenTensorLayout_t default_layout = miopenTensorNCHW; if(layout_str == "NCHWc4") return miopenTensorNCHWc4; else if(layout_str == "NCHWc8") return miopenTensorNCHWc8; else if(layout_str == "CHWNc4") return miopenTensorCHWNc4; else if(layout_str == "CHWNc8") return miopenTensorCHWNc8; else if(layout_str == "NCHW") { return miopenTensorNCHW; } else if(layout_str == "NHWC") { return miopenTensorNHWC; } else if(layout_str == "NDHWC") { return miopenTensorNDHWC; } else if(layout_str == "NCDHW") { return miopenTensorNCDHW; } else { MIOPEN_THROW("We only support NCHWc4, NCHWc8, CHWNc4, CHWNc8, NCHW, NHWC, NDHWC, NCDHW " "vectorized tensor layout."); return default_layout; } } inline void LengthReorder(std::vector& lens, const std::initializer_list& indices) { std::vector out_lens; out_lens.reserve(indices.size()); for(int index : indices) { assert(0 <= index && index < lens.size()); out_lens.push_back(std::move(lens[index])); } lens = std::move(out_lens); } inline std::size_t GetTensorVectorLength(const miopenTensorDescriptor_t& tensor) { std::size_t vectorLength; int size = 0; miopenGetTensorDescriptorSize(tensor, &size); miopenGetNdTensorDescriptorVectorLength(tensor, &vectorLength); return vectorLength; } inline std::vector GetTensorLengths(const miopenTensorDescriptor_t& tensor) { int n; int c; int h; int w; int d; int size = 0; miopenGetTensorDescriptorSize(tensor, &size); if(size == 5) { miopenGet5dTensorDescriptorLengths(tensor, &n, &c, &d, &h, &w); return std::vector({n, c, d, h, w}); } else if(size == 4) { miopenGet4dTensorDescriptorLengths(tensor, &n, &c, &h, &w); return std::vector({n, c, h, w}); } std::vector tensor_len; tensor_len.resize(miopen::deref(tensor).GetNumDims()); miopenGetTensorDescriptor(tensor, nullptr, tensor_len.data(), nullptr); return tensor_len; } inline std::vector GetTensorStrides(const miopenTensorDescriptor_t& tensor) { int nstride; int cstride; int dstride; int hstride; int wstride; int size = 0; miopenGetTensorDescriptorSize(tensor, &size); if(size == 5) { miopenGet5dTensorDescriptorStrides( tensor, &nstride, &cstride, &dstride, &hstride, &wstride); return std::vector({nstride, cstride, dstride, hstride, wstride}); } else if(size == 4) { miopenGet4dTensorDescriptorStrides(tensor, &nstride, &cstride, &hstride, &wstride); return std::vector({nstride, cstride, hstride, wstride}); } std::vector tensor_strides; tensor_strides.resize(miopen::deref(tensor).GetNumDims()); miopenGetTensorDescriptor(tensor, nullptr, nullptr, tensor_strides.data()); return tensor_strides; } inline int SetTensor4d(miopenTensorDescriptor_t t, std::vector& len, miopenDataType_t data_type = miopenFloat) { return miopenSet4dTensorDescriptor(t, data_type, UNPACK_VEC4(len)); } inline int SetTensorNdVector(miopenTensorDescriptor_t t, std::vector& len, miopenTensorLayout_t layout, miopenDataType_t data_type = miopenFloat) { if(layout == miopenTensorNCHWc4 || layout == miopenTensorNCHWc8 || layout == miopenTensorNHWC || layout == miopenTensorNCHW || layout == miopenTensorNDHWC || layout == miopenTensorNCDHW) { // Do nothing, MIOpen implicit logic that lens are in NCHW order. } else if(layout == miopenTensorCHWNc4 || layout == miopenTensorCHWNc8) { LengthReorder(len, {1, 2, 3, 0}); } else { MIOPEN_THROW("We only support NCHWc4, NCHWc8, CHWNc4, CHWNc8, NCHW, NHWC, NDHWC, NCDHW " "vectorized tensor layout."); return -1; } return miopenSetNdTensorDescriptorWithLayout(t, data_type, layout, len.data(), len.size()); } inline int SetTensorNd(miopenTensorDescriptor_t t, std::vector& len, miopenDataType_t data_type = miopenFloat) { return miopenSetTensorDescriptor(t, data_type, len.size(), len.data(), nullptr); } inline int SetTensorNd(miopenTensorDescriptor_t t, std::vector& len, miopenDataType_t data_type = miopenFloat) { return miopenSetTensorDescriptorV2(t, data_type, len.size(), len.data(), nullptr); } inline int SetTensorNd(miopenTensorDescriptor_t t, std::vector& len, std::vector& strides, miopenDataType_t data_type = miopenFloat) { return miopenSetTensorDescriptor(t, data_type, len.size(), len.data(), strides.data()); } inline int SetTensorNd(miopenTensorDescriptor_t t, std::vector& len, std::vector& strides, miopenDataType_t data_type = miopenFloat) { return miopenSetTensorDescriptorV2(t, data_type, len.size(), len.data(), strides.data()); } inline int SetTensorNd(miopenTensorDescriptor_t t, std::vector& len, const std::string& layout, miopenDataType_t data_type = miopenFloat) { if(layout.empty()) { return SetTensorNd(t, len, data_type); } if(layout.size() != len.size() && layout.find('c') == std::string::npos) { MIOPEN_THROW("unmatched layout and dimension size"); } if(layout.find('c') != std::string::npos) { return SetTensorNdVector(t, len, StringToLayoutType(layout), data_type); } // Dimension lengths vector 'len' comes with a default layout. std::string len_layout = miopen::tensor_layout_get_default(layout.size()); if(len_layout.empty()) { return SetTensorNd(t, len, data_type); } std::vector strides2; std::vector len2(len.cbegin(), len.cend()); miopen::tensor_layout_to_strides(len2, len_layout, layout, strides2); return SetTensorNd(t, len2, strides2, data_type); } // This function ignores tensor strides completely and its result should not be interpreted as // memory required for an "unpacked" tensor. In such cases GetTensorSpace should be used instead. // For "packed" tensors result may be interpreted as an amount of memory required for the tensor. // The implementation is a copy-paste from miopen::TensorDescriptor. inline size_t GetTensorSize(const miopenTensorDescriptor_t& tensor) { assert(miopen::deref(tensor).IsPacked() && "GetTensorSize should not be used on an unpacked tensor."); const auto len = GetTensorLengths(tensor); const size_t vectorLength = GetTensorVectorLength(tensor); size_t sz = std::accumulate(len.begin(), len.end(), vectorLength, std::multiplies()); return sz; } // The result of this function may be interpreted as a correct amount of memory required for both // "packed" and "unpacked" tensors. In general it should be used for such purposes rather than // GetTensorSize. Unless, of course, there is absolutely zero chance to receive an unpacked tensor. inline size_t GetTensorSpace(const miopenTensorDescriptor_t& tensor) { return miopen::deref(tensor).GetElementSpace(); } #endif // GUARD_MIOPEN_TENSOR_DRIVER_HPP