// 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 #include #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 __device__ __forceinline__ T local_rsqrt(T num) { return rsqrt(static_cast(num)); } #if CUDA_ARCH_FP16_SUPPORTED(__CUDA_ARCH__) __device__ __forceinline__ half local_rsqrt(half num) { return hrsqrt(num); } #endif template __device__ inline void LayerNormSmall(T val, const funcs::kvp &thread_data, const int ld, const int idx, const T *bias, const T *scale, T *output, T eps) { using BlockReduce = cub::BlockReduce, 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 __device__ inline void LayerNorm(const funcs::kvp &thread_data, const int ld, const int offset, const T *bias, const T *scale, T *output, T eps) { using BlockReduce = cub::BlockReduce, 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 __device__ inline void LayerNorm2(const funcs::kvp &thread_data, const int ld, const int offset, const T2 *bias, const T2 *scale, T2 *output, T eps) { using BlockReduce = cub::BlockReduce, 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 __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 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(rldval, rldval * val)); } LayerNormSmall( 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(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 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(rldval, rldval * val)); } LayerNormSmall( val, thread_data, hidden, idx, bias, scale, output, eps); #endif } template <> __global__ void SkipLayerNormSmallKernel(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 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(rldval, rldval * val)); } LayerNormSmall( val, thread_data, hidden, idx, bias, scale, output, eps); #endif } template <> __global__ void SkipLayerNormSmallKernel(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 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(rldval, rldval * val)); } LayerNormSmall( val, thread_data, hidden, idx, bias, scale, output, eps); #endif } #endif // @} End Half kernel: SkipLayerNormSmallKernel template __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 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(rldval, rldval * val)); output[idx] = val; } LayerNorm(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(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 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(rldval, rldval * val)); output[idx] = val; } LayerNorm(thread_data, hidden, offset, bias, scale, output, eps); #endif } #endif // @} End Half kernel: SkipLayerNormKernel template __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 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(rld * (val2.x + val2.y), rld * val2.x * val2.x + rld * val2.y * val2.y)); output[idx] = val2; } LayerNorm2(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(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 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(rld * (val2.x + val2.y), rld * val2.x * val2.x + rld * val2.y * val2.y)); output[idx] = val2; } LayerNorm2( thread_data, hidden, offset, bias, scale, output, eps); #endif } #endif // @} End Half kernel: SkipLayerNormKernel2 template void SkipLayerNormFunctor::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<<>>( num, hidden, input1, input2, output, scale, bias, eps); } else if (hidden <= 128) { const int threads = 128; SkipLayerNormSmallKernel<<>>( num, hidden, input1, input2, output, scale, bias, eps); } else if (hidden == 384) { const int threads = 384; SkipLayerNormSmallKernel<<>>( num, hidden, input1, input2, output, scale, bias, eps); } else { const int threads = 256; if (hidden % 2 == 0) { if (std::is_same::value) { SkipLayerNormKernel2 <<>>( num, hidden / 2, reinterpret_cast(input1), reinterpret_cast(input2), reinterpret_cast(output), reinterpret_cast(scale), reinterpret_cast(bias), eps); // HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake #ifndef __HIPCC__ } else if (std::is_same::value) { SkipLayerNormKernel2<__half, __half2, threads> <<>>( num, hidden / 2, reinterpret_cast(input1), reinterpret_cast(input2), reinterpret_cast<__half2 *>(output), reinterpret_cast(scale), reinterpret_cast(bias), eps); #endif } else { assert(false); // should not be here } } else { SkipLayerNormKernel<<>>( num, hidden, input1, input2, output, scale, bias, eps); } } } template class SkipLayerNormFunctor; // HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake #if defined(PADDLE_WITH_CUDA) template class SkipLayerNormFunctor; #endif } // namespace math } // namespace phi