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

416 lines
16 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/funcs/math/bert_encoder_functor.h"
#include <algorithm>
#include <type_traits>
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/funcs/math_cuda_utils.h"
namespace phi {
namespace math {
// NOTE(chenfeiyu): explicitly use operator+ for float2
// since float2 is not in namespace phi::funcs, ADL won't help
using funcs::operator+;
template <typename T>
__device__ __forceinline__ T local_rsqrt(T num) {
return rsqrt(static_cast<float>(num));
}
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
__device__ __forceinline__ half local_rsqrt(half num) { return hrsqrt(num); }
#endif
template <typename T, int TPB>
__device__ inline void LayerNormSmall(T val,
const funcs::kvp<T> &thread_data,
const int ld,
const int idx,
const T *bias,
const T *scale,
T *output,
T eps) {
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
__shared__ typename BlockReduce::TempStorage temp_storage;
__shared__ T mu; // mean
__shared__ T rsigma; // 1 / std.dev.
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
if (threadIdx.x == 0) {
mu = sum_kv.key;
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
}
__syncthreads();
if (threadIdx.x < ld) {
const T g(scale[threadIdx.x]);
const T b(bias[threadIdx.x]);
output[idx] = g * (val - mu) * rsigma + b;
}
}
template <typename T, int TPB>
__device__ inline void LayerNorm(const funcs::kvp<T> &thread_data,
const int ld,
const int offset,
const T *bias,
const T *scale,
T *output,
T eps) {
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
__shared__ typename BlockReduce::TempStorage temp_storage;
__shared__ T mu; // mean
__shared__ T rsigma; // 1 / std.dev.
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
if (threadIdx.x == 0) {
mu = sum_kv.key;
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
}
__syncthreads();
for (int i = threadIdx.x; i < ld; i += TPB) {
const int idx = offset + i;
const T val = output[idx];
const T g(scale[i]);
const T b(bias[i]);
output[idx] = g * (val - mu) * rsigma + b;
}
}
template <typename T, typename T2, int TPB>
__device__ inline void LayerNorm2(const funcs::kvp<T> &thread_data,
const int ld,
const int offset,
const T2 *bias,
const T2 *scale,
T2 *output,
T eps) {
using BlockReduce = cub::BlockReduce<funcs::kvp<T>, TPB>;
__shared__ typename BlockReduce::TempStorage temp_storage;
__shared__ T mu; // mean
__shared__ T rsigma; // 1 / std.dev.
const auto sum_kv = BlockReduce(temp_storage).Reduce(thread_data, cub::Sum());
if (threadIdx.x == 0) {
mu = sum_kv.key;
rsigma = local_rsqrt(sum_kv.value - mu * mu + eps);
}
__syncthreads();
for (int i = threadIdx.x; i < ld; i += TPB) {
const int idx = offset + i;
T2 val = output[idx];
const T2 g = scale[i];
const T2 b = bias[i];
val.x = T(g.x) * (val.x - mu) * rsigma + T(b.x);
val.y = T(g.y) * (val.y - mu) * rsigma + T(b.y);
output[idx] = val;
}
}
template <typename T, unsigned TPB>
__global__ void SkipLayerNormSmallKernel(int num,
int hidden,
const T *input1,
const T *input2,
T *output,
const T *scale,
const T *bias,
T eps) {
const T rld = T(1) / T(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<T> thread_data(0, 0);
const int idx = offset + threadIdx.x;
T val = 0;
if (threadIdx.x < hidden) {
val = input1[idx] + input2[idx];
const T rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<T>(rldval, rldval * val));
}
LayerNormSmall<T, TPB>(
val, thread_data, hidden, idx, bias, scale, output, eps);
}
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormSmallKernel
template <>
__global__ void SkipLayerNormSmallKernel<half, 32>(int num,
int hidden,
const half *input1,
const half *input2,
half *output,
const half *scale,
const half *bias,
half eps) {
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
const half rld = half(1) / half(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<half> thread_data(0, 0);
const int idx = offset + threadIdx.x;
half val = 0;
if (threadIdx.x < hidden) {
val = input1[idx] + input2[idx];
const half rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
}
LayerNormSmall<half, 32>(
val, thread_data, hidden, idx, bias, scale, output, eps);
#endif
}
template <>
__global__ void SkipLayerNormSmallKernel<half, 128>(int num,
int hidden,
const half *input1,
const half *input2,
half *output,
const half *scale,
const half *bias,
half eps) {
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
const half rld = half(1) / half(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<half> thread_data(0, 0);
const int idx = offset + threadIdx.x;
half val = 0;
if (threadIdx.x < hidden) {
val = input1[idx] + input2[idx];
const half rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
}
LayerNormSmall<half, 128>(
val, thread_data, hidden, idx, bias, scale, output, eps);
#endif
}
template <>
__global__ void SkipLayerNormSmallKernel<half, 384>(int num,
int hidden,
const half *input1,
const half *input2,
half *output,
const half *scale,
const half *bias,
half eps) {
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
const half rld = half(1) / half(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<half> thread_data(0, 0);
const int idx = offset + threadIdx.x;
half val = 0;
if (threadIdx.x < hidden) {
val = input1[idx] + input2[idx];
const half rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
}
LayerNormSmall<half, 384>(
val, thread_data, hidden, idx, bias, scale, output, eps);
#endif
}
#endif // @} End Half kernel: SkipLayerNormSmallKernel
template <typename T, unsigned TPB>
__global__ void SkipLayerNormKernel(int num,
int hidden,
const T *input1,
const T *input2,
T *output,
const T *scale,
const T *bias,
T eps) {
const T rld = T(1) / T(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<T> thread_data(0, 0);
for (int it = threadIdx.x; it < hidden; it += TPB) {
const int idx = offset + it;
const T val = input1[idx] + input2[idx];
const T rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<T>(rldval, rldval * val));
output[idx] = val;
}
LayerNorm<T, TPB>(thread_data, hidden, offset, bias, scale, output, eps);
}
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormKernel
template <>
__global__ void SkipLayerNormKernel<half, 256>(int num,
int hidden,
const half *input1,
const half *input2,
half *output,
const half *scale,
const half *bias,
half eps) {
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
const half rld = half(1) / half(hidden);
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<half> thread_data(0, 0);
for (int it = threadIdx.x; it < hidden; it += 256) {
const int idx = offset + it;
const half val = input1[idx] + input2[idx];
const half rldval = rld * val;
thread_data = pair_sum(thread_data, funcs::kvp<half>(rldval, rldval * val));
output[idx] = val;
}
LayerNorm<half, 256>(thread_data, hidden, offset, bias, scale, output, eps);
#endif
}
#endif // @} End Half kernel: SkipLayerNormKernel
template <typename T, typename T2, unsigned TPB>
__global__ void SkipLayerNormKernel2(int num,
int hidden,
const T2 *input1,
const T2 *input2,
T2 *output,
const T2 *scale,
const T2 *bias,
float eps) {
const T rld = T(0.5f / hidden); // because hidden is hidden/2
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<T> thread_data(0, 0);
for (int it = threadIdx.x; it < hidden; it += TPB) {
const int idx = offset + it;
const T2 val2 = input1[idx] + input2[idx];
thread_data =
pair_sum(thread_data,
funcs::kvp<T>(rld * (val2.x + val2.y),
rld * val2.x * val2.x + rld * val2.y * val2.y));
output[idx] = val2;
}
LayerNorm2<T, T2, TPB>(thread_data, hidden, offset, bias, scale, output, eps);
}
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#ifndef __HIPCC__ // @{ Half kernel: SkipLayerNormKernel2
template <>
__global__ void SkipLayerNormKernel2<half, half2, 256>(int num,
int hidden,
const half2 *input1,
const half2 *input2,
half2 *output,
const half2 *scale,
const half2 *bias,
float eps) {
#if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__)
const half rld = half(0.5f / hidden); // because hidden is hidden/2
const int offset = blockIdx.x * hidden;
cub::Sum pair_sum;
funcs::kvp<half> thread_data(0, 0);
for (int it = threadIdx.x; it < hidden; it += 256) {
const int idx = offset + it;
const half2 val2 = input1[idx] + input2[idx];
thread_data = pair_sum(
thread_data,
funcs::kvp<half>(rld * (val2.x + val2.y),
rld * val2.x * val2.x + rld * val2.y * val2.y));
output[idx] = val2;
}
LayerNorm2<half, half2, 256>(
thread_data, hidden, offset, bias, scale, output, eps);
#endif
}
#endif // @} End Half kernel: SkipLayerNormKernel2
template <typename T>
void SkipLayerNormFunctor<T>::operator()(const int num,
const int hidden,
const T *input1,
const T *input2,
const T *scale,
const T *bias,
T *output,
float eps,
gpuStream_t stream) {
int block = num / hidden;
if (hidden <= WARP_SIZE) {
const int threads = WARP_SIZE;
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
num, hidden, input1, input2, output, scale, bias, eps);
} else if (hidden <= 128) {
const int threads = 128;
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
num, hidden, input1, input2, output, scale, bias, eps);
} else if (hidden == 384) {
const int threads = 384;
SkipLayerNormSmallKernel<T, threads><<<block, threads, 0, stream>>>(
num, hidden, input1, input2, output, scale, bias, eps);
} else {
const int threads = 256;
if (hidden % 2 == 0) {
if (std::is_same<T, float>::value) {
SkipLayerNormKernel2<float, float2, threads>
<<<block, threads, 0, stream>>>(
num,
hidden / 2,
reinterpret_cast<const float2 *>(input1),
reinterpret_cast<const float2 *>(input2),
reinterpret_cast<float2 *>(output),
reinterpret_cast<const float2 *>(scale),
reinterpret_cast<const float2 *>(bias),
eps);
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#ifndef __HIPCC__
} else if (std::is_same<T, __half>::value) {
SkipLayerNormKernel2<__half, __half2, threads>
<<<block, threads, 0, stream>>>(
num,
hidden / 2,
reinterpret_cast<const __half2 *>(input1),
reinterpret_cast<const __half2 *>(input2),
reinterpret_cast<__half2 *>(output),
reinterpret_cast<const __half2 *>(scale),
reinterpret_cast<const __half2 *>(bias),
eps);
#endif
} else {
assert(false);
// should not be here
}
} else {
SkipLayerNormKernel<T, threads><<<block, threads, 0, stream>>>(
num, hidden, input1, input2, output, scale, bias, eps);
}
}
}
template class SkipLayerNormFunctor<float>;
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#if defined(PADDLE_WITH_CUDA)
template class SkipLayerNormFunctor<half>;
#endif
} // namespace math
} // namespace phi