// 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. /* * Copyright (c) 2022-2023, NVIDIA CORPORATION. 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/apply_per_channel_scale_kernel.h" #include #include #include #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/common/amp_type_traits.h" #include "paddle/phi/common/datatype_traits.h" #include "paddle/phi/core/kernel_registry.h" namespace phi { namespace { #ifdef PADDLE_WITH_CUDA template struct CUDA_HALF_2_TYPE_TARIS {}; template <> struct CUDA_HALF_2_TYPE_TARIS { using type = half2; }; #ifdef PADDLE_CUDA_BF16 template <> struct CUDA_HALF_2_TYPE_TARIS<__nv_bfloat16> { using type = __nv_bfloat162; }; #endif template struct HalfMul2 {}; template <> struct HalfMul2 { static __device__ __forceinline__ half2 apply(const half2& x, const half2& y) { return __hmul2(x, y); } }; #ifdef PADDLE_CUDA_BF16 template <> struct HalfMul2<__nv_bfloat162> { static __device__ __forceinline__ __nv_bfloat162 apply(const __nv_bfloat162& x, const __nv_bfloat162& y) { #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800)) return __hmul2(x, y); #else float fxl, fxh, fyl, fyh; fxl = __low2float(x); fxh = __high2float(x); fyl = __low2float(y); fyh = __high2float(y); return __floats2bfloat162_rn(fxl * fyl, fxh * fyh); #endif } }; #endif template __global__ void apply_per_channel_scale( const T* act, const T* scales, int rows, int cols, T* out) { using HALF_2_TYPE = typename CUDA_HALF_2_TYPE_TARIS::type; static constexpr int kElems = sizeof(AccessType) / sizeof(T); T scale[kElems], act_vec[kElems]; int64_t col_offset = static_cast(blockIdx.x) * static_cast(blockDim.x) + static_cast(threadIdx.x); int row_offset = blockIdx.y; if (col_offset * kElems >= cols || row_offset * kProcessRows >= rows) return; act += row_offset * kProcessRows * cols; out += row_offset * kProcessRows * cols; *reinterpret_cast(scale) = reinterpret_cast(scales)[col_offset]; #pragma unroll for (int i = 0; i < kProcessRows; ++i) { *reinterpret_cast(act_vec) = reinterpret_cast(act + i * cols)[col_offset]; if constexpr (kElems % 2 == 0 && (std::is_same::value #ifdef PADDLE_CUDA_BF16 || std::is_same::value #endif )) { #pragma unroll for (int j = 0; j < kElems; j += 2) { *reinterpret_cast(act_vec + j) = HalfMul2::apply( *reinterpret_cast(act_vec + j), *reinterpret_cast(scale + j)); } } else { #pragma unroll for (int j = 0; j < kElems; ++j) { act_vec[j] *= scale[j]; } } reinterpret_cast(out + i * cols)[col_offset] = *reinterpret_cast(act_vec); } } template void apply_per_channel_scale_launcher(const T* act, const T* scales, int rows, int cols, T* out, cudaStream_t stream = 0) { static constexpr int kElems = sizeof(AccessType) / sizeof(T); dim3 block(128); dim3 grid((cols / kElems + block.x - 1) / block.x, (rows + kProcessRows - 1) / kProcessRows); apply_per_channel_scale <<>>(act, scales, rows, cols, out); } } // namespace #endif template void ApplyPerChannelScaleKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& scales, DenseTensor* out) { #ifdef PADDLE_WITH_CUDA using DataType = typename PDDataTypeTraits::DataType; int64_t rows = x.dims()[0]; int64_t cols = x.dims()[1]; int64_t elems = rows * cols; const T* x_data = x.data(); const T* scales_data = scales.data(); T* out_data = dev_ctx.template Alloc(out); if (elems < 2048 * 2048) { apply_per_channel_scale_launcher( reinterpret_cast(x_data), reinterpret_cast(scales_data), rows, cols, reinterpret_cast(out_data), dev_ctx.stream()); } else if (elems < 4096 * 4096) { apply_per_channel_scale_launcher( reinterpret_cast(x_data), reinterpret_cast(scales_data), rows, cols, reinterpret_cast(out_data), dev_ctx.stream()); } else if (elems < 8192 * 8192) { apply_per_channel_scale_launcher( reinterpret_cast(x_data), reinterpret_cast(scales_data), rows, cols, reinterpret_cast(out_data), dev_ctx.stream()); } else { PADDLE_ENFORCE_LE_INT_MAX(rows, "rows"); PADDLE_ENFORCE_LE_INT_MAX(cols, "cols"); apply_per_channel_scale_launcher( reinterpret_cast(x_data), reinterpret_cast(scales_data), rows, cols, reinterpret_cast(out_data), dev_ctx.stream()); } #endif } } // namespace phi PD_REGISTER_KERNEL(apply_per_channel_scale, GPU, ALL_LAYOUT, phi::ApplyPerChannelScaleKernel, phi::float16, phi::bfloat16) {}