Files
TheRock/rocm-libraries/projects/miopen/test/tensor_holder.hpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

506 lines
15 KiB
C++

/*******************************************************************************
*
* 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 <miopen/ford.hpp>
#include <miopen/tensor.hpp>
#include <miopen/functional.hpp>
#include <miopen/type_name.hpp>
#include <miopen/each_args.hpp>
#include <miopen/bfloat16.hpp>
#include "../driver/random.hpp"
#include "serialize.hpp"
#include <half/half.hpp>
using half = half_float::half;
using hip_bfloat16 = bfloat16;
#include "../../src/kernels/hip_float8.hpp"
using float8_fnuz = miopen_f8::hip_f8<miopen_f8::hip_f8_type::fp8>;
using bfloat8_fnuz = miopen_f8::hip_f8<miopen_f8::hip_f8_type::bf8>;
#include <iomanip>
#include <fstream>
template <class F>
void visit_tensor_size(std::size_t n, F f)
{
switch(n)
{
case 0: {
f(std::integral_constant<std::size_t, 0>{});
break;
}
case 1: {
f(std::integral_constant<std::size_t, 1>{});
break;
}
case 2: {
f(std::integral_constant<std::size_t, 2>{});
break;
}
case 3: {
f(std::integral_constant<std::size_t, 3>{});
break;
}
case 4: {
f(std::integral_constant<std::size_t, 4>{});
break;
}
case 5: {
f(std::integral_constant<std::size_t, 5>{});
break;
}
default: throw std::runtime_error("Unknown tensor size");
}
}
template <class T>
struct miopen_type;
template <>
struct miopen_type<float> : std::integral_constant<miopenDataType_t, miopenFloat>
{
};
template <>
struct miopen_type<double> : std::integral_constant<miopenDataType_t, miopenDouble>
{
};
template <>
struct miopen_type<half_float::half> : std::integral_constant<miopenDataType_t, miopenHalf>
{
};
template <>
struct miopen_type<bfloat16> : std::integral_constant<miopenDataType_t, miopenBFloat16>
{
};
template <>
struct miopen_type<int8_t> : std::integral_constant<miopenDataType_t, miopenInt8>
{
};
template <>
struct miopen_type<int> : std::integral_constant<miopenDataType_t, miopenInt32>
{
};
template <>
struct miopen_type<int64_t> : std::integral_constant<miopenDataType_t, miopenInt64>
{
};
template <>
struct miopen_type<float8_fnuz> : std::integral_constant<miopenDataType_t, miopenFloat8_fnuz>
{
};
template <>
struct miopen_type<bfloat8_fnuz> : std::integral_constant<miopenDataType_t, miopenBFloat8_fnuz>
{
};
template <>
struct miopen_type<uint8_t> : std::integral_constant<miopenDataType_t, miopenInt8>
{
};
template <>
struct miopen_type<uint16_t> : std::integral_constant<miopenDataType_t, miopenHalf>
{
};
template <>
struct miopen_type<uint64_t> : std::integral_constant<miopenDataType_t, miopenInt64>
{
};
template <class T>
struct tensor
{
using value_type = T;
miopen::TensorDescriptor desc;
std::vector<T> data;
#if defined(__clang__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
tensor() : desc(miopen_type<T>{}) {}
#if defined(__clang__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
template <class X>
tensor(const std::vector<X>& dims) : desc(miopen_type<T>{}, dims), data(desc.GetElementSpace())
{
}
template <class X>
tensor(const std::vector<X>& dims, const std::vector<X>& strides)
: desc(miopen_type<T>{}, dims, strides), data(desc.GetElementSpace())
{
assert(dims.size() == strides.size());
}
template <class X>
tensor(miopenTensorLayout_t layout, const std::vector<X>& dims)
: desc(miopen_type<T>{}, layout, dims), data(desc.GetElementSpace())
{
}
template <class X>
tensor(miopenTensorLayout_t layout, const std::vector<X>& dims, const std::vector<X>& strides)
: desc(miopen_type<T>{}, 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<T>{}, {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<T>{}, 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<T>{}, {n, c, d, h, w}), data(n * c * d * h * w)
{
}
tensor(std::size_t n) : desc(miopen_type<T>{}, {n}), data(n) {}
tensor(miopen::TensorDescriptor rhs) : desc(std::move(rhs))
{
assert(desc.GetType() == miopen_type<T>{}
/// 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<T>{} == miopenInt8 && desc.GetType() == miopenInt32));
data.resize(desc.GetElementSpace());
}
size_t GetDataByteSize() const { return GetSize() * sizeof(T); }
size_t GetSize() const { return desc.GetElementSpace(); }
template <class G>
tensor& generate(G g) &
{
if(this->desc.GetVectorLength() > 1)
this->generate_vect_impl(g);
else
this->generate_impl(g);
return *this;
}
template <class G>
tensor&& generate(G g) &&
{
if(this->desc.GetVectorLength() > 1)
this->generate_vect_impl(g);
else
this->generate_impl(g);
return std::move(*this);
}
template <class G>
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<T>()), std::move(g)));
}
template <class G>
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<T>()), std::move(g)));
}
template <class Loop, class F>
struct for_each_unpacked
{
Loop loop;
F f;
template <class... Ts>
auto operator()(Ts... xs) const -> decltype(f(xs...), void())
{
loop(xs...)(std::move(f));
}
struct any
{
any() {}
template <class X>
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<F>() + " can not be called.");
}
};
struct for_each_handler
{
template <class Self, class Loop, class F, class Size>
void operator()(Self* self, Loop loop, F f, Size size) const
{
auto dims = miopen::tien<size>(self->desc.GetLengths());
miopen::unpack(for_each_unpacked<Loop, F>{loop, std::move(f)}, dims);
}
};
template <class F>
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 <class F>
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 <class... Ts>
T& operator()(Ts... xs)
{
assert(this->desc.GetIndex(xs...) < data.size());
return this->data[this->desc.GetIndex(xs...)];
}
template <class... Ts>
const T& operator()(Ts... xs) const
{
assert(this->desc.GetIndex(xs...) < data.size());
return this->data[this->desc.GetIndex(xs...)];
}
template <class Integer, Integer N>
const T& operator()(const std::array<Integer, N>& 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<T>::iterator begin() { return data.begin(); }
typename std::vector<T>::iterator end() { return data.end(); }
typename std::vector<T>::const_iterator begin() const { return data.begin(); }
typename std::vector<T>::const_iterator end() const { return data.end(); }
friend std::ostream& operator<<(std::ostream& stream, const tensor& t)
{
return stream << t.desc;
}
template <size_t N, typename Stream>
void dump_inner(size_t dim, std::array<size_t, N>& 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<N>(dim + 1, coord, stream);
}
}
}
template <typename Stream = decltype(std::cout)>
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<size_t, N> coord;
this->dump_inner<N>(0, coord, stream);
});
stream.flags(flags);
}
};
template <class T>
void serialize(std::istream& s, tensor<T>& x)
{
std::vector<std::size_t> lens;
serialize(s, lens);
std::vector<std::size_t> strides;
serialize(s, strides);
x.desc = miopen::TensorDescriptor{miopen_type<T>{}, lens, strides};
serialize(s, x.data);
}
template <class T>
void serialize(std::ostream& s, const tensor<T>& 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 <class Tensor, class G>
Tensor&& operator()(Tensor&& t, G g) const
{
return std::forward<Tensor>(t.generate(g));
}
};
struct tensor_elem_gen_integer
{
uint64_t max_value = 17;
template <class... Ts>
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<uint64_t, sizeof...(Ts)> left = {{Xs...}};
std::array<uint64_t, 5> right = {{613, 547, 701, 877, 1049}};
uint64_t dot =
std::inner_product(left.begin(), left.end(), right.begin(), static_cast<uint64_t>(173));
return static_cast<double>(dot % max_value);
}
};
#endif