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
740 lines
28 KiB
C++
740 lines
28 KiB
C++
// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <miopen/env.hpp>
|
|
#include <miopen/filesystem.hpp>
|
|
#include <miopen/solver/ck_impl_error.hpp>
|
|
#include <miopen/solver/ck_impl_lib_loader.hpp>
|
|
#include <miopen/conv/problem_description.hpp>
|
|
#include <miopen/conv_solution.hpp>
|
|
#include <miopen/convolution.hpp>
|
|
#include <miopen/execution_context.hpp>
|
|
#include <miopen/tensor.hpp>
|
|
|
|
#include <thread>
|
|
|
|
#if MIOPEN_BACKEND_HIP
|
|
#include <hip/hip_runtime.h>
|
|
#endif
|
|
|
|
using miopen::solver::CKSolverType;
|
|
using miopen::solver::GetCurrentDeviceName;
|
|
using miopen::solver::IsDeterministicSplitKValid;
|
|
|
|
MIOPEN_DECLARE_ENV_VAR_STR(MIOPEN_CK_LIB_PATH)
|
|
|
|
namespace {
|
|
|
|
class ScopedCKLibraryPath
|
|
{
|
|
public:
|
|
explicit ScopedCKLibraryPath(std::string value)
|
|
: had_previous_value(static_cast<bool>(MIOPEN_CK_LIB_PATH))
|
|
{
|
|
if(had_previous_value)
|
|
previous_value = miopen::env::value(MIOPEN_CK_LIB_PATH);
|
|
miopen::env::update(MIOPEN_CK_LIB_PATH, std::move(value));
|
|
}
|
|
|
|
~ScopedCKLibraryPath()
|
|
{
|
|
if(had_previous_value)
|
|
miopen::env::update(MIOPEN_CK_LIB_PATH, previous_value);
|
|
else
|
|
miopen::env::clear(MIOPEN_CK_LIB_PATH);
|
|
}
|
|
|
|
ScopedCKLibraryPath(const ScopedCKLibraryPath&) = delete;
|
|
ScopedCKLibraryPath& operator=(const ScopedCKLibraryPath&) = delete;
|
|
|
|
private:
|
|
bool had_previous_value = false;
|
|
std::string previous_value;
|
|
};
|
|
|
|
miopen::fs::path MakeMissingDirectory(const std::string& tag)
|
|
{
|
|
return miopen::fs::temp_directory_path() / "miopen_ck_impl_loader_missing" / tag /
|
|
"not_created";
|
|
}
|
|
|
|
/// Build a minimal grouped-conv forward ProblemDescription suitable for
|
|
/// querying the CK loader. Uses group=4, NHWC, FP16, small spatial dims.
|
|
miopen::conv::ProblemDescription MakeGroupedConvProblem()
|
|
{
|
|
// Input: N=1, C=16, H=8, W=8 (NHWC layout)
|
|
// Weights: K=16, C/G=4, R=3, S=3 (NHWC layout, group=4 → C_per_group=4)
|
|
// Conv: pad=1, stride=1, dilation=1, group=4
|
|
const miopen::TensorDescriptor in_desc(miopenHalf, miopenTensorNHWC, {1, 16, 8, 8});
|
|
const miopen::TensorDescriptor wei_desc(miopenHalf, miopenTensorNHWC, {16, 4, 3, 3});
|
|
|
|
const miopen::ConvolutionDescriptor conv_desc(
|
|
/*pads=*/{1, 1},
|
|
/*strides=*/{1, 1},
|
|
/*dilations=*/{1, 1},
|
|
/*trans_output_pads=*/{0, 0},
|
|
/*group_count=*/4);
|
|
|
|
const auto out_desc = conv_desc.GetForwardOutputTensor(in_desc, wei_desc, miopenHalf);
|
|
|
|
return miopen::conv::ProblemDescription(
|
|
in_desc, wei_desc, out_desc, conv_desc, miopen::conv::Direction::Forward);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// -- GPU tests (require a HIP device) -----------------------------------------
|
|
|
|
#if MIOPEN_BACKEND_HIP
|
|
|
|
TEST(GPU_CkImplLoader_FP16, LoaderLoadsForCurrentDevice)
|
|
{
|
|
const auto device_name = GetCurrentDeviceName();
|
|
ASSERT_FALSE(device_name.empty()) << "Failed to query HIP device";
|
|
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get(device_name);
|
|
|
|
// The library may or may not be installed; if it loads, symbols must resolve.
|
|
// We don't hard-fail on IsLoaded()==false because the .so might not be
|
|
// present in all CI environments.
|
|
if(loader.IsLoaded())
|
|
{
|
|
SUCCEED() << "Loader successfully loaded library for " << device_name;
|
|
}
|
|
else
|
|
{
|
|
GTEST_SKIP() << "CK grouped conv library not installed for " << device_name;
|
|
}
|
|
}
|
|
|
|
TEST(GPU_CkImplLoader_FP16, LoaderFillsValidKernels)
|
|
{
|
|
const auto device_name = GetCurrentDeviceName();
|
|
ASSERT_FALSE(device_name.empty());
|
|
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get(device_name);
|
|
if(!loader.IsLoaded())
|
|
GTEST_SKIP() << "CK grouped conv library not installed for " << device_name;
|
|
|
|
const auto problem = MakeGroupedConvProblem();
|
|
const auto kernels =
|
|
loader.FillValidKernels(CKSolverType::GrpConvFwd, problem, miopenHalf, false);
|
|
|
|
EXPECT_FALSE(kernels.empty()) << "Expected at least one valid CK grouped conv kernel for "
|
|
<< device_name;
|
|
}
|
|
|
|
TEST(GPU_CkImplLoader_FP16, LoaderFillsValidKernelsWithTf32Fallback)
|
|
{
|
|
const auto device_name = GetCurrentDeviceName();
|
|
ASSERT_FALSE(device_name.empty());
|
|
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get(device_name);
|
|
if(!loader.IsLoaded())
|
|
GTEST_SKIP() << "CK grouped conv library not installed for " << device_name;
|
|
|
|
const auto problem = MakeGroupedConvProblem();
|
|
bool use_tf32 = false;
|
|
const auto kernels = loader.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvFwd, problem, miopenHalf, use_tf32);
|
|
|
|
EXPECT_FALSE(kernels.empty()) << "Expected at least one valid kernel for " << device_name;
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false when called with false";
|
|
}
|
|
|
|
TEST(GPU_CkImplLoader_FP16, LoaderCachesPerDevice)
|
|
{
|
|
const auto device_name = GetCurrentDeviceName();
|
|
ASSERT_FALSE(device_name.empty());
|
|
|
|
const auto& loader1 = miopen::solver::CkImplLibLoader::Get(device_name);
|
|
const auto& loader2 = miopen::solver::CkImplLibLoader::Get(device_name);
|
|
|
|
EXPECT_EQ(&loader1, &loader2) << "Get() should return the same cached instance";
|
|
}
|
|
|
|
TEST(GPU_CkImplLoader_FP16, LoaderStripsDeviceSuffix)
|
|
{
|
|
const auto device_name = GetCurrentDeviceName();
|
|
ASSERT_FALSE(device_name.empty());
|
|
|
|
// Strip any existing suffix to get the base arch
|
|
auto base_arch = device_name;
|
|
auto colon_pos = base_arch.find(':');
|
|
if(colon_pos != std::string::npos)
|
|
base_arch = base_arch.substr(0, colon_pos);
|
|
|
|
// Query with a synthesized suffix and with the bare base arch
|
|
const auto suffixed = base_arch + ":sramecc+:xnack-";
|
|
const auto& loader1 = miopen::solver::CkImplLibLoader::Get(suffixed);
|
|
const auto& loader2 = miopen::solver::CkImplLibLoader::Get(base_arch);
|
|
|
|
EXPECT_EQ(&loader1, &loader2)
|
|
<< "Suffixed and bare device names should resolve to the same cached loader";
|
|
}
|
|
|
|
#endif // MIOPEN_BACKEND_HIP
|
|
|
|
// -- CPU tests (no GPU required) ----------------------------------------------
|
|
|
|
TEST(CPU_CkImplLoader_NONE, LoaderFailsGracefullyForUnknownDevice)
|
|
{
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get("gfx_nonexistent");
|
|
EXPECT_FALSE(loader.IsLoaded());
|
|
}
|
|
|
|
TEST(CPU_CkImplLoader_NONE, LoaderStripsDeviceSuffixWithoutGpu)
|
|
{
|
|
const auto base_arch = std::string{"gfx_ck_loader_suffix_unit_test"};
|
|
const auto suffixed = base_arch + ":sramecc+:xnack-";
|
|
|
|
const auto& loader1 = miopen::solver::CkImplLibLoader::Get(suffixed);
|
|
const auto& loader2 = miopen::solver::CkImplLibLoader::Get(base_arch);
|
|
|
|
EXPECT_EQ(&loader1, &loader2)
|
|
<< "Suffixed and bare device names should resolve to the same cached loader";
|
|
EXPECT_FALSE(loader1.IsLoaded());
|
|
}
|
|
|
|
TEST(CPU_CkImplLoader_NONE, LoaderFailsGracefullyWithInvalidEnvPath)
|
|
{
|
|
const auto missing_dir = MakeMissingDirectory("invalid_env_path");
|
|
ASSERT_FALSE(miopen::fs::exists(missing_dir));
|
|
|
|
const ScopedCKLibraryPath scoped_ck_lib_path{missing_dir.string()};
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get("gfx_ck_loader_invalid_env");
|
|
|
|
EXPECT_FALSE(loader.IsLoaded());
|
|
EXPECT_TRUE(
|
|
loader
|
|
.FillValidKernels(CKSolverType::GrpConvFwd, MakeGroupedConvProblem(), miopenHalf, false)
|
|
.empty());
|
|
}
|
|
|
|
TEST(CPU_CkImplLoader_NONE, LoaderReturnsEmptyOnFailure)
|
|
{
|
|
const auto& loader = miopen::solver::CkImplLibLoader::Get("gfx_bogus");
|
|
ASSERT_FALSE(loader.IsLoaded());
|
|
|
|
const auto problem = MakeGroupedConvProblem();
|
|
miopen::ExecutionContext ctx;
|
|
|
|
// All wrappers should return safe defaults when the library is not loaded
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConvFwd, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvFwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvFwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConvFwd, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConvFwd, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConvFwd, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConvFwd, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConvBwd, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvBwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvBwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConvBwd, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConvBwd, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConvBwd, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConvBwd, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConvWrw, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvWrw, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConvWrw, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConvWrw, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConvWrw, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConvWrw, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConvWrw, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConv3dFwd, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dFwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dFwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConv3dFwd, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConv3dFwd, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConv3dFwd, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConv3dFwd, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConv3dBwd, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dBwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dBwd, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConv3dBwd, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConv3dBwd, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConv3dBwd, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConv3dBwd, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::GrpConv3dWrw, problem, miopenHalf, false).empty());
|
|
{
|
|
bool use_tf32 = true;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dWrw, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should become false when no kernels found";
|
|
}
|
|
{
|
|
bool use_tf32 = false;
|
|
EXPECT_TRUE(loader
|
|
.FillValidKernelsWithTf32Fallback(
|
|
CKSolverType::GrpConv3dWrw, problem, miopenHalf, use_tf32)
|
|
.empty());
|
|
EXPECT_FALSE(use_tf32) << "use_tf32 should remain false";
|
|
}
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::GrpConv3dWrw, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::GrpConv3dWrw, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::GrpConv3dWrw, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::GrpConv3dWrw, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::FusedBiasActiv, problem, miopenHalf, false).empty());
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::FusedBiasActiv, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::FusedBiasActiv, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::FusedBiasActiv, problem, miopenHalf, false),
|
|
0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::FusedBiasActiv, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::FusedBiasResAddActiv, problem, miopenHalf, false)
|
|
.empty());
|
|
EXPECT_FALSE(
|
|
loader.IsApplicable(CKSolverType::FusedBiasResAddActiv, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::FusedBiasResAddActiv, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(
|
|
loader.GetWorkspaceSize(CKSolverType::FusedBiasResAddActiv, problem, miopenHalf, false),
|
|
0u);
|
|
EXPECT_EQ(
|
|
loader.GetSolution(CKSolverType::FusedBiasResAddActiv, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(
|
|
loader.FillValidKernels(CKSolverType::FusedGrpActiv, problem, miopenHalf, false).empty());
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::FusedGrpActiv, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::FusedGrpActiv, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::FusedGrpActiv, problem, miopenHalf, false), 0u);
|
|
EXPECT_EQ(loader.GetSolution(CKSolverType::FusedGrpActiv, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
|
|
EXPECT_TRUE(loader.FillValidKernels(CKSolverType::FusedGrpBiasActiv, problem, miopenHalf, false)
|
|
.empty());
|
|
EXPECT_FALSE(loader.IsApplicable(CKSolverType::FusedGrpBiasActiv, problem, miopenHalf, false));
|
|
EXPECT_FALSE(loader.IsArgsSupported(
|
|
CKSolverType::FusedGrpBiasActiv, problem, "dummy_kernel", miopenHalf, false));
|
|
EXPECT_EQ(loader.GetWorkspaceSize(CKSolverType::FusedGrpBiasActiv, problem, miopenHalf, false),
|
|
0u);
|
|
EXPECT_EQ(
|
|
loader.GetSolution(CKSolverType::FusedGrpBiasActiv, ctx, problem, "dummy", false).status,
|
|
miopenStatusInternalError);
|
|
}
|
|
|
|
TEST(CPU_CkImplLoader_NONE, IsDeterministicSplitKValid)
|
|
{
|
|
// Non-deterministic mode: always valid regardless of split_k
|
|
EXPECT_TRUE(IsDeterministicSplitKValid("Kernel+4", false));
|
|
EXPECT_TRUE(IsDeterministicSplitKValid("Kernel+1", false));
|
|
EXPECT_TRUE(IsDeterministicSplitKValid("Kernel", false));
|
|
|
|
// Deterministic mode with split_k == 1 or no split_k: valid
|
|
EXPECT_TRUE(IsDeterministicSplitKValid("Kernel+1", true));
|
|
EXPECT_TRUE(IsDeterministicSplitKValid("Kernel", true));
|
|
|
|
// Deterministic mode with split_k > 1: invalid
|
|
EXPECT_FALSE(IsDeterministicSplitKValid("Kernel+4", true));
|
|
EXPECT_FALSE(IsDeterministicSplitKValid("Kernel+2", true));
|
|
|
|
// Deterministic mode with parse failures: invalid
|
|
EXPECT_FALSE(IsDeterministicSplitKValid("Kernel+abc", true));
|
|
EXPECT_FALSE(IsDeterministicSplitKValid("Kernel+", true));
|
|
}
|
|
|
|
// -- Error infrastructure tests (CPU, no GPU required) ------------------------
|
|
|
|
TEST(CPU_CkImplError_NONE, LastErrorSetAndGet)
|
|
{
|
|
// Setting a non-success status stores the message
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_BAD_PARAM, "test error message");
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "test error message");
|
|
|
|
// Setting SUCCESS does NOT overwrite the buffer
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_SUCCESS, "should not appear");
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "test error message");
|
|
|
|
// Setting another error overwrites the buffer
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_INTERNAL_ERROR, "second error");
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "second error");
|
|
|
|
// Null message clears the buffer
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_BAD_PARAM, nullptr);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, LastErrorThreadIsolation)
|
|
{
|
|
// Set an error on the main thread
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_BAD_PARAM, "main thread error");
|
|
|
|
std::string child_error;
|
|
std::thread t([&child_error]() {
|
|
// Child thread should see empty last error (thread-local)
|
|
child_error = CkImplLastError::getLastError();
|
|
});
|
|
t.join();
|
|
|
|
EXPECT_EQ(child_error, "") << "Child thread should have empty last error";
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "main thread error")
|
|
<< "Main thread error should be unaffected";
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, TryCatchSuccess)
|
|
{
|
|
auto status = ck_impl_try_catch([]() {
|
|
// no-op, no throw
|
|
});
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_SUCCESS);
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, TryCatchCkImplException)
|
|
{
|
|
auto status = ck_impl_try_catch(
|
|
[]() { throw CkImplException(CK_IMPL_STATUS_BAD_PARAM, "bad parameter test"); });
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_BAD_PARAM);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "bad parameter test");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, TryCatchStdException)
|
|
{
|
|
auto status = ck_impl_try_catch([]() { throw std::runtime_error("std runtime error test"); });
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_INTERNAL_ERROR);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "std runtime error test");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, TryCatchUnknownException)
|
|
{
|
|
auto status = ck_impl_try_catch([]() {
|
|
throw 42; // NOLINT(hicpp-exception-baseclass)
|
|
});
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_INTERNAL_ERROR);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "Unknown exception occurred");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, StatusToString)
|
|
{
|
|
EXPECT_STREQ(toString(CK_IMPL_STATUS_SUCCESS), "CK_IMPL_STATUS_SUCCESS");
|
|
EXPECT_STREQ(toString(CK_IMPL_STATUS_BAD_PARAM), "CK_IMPL_STATUS_BAD_PARAM");
|
|
EXPECT_STREQ(toString(CK_IMPL_STATUS_INVALID_VALUE), "CK_IMPL_STATUS_INVALID_VALUE");
|
|
EXPECT_STREQ(toString(CK_IMPL_STATUS_INTERNAL_ERROR), "CK_IMPL_STATUS_INTERNAL_ERROR");
|
|
EXPECT_STREQ(toString(CK_IMPL_STATUS_ALLOC_FAILED), "CK_IMPL_STATUS_ALLOC_FAILED");
|
|
EXPECT_STREQ(toString(static_cast<ck_impl_status_t>(99)), "CK_IMPL_STATUS_UNKNOWN");
|
|
}
|
|
|
|
// -- CkImplException direct tests -----------------------------------------
|
|
|
|
TEST(CPU_CkImplError_NONE, ExceptionStoresStatusAndMessage)
|
|
{
|
|
CkImplException ex(CK_IMPL_STATUS_INVALID_VALUE, "invalid value message");
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_INVALID_VALUE);
|
|
EXPECT_STREQ(ex.what(), "invalid value message");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ExceptionIsStdException)
|
|
{
|
|
CkImplException ex(CK_IMPL_STATUS_ALLOC_FAILED, "alloc failed");
|
|
const std::exception& base = ex;
|
|
EXPECT_STREQ(base.what(), "alloc failed");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ExceptionCopyPreservesFields)
|
|
{
|
|
CkImplException original(CK_IMPL_STATUS_BAD_PARAM, "original message");
|
|
CkImplException copy(original); // NOLINT(performance-unnecessary-copy-initialization)
|
|
EXPECT_EQ(copy.getStatus(), CK_IMPL_STATUS_BAD_PARAM);
|
|
EXPECT_STREQ(copy.what(), "original message");
|
|
}
|
|
|
|
// -- THROW_IF macro tests ----------------------------------------------------
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfNullThrowsOnNull)
|
|
{
|
|
int* ptr = nullptr;
|
|
EXPECT_THROW(
|
|
{ CK_IMPL_THROW_IF_NULL(ptr, CK_IMPL_STATUS_BAD_PARAM, "null pointer"); }, CkImplException);
|
|
|
|
try
|
|
{
|
|
CK_IMPL_THROW_IF_NULL(ptr, CK_IMPL_STATUS_BAD_PARAM, "null pointer detail");
|
|
FAIL() << "Expected CkImplException";
|
|
}
|
|
catch(const CkImplException& ex)
|
|
{
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_BAD_PARAM);
|
|
EXPECT_STREQ(ex.what(), "null pointer detail");
|
|
}
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfNullPassesOnNonNull)
|
|
{
|
|
int value = 42;
|
|
int* ptr = &value;
|
|
EXPECT_NO_THROW({ CK_IMPL_THROW_IF_NULL(ptr, CK_IMPL_STATUS_BAD_PARAM, "should not throw"); });
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfFalseThrowsOnFalse)
|
|
{
|
|
try
|
|
{
|
|
CK_IMPL_THROW_IF_FALSE(false, CK_IMPL_STATUS_INVALID_VALUE, "condition was false");
|
|
FAIL() << "Expected CkImplException";
|
|
}
|
|
catch(const CkImplException& ex)
|
|
{
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_INVALID_VALUE);
|
|
EXPECT_STREQ(ex.what(), "condition was false");
|
|
}
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfFalsePassesOnTrue)
|
|
{
|
|
EXPECT_NO_THROW(
|
|
{ CK_IMPL_THROW_IF_FALSE(true, CK_IMPL_STATUS_INVALID_VALUE, "should not throw"); });
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfTrueThrowsOnTrue)
|
|
{
|
|
try
|
|
{
|
|
CK_IMPL_THROW_IF_TRUE(true, CK_IMPL_STATUS_INTERNAL_ERROR, "condition was true");
|
|
FAIL() << "Expected CkImplException";
|
|
}
|
|
catch(const CkImplException& ex)
|
|
{
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_INTERNAL_ERROR);
|
|
EXPECT_STREQ(ex.what(), "condition was true");
|
|
}
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfTruePassesOnFalse)
|
|
{
|
|
EXPECT_NO_THROW(
|
|
{ CK_IMPL_THROW_IF_TRUE(false, CK_IMPL_STATUS_INTERNAL_ERROR, "should not throw"); });
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfNeThrowsOnNotEqual)
|
|
{
|
|
try
|
|
{
|
|
CK_IMPL_THROW_IF_NE(3, 5, CK_IMPL_STATUS_INVALID_VALUE, "values not equal");
|
|
FAIL() << "Expected CkImplException";
|
|
}
|
|
catch(const CkImplException& ex)
|
|
{
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_INVALID_VALUE);
|
|
EXPECT_STREQ(ex.what(), "values not equal");
|
|
}
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfNePassesOnEqual)
|
|
{
|
|
EXPECT_NO_THROW(
|
|
// cppcheck-suppress duplicateExpression
|
|
{ CK_IMPL_THROW_IF_NE(7, 7, CK_IMPL_STATUS_INVALID_VALUE, "should not throw"); });
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfEqThrowsOnEqual)
|
|
{
|
|
try
|
|
{
|
|
// cppcheck-suppress duplicateExpression
|
|
CK_IMPL_THROW_IF_EQ(10, 10, CK_IMPL_STATUS_BAD_PARAM, "values equal");
|
|
FAIL() << "Expected CkImplException";
|
|
}
|
|
catch(const CkImplException& ex)
|
|
{
|
|
EXPECT_EQ(ex.getStatus(), CK_IMPL_STATUS_BAD_PARAM);
|
|
EXPECT_STREQ(ex.what(), "values equal");
|
|
}
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfEqPassesOnNotEqual)
|
|
{
|
|
EXPECT_NO_THROW({ CK_IMPL_THROW_IF_EQ(1, 2, CK_IMPL_STATUS_BAD_PARAM, "should not throw"); });
|
|
}
|
|
|
|
// -- LastError additional coverage -------------------------------------------
|
|
|
|
TEST(CPU_CkImplError_NONE, LastErrorSetReturnsStatus)
|
|
{
|
|
auto result = CkImplLastError::setLastError(CK_IMPL_STATUS_BAD_PARAM, "msg");
|
|
EXPECT_EQ(result, CK_IMPL_STATUS_BAD_PARAM);
|
|
|
|
result = CkImplLastError::setLastError(CK_IMPL_STATUS_ALLOC_FAILED, "alloc");
|
|
EXPECT_EQ(result, CK_IMPL_STATUS_ALLOC_FAILED);
|
|
|
|
result = CkImplLastError::setLastError(CK_IMPL_STATUS_SUCCESS, "ignored");
|
|
EXPECT_EQ(result, CK_IMPL_STATUS_SUCCESS);
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, LastErrorStdStringOverload)
|
|
{
|
|
std::string msg = "std::string overload test";
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_INTERNAL_ERROR, msg);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "std::string overload test");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, LastErrorTruncatesLongMessage)
|
|
{
|
|
// Create a message longer than CK_IMPL_ERROR_STRING_MAX_LENGTH
|
|
std::string long_msg(CK_IMPL_ERROR_STRING_MAX_LENGTH + 100, 'X');
|
|
CkImplLastError::setLastError(CK_IMPL_STATUS_INTERNAL_ERROR, long_msg);
|
|
|
|
const char* stored = CkImplLastError::getLastError();
|
|
auto stored_len = std::strlen(stored);
|
|
EXPECT_EQ(stored_len, CK_IMPL_ERROR_STRING_MAX_LENGTH - 1)
|
|
<< "Stored message should be truncated to buffer size - 1";
|
|
EXPECT_EQ(stored[stored_len], '\0') << "Stored message must be null-terminated";
|
|
}
|
|
|
|
// -- tryCatch status preservation for each status code -----------------------
|
|
|
|
TEST(CPU_CkImplError_NONE, TryCatchPreservesAllStatusCodes)
|
|
{
|
|
const ck_impl_status_t codes[] = {
|
|
CK_IMPL_STATUS_BAD_PARAM,
|
|
CK_IMPL_STATUS_INVALID_VALUE,
|
|
CK_IMPL_STATUS_INTERNAL_ERROR,
|
|
CK_IMPL_STATUS_ALLOC_FAILED,
|
|
};
|
|
|
|
for(auto code : codes)
|
|
{
|
|
auto status = ck_impl_try_catch([code]() { throw CkImplException(code, toString(code)); });
|
|
EXPECT_EQ(status, code) << "tryCatch should preserve status " << toString(code);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), toString(code));
|
|
}
|
|
}
|
|
|
|
// -- Throw macros through tryCatch (end-to-end error propagation) ------------
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfNullPropagatesThroughTryCatch)
|
|
{
|
|
int* ptr = nullptr;
|
|
auto status = ck_impl_try_catch(
|
|
[&]() { CK_IMPL_THROW_IF_NULL(ptr, CK_IMPL_STATUS_BAD_PARAM, "null ptr in tryCatch"); });
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_BAD_PARAM);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "null ptr in tryCatch");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, ThrowIfFalsePropagatesThroughTryCatch)
|
|
{
|
|
auto status = ck_impl_try_catch([]() {
|
|
size_t idx = 10;
|
|
size_t size = 5;
|
|
CK_IMPL_THROW_IF_FALSE(idx < size, CK_IMPL_STATUS_INVALID_VALUE, "Index out of range");
|
|
});
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_INVALID_VALUE);
|
|
EXPECT_STREQ(CkImplLastError::getLastError(), "Index out of range");
|
|
}
|
|
|
|
TEST(CPU_CkImplError_NONE, NoThrowOnValidInputsThroughTryCatch)
|
|
{
|
|
int value = 42;
|
|
int* ptr = &value;
|
|
auto status = ck_impl_try_catch([&]() {
|
|
CK_IMPL_THROW_IF_NULL(ptr, CK_IMPL_STATUS_BAD_PARAM, "should not throw");
|
|
CK_IMPL_THROW_IF_FALSE(true, CK_IMPL_STATUS_INVALID_VALUE, "should not throw");
|
|
CK_IMPL_THROW_IF_TRUE(false, CK_IMPL_STATUS_INTERNAL_ERROR, "should not throw");
|
|
// cppcheck-suppress duplicateExpression
|
|
CK_IMPL_THROW_IF_NE(1, 1, CK_IMPL_STATUS_BAD_PARAM, "should not throw");
|
|
CK_IMPL_THROW_IF_EQ(1, 2, CK_IMPL_STATUS_BAD_PARAM, "should not throw");
|
|
});
|
|
EXPECT_EQ(status, CK_IMPL_STATUS_SUCCESS);
|
|
}
|