Files
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

477 lines
19 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include "MiopenBatchnormFwdTrainingPlan.hpp"
#include "MiopenUtils.hpp"
#include <hipdnn_data_sdk/utilities/ScopedResource.hpp>
namespace miopen_plugin
{
// We have made the intentional decision to hardcode the batchnorm mode to miopenBNSpatial
// rather than making it configurable and adding extra complexity.
const miopenBatchNormMode_t MIOPEN_BATCHNORM_MODE_TRAINING = miopenBNSpatial;
BatchnormFwdTrainingParams::BatchnormFwdTrainingParams(
const hipdnn_flatbuffers_sdk::data_objects::BatchnormAttributes& attributes,
const std::unordered_map<int64_t,
const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes*>&
tensorMap)
: _x(miopen_utils::createBatchnormTensor(tensorMap, attributes.x_tensor_uid()))
, _y(miopen_utils::createBatchnormTensor(tensorMap, attributes.y_tensor_uid()))
, _scale(miopen_utils::createBatchnormTensor(tensorMap, attributes.scale_tensor_uid()))
, _bias(miopen_utils::createBatchnormTensor(tensorMap, attributes.bias_tensor_uid()))
{
_epsilon = hipdnn_plugin_sdk::makeScalarOperand(
tensorMap, attributes.epsilon_tensor_uid(), "Epsilon");
// Save mean and inv_variance are optional (controlled by MIO_SAVE_MEAN_VARIANCE)
auto optMeanUid = attributes.mean_tensor_uid();
if(optMeanUid.has_value())
{
_mean = miopen_utils::createBatchnormTensor(tensorMap, *optMeanUid);
}
auto optInvVarUid = attributes.inv_variance_tensor_uid();
if(optInvVarUid.has_value())
{
_invVariance = miopen_utils::createBatchnormTensor(tensorMap, *optInvVarUid);
}
auto optPrevRunMeanUid = attributes.prev_running_mean_tensor_uid();
auto optPrevRunVarUid = attributes.prev_running_variance_tensor_uid();
auto optMomentumUid = attributes.momentum_tensor_uid();
auto optNextRunMeanUid = attributes.next_running_mean_tensor_uid();
auto optNextRunVarUid = attributes.next_running_variance_tensor_uid();
if(optPrevRunMeanUid.has_value() && optPrevRunVarUid.has_value() && optMomentumUid.has_value()
&& optNextRunMeanUid.has_value() && optNextRunVarUid.has_value())
{
_momentum = hipdnn_plugin_sdk::makeScalarOperand(tensorMap, *optMomentumUid, "Momentum");
_prevRunningMean = miopen_utils::createBatchnormTensor(tensorMap, *optPrevRunMeanUid);
_prevRunningVariance = miopen_utils::createBatchnormTensor(tensorMap, *optPrevRunVarUid);
_nextRunningMean = miopen_utils::createBatchnormTensor(tensorMap, *optNextRunMeanUid);
_nextRunningVariance = miopen_utils::createBatchnormTensor(tensorMap, *optNextRunVarUid);
_hasRunningStats = true;
}
}
BatchnormFwdTrainingParams::BatchnormFwdTrainingParams(
const hipdnn_flatbuffers_sdk::data_objects::BatchnormAttributes& attributes,
const hipdnn_flatbuffers_sdk::data_objects::PointwiseAttributes& pointwiseAttributes,
const std::unordered_map<int64_t,
const hipdnn_flatbuffers_sdk::data_objects::TensorAttributes*>&
tensorMap)
: _x(miopen_utils::createBatchnormTensor(tensorMap, attributes.x_tensor_uid()))
, _y(miopen_utils::createBatchnormTensor(tensorMap, attributes.y_tensor_uid()))
, _scale(miopen_utils::createBatchnormTensor(tensorMap, attributes.scale_tensor_uid()))
, _bias(miopen_utils::createBatchnormTensor(tensorMap, attributes.bias_tensor_uid()))
, _activationOut(
miopen_utils::createBatchnormTensor(tensorMap, pointwiseAttributes.out_0_tensor_uid()))
{
using namespace miopen_utils;
_epsilon = hipdnn_plugin_sdk::makeScalarOperand(
tensorMap, attributes.epsilon_tensor_uid(), "Epsilon");
// Validate that activation input matches batchnorm output
if(pointwiseAttributes.in_0_tensor_uid() != attributes.y_tensor_uid())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR,
"BatchnormFwdTrainingParams: Activation input must match batchnorm output");
}
// Get activation parameters
HIPDNN_PREPEND_MESSAGE_ON_THROW(_optActivation
= mapPointwiseModeToMiopenActivation(pointwiseAttributes),
"BatchnormFwdTrainingParams: ");
// Save mean and inv_variance are optional (controlled by MIO_SAVE_MEAN_VARIANCE)
auto optMeanUid = attributes.mean_tensor_uid();
if(optMeanUid.has_value())
{
_mean = createBatchnormTensor(tensorMap, *optMeanUid);
}
auto optInvVarUid = attributes.inv_variance_tensor_uid();
if(optInvVarUid.has_value())
{
_invVariance = createBatchnormTensor(tensorMap, *optInvVarUid);
}
auto optPrevRunMeanUid = attributes.prev_running_mean_tensor_uid();
auto optPrevRunVarUid = attributes.prev_running_variance_tensor_uid();
auto optMomentumUid = attributes.momentum_tensor_uid();
auto optNextRunMeanUid = attributes.next_running_mean_tensor_uid();
auto optNextRunVarUid = attributes.next_running_variance_tensor_uid();
if(optPrevRunMeanUid.has_value() && optPrevRunVarUid.has_value() && optMomentumUid.has_value()
&& optNextRunMeanUid.has_value() && optNextRunVarUid.has_value())
{
_momentum = hipdnn_plugin_sdk::makeScalarOperand(tensorMap, *optMomentumUid, "Momentum");
_prevRunningMean = miopen_utils::createBatchnormTensor(tensorMap, *optPrevRunMeanUid);
_prevRunningVariance = miopen_utils::createBatchnormTensor(tensorMap, *optPrevRunVarUid);
_nextRunningMean = miopen_utils::createBatchnormTensor(tensorMap, *optNextRunMeanUid);
_nextRunningVariance = miopen_utils::createBatchnormTensor(tensorMap, *optNextRunVarUid);
_hasRunningStats = true;
}
}
const MiopenTensor& BatchnormFwdTrainingParams::x() const
{
return _x;
}
const MiopenTensor& BatchnormFwdTrainingParams::y() const
{
return _y;
}
const MiopenTensor& BatchnormFwdTrainingParams::scale() const
{
return _scale;
}
const MiopenTensor& BatchnormFwdTrainingParams::bias() const
{
return _bias;
}
double BatchnormFwdTrainingParams::epsilonValue(const hipdnnPluginDeviceBuffer_t* deviceBuffers,
uint32_t numDeviceBuffers) const
{
return hipdnn_plugin_sdk::toDouble(
hipdnn_plugin_sdk::resolveScalarOperand(_epsilon, deviceBuffers, numDeviceBuffers));
}
bool BatchnormFwdTrainingParams::hasSaveMeanVariance() const
{
return _mean.has_value() && _invVariance.has_value();
}
const MiopenTensor& BatchnormFwdTrainingParams::mean() const
{
if(!_mean.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR,
"mean() called but mean tensor was not set");
}
return *_mean;
}
const MiopenTensor& BatchnormFwdTrainingParams::invVariance() const
{
if(!_invVariance.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR, "invVariance() called but tensor was not set");
}
return *_invVariance;
}
bool BatchnormFwdTrainingParams::hasRunningStats() const
{
return _hasRunningStats;
}
const MiopenTensor& BatchnormFwdTrainingParams::prevRunningMean() const
{
if(!_prevRunningMean.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR, "prevRunningMean() called but tensor was not set");
}
return *_prevRunningMean;
}
const MiopenTensor& BatchnormFwdTrainingParams::prevRunningVariance() const
{
if(!_prevRunningVariance.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR,
"prevRunningVariance() called but tensor was not set");
}
return *_prevRunningVariance;
}
double BatchnormFwdTrainingParams::momentumValue(const hipdnnPluginDeviceBuffer_t* deviceBuffers,
uint32_t numDeviceBuffers) const
{
if(!_momentum.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR, "momentumValue() called but momentum was not set");
}
return hipdnn_plugin_sdk::toDouble(
hipdnn_plugin_sdk::resolveScalarOperand(*_momentum, deviceBuffers, numDeviceBuffers));
}
const MiopenTensor& BatchnormFwdTrainingParams::nextRunningMean() const
{
if(!_nextRunningMean.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR, "nextRunningMean() called but tensor was not set");
}
return *_nextRunningMean;
}
const MiopenTensor& BatchnormFwdTrainingParams::nextRunningVariance() const
{
if(!_nextRunningVariance.has_value())
{
throw hipdnn_plugin_sdk::HipdnnPluginException(
HIPDNN_PLUGIN_STATUS_INTERNAL_ERROR,
"nextRunningVariance() called but tensor was not set");
}
return *_nextRunningVariance;
}
const std::optional<miopen_utils::ActivationParams>&
BatchnormFwdTrainingParams::optActivation() const
{
return _optActivation;
}
const std::optional<MiopenTensor>& BatchnormFwdTrainingParams::activationOut() const
{
return _activationOut;
}
BatchnormFwdTrainingPlan::BatchnormFwdTrainingPlan(BatchnormFwdTrainingParams&& trainingParams,
const HipdnnMiopenSettings& executionSettings)
: _trainingParams(std::move(trainingParams))
, _executionSettings(executionSettings)
{
}
size_t BatchnormFwdTrainingPlan::getWorkspaceSize(
[[maybe_unused]] const HipdnnMiopenHandle& handle) const
{
// No workspace needed for batchnorm training
return 0;
}
void BatchnormFwdTrainingPlan::execute(const HipdnnMiopenHandle& handle,
const hipdnnPluginDeviceBuffer_t* deviceBuffers,
uint32_t numDeviceBuffers,
[[maybe_unused]] void* workspace) const
{
// Set tuning policy based on benchmarking flag - RAII ensures restoration
const ScopedTuningPolicy tuningGuard(handle.miopenHandle,
_executionSettings.benchmarkingEnabled());
float alpha = 1.0f;
float beta = 0.0f;
// Resolve epsilon (baked default or runtime-user-supplied host scalar).
const double epsilon = _trainingParams.epsilonValue(deviceBuffers, numDeviceBuffers);
// Resolve momentum (baked default or runtime-user-supplied host scalar) if running stats exist
double expAvgFactor = 0.0;
if(_trainingParams.hasRunningStats())
{
expAvgFactor = _trainingParams.momentumValue(deviceBuffers, numDeviceBuffers);
HIPDNN_PLUGIN_LOG_INFO(
"BatchnormFwdTrainingPlan: expAvgFactor (momentum) = " << expAvgFactor);
}
// Get all required device buffers
auto xBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.x().uid(), deviceBuffers, numDeviceBuffers);
auto scaleBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.scale().uid(), deviceBuffers, numDeviceBuffers);
auto biasBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.bias().uid(), deviceBuffers, numDeviceBuffers);
// Handle save mean/variance if provided (optional)
void* resultSaveMeanPtr = nullptr;
void* resultSaveInvVariancePtr = nullptr;
miopenTensorDescriptor_t savedMeanDesc = nullptr;
miopenTensorDescriptor_t savedVarDesc = nullptr;
if(_trainingParams.hasSaveMeanVariance())
{
auto meanBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.mean().uid(), deviceBuffers, numDeviceBuffers);
auto invVarianceBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.invVariance().uid(), deviceBuffers, numDeviceBuffers);
resultSaveMeanPtr = meanBuffer.ptr;
resultSaveInvVariancePtr = invVarianceBuffer.ptr;
savedMeanDesc = _trainingParams.mean().tensorDescriptor();
savedVarDesc = _trainingParams.invVariance().tensorDescriptor();
}
void* prevRunningMeanPtr = nullptr;
void* prevRunningVariancePtr = nullptr;
void* nextRunningMeanPtr = nullptr;
void* nextRunningVariancePtr = nullptr;
if(_trainingParams.hasRunningStats())
{
prevRunningMeanPtr
= hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.prevRunningMean().uid(), deviceBuffers, numDeviceBuffers)
.ptr;
prevRunningVariancePtr
= hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.prevRunningVariance().uid(), deviceBuffers, numDeviceBuffers)
.ptr;
nextRunningMeanPtr
= hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.nextRunningMean().uid(), deviceBuffers, numDeviceBuffers)
.ptr;
nextRunningVariancePtr
= hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.nextRunningVariance().uid(), deviceBuffers, numDeviceBuffers)
.ptr;
}
// Check if activation fusion is enabled
const auto& optActivation = _trainingParams.optActivation();
const auto& optActivationOut = _trainingParams.activationOut();
if(optActivation.has_value() && optActivationOut.has_value())
{
const auto& activOutTensor = *optActivationOut;
// Use activation fusion API
auto yBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
activOutTensor.uid(), deviceBuffers, numDeviceBuffers);
// Create activation descriptor
miopenActivationDescriptor_t activationDesc;
THROW_ON_MIOPEN_FAILURE(miopenCreateActivationDescriptor(&activationDesc));
auto activationDescRes
= hipdnn_data_sdk::utilities::ScopedResource<miopenActivationDescriptor_t>(
activationDesc, [](miopenActivationDescriptor_t desc) {
auto status = miopenDestroyActivationDescriptor(desc);
if(status != miopenStatusSuccess)
{
HIPDNN_PLUGIN_LOG_ERROR("miopenDestroyActivationDescriptor failed in "
"BatchnormFwdTrainingPlan::execute");
}
});
const auto& activParams = *optActivation;
THROW_ON_MIOPEN_FAILURE(miopenSetActivationDescriptor(activationDesc,
activParams.mode,
activParams.alpha,
activParams.beta,
activParams.gamma));
if(_trainingParams.hasRunningStats())
{
THROW_ON_MIOPEN_FAILURE(miopenBatchNormForwardTrainingActivation_V2(
handle.miopenHandle,
MIOPEN_BATCHNORM_MODE_TRAINING,
&alpha,
&beta,
_trainingParams.x().tensorDescriptor(),
xBuffer.ptr,
activOutTensor.tensorDescriptor(),
yBuffer.ptr,
_trainingParams.scale().tensorDescriptor(),
_trainingParams.bias().tensorDescriptor(),
savedMeanDesc,
savedVarDesc,
scaleBuffer.ptr,
biasBuffer.ptr,
expAvgFactor,
prevRunningMeanPtr,
prevRunningVariancePtr,
nextRunningMeanPtr,
nextRunningVariancePtr,
epsilon,
resultSaveMeanPtr,
resultSaveInvVariancePtr,
activationDesc));
}
else
{
THROW_ON_MIOPEN_FAILURE(miopenBatchNormForwardTrainingActivation(
handle.miopenHandle,
MIOPEN_BATCHNORM_MODE_TRAINING,
&alpha,
&beta,
_trainingParams.x().tensorDescriptor(),
xBuffer.ptr,
activOutTensor.tensorDescriptor(),
yBuffer.ptr,
_trainingParams.scale().tensorDescriptor(),
_trainingParams.bias().tensorDescriptor(),
savedMeanDesc,
savedVarDesc,
scaleBuffer.ptr,
biasBuffer.ptr,
expAvgFactor,
nullptr, // resultRunningMean: nullptr means running mean is not saved
nullptr, // resultRunningVariance: nullptr means running variance is not saved
epsilon,
resultSaveMeanPtr,
resultSaveInvVariancePtr,
activationDesc));
}
}
else
{
// Use standard batchnorm training API (no activation)
auto yBuffer = hipdnn_plugin_sdk::findDeviceBuffer(
_trainingParams.y().uid(), deviceBuffers, numDeviceBuffers);
if(_trainingParams.hasRunningStats())
{
THROW_ON_MIOPEN_FAILURE(miopenBatchNormalizationForwardTraining_V3(
handle.miopenHandle,
MIOPEN_BATCHNORM_MODE_TRAINING,
&alpha,
&beta,
_trainingParams.x().tensorDescriptor(),
xBuffer.ptr,
_trainingParams.y().tensorDescriptor(),
yBuffer.ptr,
_trainingParams.scale().tensorDescriptor(),
_trainingParams.bias().tensorDescriptor(),
savedMeanDesc,
savedVarDesc,
scaleBuffer.ptr,
biasBuffer.ptr,
expAvgFactor,
prevRunningMeanPtr,
prevRunningVariancePtr,
nextRunningMeanPtr,
nextRunningVariancePtr,
epsilon,
resultSaveMeanPtr,
resultSaveInvVariancePtr));
}
else
{
THROW_ON_MIOPEN_FAILURE(miopenBatchNormalizationForwardTraining(
handle.miopenHandle,
MIOPEN_BATCHNORM_MODE_TRAINING,
&alpha,
&beta,
_trainingParams.x().tensorDescriptor(),
xBuffer.ptr,
_trainingParams.y().tensorDescriptor(),
yBuffer.ptr,
_trainingParams.scale().tensorDescriptor(),
scaleBuffer.ptr,
biasBuffer.ptr,
expAvgFactor,
nullptr, // resultRunningMean: nullptr means running mean is not saved
nullptr, // resultRunningVariance: nullptr means running variance is not saved
epsilon,
resultSaveMeanPtr,
resultSaveInvVariancePtr));
}
}
}
}