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

226 lines
9.0 KiB
Plaintext

// Copyright (c) 2024 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 "paddle/phi/kernels/sync_batch_norm_kernel.h"
#include "paddle/common/enforce.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/sync_batch_norm_utils.h"
namespace phi {
template <typename T, typename Context>
void SyncBatchNormKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& mean,
const DenseTensor& variance,
const DenseTensor& scale,
const DenseTensor& bias,
bool is_test,
float momentum,
float epsilon_f,
const std::string& data_layout_str,
bool use_global_stats,
bool trainable_statistics,
DenseTensor* y,
DenseTensor* mean_out,
DenseTensor* variance_out,
DenseTensor* saved_mean,
DenseTensor* saved_variance,
DenseTensor* reserve_space) {
PADDLE_ENFORCE_EQ(use_global_stats,
false,
common::errors::InvalidArgument(
"sync_batch_norm doesn't support "
"to set use_global_stats True. Please use batch_norm "
"in this case."));
double epsilon = epsilon_f;
const bool trainable_stats = trainable_statistics;
const DataLayout layout = StringToDataLayout(data_layout_str);
bool test_mode = is_test && (!trainable_statistics);
const auto& x_dims = x.dims();
PADDLE_ENFORCE_GE(x_dims.size(),
2,
common::errors::InvalidArgument(
"The Input dim size should be larger than 1."));
PADDLE_ENFORCE_LE(x_dims.size(),
5,
common::errors::InvalidArgument(
"The Input dim size should be less than 6."));
int N, C, H, W, D;
funcs::ExtractNCWHD(x_dims, layout, &N, &C, &H, &W, &D);
int64_t x_numel = x.numel();
const int64_t fsize = static_cast<int64_t>(H) * W * D;
const T* x_d = x.template data<T>();
const auto* s_d = scale.template data<BatchNormParamType<T>>();
const auto* b_d = bias.template data<BatchNormParamType<T>>();
T* y_d = dev_ctx.template Alloc<T>(y);
const BatchNormParamType<T>* mean_data = nullptr;
const BatchNormParamType<T>* var_data = nullptr;
auto stream = dev_ctx.stream();
const int block = 512;
int max_threads = dev_ctx.GetMaxPhysicalThreadCount();
Allocator::AllocationPtr alloc_ptr{nullptr};
if (test_mode) {
mean_data = mean.template data<BatchNormParamType<T>>();
var_data = variance.template data<BatchNormParamType<T>>();
} else {
// x, x^2, 1, here 1 is used to calc device num
// device num also can be got from DeviceContextPool
const int64_t bytes_64 =
(static_cast<int64_t>(C) * 2 + 1) * sizeof(BatchNormParamType<T>);
DenseTensor stats_tensor;
stats_tensor.Resize({bytes_64});
dev_ctx.template Alloc<BatchNormParamType<T>>(&stats_tensor);
auto* stats_data = stats_tensor.data<BatchNormParamType<T>>();
auto* stats = reinterpret_cast<BatchNormParamType<T>*>(stats_data);
const int threads = 512;
int grid = std::min(C, (max_threads + threads - 1) / threads);
if (layout == DataLayout::NCHW) {
KeLocalStats<T, threads, DataLayout::NCHW>
<<<grid, threads, 0, stream>>>(x_d, N, fsize, C, stats);
} else {
KeLocalStats<T, threads, DataLayout::NHWC>
<<<grid, threads, 0, stream>>>(x_d, N, fsize, C, stats);
}
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
auto comm_ctx =
static_cast<distributed::NCCLCommContext*>(dev_ctx.GetCommContext());
if (comm_ctx) {
comm_ctx->AllReduce(&stats_tensor, stats_tensor, ncclSum, stream);
}
#endif
auto* est_mean_data =
dev_ctx.template Alloc<BatchNormParamType<T>>(mean_out);
auto* est_var_data =
dev_ctx.template Alloc<BatchNormParamType<T>>(variance_out);
auto* sv_mean_data =
dev_ctx.template Alloc<BatchNormParamType<T>>(saved_mean);
auto* sv_inv_var_data =
dev_ctx.template Alloc<BatchNormParamType<T>>(saved_variance);
int64_t reserve_space_size = 0;
DenseTensor tmp_reserve_space;
if (reserve_space == nullptr) {
reserve_space = &tmp_reserve_space;
}
reserve_space->Resize({reserve_space_size});
dev_ctx.template Alloc<T>(reserve_space);
// Note, Input('Mean')/Input('Variance') share variable with
// Output('MeanOut')/Output('VarianceOut')
KeSyncAndMovingStats<T>
<<<(C + block - 1) / block, block, 0, stream>>>(stats,
stats + C,
stats + 2 * C,
C,
momentum,
epsilon,
sv_mean_data,
sv_inv_var_data,
est_mean_data,
est_var_data);
mean_data = sv_mean_data;
var_data = stats + C;
}
const int64_t grid2_64 =
(std::min(x_numel, static_cast<int64_t>(max_threads)) + block - 1) /
block;
uint32_t grid2 = static_cast<uint32_t>(grid2_64);
if (layout == DataLayout::NCHW) {
KeNormAffine<T, DataLayout::NCHW><<<grid2, block, 0, stream>>>(
x_d, s_d, b_d, mean_data, var_data, epsilon, C, fsize, x_numel, y_d);
} else {
KeNormAffine<T, DataLayout::NHWC><<<grid2, block, 0, stream>>>(
x_d, s_d, b_d, mean_data, var_data, epsilon, C, fsize, x_numel, y_d);
}
}
} // namespace phi
#ifdef PADDLE_WITH_HIP
PD_REGISTER_KERNEL(sync_batch_norm,
GPU,
ALL_LAYOUT,
phi::SyncBatchNormKernel,
float,
phi::float16) {
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->InputAt(1).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(2).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(3).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(4).SetDataType(phi::DataType::FLOAT32);
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);
}
}
#else
#if CUDNN_VERSION_MIN(8, 1, 0)
PD_REGISTER_KERNEL(sync_batch_norm,
GPU,
ALL_LAYOUT,
phi::SyncBatchNormKernel,
float,
double,
phi::float16,
phi::bfloat16) {
if (kernel_key.dtype() == phi::DataType::FLOAT16 ||
kernel_key.dtype() == phi::DataType::BFLOAT16) {
kernel->InputAt(1).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(2).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(3).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(4).SetDataType(phi::DataType::FLOAT32);
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);
}
}
#else
PD_REGISTER_KERNEL(sync_batch_norm,
GPU,
ALL_LAYOUT,
phi::SyncBatchNormKernel,
float,
double,
phi::float16) {
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->InputAt(1).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(2).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(3).SetDataType(phi::DataType::FLOAT32);
kernel->InputAt(4).SetDataType(phi::DataType::FLOAT32);
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);
}
}
#endif
#endif