Files
TheRock/rocm-libraries/projects/miopen/test/gtest/tensor_reorder.cpp
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

398 lines
16 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (c) 2020-2026 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.
*
*******************************************************************************/
#include <miopen/invoker.hpp>
#include <miopen/tensor_reorder_util.hpp>
#include <miopen/tensor_layout.hpp>
#include "gtest_common.hpp"
#include "random.hpp"
#include "test_parameter_name_generator.hpp"
#include "workspace.hpp"
namespace {
using TestCase = std::tuple<NamedContainer<std::vector<uint32_t>>,
NamedParameter<uint32_t>,
NamedParameter<uint32_t>,
NamedParameter<uint32_t>,
NamedParameter<uint32_t>>;
template <typename T>
void cpu_tensor_reorder(T* dst,
const T* src,
uint64_t dim_0,
uint64_t dim_1,
uint64_t dim_2,
uint64_t dim_3,
uint64_t order_0,
uint64_t order_1,
uint64_t order_2,
uint64_t order_3)
{
const uint64_t src_dim[4] = {dim_0, dim_1, dim_2, dim_3};
const uint64_t dst_dim[4] = {
src_dim[order_0], src_dim[order_1], src_dim[order_2], src_dim[order_3]};
const uint64_t src_stride[4] = {
src_dim[1] * src_dim[2] * src_dim[3], src_dim[2] * src_dim[3], src_dim[3], 1};
const uint64_t dst_stride[4] = {
dst_dim[1] * dst_dim[2] * dst_dim[3], dst_dim[2] * dst_dim[3], dst_dim[3], 1};
uint64_t itr_src_dim[4] = {0, 0, 0, 0};
uint64_t itr_dst_dim[4] = {0, 0, 0, 0};
for(itr_src_dim[0] = 0; itr_src_dim[0] < src_dim[0]; itr_src_dim[0]++)
{
for(itr_src_dim[1] = 0; itr_src_dim[1] < src_dim[1]; itr_src_dim[1]++)
{
for(itr_src_dim[2] = 0; itr_src_dim[2] < src_dim[2]; itr_src_dim[2]++)
{
for(itr_src_dim[3] = 0; itr_src_dim[3] < src_dim[3]; itr_src_dim[3]++)
{
itr_dst_dim[0] = itr_src_dim[order_0];
itr_dst_dim[1] = itr_src_dim[order_1];
itr_dst_dim[2] = itr_src_dim[order_2];
itr_dst_dim[3] = itr_src_dim[order_3];
const uint64_t idx_src =
itr_src_dim[0] * src_stride[0] + itr_src_dim[1] * src_stride[1] +
itr_src_dim[2] * src_stride[2] + itr_src_dim[3] * src_stride[3];
const uint64_t idx_dst =
itr_dst_dim[0] * dst_stride[0] + itr_dst_dim[1] * dst_stride[1] +
itr_dst_dim[2] * dst_stride[2] + itr_dst_dim[3] * dst_stride[3];
dst[idx_dst] = src[idx_src];
}
}
}
}
}
template <typename T>
struct cpu_reorder
{
static void run(T* dst,
const T* src,
uint64_t dim_0,
uint64_t dim_1,
uint64_t dim_2,
uint64_t dim_3,
uint64_t order_0,
uint64_t order_1,
uint64_t order_2,
uint64_t order_3)
{
cpu_tensor_reorder<T>(
dst, src, dim_0, dim_1, dim_2, dim_3, order_0, order_1, order_2, order_3);
}
};
struct reorder_str
{
static std::string get(uint32_t order_0, uint32_t order_1, uint32_t order_2, uint32_t order_3)
{
return ("r" + std::to_string(order_0) + std::to_string(order_1) + std::to_string(order_2) +
std::to_string(order_3));
}
};
std::string
supported_reorder_to_string(uint32_t order_0, uint32_t order_1, uint32_t order_2, uint32_t order_3)
{
std::string layout_string("N/A");
// NOLINTBEGIN(*-braces-around-statements)
if((order_0 == 0) && (order_1 == 1) && (order_2 == 3) && (order_3 == 2))
layout_string = "r0132";
else if((order_0 == 0) && (order_1 == 2) && (order_2 == 1) && (order_3 == 3))
layout_string = "r0213";
else if((order_0 == 0) && (order_1 == 2) && (order_2 == 3) && (order_3 == 1))
layout_string = "r0231";
else if((order_0 == 0) && (order_1 == 3) && (order_2 == 1) && (order_3 == 2))
layout_string = "r0312";
else if((order_0 == 0) && (order_1 == 3) && (order_2 == 2) && (order_3 == 1))
layout_string = "r0321";
else if((order_0 == 1) && (order_1 == 0) && (order_2 == 2) && (order_3 == 3))
layout_string = "r1023";
else if((order_0 == 1) && (order_1 == 0) && (order_2 == 3) && (order_3 == 2))
layout_string = "r1032";
else if((order_0 == 1) && (order_1 == 2) && (order_2 == 0) && (order_3 == 3))
layout_string = "r1203";
else if((order_0 == 1) && (order_1 == 2) && (order_2 == 3) && (order_3 == 0))
layout_string = "r1230";
else if((order_0 == 1) && (order_1 == 3) && (order_2 == 0) && (order_3 == 2))
layout_string = "r1302";
else if((order_0 == 1) && (order_1 == 3) && (order_2 == 2) && (order_3 == 0))
layout_string = "r1320";
else if((order_0 == 2) && (order_1 == 0) && (order_2 == 1) && (order_3 == 3))
layout_string = "r2013";
else if((order_0 == 2) && (order_1 == 0) && (order_2 == 3) && (order_3 == 1))
layout_string = "r2031";
else if((order_0 == 2) && (order_1 == 1) && (order_2 == 0) && (order_3 == 3))
layout_string = "r2103";
else if((order_0 == 2) && (order_1 == 1) && (order_2 == 3) && (order_3 == 0))
layout_string = "r2130";
else if((order_0 == 2) && (order_1 == 3) && (order_2 == 0) && (order_3 == 1))
layout_string = "r2301";
else if((order_0 == 2) && (order_1 == 3) && (order_2 == 1) && (order_3 == 0))
layout_string = "r2310";
else if((order_0 == 3) && (order_1 == 0) && (order_2 == 1) && (order_3 == 2))
layout_string = "r3012";
else if((order_0 == 3) && (order_1 == 0) && (order_2 == 2) && (order_3 == 1))
layout_string = "r3021";
else if((order_0 == 3) && (order_1 == 1) && (order_2 == 0) && (order_3 == 2))
layout_string = "r3102";
else if((order_0 == 3) && (order_1 == 1) && (order_2 == 2) && (order_3 == 0))
layout_string = "r3120";
else if((order_0 == 3) && (order_1 == 2) && (order_2 == 0) && (order_3 == 1))
layout_string = "r3201";
else if((order_0 == 3) && (order_1 == 2) && (order_2 == 1) && (order_3 == 0))
layout_string = "r3210";
else
MIOPEN_THROW("Unsupported reorder layout");
// NOLINTEND(*-braces-around-statements)
return layout_string;
}
static const constexpr int RAND_INTEGER_MAX = 120;
static const constexpr int RAND_INTEGER_MIN = -88;
template <typename T>
void rand_tensor_integer(tensor<T>& t, int max = RAND_INTEGER_MAX, int min = RAND_INTEGER_MIN)
{
// use integer to random.
for(size_t i = 0; i < t.data.size(); i++)
t[i] = static_cast<T>(prng::gen_A_to_B(min, max));
}
template <typename T>
bool compare_equal(T r1, T r2)
{
return r1 == r2;
}
template <>
bool compare_equal<double>(double r1, double r2)
{
return miopen::float_equal(r1, r2);
}
template <>
bool compare_equal<float>(float r1, float r2)
{
return miopen::float_equal(r1, r2);
}
struct reorder_invoke_param : public miopen::InvokeParams
{
ConstData_t src = nullptr;
Data_t dst = nullptr;
[[maybe_unused]] reorder_invoke_param(ConstData_t src_, Data_t dst_) : src(src_), dst(dst_) {}
[[maybe_unused]] reorder_invoke_param(miopen::InvokeType type_, ConstData_t src_, Data_t dst_)
: InvokeParams{type_}, src(src_), dst(dst_)
{
}
Data_t GetWorkspace() const { return nullptr; }
std::size_t GetWorkspaceSize() const { return 0; }
};
inline auto GenCases()
{
static const std::vector<std::vector<uint32_t>> all_possible_order{
{0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1}, {1, 0, 2, 3},
{1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0}, {2, 0, 1, 3},
{2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0}, {3, 0, 1, 2},
{3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}};
return testing::Combine(
MakeNamedParameterCollectionValues<std::vector<uint32_t>>("order", all_possible_order, ","),
MakeNamedParameterValues<uint32_t>(
"dim0", 1U, 2U, static_cast<uint32_t>(prng::gen_off_range(3, 4))),
MakeNamedParameterValues<uint32_t>(
"dim1", 3U, 8U, static_cast<uint32_t>(prng::gen_off_range(15, 13))),
MakeNamedParameterValues<uint32_t>(
"dim2", 1U, 9U, static_cast<uint32_t>(prng::gen_off_range(29, 13))),
MakeNamedParameterValues<uint32_t>(
"dim3", 1U, 9U, static_cast<uint32_t>(prng::gen_off_range(29, 13))));
}
inline auto GetCases()
{
static const auto cases = GenCases();
return cases;
}
} // namespace
template <typename T>
struct tensor_reorder_test : public testing::TestWithParam<TestCase>
{
void SetUp() override
{
prng::reset_seed();
std::tie(order, dim0, dim1, dim2, dim3) = GetParam();
}
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)
void Run()
{
const int tensor_sz = dim0 * dim1 * dim2 * dim3;
const std::vector<int> tensor_len({static_cast<int>(dim0),
static_cast<int>(dim1),
static_cast<int>(dim2),
static_cast<int>(dim3)});
std::vector<int> tensor_strides;
std::string layout_default = miopen::tensor_layout_get_default(4);
std::string layout_string = miopen::TensorDescriptor::LayoutEnumToStr(miopenTensorNCHW);
std::string reorder_string =
supported_reorder_to_string(order[0], order[1], order[2], order[3]);
miopen::tensor_layout_to_strides(tensor_len, layout_default, layout_string, tensor_strides);
tensor<T> t_src(tensor_len, tensor_strides);
tensor<T> t_dst(tensor_len, tensor_strides);
tensor<T> t_dst_gpu(tensor_len, tensor_strides);
rand_tensor_integer(t_src);
auto& handle = get_handle();
miopen::ExecutionContext ctx;
ctx.SetStream(&handle);
const auto reorder_sol = MakeTensorReorderAttributes(
ctx, miopen_type<T>{}, dim0, dim1, dim2, dim3, order[0], order[1], order[2], order[3]);
ASSERT_NE(reorder_sol, nullptr);
size_t workspace_size =
reorder_sol->IsSkippable() ? sizeof(T) * tensor_sz : reorder_sol->GetOutputTensorSize();
Workspace wspace{workspace_size};
auto src_dev = handle.Write(t_src.data);
const auto invoke_param = reorder_invoke_param{src_dev.get(), wspace.ptr()};
std::vector<OpKernelArg> opArgs = reorder_sol->GetKernelArg();
std::optional<miopen::InvokerFactory> invoker_factory(
[=](const std::vector<miopen::Kernel>& kernels) mutable {
return [=](const miopen::Handle& handle_,
const miopen::AnyInvokeParams& primitive_param) mutable {
decltype(auto) invoke_params = primitive_param.CastTo<reorder_invoke_param>();
const auto k = handle_.Run(kernels[0]);
opArgs[0] = OpKernelArg(invoke_params.dst);
opArgs[1] = OpKernelArg(invoke_params.src);
k(opArgs);
};
});
const std::vector<miopen::solver::KernelInfo> construction_params{
reorder_sol->GetKernelInfo()};
if(invoker_factory.has_value())
{
const auto invoker = handle.PrepareInvoker(*invoker_factory, construction_params);
// run gpu
invoker(handle, invoke_param);
// run cpu
cpu_reorder<T>::run(t_dst.data.data(),
t_src.data.data(),
dim0,
dim1,
dim2,
dim3,
order[0],
order[1],
order[2],
order[3]);
}
invoker_factory.reset();
t_dst_gpu.data = wspace.Read<decltype(t_dst_gpu.data)>();
// we expect excact match, since use integer
VerifyTensor(t_dst_gpu, t_dst);
}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
private:
std::vector<uint32_t> order;
uint32_t dim0{0};
uint32_t dim1{0};
uint32_t dim2{0};
uint32_t dim3{0};
void VerifyTensor(const tensor<T>& t_gpu, const tensor<T>& t_cpu)
{
ASSERT_EQ(t_gpu.data.size(), t_cpu.data.size());
const auto idx = miopen::mismatch_idx(t_gpu.data, t_cpu.data, compare_equal<T>);
const bool valid_result = idx >= miopen::range_distance(t_cpu);
std::cout << "[" << reorder_str::get(order[0], order[1], order[2], order[3]) << ", b"
<< (sizeof(T) * 8) << " ] " << "dim0:" << dim0 << ", dim1:" << dim1
<< ", dim2:" << dim2 << ", dim3:" << dim3 << ", valid:" << valid_result
<< std::endl;
EXPECT_TRUE(valid_result) << "diff at:" << idx << ", gpu:" << t_gpu[idx]
<< ", cpu:" << t_cpu[idx] << std::endl;
}
};
struct TestNameGenerator
{
std::string operator()(const auto& info)
{
const auto& [order, dim0, dim1, dim2, dim3] = info.param;
std::stringstream ss;
std::string str;
ss << "order_" << GetRangeAsString(order(), ",") << "_dim0_" << dim0() << "_dim1_" << dim1()
<< "_dim2_" << dim2() << "_dim3_" << dim3() << "_test_id_" << info.index;
str = ss.str();
// Name format only supports letters, numbers and underscores.
std::transform(str.begin(), str.end(), str.begin(), [](char c) -> char {
return (c == '.') ? 'p' : (std::isalnum(c) ? c : '_');
});
return str;
}
};
using GPU_TensorReorder_I8 = tensor_reorder_test<int8_t>;
using GPU_TensorReorder_FP16 = tensor_reorder_test<half_float::half>;
using GPU_TensorReorder_FP32 = tensor_reorder_test<float>;
using GPU_TensorReorder_FP64 = tensor_reorder_test<double>;
TEST_P(GPU_TensorReorder_I8, TestInt8) { this->Run(); }
TEST_P(GPU_TensorReorder_FP16, TestFloat16) { this->Run(); }
TEST_P(GPU_TensorReorder_FP32, TestFloat32) { this->Run(); }
TEST_P(GPU_TensorReorder_FP64, TestFloat64) { this->Run(); }
INSTANTIATE_TEST_SUITE_P(Smoke, GPU_TensorReorder_I8, GetCases(), TestNameGenerator{});
INSTANTIATE_TEST_SUITE_P(Smoke, GPU_TensorReorder_FP16, GetCases(), TestNameGenerator{});
INSTANTIATE_TEST_SUITE_P(Smoke, GPU_TensorReorder_FP32, GetCases(), TestNameGenerator{});
INSTANTIATE_TEST_SUITE_P(Smoke, GPU_TensorReorder_FP64, GetCases(), TestNameGenerator{});