/******************************************************************************* * * 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_TENSOR_HOLDER_HPP #define GUARD_TENSOR_HOLDER_HPP #include "network_data.hpp" #include #include #include #include #include #include #include "../driver/random.hpp" #include "serialize.hpp" #include using half = half_float::half; using hip_bfloat16 = bfloat16; #include "../../src/kernels/hip_float8.hpp" using float8_fnuz = miopen_f8::hip_f8; using bfloat8_fnuz = miopen_f8::hip_f8; #include #include template void visit_tensor_size(std::size_t n, F f) { switch(n) { case 0: { f(std::integral_constant{}); break; } case 1: { f(std::integral_constant{}); break; } case 2: { f(std::integral_constant{}); break; } case 3: { f(std::integral_constant{}); break; } case 4: { f(std::integral_constant{}); break; } case 5: { f(std::integral_constant{}); break; } default: throw std::runtime_error("Unknown tensor size"); } } template struct miopen_type; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template <> struct miopen_type : std::integral_constant { }; template struct tensor { using value_type = T; miopen::TensorDescriptor desc; std::vector data; #if defined(__clang__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif tensor() : desc(miopen_type{}) {} #if defined(__clang__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif template tensor(const std::vector& dims) : desc(miopen_type{}, dims), data(desc.GetElementSpace()) { } template tensor(const std::vector& dims, const std::vector& strides) : desc(miopen_type{}, dims, strides), data(desc.GetElementSpace()) { assert(dims.size() == strides.size()); } template tensor(miopenTensorLayout_t layout, const std::vector& dims) : desc(miopen_type{}, layout, dims), data(desc.GetElementSpace()) { } template tensor(miopenTensorLayout_t layout, const std::vector& dims, const std::vector& strides) : desc(miopen_type{}, layout, dims, strides), data(desc.GetElementSpace()) { assert(dims.size() == strides.size()); } tensor(std::size_t n, std::size_t c, std::size_t h, std::size_t w) : desc(miopen_type{}, {n, c, h, w}), data(n * c * h * w) { } tensor(miopenTensorLayout_t layout, std::size_t n, std::size_t c, std::size_t h, std::size_t w) : desc(miopen_type{}, layout, {n, c, h, w}), data(desc.GetElementSpace()) { } tensor(std::size_t n, std::size_t c, std::size_t d, std::size_t h, std::size_t w) : desc(miopen_type{}, {n, c, d, h, w}), data(n * c * d * h * w) { } tensor(std::size_t n) : desc(miopen_type{}, {n}), data(n) {} tensor(miopen::TensorDescriptor rhs) : desc(std::move(rhs)) { assert(desc.GetType() == miopen_type{} /// In the driver, T is input tensor type, but output tensor holders /// are instantiatied with T as well. This leads to false assertion /// failures when T is INT8 because output type is different. /// \todo Get rid of this hack when the driver is improved: || (miopen_type{} == miopenInt8 && desc.GetType() == miopenInt32)); data.resize(desc.GetElementSpace()); } size_t GetDataByteSize() const { return GetSize() * sizeof(T); } size_t GetSize() const { return desc.GetElementSpace(); } template tensor& generate(G g) & { if(this->desc.GetVectorLength() > 1) this->generate_vect_impl(g); else this->generate_impl(g); return *this; } template tensor&& generate(G g) && { if(this->desc.GetVectorLength() > 1) this->generate_vect_impl(g); else this->generate_impl(g); return std::move(*this); } template void generate_impl(G g) { auto seed = std::accumulate(desc.GetLengths().begin(), desc.GetLengths().end(), std::size_t{521288629}, [](auto x, auto y) { x ^= x << 1U; return x ^ y; }); seed ^= data.size(); seed ^= desc.GetLengths().size(); prng::reset_seed(seed); auto iterator = data.begin(); auto assign = [&](T x) { *iterator = x; ++iterator; }; this->for_each( miopen::compose(miopen::compose(assign, miopen::cast_to()), std::move(g))); } template void generate_vect_impl(G g) { auto seed = std::accumulate(desc.GetLengths().begin(), desc.GetLengths().end(), std::size_t{521288629}, [](auto x, auto y) { x ^= x << 1U; return x ^ y; }); seed ^= data.size(); seed ^= desc.GetLengths().size(); prng::reset_seed(seed); auto iterator = data.begin(); auto vectorLength = desc.GetVectorLength(); auto assign = [&](T x) { assert(iterator < data.end()); // for debugging for(auto i = 0; i < vectorLength; i++) { *(iterator + i) = x; } iterator += vectorLength; }; this->for_each( miopen::compose(miopen::compose(assign, miopen::cast_to()), std::move(g))); } template struct for_each_unpacked { Loop loop; F f; template auto operator()(Ts... xs) const -> decltype(f(xs...), void()) { loop(xs...)(std::move(f)); } struct any { any() {} template any(X) { } }; [[noreturn]] void operator()(any = {}, any = {}, any = {}, any = {}, any = {}, any = {}, any = {}, any = {}, any = {}) const { throw std::runtime_error( "Arguments to for_each do not match tensor size or the function " + miopen::get_type_name() + " can not be called."); } }; struct for_each_handler { template void operator()(Self* self, Loop loop, F f, Size size) const { auto dims = miopen::tien(self->desc.GetLengths()); miopen::unpack(for_each_unpacked{loop, std::move(f)}, dims); } }; template void for_each(F f) const { visit_tensor_size( desc.GetLengths().size(), std::bind(for_each_handler{}, this, miopen::ford, std::move(f), std::placeholders::_1)); } template void par_for_each(F f) const { visit_tensor_size( desc.GetLengths().size(), std::bind( for_each_handler{}, this, miopen::par_ford, std::move(f), std::placeholders::_1)); } template T& operator()(Ts... xs) { assert(this->desc.GetIndex(xs...) < data.size()); return this->data[this->desc.GetIndex(xs...)]; } template const T& operator()(Ts... xs) const { assert(this->desc.GetIndex(xs...) < data.size()); return this->data[this->desc.GetIndex(xs...)]; } template const T& operator()(const std::array& multi_id) const { auto f = [&](auto... is) { return this->desc.GetIndex(is...); }; assert(miopen::unpack(f, multi_id) < data.size()); return this->data[miopen::unpack(f, multi_id)]; } T& operator[](std::size_t i) { return data.at(i); } const T& operator[](std::size_t i) const { return data.at(i); } typename std::vector::iterator begin() { return data.begin(); } typename std::vector::iterator end() { return data.end(); } typename std::vector::const_iterator begin() const { return data.begin(); } typename std::vector::const_iterator end() const { return data.end(); } friend std::ostream& operator<<(std::ostream& stream, const tensor& t) { return stream << t.desc; } template void dump_inner(size_t dim, std::array& coord, Stream& stream) const { const auto lengths = this->desc.GetLengths(); if(lengths.size() == 0) { // 0D special case: Just print the one value that we have and return. stream << (*this)(coord); } else if(dim + 1 == lengths.size()) { // 1D special case: dump everything on one line for(size_t i = 0; i < lengths[dim]; ++i) { if(i != 0) stream << ' '; coord[dim] = i; stream << std::setw(4) << (*this)(coord); } stream << '\n'; } else { if(dim + 2 == lengths.size()) { // 2D special case: Also print which 2D slice we are currently printing // Note: this is not needed for higher dimensions, as they will also pass // through this branch. stream << "slice ["; for(size_t i = 0; i < dim; ++i) { stream << coord[i] << ", "; } stream << ":, :]\n"; } for(size_t i = 0; i < lengths[dim]; ++i) { coord[dim] = i; this->dump_inner(dim + 1, coord, stream); } } } template void dump(const char* name, Stream& stream = std::cout) const { const auto n = this->desc.GetLengths().size(); stream << "==== " << name << ": " << *this << n << '\n'; stream.fill(' '); const auto flags = stream.flags(); visit_tensor_size(n, [&](const auto size) { constexpr size_t N = decltype(size)::value; std::array coord; this->dump_inner(0, coord, stream); }); stream.flags(flags); } }; template void serialize(std::istream& s, tensor& x) { std::vector lens; serialize(s, lens); std::vector strides; serialize(s, strides); x.desc = miopen::TensorDescriptor{miopen_type{}, lens, strides}; serialize(s, x.data); } template void serialize(std::ostream& s, const tensor& x) { const auto& lens = x.desc.GetLengths(); const auto& strides = x.desc.GetStrides(); serialize(s, lens); serialize(s, strides); serialize(s, x.data); } struct tensor_generate { template Tensor&& operator()(Tensor&& t, G g) const { return std::forward(t.generate(g)); } }; struct tensor_elem_gen_integer { uint64_t max_value = 17; template double operator()(Ts... Xs) const { static_assert(sizeof...(Ts) < 6, "Dimensions in tensor_elem_gen_integer must be less than 6."); assert(max_value > 0); std::array left = {{Xs...}}; std::array right = {{613, 547, 701, 877, 1049}}; uint64_t dot = std::inner_product(left.begin(), left.end(), right.begin(), static_cast(173)); return static_cast(dot % max_value); } }; #endif