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

842 lines
34 KiB
Plaintext

// Copyright (c) 2022 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/layer_norm_kernel.h"
#include "paddle/common/enforce.h"
#include "paddle/common/flags.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#if defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP) && !defined(_WIN32)
#include "paddle/phi/kernels/funcs/fast_ln_v2.h"
#endif
#include "paddle/phi/kernels/funcs/layer_norm_impl.cu.h"
#include "paddle/phi/kernels/funcs/layer_norm_util.h"
#ifdef PADDLE_WITH_CUDA
#include "paddle/phi/kernels/gpu/rms_norm_cuda_kernel.h"
#endif
COMMON_DECLARE_bool(use_fast_math);
COMMON_DECLARE_bool(use_apex_layer_norm_kernel);
namespace phi {
enum class LayerNormKernelVariant { FAST_LN_V1, FAST_LN_V2, GENERIC };
#ifdef PADDLE_WITH_CUDA
template <typename U>
__device__ inline void WelfordOnline(U val, U* mean, U* square, U* count) {
*count += 1;
U delta1 = val - *mean;
*mean += delta1 / (*count);
U delta2 = val - *mean;
*square += delta1 * delta2;
}
template <typename U>
__device__ inline void WelfordOnline(
U b_mean, U b_square, U b_cnt, U* mean, U* square, U* count) {
if (b_cnt == 0) {
return;
}
U new_cnt = *count + b_cnt;
U nb_n = b_cnt / new_cnt;
U delta = b_mean - *mean;
*mean += delta * nb_n;
*square += b_square + delta * delta * (*count) * nb_n;
*count = new_cnt;
}
template <typename U>
__device__ inline void WelfordWarpAllReduce(U* mean, U* square, U* count) {
constexpr int kWarpSize = 32;
#pragma unroll
for (int mask = 1; mask < kWarpSize; mask *= 2) {
U b_mean = __shfl_down_sync(0xffffffff, *mean, mask);
U b_square = __shfl_down_sync(0xffffffff, *square, mask);
U b_cnt = __shfl_down_sync(0xffffffff, *count, mask);
WelfordOnline<U>(b_mean, b_square, b_cnt, mean, square, count);
}
*mean = __shfl_sync(0xffffffff, *mean, 0, kWarpSize);
*square = __shfl_sync(0xffffffff, *square, 0, kWarpSize);
*count = __shfl_sync(0xffffffff, *count, 0, kWarpSize);
}
template <int VecSize>
struct ThreadAssigner {
__device__ __forceinline__ int operator()(const int cols,
const int cols_per_thread,
int32_t* last_tid_idx) {
return cols_per_thread;
}
};
template <>
struct ThreadAssigner<1> {
__device__ inline int operator()(const int cols,
const int cols_per_thread,
int* last_tid_idx) {
int cols_this_thread = cols_per_thread;
int last_tid = (cols / cols_per_thread);
*last_tid_idx = last_tid;
if (threadIdx.x == last_tid) {
cols_this_thread = cols - cols_per_thread * last_tid;
} else if (threadIdx.x > last_tid) {
cols_this_thread = 0;
}
return cols_this_thread;
}
};
template <typename T, typename U, int VecSize>
struct LayerNormDataReader {
__device__ inline void operator()(const T* __restrict__ row_src,
U* buffer,
const int last_tid_idx,
const int read_times,
const int cols_this_thread) {
using VecT = AlignedVector<T, VecSize>;
const VecT* __restrict__ v_src =
reinterpret_cast<const VecT* __restrict__>(row_src);
for (int i = 0; i < read_times; ++i) {
VecT temp_src = v_src[threadIdx.x + i * blockDim.x];
#pragma unroll
for (int j = 0; j < VecSize; ++j) {
buffer[i * VecSize + j] = static_cast<U>(temp_src[j]);
}
}
}
};
template <typename T, typename U>
struct LayerNormDataReader<T, U, 1> {
__device__ inline void operator()(const T* __restrict__ row_src,
U* buffer,
const int last_tid_idx,
const int read_times,
const int cols_this_thread) {
// read_time is just cols_per_thread while VecSize is 1.
if (threadIdx.x < last_tid_idx) {
for (int i = 0; i < cols_this_thread; ++i) {
buffer[i] = static_cast<U>(row_src[threadIdx.x + last_tid_idx * i]);
}
} else {
for (int i = 0; i < cols_this_thread; ++i) {
buffer[i] = static_cast<U>(row_src[i + read_times * last_tid_idx]);
}
}
}
};
template <typename T, typename U, bool IsSameType, int VecSize>
struct LayerNormDataWriter {
__device__ inline void operator()(
T* __restrict__ row_dst,
const U* __restrict__ buffer,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ scale,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ bias,
const U row_mean,
const U row_inv_var,
const int write_times,
const int cols_this_thread,
const int last_tid_idx,
const bool valid_scale,
const bool valid_bias) {
using VecT = AlignedVector<T, VecSize>;
using ScaleT = funcs::LayerNormScaleBiasT<T, U, IsSameType>;
using VecScaleT = AlignedVector<ScaleT, VecSize>;
VecT* v_dst = reinterpret_cast<VecT*>(row_dst);
// cols_this_thread is just cols_per_thread
if ((!valid_scale) && (!valid_bias)) {
for (int i = 0; i < write_times; ++i) {
VecT temp_dst;
#pragma unroll
for (int j = 0; j < VecSize; ++j) {
temp_dst[j] = static_cast<T>((buffer[i * VecSize + j] - row_mean) *
row_inv_var);
}
v_dst[threadIdx.x + static_cast<int64_t>(blockDim.x) * i] = temp_dst;
}
} else {
const VecScaleT* __restrict__ v_scale =
reinterpret_cast<const VecScaleT* __restrict__>(scale);
const VecScaleT* __restrict__ v_bias =
reinterpret_cast<const VecScaleT* __restrict__>(bias);
if (valid_scale && valid_bias) {
for (int i = 0; i < write_times; ++i) {
int64_t idx = threadIdx.x + static_cast<int64_t>(blockDim.x) * i;
VecT temp_dst;
VecScaleT temp_v_scale = v_scale[idx];
VecScaleT temp_v_bias = v_bias[idx];
#pragma unroll
for (int j = 0; j < VecSize; ++j) {
temp_dst[j] = static_cast<T>(
static_cast<U>(temp_v_scale[j]) *
(buffer[i * VecSize + j] - row_mean) * row_inv_var +
static_cast<U>(temp_v_bias[j]));
}
v_dst[idx] = temp_dst;
}
} else {
if (valid_scale) {
for (int i = 0; i < write_times; ++i) {
int64_t idx = threadIdx.x + static_cast<int64_t>(blockDim.x) * i;
VecT temp_dst;
VecScaleT temp_v_scale = v_scale[idx];
#pragma unroll
for (int j = 0; j < VecSize; ++j) {
temp_dst[j] = static_cast<T>(
static_cast<U>(temp_v_scale[j]) *
(buffer[i * VecSize + j] - row_mean) * row_inv_var);
}
v_dst[idx] = temp_dst;
}
} else {
for (int i = 0; i < write_times; ++i) {
int idx = threadIdx.x + blockDim.x * i;
VecT temp_dst;
VecScaleT temp_v_bias = v_bias[idx];
#pragma unroll
for (int j = 0; j < VecSize; ++j) {
temp_dst[j] = static_cast<T>(
(buffer[i * VecSize + j] - row_mean) * row_inv_var +
static_cast<U>(temp_v_bias[j]));
}
v_dst[idx] = temp_dst;
}
}
}
}
}
};
template <typename T, typename U, bool IsSameType>
struct LayerNormDataWriter<T, U, IsSameType, 1> {
__device__ __forceinline__ void operator()(
T* __restrict__ row_dst,
U* __restrict__ buffer,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ scale,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ bias,
const U row_mean,
const U row_inv_var,
const int write_times,
const int cols_this_thread,
const int last_tid_idx,
const bool valid_scale,
const bool valid_bias) {
// write_times is just col_per_thread.
if ((!valid_scale) && (!valid_bias)) {
if (threadIdx.x < last_tid_idx) {
for (int i = 0; i < cols_this_thread; ++i) {
row_dst[threadIdx.x + static_cast<int64_t>(last_tid_idx) * i] =
(buffer[i] - row_mean) * row_inv_var;
}
} else {
for (int i = 0; i < cols_this_thread; ++i) {
row_dst[static_cast<int64_t>(last_tid_idx) * write_times + i] =
(buffer[i] - row_mean) * row_inv_var;
}
}
} else if (valid_scale && valid_bias) {
if (threadIdx.x < last_tid_idx) {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = threadIdx.x + static_cast<int64_t>(last_tid_idx) * i;
row_dst[idx] =
static_cast<T>(static_cast<U>(scale[idx]) *
(buffer[i] - row_mean) * row_inv_var +
static_cast<U>(bias[idx]));
}
} else {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = static_cast<int64_t>(last_tid_idx) * write_times + i;
row_dst[idx] =
static_cast<T>(static_cast<U>(scale[idx]) *
(buffer[i] - row_mean) * row_inv_var +
static_cast<U>(bias[idx]));
}
}
} else {
if (valid_scale) {
if (threadIdx.x < last_tid_idx) {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = threadIdx.x + static_cast<int64_t>(last_tid_idx) * i;
row_dst[idx] = static_cast<T>(static_cast<U>(scale[idx]) *
(buffer[i] - row_mean) * row_inv_var);
}
} else {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = static_cast<int64_t>(last_tid_idx) * write_times + i;
row_dst[idx] = static_cast<T>(static_cast<U>(scale[idx]) *
(buffer[i] - row_mean) * row_inv_var);
}
}
} else {
if (threadIdx.x < last_tid_idx) {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = threadIdx.x + static_cast<int64_t>(last_tid_idx) * i;
row_dst[idx] = static_cast<T>((buffer[i] - row_mean) * row_inv_var +
static_cast<U>(bias[idx]));
}
} else {
for (int i = 0; i < cols_this_thread; ++i) {
int64_t idx = static_cast<int64_t>(last_tid_idx) * write_times + i;
row_dst[idx] = static_cast<T>((buffer[i] - row_mean) * row_inv_var +
static_cast<U>(bias[idx]));
}
}
}
}
}
};
template <typename IndexT, typename T, typename U, bool IsSameType, int VecSize>
__global__ void LayerNormFwdWithWelford(
const T* __restrict__ src_data,
T* dst_data,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ scale,
const funcs::LayerNormScaleBiasT<T, U, IsSameType>* __restrict__ bias,
U* mean,
U* var,
const U epsilon,
const IndexT rows,
const int32_t cols,
const int32_t cols_per_thread,
const bool valid_scale,
const bool valid_bias) {
constexpr int kWarpSize = 32;
int last_tid_idx = 0; // For condition once vecSize is 1.
IndexT row_offset = blockIdx.x * blockDim.y + threadIdx.y;
int cols_this_thread =
ThreadAssigner<VecSize>()(cols, cols_per_thread, &last_tid_idx);
int read_times = cols_per_thread / VecSize;
if (row_offset < rows) {
U buffer[kWarpSize];
U tid_cnt = static_cast<U>(0);
U tid_mean = static_cast<U>(0);
U tid_square = static_cast<U>(0);
const T* __restrict__ row_src = src_data + row_offset * cols;
T* row_dst = dst_data + row_offset * cols;
LayerNormDataReader<T, U, VecSize>()(
row_src, buffer, last_tid_idx, read_times, cols_this_thread);
for (int i = 0; i < cols_this_thread; i++) {
WelfordOnline<U>(buffer[i], &tid_mean, &tid_square, &tid_cnt);
}
U warp_cnt = tid_cnt;
U warp_mean = tid_mean;
U warp_square = tid_square;
WelfordWarpAllReduce<U>(&warp_mean, &warp_square, &warp_cnt);
U row_variance = max(warp_square / warp_cnt, 0.f);
U row_inv_var = funcs::rsqrt_(row_variance + epsilon);
// TODO(limingshu): make code below vectorization.
if (threadIdx.x == 0) {
// warp_mean is just row_mean here.
mean[row_offset] = warp_mean;
var[row_offset] = row_variance;
}
LayerNormDataWriter<T, U, IsSameType, VecSize>()(row_dst,
buffer,
scale,
bias,
warp_mean,
row_inv_var,
read_times,
cols_this_thread,
last_tid_idx,
valid_scale,
valid_bias);
}
}
template <typename Context, typename T, typename U>
void LaunchLayerNormKernel(const Context& dev_ctx,
const T* x_data,
T* y_data,
const void* void_scale_data,
const void* void_bias_data,
U* mean_data,
U* var_data,
double epsilon,
const int64_t rows,
const int cols,
const bool valid_scale,
const bool valid_bias,
const bool is_same_type) {
constexpr int WarpSize = 32;
constexpr int RowPerBlock = 4;
int64_t block_size = (rows + (RowPerBlock - 1)) / RowPerBlock;
dim3 threads(WarpSize, RowPerBlock, 1);
int vec_size = 1;
int cols_per_thread = (cols + (WarpSize - 1)) / WarpSize;
if (cols_per_thread > 1 && (cols % WarpSize == 0)) {
int data_vec_size = 0;
uint64_t addr = (reinterpret_cast<uint64_t>(x_data) |
reinterpret_cast<uint64_t>(y_data));
if (valid_bias || valid_scale) {
if (is_same_type) {
addr = valid_scale
? (addr | reinterpret_cast<uint64_t>(void_scale_data))
: addr;
addr = valid_bias ? (addr | reinterpret_cast<uint64_t>(void_bias_data))
: addr;
data_vec_size =
std::min(4, GetVectorizedSize<T>(reinterpret_cast<T*>(addr)));
} else {
uint64_t bias_addr = reinterpret_cast<uint64_t>(void_bias_data);
uint64_t attr_addr = valid_scale
? reinterpret_cast<uint64_t>(void_scale_data)
: bias_addr;
attr_addr = valid_bias
? (valid_scale ? (attr_addr | bias_addr) : attr_addr)
: attr_addr;
data_vec_size =
std::min(GetVectorizedSize<T>(reinterpret_cast<T*>(addr)),
GetVectorizedSize<U>(reinterpret_cast<U*>(attr_addr)));
data_vec_size = std::min(4, data_vec_size);
}
}
for (int size = data_vec_size; size > 0; size /= 2) {
if (cols_per_thread % size == 0) {
vec_size = size;
break;
}
}
}
#define IMPL_LAYER_NORM_WELFORD_CASE(index_t, scale_t, is_same_, vec_size_) \
case (vec_size_): { \
PADDLE_ENFORCE_LE_UINT32_MAX(block_size, "grid.x"); \
LayerNormFwdWithWelford<index_t, T, U, is_same_, vec_size_> \
<<<static_cast<uint32_t>(block_size), threads, 0, dev_ctx.stream()>>>( \
x_data, \
y_data, \
static_cast<const scale_t*>(void_scale_data), \
static_cast<const scale_t*>(void_bias_data), \
mean_data, \
var_data, \
static_cast<const U>(epsilon), \
rows, \
cols, \
cols_per_thread, \
valid_scale, \
valid_bias); \
} break
#define IMPL_LAYER_NORM_WELFORD(index_t, scale_t, is_same_) \
IMPL_LAYER_NORM_WELFORD_CASE(index_t, scale_t, is_same_, 4); \
IMPL_LAYER_NORM_WELFORD_CASE(index_t, scale_t, is_same_, 2); \
IMPL_LAYER_NORM_WELFORD_CASE(index_t, scale_t, is_same_, 1);
if (rows < std::numeric_limits<int32_t>::max()) {
if (is_same_type) {
switch (vec_size) { IMPL_LAYER_NORM_WELFORD(int32_t, T, true); }
} else {
switch (vec_size) { IMPL_LAYER_NORM_WELFORD(int32_t, U, false); }
}
} else {
if (is_same_type) {
switch (vec_size) { IMPL_LAYER_NORM_WELFORD(int64_t, T, true); }
} else {
switch (vec_size) { IMPL_LAYER_NORM_WELFORD(int64_t, U, false); }
}
}
#undef IMPL_LAYER_NORM_WELFORD_CASE
#undef IMPL_LAYER_NORM_WELFORD
}
#endif // PADDLE_WITH_CUDA
template <typename T, typename U>
void LayerNormDirectCUDAFunctor<T, U>::operator()(
gpuStream_t stream,
const T* input,
std::vector<int64_t> input_shape,
const U* bias,
const U* scale,
T* output,
U* mean,
U* variance,
int begin_norm_axis,
float eps) {
const auto x_dims = make_ddim(input_shape);
auto matrix_dim = common::flatten_to_2d(x_dims, begin_norm_axis);
int64_t batch_size = matrix_dim[0];
int64_t feature_size = matrix_dim[1];
// TODO(large-tensor): generic kernel launch uses uint32 grid dim
PADDLE_ENFORCE_LE_UINT32_MAX(batch_size,
"Kernel launch requires uint32 for grid dim");
switch (funcs::GetDesiredBlockDim(feature_size)) {
FIXED_BLOCK_DIM_CASE(
funcs::LayerNormForward<T, U, kBlockDim>
<<<static_cast<uint32_t>(batch_size), kBlockDim, 0, stream>>>(
input, scale, bias, output, mean, variance, eps, feature_size));
default:
PADDLE_THROW(common::errors::InvalidArgument(
"Product from begin_norm_axis to end in layer_norm must be larger "
"than 1"));
break;
}
}
template class PADDLE_API LayerNormDirectCUDAFunctor<float, float>;
template class PADDLE_API LayerNormDirectCUDAFunctor<double, double>;
#if defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP)
template class PADDLE_API LayerNormDirectCUDAFunctor<half, float>;
#endif
static inline LayerNormKernelVariant LayerNormKernelDispatch(
const DataType weight_type,
const DataType input_type,
const DataType output_type,
const DataType compute_type,
const int64_t hidden_size,
const int64_t x_numel,
const DenseTensor* scale,
const DenseTensor* bias) {
if (scale == nullptr || bias == nullptr) {
return LayerNormKernelVariant::GENERIC;
}
#if defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP) && !defined(_WIN32)
if (FLAGS_use_apex_layer_norm_kernel) {
if (funcs::fast_ln_v2::has_fast_ln_v2_fwd_kernel(
weight_type, input_type, output_type, compute_type, hidden_size)) {
return LayerNormKernelVariant::FAST_LN_V2;
}
PADDLE_THROW(common::errors::InvalidArgument(
"FLAGS_use_apex_layer_norm_kernel requires inputs supported by "
"fast_ln_v2 forward kernel."));
}
#endif
#ifdef PADDLE_WITH_CUDA
if (FLAGS_use_accuracy_compatible_kernel) {
return LayerNormKernelVariant::GENERIC;
}
#endif
#if defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP) && !defined(_WIN32)
if (input_type != DataType::FLOAT32 && hidden_size != 4096 &&
hidden_size > 1024 && hidden_size <= 10240 &&
x_numel <= std::numeric_limits<uint32_t>::max()) {
// using fast_ln_v2 only sm > 70 and x_numel <= uint32_max
auto prop = funcs::fast_ln_v2::GetDeviceProp();
uint32_t hidden_size_32 = static_cast<uint32_t>(hidden_size);
if (prop->major > 7 &&
funcs::fast_ln_v2::has_fast_ln_v2_fwd_kernel(weight_type,
input_type,
output_type,
compute_type,
hidden_size_32)) {
return LayerNormKernelVariant::FAST_LN_V2;
}
}
#endif
if ((hidden_size >= 768 && hidden_size <= 2048 && hidden_size % 256 == 0 ||
hidden_size == 4096) &&
x_numel <= std::numeric_limits<int>::max() && scale != nullptr &&
bias != nullptr) {
return LayerNormKernelVariant::FAST_LN_V1;
}
return LayerNormKernelVariant::GENERIC;
}
template <typename T, typename Context>
void LayerNormKernel(const Context& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& scale_opt,
const optional<DenseTensor>& bias_opt,
double epsilon,
int begin_norm_axis,
DenseTensor* y,
DenseTensor* mean,
DenseTensor* var) {
using U = funcs::LayerNormParamType<T>;
auto* scale = scale_opt.get_ptr();
auto* bias = bias_opt.get_ptr();
const auto x_dims = x.dims();
auto* x_data = x.data<T>();
auto* y_data = dev_ctx.template Alloc<T>(y);
auto* mean_data = dev_ctx.template Alloc<U>(mean);
auto* var_data = dev_ctx.template Alloc<U>(var);
if (x.numel() == 0) return;
bool valid_scale = (scale != nullptr);
bool valid_bias = (bias != nullptr);
auto* void_scale_data = valid_scale ? scale->data() : nullptr;
auto* void_bias_data = valid_bias ? bias->data() : nullptr;
auto x_dtype = x.dtype();
auto y_dtype = y->dtype();
DataType scale_bias_dtype;
if (valid_scale) {
scale_bias_dtype = scale->dtype();
if (valid_bias) {
PADDLE_ENFORCE_EQ(scale->dtype(),
bias->dtype(),
common::errors::InvalidArgument(
"This Scale and Bias of layer_norm op "
"should have the same data type."));
}
} else {
scale_bias_dtype = valid_bias ? bias->dtype() : x_dtype;
}
bool is_scale_bias_same_dtype_with_x = x_dtype == scale_bias_dtype;
if (!is_scale_bias_same_dtype_with_x) {
PADDLE_ENFORCE_EQ(scale_bias_dtype,
CppTypeToDataType<U>::Type(),
common::errors::InvalidArgument(
"Unsupported data type of Scale and Bias"));
}
auto matrix_dim = common::flatten_to_2d(x_dims, begin_norm_axis);
int64_t batch_size = matrix_dim[0];
// TODO(large-tensor): generic kernel launch uses uint32 grid dim
PADDLE_ENFORCE_LE_UINT32_MAX(batch_size,
"Kernel launch requires uint32 for grid dim");
int64_t feature_size = matrix_dim[1];
auto stream = dev_ctx.stream();
auto place = x.place();
#define PADDLE_LAUNCH_LAYERNORM_FWD(ScaleBiasT, IsScaleBiasSameDTypeWithX) \
do { \
switch (funcs::GetDesiredBlockDim(feature_size)) { \
FIXED_BLOCK_DIM_CASE( \
funcs::LayerNormForward<T, U, kBlockDim, IsScaleBiasSameDTypeWithX> \
<<<static_cast<uint32_t>(batch_size), kBlockDim, 0, stream>>>( \
x_data, \
static_cast<const ScaleBiasT*>(void_scale_data), \
static_cast<const ScaleBiasT*>(void_bias_data), \
y_data, \
mean_data, \
var_data, \
epsilon, \
feature_size)); \
default: \
PADDLE_THROW(common::errors::InvalidArgument( \
"Product from begin_norm_axis to end must be larger than 1")); \
break; \
} \
} while (0)
#define PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, feature_size) \
case (feature_size): { \
constexpr int WARPS_N = feature_size < 1024 ? 1 : (feature_size / 1024); \
constexpr int WARPS_M = 4 / WARPS_N; \
const int THREADS_PER_WARP = 32; \
const int BYTES_PER_LDG = 16; \
const int VecSize = BYTES_PER_LDG / sizeof(T); \
const int THREADS_PER_CTA = WARPS_N * THREADS_PER_WARP * WARPS_M; \
const int ROWS_PER_CTA = WARPS_M; \
const int64_t grid = (batch_size + ROWS_PER_CTA - 1) / ROWS_PER_CTA; \
PADDLE_ENFORCE_LE_UINT32_MAX(grid, "layer_norm fast v1 grid"); \
PADDLE_ENFORCE_LE_INT_MAX(batch_size, "layer_norm fast v1 batch_size"); \
funcs::fast_ln_v1::fast_ln_v1_fwd_kernel<T, \
U, \
ScaleT, \
VecSize, \
WARPS_M, \
WARPS_N, \
BYTES_PER_LDG, \
feature_size> \
<<<static_cast<uint32_t>(grid), THREADS_PER_CTA, 0, stream>>>( \
static_cast<int>(batch_size), \
feature_size, \
epsilon, \
x_data, \
static_cast<const ScaleT*>(void_scale_data), \
static_cast<const ScaleT*>(void_bias_data), \
mean_data, \
var_data, \
y_data); \
} break
#define PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD(ScaleT) \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 768); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 1024); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 1280); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 1536); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 1792); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 2048); \
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD_BASE(ScaleT, 4096)
auto compute_dtype = CppTypeToDataType<U>::Type();
auto kernel_variant = LayerNormKernelDispatch(scale_bias_dtype,
x_dtype,
y_dtype,
compute_dtype,
feature_size,
x.numel(),
scale,
bias);
switch (kernel_variant) {
#if defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP) && !defined(_WIN32)
case LayerNormKernelVariant::FAST_LN_V2: {
uint32_t hidden_size = static_cast<uint32_t>(feature_size);
funcs::fast_ln_v2::LaunchNormFwd<T, Context>(dev_ctx,
stream,
place,
x_data,
void_scale_data,
void_bias_data,
y_data,
mean_data,
var_data,
scale_bias_dtype,
x_dtype,
y_dtype,
compute_dtype,
hidden_size,
batch_size,
feature_size,
epsilon);
break;
}
#endif
case LayerNormKernelVariant::FAST_LN_V1:
if (is_scale_bias_same_dtype_with_x) {
switch (feature_size) {
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD(T);
default:
break;
}
} else {
switch (feature_size) {
PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD(U);
default:
break;
}
}
break;
case LayerNormKernelVariant::GENERIC:
default:
#ifdef PADDLE_WITH_CUDA
if ((x_dtype == scale_bias_dtype) &&
(FLAGS_use_accuracy_compatible_kernel ||
(!isPowerOfTwo(feature_size) && feature_size > 1024))) {
LayerNormFwdCompatKernel<T, Context>(
dev_ctx,
x_data,
valid_scale ? static_cast<const T*>(void_scale_data) : nullptr,
valid_bias ? static_cast<const T*>(void_bias_data) : nullptr,
epsilon,
batch_size,
feature_size,
y_data,
mean_data,
var_data);
} else if (FLAGS_use_fast_math && feature_size <= 1024 &&
(!std::is_same<T, int8_t>::value)) {
// WarpShuffle intrinsics is involved in LaunchLayerNormKernel.
LaunchLayerNormKernel<Context, T, U>(dev_ctx,
x_data,
y_data,
void_scale_data,
void_bias_data,
mean_data,
var_data,
epsilon,
batch_size,
feature_size,
valid_scale,
valid_bias,
is_scale_bias_same_dtype_with_x);
} else {
#endif
if (is_scale_bias_same_dtype_with_x) {
PADDLE_LAUNCH_LAYERNORM_FWD(T, true);
} else {
PADDLE_LAUNCH_LAYERNORM_FWD(U, false);
}
#ifdef PADDLE_WITH_CUDA
}
#endif
break;
}
#undef PADDLE_LAUNCH_LAYERNORM_FWD
#undef PADDLE_LAUNCH_FAST_LAYERNORM_V1_FWD
}
#ifdef _WIN32
template PADDLE_API void LayerNormKernel<float, GPUContext>(
const GPUContext& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& scale_opt,
const optional<DenseTensor>& bias_opt,
double epsilon,
int begin_norm_axis,
DenseTensor* y,
DenseTensor* mean,
DenseTensor* var);
template PADDLE_API void LayerNormKernel<dtype::float16, GPUContext>(
const GPUContext& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& scale_opt,
const optional<DenseTensor>& bias_opt,
double epsilon,
int begin_norm_axis,
DenseTensor* y,
DenseTensor* mean,
DenseTensor* var);
template PADDLE_API void LayerNormKernel<double, GPUContext>(
const GPUContext& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& scale_opt,
const optional<DenseTensor>& bias_opt,
double epsilon,
int begin_norm_axis,
DenseTensor* y,
DenseTensor* mean,
DenseTensor* var);
#endif
} // namespace phi
#ifdef PADDLE_WITH_HIP
// MIOPEN do not support double
PD_REGISTER_KERNEL(
layer_norm, GPU, ALL_LAYOUT, phi::LayerNormKernel, float, phi::float16) {
kernel->OutputAt(1).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(2).SetDataType(phi::DataType::UNDEFINED);
}
#elif CUDNN_VERSION_MIN(8, 1, 0)
PD_REGISTER_KERNEL(layer_norm,
GPU,
ALL_LAYOUT,
phi::LayerNormKernel,
float,
double,
phi::float16,
phi::bfloat16) {
kernel->OutputAt(1).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(2).SetDataType(phi::DataType::UNDEFINED);
}
#else
PD_REGISTER_KERNEL(layer_norm,
GPU,
ALL_LAYOUT,
phi::LayerNormKernel,
float,
double,
phi::float16) {
kernel->OutputAt(1).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(2).SetDataType(phi::DataType::UNDEFINED);
}
#endif