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

392 lines
15 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (c) 2023 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 <gtest/gtest_common.hpp>
#include <miopen/miopen.h>
#include <miopen/fusion.hpp>
#include <miopen/fusion/solvers.hpp>
#include <miopen/find_solution.hpp>
#include "tensor_holder.hpp"
#include "get_handle.hpp"
#include "cba.hpp"
#include "gtest_desc_guard.hpp"
#include "../lib_env_var.hpp"
#if MIOPEN_BACKEND_HIP
namespace {
bool IsTestSupportedForDevice()
{
using e_mask = enabled<Gpu::gfx94X, Gpu::gfx103X, Gpu::gfx110X, Gpu::gfx115X>;
// gfx120X is not enabled due to WORKAROUND_SWDEV_479810
using d_mask = disabled<Gpu::None>;
return ::IsTestSupportedForDevMask<d_mask, e_mask>();
}
bool IsWorkspaceTestSupportedForDevice()
{
using e_mask = enabled<Gpu::gfx94X>;
// requires ConvCKIgemmGrpFwdBiasActivFused solver
using d_mask = disabled<Gpu::gfx900, Gpu::gfx906>;
return ::IsTestSupportedForDevMask<d_mask, e_mask>();
}
template <typename T>
class FusionSetArgTest : public ConvBiasActivInferTest<T>
{
public:
void SetUp() override
{
cba<T>::SetUp();
weights2 = tensor<T>{cba<T>::tensor_layout, cba<T>::conv_config.GetWeights()};
weights2.generate(tensor_elem_gen_integer{3});
cba<T>::weights = weights2;
auto&& handle = get_handle();
cba<T>::wei_dev = handle.Write(weights2.data);
handle.Finish();
}
void TearDown() override { cba<T>::TearDown(); }
template <typename Tp>
using cba = ConvBiasActivInferTest<Tp>;
tensor<T> weights2;
miopen::Allocator::ManageDataPtr wei_dev2;
};
bool SkipTest() { return get_handle_xnack(); }
} // namespace
using GPU_FusionSetArg_FP16 = FusionSetArgTest<float>;
TEST_P(GPU_FusionSetArg_FP16, TestSetArgApiCall)
{
// Original fusion_plan/args execution happens in cba_infer.cpp
// Original is checked independently and not sequentially, prior to FusionTestSetArgTest.
if(SkipTest())
{
test_skipped = true;
GTEST_SKIP() << "Fusion does not support xnack";
}
if(!IsTestSupportedForDevice())
{
test_skipped = true;
GTEST_SKIP() << "CBA fusion_test is not supported for this device";
}
using cba_float = cba<float>;
auto&& handle = get_handle();
auto convOp = std::make_shared<miopen::ConvForwardOpDescriptor>(cba_float::conv_desc,
cba_float::weights.desc);
miopenOperatorArgs_t fusion_args = static_cast<miopenOperatorArgs_t>(&(cba_float::params));
miopenFusionPlanDescriptor_t fusion_plan =
static_cast<miopenFusionPlanDescriptor_t>(&(cba_float::fusePlanDesc));
miopenFusionOpDescriptor_t conv_op = static_cast<miopenFusionOpDescriptor_t>(convOp.get());
size_t workspace_size = 0;
miopenConvFwdAlgorithm_t algo{}; // not used in GetWorkSpaceSize
EXPECT_EQ(miopenFusionPlanGetWorkSpaceSize(&handle, fusion_plan, &workspace_size, algo),
miopenStatusSuccess)
<< "Workspace size for a non-compiled fusion plan failure";
EXPECT_EQ(miopenCompileFusionPlan(&handle, fusion_plan), 0);
EXPECT_EQ(miopenSetOpArgsConvForward(fusion_args,
conv_op,
&(cba_float::alpha),
&(cba_float::beta),
cba_float::wei_dev.get()),
0);
workspace_size = 0;
EXPECT_EQ(miopenFusionPlanGetWorkSpaceSize(&handle, fusion_plan, &workspace_size, algo),
miopenStatusSuccess)
<< "Workspace size for a compiled fusion plan failure";
cba_float::wspace.resize(workspace_size);
EXPECT_EQ(miopenExecuteFusionPlan_v2(&handle,
fusion_plan,
&(cba_float::input.desc),
cba_float::in_dev.get(),
&(cba_float::output.desc),
cba_float::out_dev.get(),
fusion_args,
cba_float::wspace.ptr(),
cba_float::wspace.size()),
0);
handle.Finish();
using ConvParam = miopen::fusion::ConvolutionOpInvokeParam;
ConvParam* conv_param = dynamic_cast<ConvParam*>(miopen::deref(fusion_args).params[0].get());
ASSERT_EQ(conv_param->weights, wei_dev.get());
}
INSTANTIATE_TEST_SUITE_P(Full,
GPU_FusionSetArg_FP16,
testing::Combine(testing::Values(miopenActivationRELU),
testing::ValuesIn(GetNetwork1<ConvTestCaseBase>()),
testing::Values(miopenTensorNCHW),
testing::Values(0.25f),
testing::Values(0.75f),
testing::Values(0.5f)));
TEST(CPU_FusionCreateOpConvForward_FP32, TestInvalidConvLayout)
{
std::vector<int> xDims{4, 4, 4, 4};
std::vector<int> xStrides{1, 4, 16, 64}; // WHCN order
std::vector<int> wDims{1, 4, 4, 4};
std::vector<int> wStrides{16, 4, 1, 1};
std::vector<int> padding{0, 0};
std::vector<int> dilation{1, 1};
std::vector<int> stride{1, 1};
TensorDescGuard xDesc;
miopenSetTensorDescriptor(
xDesc, miopenDataType_t::miopenFloat, xDims.size(), xDims.data(), xStrides.data());
TensorDescGuard wDesc;
miopenSetTensorDescriptor(
wDesc, miopenDataType_t::miopenFloat, wDims.size(), wDims.data(), wStrides.data());
miopenFusionPlanDescriptor_t fusionPlanDesc;
miopenCreateFusionPlan(&fusionPlanDesc, miopenVerticalFusion, xDesc);
ConvDescGuard convDesc;
miopenInitConvolutionNdDescriptor(convDesc,
2,
padding.data(),
stride.data(),
dilation.data(),
miopenConvolutionMode_t::miopenConvolution);
miopenFusionOpDescriptor_t convOp;
auto status = miopenCreateOpConvForward(fusionPlanDesc, &convOp, convDesc, wDesc);
EXPECT_EQUAL(status, miopenStatusUnknownError);
miopenDestroyFusionPlan(fusionPlanDesc);
}
MIOPEN_LIB_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_F2X3_G1)
// The test uses a specific fusion configuration that triggers the solver.
// ConvCKIgemmGrpFwdBiasActivFused
// The test runs two cases:
// 1) with exact or larger workspace size than required (expected to pass)
// 2) with smaller workspace size than required (expected to return error)
// check if ConvCKIgemmGrpFwdBiasActivFused solver is supported on the device
bool IsConvCKIgemmGrpFwdBiasActivFusedSolverSupported(miopen::FusionPlanDescriptor& fusion_plan)
{
const auto fusion_problem = miopen::FusionDescription{&fusion_plan};
auto fusion_ctx = miopen::FusionContext{get_handle()};
miopen::solver::fusion::ConvCKIgemmGrpFwdBiasActivFused solv{};
return solv.IsApplicable(fusion_ctx, fusion_problem);
}
//
// Check that only ConvCKIgemmGrpFwdBiasActivFused solver is applicable for the fusion problem
// This is to ensure that no other solver interferes with the test
//
bool IsOnlyConvCKIgemmGrpFwdBiasActivFusedSolverApplicable(
miopen::FusionPlanDescriptor& fusion_plan)
{
const auto fusion_problem = miopen::FusionDescription{&fusion_plan};
auto ctx = miopen::FusionContext{get_handle()};
const auto target_id = miopen::solver::Id("ConvCKIgemmGrpFwdBiasActivFused");
std::vector<miopen::solver::Id> ids =
miopen::debug::GetAllApplicableFusionSolutions(ctx, fusion_problem);
// find all fusion solvers that are applicable but not expected
for(auto id : ids)
{
if(id.Value() != target_id.Value())
return false;
}
return true;
}
template <typename T>
class GPU_CBAFind2FusionWorkspace : public ConvBiasActivInferTest<T>
{
public:
using cba_base = ConvBiasActivInferTest<T>;
// Setup should be extanded to add some specific fields for Fusion
void SetUp() override
{
cba_base::SetUp();
fusion_args = static_cast<miopenOperatorArgs_t>(&(cba_base::params));
fusion_plan = static_cast<miopenFusionPlanDescriptor_t>(&(cba_base::fusePlanDesc));
miopen::deref(fusion_plan).findMode.Set(miopen::FindMode::Values::Normal);
}
void RunTest(bool PositiveTest)
{
if(IsConvCKIgemmGrpFwdBiasActivFusedSolverSupported(cba_base::fusePlanDesc) == false)
{
this->test_skipped = true;
GTEST_SKIP()
<< "ConvCKIgemmGrpFwdBiasActivFused solver is not supported on this device";
}
miopen::solver::debug::TuningIterationScopedLimiter tuning_limit{5};
auto&& handle = get_handle();
size_t workspace_size = 0;
miopenConvFwdAlgorithm_t algo{}; // not used in GetWorkSpaceSize
{
ScopedEnvironment<bool> find_mode_env1(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_F2X3_G1, false);
ASSERT_TRUE(
IsOnlyConvCKIgemmGrpFwdBiasActivFusedSolverApplicable(cba_base::fusePlanDesc))
<< "Test configuration is invalid as other solvers are applicable. Please update "
"the test case.";
EXPECT_EQ(miopenFusionPlanGetWorkSpaceSize(&handle, fusion_plan, &workspace_size, algo),
miopenStatusSuccess)
<< "Workspace size for a non-compiled fusion plan failure";
EXPECT_EQ(miopenCompileFusionPlan(&handle, fusion_plan), miopenStatusSuccess);
}
workspace_size = 0;
EXPECT_EQ(miopenFusionPlanGetWorkSpaceSize(&handle, fusion_plan, &workspace_size, algo),
miopenStatusSuccess)
<< "Workspace size for a compiled fusion plan failure";
// This test requires a case with a non-zero workspace size.
// If this check fails, the test configuration needs to be updated
// to a case that requires workspace.
ASSERT_TRUE(workspace_size > 0)
<< "Test configuration does not require workspace. Please update the test case.";
if(PositiveTest)
{
// Test with exact workspace size
cba_base::wspace.resize(workspace_size);
EXPECT_EQ(miopenExecuteFusionPlan_v2(&handle,
fusion_plan,
&(cba_base::input.desc),
cba_base::in_dev.get(),
&(cba_base::output.desc),
cba_base::out_dev.get(),
fusion_args,
cba_base::wspace.ptr(),
cba_base::wspace.size()),
miopenStatusSuccess);
// Test with a larger workspace than required
cba_base::wspace.resize(workspace_size + 10);
EXPECT_EQ(miopenExecuteFusionPlan_v2(&handle,
fusion_plan,
&(cba_base::input.desc),
cba_base::in_dev.get(),
&(cba_base::output.desc),
cba_base::out_dev.get(),
fusion_args,
cba_base::wspace.ptr(),
cba_base::wspace.size()),
miopenStatusSuccess);
}
else
{
// Test with a smaller workspace than required
// Should return miopenStatusBadParm
cba_base::wspace.resize(workspace_size - 10);
EXPECT_EQ(miopenExecuteFusionPlan_v2(&handle,
fusion_plan,
&(cba_base::input.desc),
cba_base::in_dev.get(),
&(cba_base::output.desc),
cba_base::out_dev.get(),
fusion_args,
cba_base::wspace.ptr(),
cba_base::wspace.size()),
miopenStatusBadParm);
test_verification = false;
}
handle.Finish();
}
void TearDown() override
{
if(test_verification)
cba_base::TearDown();
}
miopenOperatorArgs_t fusion_args;
miopenFusionPlanDescriptor_t fusion_plan;
bool test_verification = true;
};
using GPU_CBAFind2FusionWorkspace_FP32 = GPU_CBAFind2FusionWorkspace<float>;
TEST_P(GPU_CBAFind2FusionWorkspace_FP32, CBAFind2_testFindWorkspace)
{
if(SkipTest())
{
test_skipped = true;
GTEST_SKIP() << "Fusion does not support xnack";
}
if(!IsWorkspaceTestSupportedForDevice())
{
test_skipped = true;
GTEST_SKIP() << "Fusion not supported for this device";
}
RunTest(true);
}
TEST_P(GPU_CBAFind2FusionWorkspace_FP32, CBAFind2_testWorkspaceInvalidSize)
{
if(SkipTest())
{
test_skipped = true;
GTEST_SKIP() << "Fusion does not support xnack";
}
if(!IsWorkspaceTestSupportedForDevice())
{
test_skipped = true;
GTEST_SKIP() << "Fusion not supported for this device";
}
RunTest(false);
}
INSTANTIATE_TEST_SUITE_P(
Smoke,
GPU_CBAFind2FusionWorkspace_FP32,
testing::Combine(testing::Values(miopenActivationRELU),
testing::Values(ConvTestCaseBase{
1, 64, 52, 53, 63, 1, 1, 0, 0, 1, 1, 1, 1, miopenConvolution}),
// try to use unique case that uses ConvCKIgemmGrpFwdBiasActivFused solver
// to avoid interference with other tests
testing::Values(miopenTensorNCHW),
testing::Values(0.25f),
testing::Values(0.25f),
testing::Values(0.25f)));
#endif