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

975 lines
34 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 <miopen/conv/data_invoke_params.hpp>
#include <miopen/conv/wrw_invoke_params.hpp>
#include <miopen/errors.hpp>
#include <miopen/generic_search.hpp>
#include <miopen/any_solver.hpp>
#include <miopen/solver/ck_impl_lib_loader.hpp>
#include "unit_conv_solver.hpp"
#include "get_handle.hpp"
#include "../gpu_conv.hpp"
#include "conv_common.hpp"
#include "conv_tensor_gen.hpp"
#include "tensor_holder.hpp"
#include "../workspace.hpp"
namespace miopen {
namespace unit_tests {
namespace {
class ConvAttrFp16AltScopedSetter
{
public:
ConvAttrFp16AltScopedSetter() noexcept {}
ConvAttrFp16AltScopedSetter(const ConvAttrFp16AltScopedSetter&) = delete;
ConvAttrFp16AltScopedSetter(ConvAttrFp16AltScopedSetter&&) = delete;
ConvAttrFp16AltScopedSetter& operator=(const ConvAttrFp16AltScopedSetter&) = delete;
ConvAttrFp16AltScopedSetter& operator=(ConvAttrFp16AltScopedSetter&&) = delete;
~ConvAttrFp16AltScopedSetter()
{
if(changed)
{
if(prev)
lib_env::update(wa::MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL, prev.value());
else
lib_env::clear(wa::MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL);
}
}
void SetValue(uint64_t value)
{
if(wa::MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL)
prev = lib_env::value<uint64_t>(wa::MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL);
if(value == prev)
return;
lib_env::update(wa::MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL, value);
changed = true;
}
private:
std::optional<uint64_t> prev;
bool changed = false;
};
bool IsDeviceSupported(Gpu supported_devs, Gpu dev)
{
if((supported_devs & dev) != Gpu::None)
return true;
return false;
}
bool IsDeviceExcluded(const UnitTestConvSolverParams& params, std::string_view dev_name)
{
// Runtime device names can include target properties such as xnack/sramecc.
// Test exclusions are written against the bare architecture name.
return params.excluded_devices.count(GetBaseDeviceName(dev_name)) != 0;
}
bool IsDeviceSupported(const UnitTestConvSolverParams& params,
Gpu dev,
std::string_view dev_name,
bool xnack_enabled)
{
return IsDeviceSupported(params.supported_devs, dev) && !IsDeviceExcluded(params, dev_name) &&
!(params.check_xnack_disabled && xnack_enabled);
}
bool IsCKDynamicLibLoaded(std::string_view dev_name)
{
return miopen::solver::CkImplLibLoader::Get(std::string{dev_name}).IsLoaded();
}
} // namespace
//************************************************************************************
// ConvTestCase
//************************************************************************************
ConvTestCase::ConvTestCase() : x(miopenHalf, {}), w(miopenHalf, {}), conv({}, {}, {}) {};
ConvTestCase::ConvTestCase(std::vector<size_t>&& x_,
std::vector<size_t>&& w_,
std::vector<int>&& pad_,
std::vector<int>&& stride_,
std::vector<int>&& dilation_,
miopenDataType_t type_)
: ConvTestCase(std::move(x_),
std::move(w_),
std::move(pad_),
std::move(stride_),
std::move(dilation_),
type_,
type_,
type_)
{
}
ConvTestCase::ConvTestCase(std::vector<size_t>&& x_,
std::vector<size_t>&& w_,
std::vector<int>&& pad_,
std::vector<int>&& stride_,
std::vector<int>&& dilation_,
int groups_,
miopenDataType_t type_)
: ConvTestCase(TensorDescriptorParams{type_, std::move(x_)},
TensorDescriptorParams{type_, std::move(w_)},
type_,
ConvolutionDescriptorParams{
std::move(pad_), std::move(stride_), std::move(dilation_), groups_})
{
}
ConvTestCase::ConvTestCase(std::vector<size_t>&& x_,
std::vector<size_t>&& w_,
std::vector<int>&& pad_,
std::vector<int>&& stride_,
std::vector<int>&& dilation_,
miopenDataType_t type_x_,
miopenDataType_t type_w_,
miopenDataType_t type_y_)
: ConvTestCase(
TensorDescriptorParams{type_x_, std::move(x_)},
TensorDescriptorParams{type_w_, std::move(w_)},
type_y_,
ConvolutionDescriptorParams{std::move(pad_), std::move(stride_), std::move(dilation_)})
{
}
ConvTestCase::ConvTestCase(TensorDescriptorParams&& x_,
TensorDescriptorParams&& w_,
miopenDataType_t type_y_,
ConvolutionDescriptorParams&& conv_)
: x(std::move(x_)), w(std::move(w_)), type_y(type_y_), conv(std::move(conv_))
{
const auto num_spatial_dims = conv.GetNumSpatialDims();
const auto num_tensor_dims = num_spatial_dims + 2;
const auto group_count = conv.GetGroupCount();
if(x.GetNumDims() != num_tensor_dims || w.GetNumDims() != num_tensor_dims ||
x.GetLens()[1] != w.GetLens()[1] * group_count)
{
throw std::runtime_error("wrong test case format");
}
}
miopen::TensorDescriptor ConvTestCase::GetXTensorDescriptor() const
{
return x.GetTensorDescriptor();
}
miopen::TensorDescriptor ConvTestCase::GetWTensorDescriptor() const
{
return w.GetTensorDescriptor();
}
miopenDataType_t ConvTestCase::GetXDataType() const { return x.GetDataType(); }
miopenDataType_t ConvTestCase::GetWDataType() const { return w.GetDataType(); }
miopenDataType_t ConvTestCase::GetYDataType() const { return type_y; }
miopen::ConvolutionDescriptor ConvTestCase::GetConv() const
{
return conv.GetConvolutionDescriptor();
}
miopen::conv::ProblemDescription
ConvTestCase::GetProblemDescription(miopen::conv::Direction direction) const
{
const auto x_desc = GetXTensorDescriptor();
const auto w_desc = GetWTensorDescriptor();
const auto conv_desc = GetConv();
const auto y_desc = conv_desc.GetForwardOutputTensor(x_desc, w_desc, GetYDataType());
switch(direction)
{
case miopen::conv::Direction::Forward:
return miopen::conv::ProblemDescription(x_desc, w_desc, y_desc, conv_desc, direction);
case miopen::conv::Direction::BackwardData:
case miopen::conv::Direction::BackwardWeights:
return miopen::conv::ProblemDescription(y_desc, w_desc, x_desc, conv_desc, direction);
}
throw std::runtime_error("unknown direction");
}
std::ostream& operator<<(std::ostream& os, const ConvTestCase& tc)
{
os << "(";
os << "x:(" << tc.x << "), ";
os << "w:(" << tc.w << "), ";
os << "type_y:" << tc.type_y << "), ";
os << "conv:(" << tc.conv << ")";
os << ")";
return os;
}
//************************************************************************************
// Unit test for convolution solver
//************************************************************************************
uint64_t Tolerances::GetKey(Gpu gpu, miopenDataType_t type)
{
static_assert(sizeof(gpu) <= sizeof(uint64_t) / 2);
static_assert(sizeof(type) <= sizeof(uint64_t) / 2);
return (static_cast<uint64_t>(gpu) << 32) | static_cast<uint64_t>(type);
}
void Tolerances::Set(Gpu gpu, miopenDataType_t type, float value)
{
if(gpu == Gpu::None)
return;
int igpu = static_cast<int>(gpu);
if((igpu & (igpu - 1)) == 0)
{
values[GetKey(gpu, type)] = value;
}
else
{
for(int g = 1; g <= static_cast<int>(Gpu::gfxLast); g <<= 1)
{
if((g & igpu) != 0)
{
values[GetKey(static_cast<Gpu>(g), type)] = value;
}
}
}
}
float Tolerances::Get(Gpu gpu, miopenDataType_t type) const
{
MIOPEN_THROW_IF((static_cast<int>(gpu) & (static_cast<int>(gpu) - 1)) != 0,
"cannot call Tolerance::Get with multiple gpus");
MIOPEN_THROW_IF(gpu > Gpu::gfxLast, "Tolerance::Get called with invalid gpu");
const auto& v = values.find(GetKey(gpu, type));
if(v == values.cend())
return 1.0f; // default value
return v->second;
}
std::ostream& operator<<(std::ostream& os, const Tolerances& t)
{
os << "(";
for(const auto [key, value] : t.values)
os << std::hex << "0x" << key << std::dec << ":" << value << ",";
os << ")";
return os;
}
UnitTestConvSolverParams::UnitTestConvSolverParams() : UnitTestConvSolverParams(Gpu::None) {}
UnitTestConvSolverParams::UnitTestConvSolverParams(Gpu supported_devs_)
: supported_devs(supported_devs_),
use_cpu_ref(false),
use_gpu_ref(false),
tunable(false),
check_xnack_disabled(false),
uses_ck_dynamic_lib(false)
{
}
void UnitTestConvSolverParams::UseCpuRef() { use_cpu_ref = true; }
void UnitTestConvSolverParams::UseGpuRef() { use_gpu_ref = true; }
void UnitTestConvSolverParams::Tunable(std::size_t iterations_max_)
{
tunable = true;
tuning_iterations_max = iterations_max_;
}
void UnitTestConvSolverParams::CheckXnackDisabled() { check_xnack_disabled = true; }
void UnitTestConvSolverParams::UsesCKDynamicLib() { uses_ck_dynamic_lib = true; }
void UnitTestConvSolverParams::SetConvAttrFp16Alt(uint64_t value) { conv_attr_fp16_alt = value; }
void UnitTestConvSolverParams::SetTolerance(Gpu gpu, miopenDataType_t type, float value)
{
tolerances.Set(gpu, type, value);
}
void UnitTestConvSolverParams::ExcludeDevice(std::string_view name)
{
excluded_devices.emplace(name);
}
std::ostream& operator<<(std::ostream& os, const UnitTestConvSolverParams& p)
{
os << "(";
os << "Devs:" << std::hex << "0x"
<< static_cast<std::underlying_type_t<decltype(p.supported_devs)>>(p.supported_devs)
<< std::dec;
if(p.use_cpu_ref)
os << ", CpuRef:" << p.use_cpu_ref;
if(p.tunable)
os << ", IterMax:" << p.tuning_iterations_max;
if(p.check_xnack_disabled)
os << ", CheckXnackOff:" << p.check_xnack_disabled;
if(p.conv_attr_fp16_alt)
os << ", AttrFp16Alt:" << p.conv_attr_fp16_alt.value();
os << ", Tolerances:" << p.tolerances;
os << ")";
return os;
}
namespace {
miopen::solver::ConvSolution FindSolution(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const miopen::ExecutionContext& ctx,
const miopen::conv::ProblemDescription& problem,
const AnyInvokeParams& invoke_ctx)
{
if(params.tunable)
{
using IterationLimiter = miopen::solver::debug::TuningIterationScopedLimiter;
IterationLimiter tuning_limit{params.tuning_iterations_max};
const auto& tunable_solv =
dynamic_cast<const miopen::solver::conv::ConvSolverInterfaceTunable&>(solv);
return tunable_solv.FindSolutionSimple(ctx, problem, invoke_ctx);
}
else
{
const auto& non_tunable_solv =
dynamic_cast<const miopen::solver::conv::ConvSolverInterfaceNonTunable&>(solv);
return non_tunable_solv.GetSolution(ctx, problem);
}
}
template <typename T>
double GetThreshold(miopenConvAlgorithm_t /*algo*/,
miopen::conv::Direction /*direction*/,
const Tolerances& tolerances,
const bool use_tf32_compute)
{
double tolerance = tolerances.Get(GetDevGpuType(), miopen_type<T>{});
double threshold = std::numeric_limits<T>::epsilon() * tolerance;
if constexpr(std::is_same_v<T, float>)
{
if(use_tf32_compute)
{
threshold = std::numeric_limits<half_float::half>::epsilon() * tolerance;
}
}
return threshold;
}
template <typename T, typename Tref>
void VerifyData(const std::vector<T>& data,
const std::vector<Tref>& ref_data,
miopenConvAlgorithm_t algo,
miopen::conv::Direction direction,
const Tolerances& tolerances,
bool use_tf32_compute = false)
{
ASSERT_FALSE(miopen::range_zero(ref_data)) << "Reference data is all zeros";
if constexpr(!std::is_integral_v<T>)
{
ASSERT_LT(miopen::find_idx(ref_data, miopen::not_finite), 0)
<< "Non finite number found in the reference data";
}
ASSERT_FALSE(miopen::range_zero(data)) << "Gpu data is all zeros";
if constexpr(!std::is_integral_v<T>)
{
ASSERT_LT(miopen::find_idx(data, miopen::not_finite), 0)
<< "Non finite number found in the Gpu data";
}
ASSERT_EQ(miopen::range_distance(ref_data), miopen::range_distance(data));
if constexpr(std::is_integral_v<T>)
{
const auto error = miopen::max_diff_v2(ref_data, data);
static_assert(std::is_integral_v<decltype(error)>);
ASSERT_EQ(error, 0) << "Error beyond tolerance";
}
else
{
const auto error = miopen::rms_range(ref_data, data);
const double threshold = GetThreshold<T>(algo, direction, tolerances, use_tf32_compute);
ASSERT_LT(error, threshold) << "Error beyond tolerance";
// std::cout << "error: " << error << " threshold: " << threshold << std::endl;
}
}
//**********************************
// Fwd
//**********************************
template <typename Tin, typename Twei, typename Tout, typename Tref>
void RunSolverFwd(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
//**********************************
// Prepare
//**********************************
auto input = tensor<Tin>{conv_config.GetXTensorDescriptor()};
auto weights = tensor<Twei>{conv_config.GetWTensorDescriptor()};
if(weights.desc.GetLayoutEnum() == miopenTensorCHWNc4 ||
weights.desc.GetLayoutEnum() == miopenTensorCHWNc8)
{
throw std::runtime_error("GenConvData do not support CHWNc filter layout");
}
const auto conv_desc = conv_config.GetConv();
const auto output_desc =
conv_desc.GetForwardOutputTensor(input.desc, weights.desc, miopen_type<Tout>{});
auto output = tensor<Tout>{output_desc};
input.generate(GenConvData<Tin, Tout>{weights.desc.GetLengths()});
weights.generate(GenConvData<Twei, Tout>{weights.desc.GetLengths()});
std::fill(output.begin(), output.end(), Tout());
auto&& handle = get_handle();
auto in_dev = handle.Write(input.data);
auto wei_dev = handle.Write(weights.data);
auto out_dev = handle.Write(output.data);
//**********************************
// Run solver
//**********************************
const auto tensors = miopen::ConvFwdTensors{
input.desc, in_dev.get(), weights.desc, wei_dev.get(), output.desc, out_dev.get()};
const auto problem = miopen::conv::ProblemDescription(
input.desc, weights.desc, output.desc, conv_desc, miopen::conv::Direction::Forward);
const auto ctx = [&] {
auto tmp = miopen::ExecutionContext{&handle};
problem.SetupFloats(tmp);
problem.SetupComputeType(tmp);
return std::move(tmp);
}();
auto device_name = ctx.GetStream().GetDeviceName();
if(!(miopen::StartsWith(device_name, "gfx942") || miopen::StartsWith(device_name, "gfx950")) &&
conv_config.GetXDataType() == miopenFloat &&
conv_config.GetConv().GetMathType() == miopenMathDefault)
{
GTEST_SKIP() << "TF32 test is not supported on this device";
}
if(!solv.IsApplicable(ctx, problem))
{
// Do not put GTEST_SKIP here.
// The usage of non-applicable config should be considered as a bug in the test.
GTEST_FAIL();
}
Workspace wspace;
if(solv.MayNeedWorkspace())
{
const auto cur_sol_ws = solv.GetWorkspaceSize(ctx, problem);
wspace.resize(cur_sol_ws);
}
const auto invoke_params = miopen::conv::DataInvokeParams{
tensors, wspace.ptr(), wspace.size(), conv_desc.attribute.gfx90aFp16alt.GetFwd()};
const auto sol = FindSolution(solv, params, ctx, problem, invoke_params);
ASSERT_TRUE(sol.Succeeded());
ASSERT_TRUE(sol.invoker_factory);
const auto invoker = handle.PrepareInvoker(*sol.invoker_factory, sol.construction_params);
(invoker)(handle, invoke_params);
handle.Finish();
//**********************************
// Verify
//**********************************
auto ref_out = tensor<Tref>{output.desc};
if(params.use_cpu_ref)
{
cpu_convolution_forward(conv_desc.GetSpatialDimension(),
input,
weights,
ref_out,
conv_desc.GetConvPads(),
conv_desc.GetConvStrides(),
conv_desc.GetConvDilations(),
conv_desc.GetGroupCount());
}
else if(params.use_gpu_ref)
{
const bool gpu_ref_used = gpu_ref_convolution_fwd(input, weights, ref_out, conv_desc);
if(!gpu_ref_used)
{
throw std::runtime_error("GPU reference not available for this configuration");
}
}
else
{
ref_out = ref_conv_fwd(input, weights, ref_out, conv_desc);
}
output.data = handle.Read<Tout>(out_dev, output.data.size());
VerifyData(output.data,
ref_out.data,
algo,
miopen::conv::Direction::Forward,
params.tolerances,
problem.UseTF32());
}
template <typename T, typename Tref>
void RunSolverFwd(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
RunSolverFwd<T, T, T, Tref>(solv, params, conv_config, algo);
}
//**********************************
// Bwd
//**********************************
template <typename Tin, typename Twei, typename Tout, typename Tref>
void RunSolverBwd(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
//**********************************
// Prepare
//**********************************
auto input = tensor<Tin>{conv_config.GetXTensorDescriptor()};
auto weights = tensor<Twei>{conv_config.GetWTensorDescriptor()};
if(weights.desc.GetLayoutEnum() == miopenTensorCHWNc4 ||
weights.desc.GetLayoutEnum() == miopenTensorCHWNc8)
{
throw std::runtime_error("GenConvData do not support CHWNc filter layout");
}
const auto conv_desc = conv_config.GetConv();
const auto output_desc =
conv_desc.GetForwardOutputTensor(input.desc, weights.desc, miopen_type<Tout>{});
auto output = tensor<Tout>{output_desc};
output.generate(GenConvData<Tout, Tin>{weights.desc.GetLengths()});
weights.generate(GenConvData<Twei, Tin>{weights.desc.GetLengths()});
std::fill(input.begin(), input.end(), Tin());
auto&& handle = get_handle();
auto in_dev = handle.Write(input.data);
auto wei_dev = handle.Write(weights.data);
auto out_dev = handle.Write(output.data);
//**********************************
// Run solver
//**********************************
const auto tensors = miopen::ConvBwdTensors{
output.desc, out_dev.get(), weights.desc, wei_dev.get(), input.desc, in_dev.get()};
const auto problem = miopen::conv::ProblemDescription(
output.desc, weights.desc, input.desc, conv_desc, miopen::conv::Direction::BackwardData);
const auto ctx = [&] {
auto tmp = miopen::ExecutionContext{&handle};
problem.SetupFloats(tmp);
problem.SetupComputeType(tmp);
return std::move(tmp);
}();
if(!solv.IsApplicable(ctx, problem))
{
// Do not put GTEST_SKIP here.
// The usage of non-applicable config should be considered as a bug in the test.
GTEST_FAIL();
}
Workspace wspace;
if(solv.MayNeedWorkspace())
{
const auto cur_sol_ws = solv.GetWorkspaceSize(ctx, problem);
wspace.resize(cur_sol_ws);
}
const auto invoke_params = miopen::conv::DataInvokeParams{
tensors, wspace.ptr(), wspace.size(), conv_desc.attribute.gfx90aFp16alt.GetBwd()};
const auto sol = FindSolution(solv, params, ctx, problem, invoke_params);
ASSERT_TRUE(sol.Succeeded());
ASSERT_TRUE(sol.invoker_factory);
const auto invoker = handle.PrepareInvoker(*sol.invoker_factory, sol.construction_params);
(invoker)(handle, invoke_params);
handle.Finish();
//**********************************
// Verify
//**********************************
auto ref_in = tensor<Tref>{input.desc};
if(params.use_cpu_ref)
{
cpu_convolution_backward_data(conv_desc.GetSpatialDimension(),
ref_in,
weights,
output,
conv_desc.GetConvPads(),
conv_desc.GetConvStrides(),
conv_desc.GetConvDilations(),
conv_desc.GetGroupCount());
}
else if(params.use_gpu_ref)
{
const bool gpu_ref_used = gpu_ref_convolution_bwd(ref_in, weights, output, conv_desc);
if(!gpu_ref_used)
{
throw std::runtime_error("GPU reference not available for this configuration");
}
}
else
{
ref_in = ref_conv_bwd(ref_in, weights, output, conv_desc);
}
input.data = handle.Read<Tin>(in_dev, input.data.size());
VerifyData(input.data,
ref_in.data,
algo,
miopen::conv::Direction::BackwardData,
params.tolerances,
problem.UseTF32());
}
template <typename T, typename Tref>
void RunSolverBwd(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
RunSolverBwd<T, T, T, Tref>(solv, params, conv_config, algo);
}
//**********************************
// Wrw
//**********************************
template <typename Tin, typename Twei, typename Tout, typename Tref>
void RunSolverWrw(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
//**********************************
// Prepare
//**********************************
auto input = tensor<Tin>{conv_config.GetXTensorDescriptor()};
auto weights = tensor<Twei>{conv_config.GetWTensorDescriptor()};
const auto conv_desc = conv_config.GetConv();
const auto output_desc =
conv_desc.GetForwardOutputTensor(input.desc, weights.desc, miopen_type<Tout>{});
if(output_desc.GetLayoutEnum() == miopenTensorCHWNc4 ||
output_desc.GetLayoutEnum() == miopenTensorCHWNc8)
{
throw std::runtime_error("GenConvData do not support CHWNc filter layout");
}
auto output = tensor<Tout>{output_desc};
input.generate(GenConvData<Tin, Twei>{output_desc.GetLengths()});
output.generate(GenConvData<Tout, Twei>{output_desc.GetLengths()});
std::fill(weights.begin(), weights.end(), Twei());
auto&& handle = get_handle();
auto in_dev = handle.Write(input.data);
auto wei_dev = handle.Write(weights.data);
auto out_dev = handle.Write(output.data);
//**********************************
// Run solver
//**********************************
const auto tensors = miopen::ConvWrwTensors{
output.desc, out_dev.get(), input.desc, in_dev.get(), weights.desc, wei_dev.get()};
const auto problem = miopen::conv::ProblemDescription(
output.desc, weights.desc, input.desc, conv_desc, miopen::conv::Direction::BackwardWeights);
const auto ctx = [&] {
auto tmp = miopen::ExecutionContext{&handle};
problem.SetupFloats(tmp);
problem.SetupComputeType(tmp);
return std::move(tmp);
}();
if(!solv.IsApplicable(ctx, problem))
{
// Do not put GTEST_SKIP here.
// The usage of non-applicable config should be considered as a bug in the test.
GTEST_FAIL() << "IsApplicable failed!";
}
Workspace wspace;
if(solv.MayNeedWorkspace())
{
const auto cur_sol_ws = solv.GetWorkspaceSize(ctx, problem);
wspace.resize(cur_sol_ws);
}
const auto invoke_params = miopen::conv::WrWInvokeParams{
tensors, wspace.ptr(), wspace.size(), conv_desc.attribute.gfx90aFp16alt.GetWrW()};
const auto sol = FindSolution(solv, params, ctx, problem, invoke_params);
ASSERT_TRUE(sol.Succeeded());
ASSERT_TRUE(sol.invoker_factory);
const auto invoker = handle.PrepareInvoker(*sol.invoker_factory, sol.construction_params);
(invoker)(handle, invoke_params);
handle.Finish();
//**********************************
// Verify
//**********************************
auto ref_weights = tensor<Tref>{weights.desc};
if(params.use_cpu_ref)
{
cpu_convolution_backward_weight(conv_desc.GetSpatialDimension(),
input,
ref_weights,
output,
conv_desc.GetConvPads(),
conv_desc.GetConvStrides(),
conv_desc.GetConvDilations(),
conv_desc.GetGroupCount());
}
else if(params.use_gpu_ref)
{
const bool gpu_ref_used = gpu_ref_convolution_wrw(input, ref_weights, output, conv_desc);
if(!gpu_ref_used)
{
throw std::runtime_error("GPU reference not available for this configuration");
}
}
else
{
ref_weights = ref_conv_wrw(input, ref_weights, output, conv_desc);
}
weights.data = handle.Read<Twei>(wei_dev, weights.data.size());
VerifyData(weights.data,
ref_weights.data,
algo,
miopen::conv::Direction::BackwardWeights,
params.tolerances,
problem.UseTF32());
}
template <typename T, typename Tref>
void RunSolverWrw(const miopen::solver::conv::ConvSolverInterface& solv,
const UnitTestConvSolverParams& params,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
RunSolverWrw<T, T, T, Tref>(solv, params, conv_config, algo);
}
template <typename T, typename Tref>
void RunSolver(const miopen::solver::conv::ConvSolverInterface& solver,
const UnitTestConvSolverParams& params,
miopen::conv::Direction direction,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
// clang-format off
switch(direction)
{
case miopen::conv::Direction::Forward:
RunSolverFwd<T, Tref>(solver, params, conv_config, algo);
return;
case miopen::conv::Direction::BackwardData:
RunSolverBwd<T, Tref>(solver, params, conv_config, algo);
return;
case miopen::conv::Direction::BackwardWeights:
RunSolverWrw<T, Tref>(solver, params, conv_config, algo);
return;
}
// clang-format on
throw std::runtime_error("unknown direction");
}
void RunSolver(const miopen::solver::conv::ConvSolverInterface& solver,
const UnitTestConvSolverParams& params,
miopen::conv::Direction direction,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
if(conv_config.GetXDataType() == conv_config.GetWDataType() &&
conv_config.GetWDataType() == conv_config.GetYDataType())
{
// clang-format off
switch(conv_config.GetXDataType())
{
case miopenHalf:
RunSolver<half_float::half, half_float::half>(solver, params, direction, conv_config, algo);
return;
case miopenFloat:
RunSolver<float, float>(solver, params, direction, conv_config, algo);
return;
case miopenBFloat16:
RunSolver<bfloat16, bfloat16>(solver, params, direction, conv_config, algo);
return;
case miopenInt8:
RunSolver<int8_t, int8_t>(solver, params, direction, conv_config, algo);
return;
case miopenInt32:
case miopenDouble:
case miopenFloat8_fnuz:
case miopenBFloat8_fnuz:
case miopenInt64:
throw std::runtime_error("handling of this data type is not yet implemented");
}
// clang-format on
}
else if(direction == miopen::conv::Direction::Forward &&
conv_config.GetXDataType() == miopenInt8 && conv_config.GetWDataType() == miopenInt8 &&
conv_config.GetYDataType() == miopenInt32)
{
RunSolverFwd<int8_t, int8_t, int32_t, int32_t>(solver, params, conv_config, algo);
return;
}
throw std::runtime_error("handling of mixed data types is not yet implemented");
}
} // namespace
void UnitTestConvSolverBase::SetUpImpl(const UnitTestConvSolverParams& params)
{
if(!IsTestSupportedByDevice(params.supported_devs))
{
GTEST_SKIP();
}
else if(IsDeviceExcluded(params, get_handle().GetDeviceName()))
{
GTEST_SKIP();
}
else if(params.check_xnack_disabled && get_handle_xnack())
{
GTEST_SKIP();
}
else if(params.uses_ck_dynamic_lib && !IsCKDynamicLibLoaded(get_handle().GetDeviceName()))
{
GTEST_SKIP() << "CK dynamic library is not available for " << get_handle().GetDeviceName();
}
}
void UnitTestConvSolverBase::RunTestImpl(const miopen::solver::conv::ConvSolverInterface& solver,
const UnitTestConvSolverParams& params,
miopen::conv::Direction direction,
const ConvTestCase& conv_config,
miopenConvAlgorithm_t algo)
{
ConvAttrFp16AltScopedSetter conv_attr_fp16_alt_setter;
if(params.conv_attr_fp16_alt)
conv_attr_fp16_alt_setter.SetValue(params.conv_attr_fp16_alt.value());
RunSolver(solver, params, direction, conv_config, algo);
}
//************************************************************************************
// This test is designed to detect the expansion of the solver's device applicability
//************************************************************************************
void UnitTestConvSolverDevApplicabilityBase::RunTestImpl(
const miopen::solver::conv::ConvSolverInterface& solver,
const UnitTestConvSolverParams& params,
miopen::conv::Direction direction,
const ConvTestCase& conv_config)
{
ConvAttrFp16AltScopedSetter conv_attr_fp16_alt_setter;
if(params.conv_attr_fp16_alt)
conv_attr_fp16_alt_setter.SetValue(params.conv_attr_fp16_alt.value());
const auto problem = conv_config.GetProblemDescription(direction);
if(params.uses_ck_dynamic_lib)
{
const auto current_dev_name = get_handle().GetDeviceName();
if(!IsCKDynamicLibLoaded(current_dev_name))
GTEST_SKIP() << "CK dynamic library is not available for " << current_dev_name;
}
const auto current_dev_name = get_handle().GetDeviceName();
const auto current_dev_base_name = GetBaseDeviceName(current_dev_name);
const auto all_known_devs = GetAllKnownDevices();
for(const auto& [dev, dev_descr] : all_known_devs)
{
if(params.uses_ck_dynamic_lib && current_dev_base_name != dev_descr.name)
continue;
const auto supported = IsDeviceSupported(params, dev, dev_descr.name, false);
// std::cout << "Test " << dev_descr << " (supported: " << supported << ")" << std::endl;
auto handle = MockHandle{dev_descr, params.check_xnack_disabled};
const auto ctx = [&] {
auto tmp = miopen::ExecutionContext{&handle};
problem.SetupFloats(tmp);
problem.SetupComputeType(tmp);
return tmp;
}();
const auto is_applicable = solver.IsApplicable(ctx, problem);
// std::cout << "IsApplicable: " << is_applicable << std::endl;
if(is_applicable != supported)
{
GTEST_FAIL() << dev_descr << " is" << (is_applicable ? "" : " not")
<< " applicable for " << solver.SolverDbId() << " but "
<< (supported ? "" : "not ") << "marked as supported";
}
}
}
} // namespace unit_tests
} // namespace miopen