Files
2026-07-13 12:40:42 +08:00

232 lines
8.5 KiB
Plaintext

// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cfloat>
#include <string>
#include <vector>
#ifdef __NVCC__
#include "cub/cub.cuh"
#endif
#include "paddle/common/flags.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_dnn.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/activation_functor.h"
#include "paddle/phi/kernels/funcs/eigen/common.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/norm_utils.h"
COMMON_DECLARE_bool(cudnn_batchnorm_spatial_persistent);
namespace phi {
namespace fusion {
template <typename T, typename Context>
void FusedBatchNormActKernel(const Context &dev_ctx,
const DenseTensor &x,
const DenseTensor &scale,
const DenseTensor &bias,
const DenseTensor &mean,
const DenseTensor &variance,
float momentum,
float epsilon,
const std::string &act_type,
DenseTensor *y,
DenseTensor *mean_out,
DenseTensor *variance_out,
DenseTensor *saved_mean,
DenseTensor *saved_variance,
DenseTensor *reserve_space) {
// Note(andsonder): Fused bn activation only used in the gpu place.
#if defined(PADDLE_WITH_CUDA) and CUDNN_VERSION >= 7401
using CudnnDataType = backends::gpu::CudnnDataType<T>;
using BatchNormParamType = typename CudnnDataType::BatchNormParamType;
double epsilon1 = static_cast<double>(epsilon);
if (epsilon1 <= CUDNN_BN_MIN_EPSILON - FLT_EPSILON) {
LOG(ERROR) << "Provided epsilon is smaller than "
<< "CUDNN_BN_MIN_EPSILON. Setting it to "
<< "CUDNN_BN_MIN_EPSILON instead.";
}
epsilon1 = std::max(epsilon1, CUDNN_BN_MIN_EPSILON);
// Get the size for each dimension.
// NHWC [batch_size, in_height, in_width, in_channels]
const auto &x_dims = x.dims();
PADDLE_ENFORCE_EQ(x_dims.size() >= 2 && x_dims.size() <= 5,
true,
common::errors::PreconditionNotMet(
"The Input dim size should be between 2 and 5"));
// Run training mode.
// obtain running mean and running inv var, and see if we need to
// initialize them.
dev_ctx.template Alloc<BatchNormParamType>(mean_out);
dev_ctx.template Alloc<BatchNormParamType>(variance_out);
dev_ctx.template Alloc<BatchNormParamType>(saved_mean);
dev_ctx.template Alloc<BatchNormParamType>(saved_variance);
dev_ctx.template Alloc<T>(y);
int N, C, H, W, D;
const DataLayout data_layout = DataLayout::NHWC;
funcs::ExtractNCWHD(x_dims, data_layout, &N, &C, &H, &W, &D);
if ((N * H * W * D) == 1) {
// Only 1 element in normalization dimension,
// skip the batch norm calculation, let y = act(x).
auto x_v = EigenVector<T>::Flatten(x);
auto y_v = EigenVector<T>::Flatten(*y);
auto &dev = *dev_ctx.eigen_device();
if (act_type == "relu") {
funcs::ReluCUDAFunctor<T>()(dev, x_v, y_v);
} else {
PADDLE_THROW(
common::errors::Unimplemented("Unsupported activation type"));
}
return;
}
// ------------------- cudnn descriptors ---------------------
auto handle = dev_ctx.cudnn_handle();
cudnnTensorDescriptor_t data_desc_;
cudnnTensorDescriptor_t bn_param_desc_;
cudnnBatchNormMode_t mode_ = CUDNN_BATCHNORM_SPATIAL_PERSISTENT;
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnCreateTensorDescriptor(&data_desc_));
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnCreateTensorDescriptor(&bn_param_desc_));
VLOG(3) << "Setting descriptors.";
std::vector<int> dims = {N, C, H, W, D};
std::vector<int> strides = {H * W * D * C, 1, W * D * C, D * C, C};
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnSetTensorNdDescriptor(data_desc_,
CudnnDataType::type,
x_dims.size() > 3 ? x_dims.size() : 4,
dims.data(),
strides.data()));
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnDeriveBNTensorDescriptor(
bn_param_desc_, data_desc_, mode_));
double this_factor = 1. - momentum;
cudnnBatchNormOps_t bnOps_ = CUDNN_BATCHNORM_OPS_BN_ACTIVATION;
backends::gpu::ScopedActivationDescriptor scope_act_desc;
cudnnActivationDescriptor_t activation_desc_ =
scope_act_desc.descriptor<T>(act_type);
size_t workspace_size = 0;
size_t reserve_space_size = 0;
void *reserve_space_ptr = nullptr;
void *workspace_ptr = nullptr;
DenseTensor workspace_tensor;
PADDLE_ENFORCE_NOT_NULL(
reserve_space,
common::errors::NotFound(
"The argument ReserveSpace of batch_norm op is not found."));
// --------------- cudnn batchnorm workspace ---------------
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnGetBatchNormalizationForwardTrainingExWorkspaceSize(
/*handle=*/handle,
/*mode=*/mode_,
/*bnOps=*/bnOps_,
/*xDesc=*/data_desc_,
/*zDesc=*/nullptr,
/*yDesc=*/data_desc_,
/*bnScaleBiasMeanVarDesc=*/bn_param_desc_,
/*activationDesc=*/activation_desc_,
/*sizeInBytes=*/&workspace_size));
// -------------- cudnn batchnorm reserve space --------------
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnGetBatchNormalizationTrainingExReserveSpaceSize(
/*handle=*/handle,
/*mode=*/mode_,
/*bnOps=*/bnOps_,
/*activationDesc=*/activation_desc_,
/*xDesc=*/data_desc_,
/*sizeInBytes=*/&reserve_space_size));
reserve_space->Resize(
{static_cast<int64_t>((reserve_space_size + phi::SizeOf(x.dtype()) - 1) /
phi::SizeOf(x.dtype()))});
reserve_space_ptr = dev_ctx.template Alloc<T>(reserve_space);
workspace_tensor.Resize({static_cast<int64_t>(
(workspace_size + phi::SizeOf(x.dtype()) - 1) / phi::SizeOf(x.dtype()))});
workspace_ptr = dev_ctx.template Alloc<T>(&workspace_tensor);
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnBatchNormalizationForwardTrainingEx(
handle,
mode_,
bnOps_,
CudnnDataType::kOne(),
CudnnDataType::kZero(),
data_desc_,
x.template data<T>(),
nullptr,
nullptr,
data_desc_,
y->template data<T>(),
bn_param_desc_,
scale.template data<BatchNormParamType>(),
bias.template data<BatchNormParamType>(),
this_factor,
dev_ctx.template Alloc<BatchNormParamType>(mean_out),
dev_ctx.template Alloc<BatchNormParamType>(variance_out),
epsilon1,
dev_ctx.template Alloc<BatchNormParamType>(saved_mean),
dev_ctx.template Alloc<BatchNormParamType>(saved_variance),
activation_desc_,
workspace_ptr,
workspace_size,
reserve_space_ptr,
reserve_space_size));
// clean when exit.
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnDestroyTensorDescriptor(data_desc_));
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnDestroyTensorDescriptor(bn_param_desc_));
#else
PADDLE_THROW(common::errors::Unimplemented(
"The fused_batch_norm_act operator is not supported on GPU "
"when CUDNN version < 7.4.1"));
#endif
}
} // namespace fusion
} // namespace phi
PD_REGISTER_KERNEL(fused_batch_norm_act,
GPU,
ALL_LAYOUT,
phi::fusion::FusedBatchNormActKernel,
float,
double,
phi::float16) {
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->OutputAt(1).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(2).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(3).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(4).SetDataType(phi::DataType::FLOAT32);
}
}