// 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. #pragma once #include #include "paddle/phi/backends/gpu/gpu_info.h" #include "paddle/phi/backends/gpu/gpu_launch_config.h" #include "paddle/phi/kernels/funcs/aligned_vector.h" #include "paddle/phi/kernels/funcs/quant_dequant.h" namespace phi { namespace fusion { using backends::gpu::GpuLaunchConfig; constexpr int DequantKernelVecSize = 4; template __forceinline__ __device__ int8_t quant_helper(const T input, const float scale, const int round_type, const float max_bound, const float min_bound) { float quant_value = max_bound * scale * static_cast(input); if (round_type == 0) { quant_value = static_cast(funcs::roundWithTiesToEven(quant_value)); } else { quant_value = static_cast(round(quant_value)); } quant_value = quant_value > max_bound ? max_bound : quant_value; quant_value = quant_value < min_bound ? min_bound : quant_value; return static_cast(quant_value); } template __global__ void QuantKernel(const T* input, char4* output, const float scale, const int m, const int n, const int round_type, const float max_bound, const float min_bound) { int64_t n_id = (static_cast(blockIdx.x) * static_cast(blockDim.x) + static_cast(threadIdx.x)) << 2; int64_t m_id = static_cast(blockIdx.y) * static_cast(blockDim.y) + static_cast(threadIdx.y); bool check = ((m_id < m) && (n_id < n)); if (check) { char4 tmp; tmp.x = quant_helper( input[m_id * n + n_id], scale, round_type, max_bound, min_bound); tmp.y = quant_helper( input[m_id * n + n_id + 1], scale, round_type, max_bound, min_bound); tmp.z = quant_helper( input[m_id * n + n_id + 2], scale, round_type, max_bound, min_bound); tmp.w = quant_helper( input[m_id * n + n_id + 3], scale, round_type, max_bound, min_bound); output[(m_id * n + n_id) >> 2] = tmp; } } template void LaunchQuantKernel(const T* input, int8_t* output, const float scale, const int m, const int n, const int round_type, const float max_bound, const float min_bound, gpuStream_t stream) { // TODO(minghaoBD): optimize the kennel launch times when m==1 or n==1 #ifdef PADDLE_WITH_HIP dim3 grid(((n >> 2) + 63) / 64, (m + 7) / 8); dim3 block(64, 8); #else dim3 grid(((n >> 2) + 31) / 32, (m + 31) / 32); dim3 block(32, 32); #endif QuantKernel<<>>(input, (char4*)output, // NOLINT scale, m, n, round_type, max_bound, min_bound); } template __global__ void DequantKernel(T* output, const int32_t* input, const int m, // batch size const int n, // hidden const float quant_in_scale, const float* dequant_out_scale_data) { int numel = m * n; int stride = blockDim.x * gridDim.x * VecSize; int64_t idx = (static_cast(blockIdx.x) * static_cast(blockDim.x) + static_cast(threadIdx.x)) * VecSize; int col_id = idx % n; AlignedVector in_vec; AlignedVector out_scale_vec; AlignedVector out_vec; for (; idx < numel; idx += stride) { Load(input + idx, &in_vec); Load(dequant_out_scale_data + col_id, &out_scale_vec); #pragma unroll for (int i = 0; i < VecSize; ++i) { out_vec[i] = static_cast(static_cast(in_vec[i]) * out_scale_vec[i]); } Store(out_vec, output + idx); } } template void LaunchDequantKernel(const int32_t* input, T* output, const int m, // m const int n, // n gpuStream_t stream, GpuLaunchConfig* gpu_config, const float quant_in_scale, const float* dequant_out_scale_data) { DequantKernel <<block_per_grid, gpu_config->thread_per_block, 0, stream>>>( output, input, m, n, quant_in_scale, dequant_out_scale_data); } } // namespace fusion } // namespace phi