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
398 lines
16 KiB
C++
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{});
|