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

305 lines
13 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (c) 2025 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 "get_handle.hpp"
#include "na.hpp"
#include "perf_helper.hpp"
#include <array>
#include <vector>
template <typename XDataType,
typename YDataType,
typename ScaleDataType,
typename BiasDataType,
typename MeanVarDataType>
struct BatchNormInferTester
: public ::testing::TestWithParam<
std::tuple<miopenActivationMode_t, BNTestCase, miopenTensorLayout_t>>
{
void SetUp() override
{
std::tie(activ_mode, bn_config, tensor_layout) = GetParam();
// Create tensors
input = tensor<XDataType>{tensor_layout, bn_config.GetInput()};
output = tensor<YDataType>{tensor_layout, bn_config.GetInput()};
ref_out = tensor<YDataType>{tensor_layout, bn_config.GetInput()};
auto derivedBnDesc = miopen::TensorDescriptor{};
miopen::DeriveBNTensorDescriptor(derivedBnDesc, input.desc, bn_config.mode);
scale = tensor<ScaleDataType>{tensor_layout, derivedBnDesc.GetLengths()};
shift = tensor<BiasDataType>{tensor_layout, derivedBnDesc.GetLengths()};
estMean = tensor<MeanVarDataType>{tensor_layout, derivedBnDesc.GetLengths()};
estVariance = tensor<MeanVarDataType>{tensor_layout, derivedBnDesc.GetLengths()};
// Fill tensors
auto gen_value = [](auto...) {
return prng::gen_descreet_uniform_sign<XDataType>(1e-2, 100);
};
input.generate(gen_value);
auto gen_scale = [](auto...) {
return prng::gen_descreet_uniform_sign<ScaleDataType>(1e-2, 100);
};
scale.generate(gen_scale);
shift.generate(gen_scale);
estMean.generate(gen_scale);
auto gen_var = [](auto...) {
return static_cast<MeanVarDataType>(1e-2 * (prng::gen_0_to_B(100) + 1));
};
estVariance.generate(gen_var);
// Write data to GPU
auto&& handle = get_handle();
in_dev = handle.Write(input.data);
scale_dev = handle.Write(scale.data);
shift_dev = handle.Write(shift.data);
estMean_dev = handle.Write(estMean.data);
estVariance_dev = handle.Write(estVariance.data);
}
virtual void RunTestGPU() = 0;
void RunTestCPU()
{ // Run the CPU implementation
if(bn_config.mode == miopenBNPerActivation)
{
batchNormPerActivHostInference(
input, ref_out, scale, shift, epsilon, estMean, estVariance);
}
else
{
batchNormSpatialHostInference(
input, ref_out, scale, shift, epsilon, estMean, estVariance);
}
activationHostInfer(
activ_mode, activ_gamma, activ_beta, activ_alpha, ref_out.data, ref_out.data);
}
void Verify()
{ // Compare the outputs
// NOTE: Some small tensors during perf tests produce zero outputs which will result in
// non-fatal gtest failures. These can be safely ignored. In this situation both the referene
// and the gpu outputs will be zero. Observed them in relu, power and clippedrelu.
EXPECT_FALSE(miopen::range_zero(ref_out)) << "CPU/GPU data is all zeros";
EXPECT_FALSE(miopen::range_zero(output)) << "GPU data is all zeros";
EXPECT_FALSE(miopen::find_idx(output, miopen::not_finite) >= 0)
<< "Non finite number found in the GPU data";
EXPECT_FALSE(miopen::find_idx(ref_out, miopen::not_finite) >= 0)
<< "Non finite number found in the CPU/GPU data";
auto error = miopen::rms_range(ref_out, output);
auto mantissa_bits = std::is_same<YDataType, float>::value ? 23
: std::is_same<YDataType, half_float::half>::value ? 10
: 7;
auto threshold =
std::max(2.0 * std::sqrt(input.data.size()) / (1 << 23), 1.0 / (1 << mantissa_bits));
EXPECT_LE(error, threshold);
}
void TearDown() override
{
if(perf_enable)
{
// get the kernel handle
auto&& handle = get_handle();
// get the input tensor size and store in a string with x in between
std::vector<size_t> in_dims = bn_config.GetInput();
std::string kernel_info = std::to_string(in_dims[0]) + "x" +
std::to_string(in_dims[1]) + "x" +
std::to_string(in_dims[2]) + "x" + std::to_string(in_dims[3]);
std::unordered_map<miopenActivationMode_t, std::string> activation_map = {
{miopenActivationPASTHRU, "pasthru"},
{miopenActivationLOGISTIC, "logistic"},
{miopenActivationTANH, "tanh"},
{miopenActivationRELU, "relu"},
{miopenActivationSOFTRELU, "softrelu"},
{miopenActivationABS, "abs"},
{miopenActivationPOWER, "power"},
{miopenActivationCLIPPEDRELU, "clippedrelu"},
{miopenActivationLEAKYRELU, "leakyrelu"},
{miopenActivationELU, "elu"},
{miopenActivationCLAMP, "clamp"}};
auto it = activation_map.find(activ_mode);
if(it != activation_map.end())
{
kernel_info += "_" + it->second;
}
perf_helper.writeStatsToCSV("batch-norm-infer-perf-" + handle.GetDeviceName() + ".csv",
"_" + input.desc.GetLayout_str() + "_" + kernel_info + "_" +
((input.desc.GetType() == miopenHalf) ? "FP16"
: (input.desc.GetType() == miopenBFloat16) ? "BFP16"
: "FP32"));
}
}
BNTestCase bn_config; // Holds the test configuration
tensor<XDataType> input; // Input tensor
tensor<YDataType> output; // Output tensor from GPU
tensor<YDataType> ref_out; // Reference output tensor
tensor<ScaleDataType> scale;
tensor<BiasDataType> shift;
tensor<MeanVarDataType> estMean;
tensor<MeanVarDataType> estVariance;
miopen::Allocator::ManageDataPtr in_dev; // GPU input data
miopen::Allocator::ManageDataPtr out_dev; // GPU output data
miopen::Allocator::ManageDataPtr scale_dev; // GPU scale data
miopen::Allocator::ManageDataPtr shift_dev; // GPU shift data
miopen::Allocator::ManageDataPtr estMean_dev; // GPU estimated mean data
miopen::Allocator::ManageDataPtr estVariance_dev; // GPU estimated variance data
miopenActivationMode_t activ_mode; // Activation mode
miopenTensorLayout_t tensor_layout; // Tensor layout
const float activ_alpha = 0.5f;
const float activ_beta = 0.5f;
const float activ_gamma = 0.5f;
double epsilon = 1.0e-5;
bool perf_enable = false;
PerfHelper perf_helper;
};
// Build ForwardInference BNTestCase params from a list of {N, C, H, W} shapes.
inline std::vector<BNTestCase> MakeBNInferConfigs(const std::vector<std::array<int, 4>>& shapes,
miopenBatchNormMode_t mode)
{
std::vector<BNTestCase> test_cases;
test_cases.reserve(shapes.size());
for(const auto& s : shapes)
{
test_cases.push_back(BNTestCase{
s[0], s[1], s[2], s[3], mode, miopen::batchnorm::Direction::ForwardInference, 0, 0});
}
return test_cases;
}
// To avoid running the same shape under more than one tier prefix in a category,
// the tiers do NOT repeat shapes: Smoke and Standard list their own shapes, and
// Full lists only the shapes that are NOT already in Standard. The category
// filters run cumulative tiers -- standard runs Smoke+Standard, comprehensive
// and full run Smoke+Standard+Full -- so each category's union is the intended
// set (Smoke / Standard / complete) with no duplicate shapes across tiers.
// (Smoke uses a single activation+layout, so its two shapes still re-appear once
// in Standard's fuller activation/layout coverage; that small overlap is kept so
// those shapes get full coverage.)
//
// Shapes are chosen to span the kernel's shape-driven branches: read_unit
// (vector width 4/2/1) and whether read_len/read_unit exceeds the 256 local-size
// cap, where read_len is h*w (spatial NCHW), c (spatial NHWC), or c*h*w
// (per-activation).
// Quick/Smoke (pre-commit): the two smallest shapes that differ in read_unit.
template <typename T>
std::vector<BNTestCase> BNInferTestConfigsSmoke(miopenBatchNormMode_t mode)
{
return MakeBNInferConfigs(
{
{64, 512, 7, 7}, // ~1.6M; h*w=49 -> read_unit=1
{64, 256, 14, 14}, // ~3.2M; h*w=196 -> read_unit=4
},
mode);
}
// Standard (per-PR): distinct channel counts {64,256,512,2048} (spatial-NHWC
// read_len=c path) and distinct h*w {49,196,784,3136} (spatial-NCHW read_len=h*w
// path), covering read_unit=1/4 and both the <=256 and >256 local-size-cap
// branches.
template <typename T>
std::vector<BNTestCase> BNInferTestConfigsStandard(miopenBatchNormMode_t mode)
{
return MakeBNInferConfigs(
{
{64, 512, 7, 7},
{64, 256, 14, 14},
{64, 2048, 7, 7},
{64, 512, 28, 28},
{64, 64, 56, 56},
},
mode);
}
// Full tier (comprehensive/nightly): the remaining resnet50 shapes not in
// Standard. Combined with the Standard tier (also run in comprehensive/full)
// this covers the complete resnet50 set. Standard and Full use the same
// activation and layout sets, so listing only the delta here loses no coverage.
template <typename T>
std::vector<BNTestCase> BNInferTestConfigsFull(miopenBatchNormMode_t mode)
{
return MakeBNInferConfigs(
{
{64, 128, 56, 56},
{64, 256, 28, 28},
{64, 256, 56, 56},
{64, 512, 14, 14},
{64, 64, 112, 112},
},
mode);
}
struct TestNameGenerator
{
std::string
operator()(const testing::TestParamInfo<
std::tuple<miopenActivationMode_t, BNTestCase, miopenTensorLayout_t>>& info) const
{
// activation mode
std::unordered_map<miopenActivationMode_t, std::string> activation_map = {
{miopenActivationPASTHRU, "pasthru"},
{miopenActivationLOGISTIC, "logistic"},
{miopenActivationTANH, "tanh"},
{miopenActivationRELU, "relu"},
{miopenActivationSOFTRELU, "softrelu"},
{miopenActivationABS, "abs"},
{miopenActivationPOWER, "power"},
{miopenActivationCLIPPEDRELU, "clippedrelu"},
{miopenActivationLEAKYRELU, "leakyrelu"},
{miopenActivationELU, "elu"},
{miopenActivationCLAMP, "clamp"}};
auto activation_it = activation_map.find(std::get<0>(info.param));
std::string activation_mode_str =
(activation_it != activation_map.end()) ? activation_it->second : "unknown";
// bn configuration
auto bn_config = std::get<1>(info.param);
std::string bn_mode_str =
(bn_config.mode == miopenBNSpatial) ? "BNSpatial" : "BNPerActivation";
std::string tensor_dims = "N" + std::to_string(bn_config.N) + "_C" +
std::to_string(bn_config.C) + "_H" + std::to_string(bn_config.H) +
"_W" + std::to_string(bn_config.W);
// tensor layout
miopenTensorLayout_t tensor_layout = std::get<2>(info.param);
std::string tensor_layout_str = (tensor_layout == miopenTensorNCHW) ? "NCHW"
: (tensor_layout == miopenTensorNHWC) ? "NHWC"
: "UnknownLayout";
std::ostringstream oss;
oss << bn_mode_str + "_" + tensor_layout_str + "_" + tensor_dims + "_" +
activation_mode_str + "_test_id_" + std::to_string(info.index);
return oss.str();
}
};