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

455 lines
17 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (c) 2024 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 <gtest/gtest.h>
#include "../../driver/conv_common.hpp"
#include <miopen/batched_transpose_sol.hpp>
#include <miopen/handle.hpp>
#include <miopen/invoker.hpp>
#include <miopen/miopen.h>
#include <miopen/tensor.hpp>
#include "../tensor_holder.hpp"
#include <miopen/tensor_layout.hpp>
#include "driver.hpp"
#include <vector>
namespace {
template <typename T>
void cpu_ncdhw2ndhwc(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
for(uint64_t i_n = 0; i_n < N; i_n++)
{
for(uint64_t i_d = 0; i_d < D; i_d++)
{
for(uint64_t i_h = 0; i_h < H; i_h++)
{
for(uint64_t i_w = 0; i_w < W; i_w++)
{
for(uint64_t i_c = 0; i_c < C; i_c++)
{
uint64_t idx_ndhwc =
i_n * D * H * W * C + i_d * H * W * C + i_h * W * C + i_w * C + i_c;
uint64_t idx_ncdhw =
i_n * C * D * H * W + i_c * D * H * W + i_d * H * W + i_h * W + i_w;
dst[idx_ndhwc] = src[idx_ncdhw];
}
}
}
}
}
}
template <typename T>
void cpu_nchw2nhwc(T* dst, T* src, uint64_t N, uint64_t C, uint64_t H, uint64_t W)
{
cpu_ncdhw2ndhwc<T>(dst, src, N, C, 1, H, W);
}
template <typename T>
void cpu_ndhwc2ncdhw(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
for(uint64_t i_n = 0; i_n < N; i_n++)
{
for(uint64_t i_c = 0; i_c < C; i_c++)
{
for(uint64_t i_d = 0; i_d < D; i_d++)
{
for(uint64_t i_h = 0; i_h < H; i_h++)
{
for(uint64_t i_w = 0; i_w < W; i_w++)
{
uint64_t idx_ndhwc =
i_n * D * H * W * C + i_d * H * W * C + i_h * W * C + i_w * C + i_c;
uint64_t idx_ncdhw =
i_n * C * D * H * W + i_c * D * H * W + i_d * H * W + i_h * W + i_w;
dst[idx_ncdhw] = src[idx_ndhwc];
}
}
}
}
}
}
template <typename T>
void cpu_nhwc2nchw(T* dst, T* src, uint64_t N, uint64_t C, uint64_t H, uint64_t W)
{
cpu_ndhwc2ncdhw<T>(dst, src, N, C, 1, H, W);
}
template <typename T, typename TRANSPOSE_SOL>
struct cpu_transpose
{
};
template <typename T>
struct cpu_transpose<T, miopen::TransposeSolutionDefault2Nhwc>
{
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t H, uint64_t W)
{
cpu_nchw2nhwc<T>(dst, src, N, C, H, W);
}
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
cpu_ncdhw2ndhwc<T>(dst, src, N, C, D, H, W);
}
};
template <typename T>
struct cpu_transpose<T, miopen::TransposeSolutionDefault2Ndhwc>
{
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
cpu_ncdhw2ndhwc<T>(dst, src, N, C, D, H, W);
}
};
template <typename T>
struct cpu_transpose<T, miopen::TransposeSolutionNhwc2Default>
{
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t H, uint64_t W)
{
cpu_nhwc2nchw<T>(dst, src, N, C, H, W);
}
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
cpu_ndhwc2ncdhw<T>(dst, src, N, C, D, H, W);
}
};
template <typename T>
struct cpu_transpose<T, miopen::TransposeSolutionNdhwc2Default>
{
static void run(T* dst, T* src, uint64_t N, uint64_t C, uint64_t D, uint64_t H, uint64_t W)
{
cpu_ndhwc2ncdhw<T>(dst, src, N, C, D, H, W);
}
};
constexpr int RAND_INTEGER_MAX = 120;
constexpr int RAND_INTEGER_MIN = -88;
template <typename T>
bool compare_equal(T r1, T r2)
{
return r1 == r2;
}
template <>
bool compare_equal<float>(float r1, float r2)
{
return miopen::float_equal(r1, r2);
}
template <typename T>
void verify_tensor(tensor<T>& t_tst, const char* _tst, tensor<T>& t_ref, const char* _ref)
{
ASSERT_EQ(t_tst.data.size(), t_ref.data.size()) << " tensor sizes not equal, should not happen";
if(t_tst.data.size() != t_ref.data.size())
return;
auto idx = miopen::mismatch_idx(t_tst.data, t_ref.data, compare_equal<T>);
EXPECT_GE(idx, miopen::range_distance(t_ref))
<< "diff at:" << idx << ", " << _tst << ":" << t_tst[idx] << ", " << _ref << ":"
<< t_ref[idx];
}
struct transpose_dims
{
static constexpr uint32_t image_size_rand_offset{29};
static constexpr uint32_t image_size_rand_range{13};
static uint32_t get_max_image_size()
{
return image_size_rand_offset + image_size_rand_range - 1;
}
static std::vector<uint32_t> get_image_size()
{
std::vector<uint32_t> v = {1, 9, 14};
v.push_back(prng::gen_off_range(image_size_rand_offset, image_size_rand_range));
return v;
}
static std::vector<uint32_t> get_channel_size()
{
std::vector<uint32_t> v = {3, 11};
v.push_back(prng::gen_off_range(19, 7));
return v;
}
static std::vector<uint32_t> get_batch_size()
{
std::vector<uint32_t> v = {1, 2, 4};
v.push_back(prng::gen_off_range(3, 4));
return v;
}
};
template <typename T>
auto gen_value =
[](auto...) { return static_cast<T>(prng::gen_A_to_B(RAND_INTEGER_MIN, RAND_INTEGER_MAX)); };
} // namespace
template <typename T, class TRANSPOSE_SOL>
struct LayoutTransposeTest_2D : public ::testing::TestWithParam<std::tuple<uint32_t, uint32_t>>
{
protected:
miopen::ExecutionContext ctx;
uint32_t n;
uint32_t c;
miopen::Invoker PrepareLayoutTransposeInvoker(const miopen::ExecutionContext& ctx_,
miopenDataType_t data_type_,
uint32_t n_,
uint32_t c_,
uint32_t h_,
uint32_t w_)
{
TRANSPOSE_SOL transpose_sol(ctx_, data_type_, n_, c_, h_, w_);
auto invoker_factory = transpose_sol.MakeBatchedTransposeInvokerFactory();
std::vector<miopen::solver::KernelInfo> construction_params{transpose_sol.GetKernelInfo()};
return ctx_.GetStream().PrepareInvoker(invoker_factory, construction_params);
}
virtual void SetUp() override { std::tie(n, c) = GetParam(); }
void RunTest()
{
auto&& handle = get_handle();
auto wh = transpose_dims::get_max_image_size();
auto max_tensor_sz = n * c * wh * wh;
auto dst_dev = handle.Create(sizeof(T) * max_tensor_sz);
auto H = transpose_dims::get_image_size();
auto W = transpose_dims::get_image_size();
for(auto h : H)
{
for(auto w : W)
{
std::vector<int> tensor_len = {static_cast<int>(n),
static_cast<int>(c),
static_cast<int>(h),
static_cast<int>(w)};
std::vector<int> tensor_strides;
std::string layout_default = miopen::tensor_layout_get_default(tensor_len.size());
std::string layout_string =
miopen::TensorDescriptor::LayoutEnumToStr(miopenTensorNCHW);
miopen::tensor_layout_to_strides(
tensor_len, layout_default, layout_string, tensor_strides);
auto t_src = tensor<T>{tensor_len, tensor_strides}.generate(gen_value<T>);
auto t_dst = tensor<T>{tensor_len, tensor_strides};
auto t_dst_gpu = tensor<T>{tensor_len, tensor_strides};
auto src_dev = handle.Write(t_src.data);
ctx.SetStream(&handle);
// prep gpu invoker
auto invoker = PrepareLayoutTransposeInvoker(ctx, miopen_type<T>{}, n, c, h, w);
const auto invoke_param = transpose_invoke_param{src_dev.get(), dst_dev.get()};
// run gpu
invoker(handle, invoke_param);
t_dst_gpu.data = handle.Read<T>(dst_dev, t_dst_gpu.data.size());
// run cpu
cpu_transpose<T, TRANSPOSE_SOL>::run(
t_dst.data.data(), t_src.data.data(), n, c, h, w);
// we expect exact match, since use integer
verify_tensor(t_dst_gpu, "gpu", t_dst, "cpu");
}
}
}
virtual void TearDown() override {}
};
template <typename T, class TRANSPOSE_SOL>
struct LayoutTransposeTest_3D : public ::testing::TestWithParam<std::tuple<uint32_t, uint32_t>>
{
protected:
miopen::ExecutionContext ctx;
uint32_t n;
uint32_t c;
miopen::Invoker PrepareLayoutTransposeInvoker(const miopen::ExecutionContext& ctx_,
miopenDataType_t data_type_,
uint32_t n_,
uint32_t c_,
uint32_t d_,
uint32_t h_,
uint32_t w_)
{
TRANSPOSE_SOL transpose_sol(ctx_, data_type_, n_, c_, d_, h_, w_);
auto invoker_factory = transpose_sol.MakeBatchedTransposeInvokerFactory();
std::vector<miopen::solver::KernelInfo> construction_params{transpose_sol.GetKernelInfo()};
return ctx_.GetStream().PrepareInvoker(invoker_factory, construction_params);
}
virtual void SetUp() override { std::tie(n, c) = GetParam(); }
void RunTest()
{
auto&& handle = get_handle();
ctx.SetStream(&handle);
auto dwh = transpose_dims::get_max_image_size();
auto max_tensor_sz = n * c * dwh * dwh * dwh;
auto dst_3d_dev = handle.Create(sizeof(T) * max_tensor_sz);
auto dst_2d_dev = handle.Create(sizeof(T) * max_tensor_sz);
auto W = transpose_dims::get_image_size();
auto H = transpose_dims::get_image_size();
auto D = transpose_dims::get_image_size();
for(auto w : W)
{
for(auto h : H)
{
for(auto d : D)
{
std::vector<int> tensor_len = {static_cast<int>(n),
static_cast<int>(c),
static_cast<int>(d),
static_cast<int>(h),
static_cast<int>(w)};
std::vector<int> tensor_strides;
std::string layout_default =
miopen::tensor_layout_get_default(tensor_len.size());
std::string layout_string =
miopen::TensorDescriptor::LayoutEnumToStr(miopenTensorNCDHW);
miopen::tensor_layout_to_strides(
tensor_len, layout_default, layout_string, tensor_strides);
auto t_src = tensor<T>{tensor_len, tensor_strides}.generate(gen_value<T>);
auto t_gpu_2d = tensor<T>{tensor_len, tensor_strides};
auto t_gpu_3d = tensor<T>{tensor_len, tensor_strides};
auto t_dst_ref = tensor<T>{tensor_len, tensor_strides};
auto src_dev = handle.Write(t_src.data);
// prep gpu 3D
auto invoker_3d =
PrepareLayoutTransposeInvoker(ctx, miopen_type<T>{}, n, c, d, h, w);
const auto invoke_param_3d =
transpose_invoke_param{src_dev.get(), dst_3d_dev.get()};
// run gpu 3D
invoker_3d(handle, invoke_param_3d);
t_gpu_3d.data = handle.Read<T>(dst_3d_dev, t_gpu_3d.data.size());
// prep gpu 2D
auto invoker_2d =
PrepareLayoutTransposeInvoker(ctx, miopen_type<T>{}, n, c, 1, d * h, w);
const auto invoke_param_2d =
transpose_invoke_param{src_dev.get(), dst_2d_dev.get()};
// run gpu 2D
invoker_2d(handle, invoke_param_2d);
t_gpu_2d.data = handle.Read<T>(dst_2d_dev, t_gpu_2d.data.size());
// run cpu
cpu_transpose<T, TRANSPOSE_SOL>::run(
t_dst_ref.data.data(), t_src.data.data(), n, c, d, h, w);
// we expect exact match, since use integer
verify_tensor(t_gpu_3d, "gpu3d", t_dst_ref, "cpu");
verify_tensor(t_gpu_2d, "gpu2d", t_dst_ref, "cpu");
}
}
}
}
virtual void TearDown() override {}
};
#define DEFINE_LayoutTransposeTest_2D(type, naming_type, sol) \
struct GPU_LayoutTransposeTest_2D_##sol##_##naming_type \
: public LayoutTransposeTest_2D<type, miopen::sol> \
{ \
}; \
TEST_P(GPU_LayoutTransposeTest_2D_##sol##_##naming_type, \
LayoutTransposeTest_2D_##sol##_##type##_P) \
{ \
RunTest(); \
} \
INSTANTIATE_TEST_SUITE_P( \
Full, \
GPU_LayoutTransposeTest_2D_##sol##_##naming_type, \
testing::Combine(testing::ValuesIn(transpose_dims::get_batch_size()), \
testing::ValuesIn(transpose_dims::get_channel_size())));
#define DEFINE_2D_TYPED_TESTS(sol) \
DEFINE_LayoutTransposeTest_2D(float, FP32, sol); \
DEFINE_LayoutTransposeTest_2D(float16, FP16, sol); \
DEFINE_LayoutTransposeTest_2D(bfloat16, BFP16, sol); \
DEFINE_LayoutTransposeTest_2D(uint16_t, I16, sol); \
DEFINE_LayoutTransposeTest_2D(uint8_t, I8, sol);
DEFINE_2D_TYPED_TESTS(TransposeSolutionDefault2Nhwc);
DEFINE_2D_TYPED_TESTS(TransposeSolutionNhwc2Default);
#define DEFINE_LayoutTransposeTest_3D(type, naming_type, sol) \
struct GPU_LayoutTransposeTest_3D_##sol##_##naming_type \
: public LayoutTransposeTest_3D<type, miopen::sol> \
{ \
}; \
TEST_P(GPU_LayoutTransposeTest_3D_##sol##_##naming_type, \
LayoutTransposeTest_3D_##sol##_##type##_P) \
{ \
RunTest(); \
} \
INSTANTIATE_TEST_SUITE_P( \
Full, \
GPU_LayoutTransposeTest_3D_##sol##_##naming_type, \
testing::Combine(testing::ValuesIn(transpose_dims::get_batch_size()), \
testing::ValuesIn(transpose_dims::get_channel_size())));
#define DEFINE_3D_TYPED_TESTS(sol) \
DEFINE_LayoutTransposeTest_3D(float, FP32, sol); \
DEFINE_LayoutTransposeTest_3D(float16, FP16, sol); \
DEFINE_LayoutTransposeTest_3D(bfloat16, BFP16, sol); \
DEFINE_LayoutTransposeTest_3D(uint16_t, I16, sol); \
DEFINE_LayoutTransposeTest_3D(uint8_t, I8, sol);
DEFINE_3D_TYPED_TESTS(TransposeSolutionDefault2Ndhwc);
DEFINE_3D_TYPED_TESTS(TransposeSolutionNdhwc2Default);