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

404 lines
14 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 <algorithm>
#include <future>
#include <miopen/activ.hpp>
#include <miopen/miopen.h>
#include <miopen/handle.hpp>
#include "activ_common.hpp"
#include "gtest_common.hpp"
#include "../network_data.hpp"
#include "../random.hpp"
#include "../tensor_holder.hpp"
#include "../tensor_util.hpp"
#include "../verify.hpp"
#define UNIT_ACTIVATION_DESCRIPTOR_DEBUG 0
namespace {
std::set<std::vector<int>> GetTestCases(bool full)
{
if(full)
return get_inputs(0);
else
return {{1, 16, 8, 8}};
}
auto GetAllActivationModes()
{
return std::vector<miopenActivationMode_t>{
miopenActivationPASTHRU,
miopenActivationLOGISTIC,
miopenActivationTANH,
miopenActivationRELU,
miopenActivationSOFTRELU,
miopenActivationABS,
miopenActivationPOWER,
miopenActivationCLIPPEDRELU,
miopenActivationLEAKYRELU,
miopenActivationELU,
};
}
Gpu GetSupportedDevices() { return Gpu::All; }
template <class T>
class UnitTestActivationDescriptor
: public ::testing::TestWithParam<std::tuple<Gpu, miopenActivationMode_t, std::vector<int>>>
{
public:
void RunTest()
{
miopenActivationMode_t mode;
std::vector<int> input_dims;
std::tie(std::ignore, mode, input_dims) = GetParam();
const double alpha = 0.95;
const double beta = 2.3;
const double gamma = 3.4;
const auto desc = miopen::ActivationDescriptor{mode, alpha, beta, gamma};
auto x_tensor = tensor<T>{input_dims};
auto y_tensor_gpu = tensor<T>{input_dims};
auto y_tensor_cpu = tensor<T>{input_dims};
auto dx_tensor_gpu = tensor<T>{input_dims};
auto dy_tensor = tensor<T>{input_dims};
auto dx_tensor_cpu = tensor<T>{input_dims};
auto x_data_ready = std::async(std::launch::async, [&]() { GenData_x(x_tensor); });
auto y_data_gpu_ready = std::async(std::launch::async, [&]() { ZeroData(y_tensor_gpu); });
auto dx_data_gpu_ready = std::async(std::launch::async, [&]() { ZeroData(dx_tensor_gpu); });
auto dy_data_ready = std::async(std::launch::async, [&]() { GenData_dy(dy_tensor); });
x_data_ready.wait();
y_data_gpu_ready.wait();
dx_data_gpu_ready.wait();
dy_data_ready.wait();
std::promise<void> fwd_gpu_ready;
std::promise<void> fwd_cpu_ready;
auto gpu_ready = std::async(std::launch::async, [&]() {
RunGpu(desc, x_tensor, y_tensor_gpu, dx_tensor_gpu, dy_tensor, fwd_gpu_ready);
});
auto cpu_ready = std::async(std::launch::async, [&]() {
RunCpu(desc, x_tensor, y_tensor_cpu, dx_tensor_cpu, dy_tensor, fwd_cpu_ready);
});
fwd_gpu_ready.get_future().wait();
fwd_cpu_ready.get_future().wait();
auto verify_fwd_ready = std::async(std::launch::async, [&]() {
return VerifyDataAsync(y_tensor_gpu, y_tensor_cpu, true);
});
gpu_ready.wait();
cpu_ready.wait();
auto verify_bwd_ready = std::async(std::launch::async, [&]() {
return VerifyDataAsync(dx_tensor_gpu, dx_tensor_cpu, false);
});
if(!verify_fwd_ready.get())
{
#if UNIT_ACTIVATION_DESCRIPTOR_DEBUG
DebugPrintTensorsForward(x_tensor, y_tensor_cpu, y_tensor_gpu);
#endif
GTEST_FAIL();
}
if(!verify_bwd_ready.get())
{
#if UNIT_ACTIVATION_DESCRIPTOR_DEBUG
DebugPrintTensorsBackward(
x_tensor, y_tensor_gpu, dy_tensor, dx_tensor_cpu, dx_tensor_gpu);
#endif
GTEST_FAIL();
}
}
protected:
static void
DebugPrintTensorsForward(const tensor<T>& x, const tensor<T>& y_cpu, const tensor<T>& y_gpu)
{
print_tensor(x, "X_TENSOR");
print_tensor(y_cpu, "Y_TENSOR_CPU");
print_tensor(y_gpu, "Y_TENSOR_GPU");
}
static void DebugPrintTensorsBackward(const tensor<T>& x,
const tensor<T>& y_gpu,
const tensor<T>& dy,
const tensor<T>& dx_cpu,
const tensor<T>& dx_gpu)
{
print_tensor(x, "X_TENSOR");
print_tensor(y_gpu, "Y_TENSOR_GPU");
print_tensor(dy, "dY_TENSOR");
print_tensor(dx_cpu, "dX_TENSOR_CPU");
print_tensor(dx_gpu, "dX_TENSOR_GPU");
}
/// \note In the original test, support for non-packed tensors was implemented in a very strange
/// way (by incrementing the stride of the last dimension), in this reincarnation, support for
/// non-packed tensors is not yet implemented. This warning is provided to help the developer
/// when adding this missing functionality.
static void NonPackedTensorWarning()
{
std::cout << "WARNING: Non-packed tensor. Some modifications are needed to achieve optimal "
"performance."
<< std::endl;
}
static void ZeroData(tensor<T>& tensor)
{
if(!tensor.desc.IsPacked())
NonPackedTensorWarning();
std::fill(tensor.begin(), tensor.end(), T());
}
template <class G>
static void GenData(tensor<T>& tensor, G g)
{
if(!tensor.desc.IsPacked())
NonPackedTensorWarning();
std::for_each(tensor.begin(), tensor.end(), g);
}
static void GenData_x(tensor<T>& tensor)
{
auto gen_x_value = [](auto& v) {
v = prng::gen_A_to_B(static_cast<T>(-2), static_cast<T>(2));
};
GenData(tensor, gen_x_value);
}
static void GenData_dy(tensor<T>& tensor)
{
auto gen_dy_value = [](auto& v) {
v = prng::gen_A_to_B(static_cast<T>(-0.5), static_cast<T>(0.5));
};
GenData(tensor, gen_dy_value);
}
static void RunGpu(const miopen::ActivationDescriptor& desc,
const tensor<T>& x,
tensor<T>& y,
tensor<T>& dx,
const tensor<T>& dy,
std::promise<void>& forward_ready)
{
miopenStatus_t status;
// Dummy values
const float alpha = 1.0f;
const float beta = 0.0f;
auto handle = miopen::Handle{};
auto x_dev = handle.Write(x.data);
auto y_dev = handle.Write(y.data);
status = desc.Forward(handle, &alpha, x.desc, x_dev.get(), &beta, y.desc, y_dev.get());
if(status != miopenStatusSuccess)
throw std::runtime_error("Forward failed");
y.data = handle.Read<T>(y_dev, y.data.size());
forward_ready.set_value();
auto dx_dev = handle.Write(dx.data);
auto dy_dev = handle.Write(dy.data);
status = desc.Backward(handle,
&alpha,
y.desc,
y_dev.get(),
dy.desc,
dy_dev.get(),
x.desc,
x_dev.get(),
&beta,
dx.desc,
dx_dev.get());
if(status != miopenStatusSuccess)
throw std::runtime_error("Backward failed");
dx.data = handle.Read<T>(dx_dev, dx.data.size());
}
static void RunCpu(const miopen::ActivationDescriptor& desc,
const tensor<T>& x,
tensor<T>& y,
tensor<T>& dx,
const tensor<T>& dy,
std::promise<void>& forward_ready)
{
miopen::tests::activ::CpuActivationForward(
desc.GetMode(), desc.GetAlpha(), desc.GetBeta(), desc.GetGamma(), x, y);
forward_ready.set_value();
miopen::tests::activ::CpuActivationBackward(
desc.GetMode(), desc.GetAlpha(), desc.GetBeta(), desc.GetGamma(), y, dy, x, dx);
}
static double GetThreshold()
{
double tolerance = 50.0;
double threshold = std::numeric_limits<T>::epsilon() * tolerance;
return threshold;
}
static bool
VerifyDataAsync(const tensor<T>& tensor_gpu, const tensor<T>& tensor_cpu, bool forward)
{
if(!(tensor_gpu.desc.IsPacked() && tensor_cpu.desc.IsPacked()))
NonPackedTensorWarning();
const auto& cpu_data = tensor_cpu.data;
const auto& gpu_data = tensor_gpu.data;
const std::string direction = forward ? "(forward)" : "(backward)";
auto check_gpu_zero = [&]() {
auto res = miopen::range_zero(gpu_data);
if(res)
{
std::cout << "GPU data is all zeros " << direction << std::endl;
return false;
}
return true;
};
auto check_cpu_finite = [&]() {
auto res = miopen::find_idx(cpu_data, miopen::not_finite);
if(res != -1)
{
std::cout << "Non finite number found in the CPU data " << direction << std::endl;
return false;
}
return true;
};
auto check_gpu_finite = [&]() {
auto res = miopen::find_idx(gpu_data, miopen::not_finite);
if(res != -1)
{
std::cout << "Non finite number found in the GPU data " << direction << std::endl;
return false;
}
return true;
};
auto check_range_distance = [&]() {
if(miopen::range_distance(cpu_data) != miopen::range_distance(gpu_data))
{
std::cout << "Range distance mismatch " << direction << std::endl;
return false;
}
return true;
};
auto check_error = [&]() {
const auto error = miopen::rms_range(cpu_data, gpu_data);
const double threshold = GetThreshold();
if(error >= threshold)
{
std::cout << "Error beyond tolerance, error=" << error
<< ", threshold=" << threshold << " " << direction << std::endl;
return false;
}
#if 0
std::cout << "error: " << error << " threshold: " << threshold << " " << direction << std::endl;
#endif
return true;
};
if(!check_range_distance())
return false;
auto check1 = std::async(std::launch::async, check_gpu_zero);
auto check2 = std::async(std::launch::async, check_cpu_finite);
auto check3 = std::async(std::launch::async, check_gpu_finite);
auto check4 = std::async(std::launch::async, check_error);
check1.wait();
check2.wait();
check3.wait();
check4.wait();
return check1.get() && check2.get() && check3.get() && check4.get();
}
void SetUp() override
{
Gpu supported_devs;
std::tie(supported_devs, std::ignore, std::ignore) = GetParam();
if(!IsTestSupportedByDevice(supported_devs))
{
GTEST_SKIP();
}
}
};
} // namespace
using GPU_UnitTestActivationDescriptor_FP16 = UnitTestActivationDescriptor<half_float::half>;
using GPU_UnitTestActivationDescriptor_FP32 = UnitTestActivationDescriptor<float>;
TEST_P(GPU_UnitTestActivationDescriptor_FP16, Activation) { this->RunTest(); };
TEST_P(GPU_UnitTestActivationDescriptor_FP32, Activation) { this->RunTest(); };
// Smoke tests
INSTANTIATE_TEST_SUITE_P(Smoke,
GPU_UnitTestActivationDescriptor_FP16,
testing::Combine(testing::Values(GetSupportedDevices()),
testing::ValuesIn(GetAllActivationModes()),
testing::ValuesIn(GetTestCases(false))));
INSTANTIATE_TEST_SUITE_P(Smoke,
GPU_UnitTestActivationDescriptor_FP32,
testing::Combine(testing::Values(GetSupportedDevices()),
testing::ValuesIn(GetAllActivationModes()),
testing::ValuesIn(GetTestCases(false))));
// Full tests
INSTANTIATE_TEST_SUITE_P(Full,
GPU_UnitTestActivationDescriptor_FP16,
testing::Combine(testing::Values(GetSupportedDevices()),
testing::ValuesIn(GetAllActivationModes()),
testing::ValuesIn(GetTestCases(true))));
INSTANTIATE_TEST_SUITE_P(Full,
GPU_UnitTestActivationDescriptor_FP32,
testing::Combine(testing::Values(GetSupportedDevices()),
testing::ValuesIn(GetAllActivationModes()),
testing::ValuesIn(GetTestCases(true))));