Files
paddlepaddle--paddle/paddle/phi/kernels/gpu/weight_dequantize_kernel.cu
T
2026-07-13 12:40:42 +08:00

488 lines
19 KiB
Plaintext

/* 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. */
#include "paddle/phi/kernels/weight_dequantize_kernel.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/transpose_kernel.h"
#if defined(PADDLE_WITH_CUTLASS)
#include "paddle/phi/kernels/funcs/weight_dequant_functor.h"
#endif
#ifdef PADDLE_WITH_HIP
#include "paddle/phi/common/datatype_traits.h"
#include "paddle/phi/kernels/funcs/aligned_vector.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/matmul_kernel.h"
#endif
namespace phi {
#ifdef PADDLE_WITH_HIP
#define NUMPERTHREAD 16
template <typename T, int Size>
struct alignas(sizeof(T) * Size) aligned_vector {
T val[Size];
};
using int8_8 = aligned_vector<int8_t, 8>;
template <typename T>
__device__ inline aligned_vector<T, 8> i82h_convert8(int8_8 signed_chars,
T scale) {
aligned_vector<T, 8> halves;
halves.val[0] = static_cast<T>(static_cast<float>(signed_chars.val[0]) *
static_cast<float>(scale));
halves.val[1] = static_cast<T>(static_cast<float>(signed_chars.val[1]) *
static_cast<float>(scale));
halves.val[2] = static_cast<T>(static_cast<float>(signed_chars.val[2]) *
static_cast<float>(scale));
halves.val[3] = static_cast<T>(static_cast<float>(signed_chars.val[3]) *
static_cast<float>(scale));
halves.val[4] = static_cast<T>(static_cast<float>(signed_chars.val[4]) *
static_cast<float>(scale));
halves.val[5] = static_cast<T>(static_cast<float>(signed_chars.val[5]) *
static_cast<float>(scale));
halves.val[6] = static_cast<T>(static_cast<float>(signed_chars.val[6]) *
static_cast<float>(scale));
halves.val[7] = static_cast<T>(static_cast<float>(signed_chars.val[7]) *
static_cast<float>(scale));
return halves;
}
template <>
__device__ inline aligned_vector<half, 8> i82h_convert8(int8_8 signed_chars,
half scale) {
aligned_vector<half, 8> halves;
halves.val[0] = __float2half(static_cast<float>(signed_chars.val[0]) *
__half2float(scale));
halves.val[1] = __float2half(static_cast<float>(signed_chars.val[1]) *
__half2float(scale));
halves.val[2] = __float2half(static_cast<float>(signed_chars.val[2]) *
__half2float(scale));
halves.val[3] = __float2half(static_cast<float>(signed_chars.val[3]) *
__half2float(scale));
halves.val[4] = __float2half(static_cast<float>(signed_chars.val[4]) *
__half2float(scale));
halves.val[5] = __float2half(static_cast<float>(signed_chars.val[5]) *
__half2float(scale));
halves.val[6] = __float2half(static_cast<float>(signed_chars.val[6]) *
__half2float(scale));
halves.val[7] = __float2half(static_cast<float>(signed_chars.val[7]) *
__half2float(scale));
return halves;
}
struct uint4_2 {
uint8_t data;
explicit uint4_2(uint8_t x = 0, uint8_t y = 0) {
setX(x);
setY(y);
}
__host__ __device__ uint8_t getX() const {
return data & 0x0F; // lower 4 bits
}
__host__ __device__ uint8_t getY() const {
return (data >> 4) & 0x0F; // upper 4 bits
}
__host__ __device__ void setX(uint8_t x) {
data = (data & 0xF0) | (x & 0x0F); // set the lower 4 bits
}
__host__ __device__ void setY(uint8_t y) {
data = (data & 0x0F) | ((y & 0x0F) << 4); // set the upper 4 bits
}
};
using uint4_2_8 = aligned_vector<uint4_2, 8>;
template <typename T>
__device__ inline aligned_vector<aligned_vector<T, 8>, 2> i42h_convert8_2(
uint4_2_8 signed_chars, T scale_0, T scale_1) {
aligned_vector<aligned_vector<T, 8>, 2> halves;
aligned_vector<T, 8> halves_0;
aligned_vector<T, 8> halves_1;
halves_0.val[0] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[0].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[1] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[1].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[2] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[2].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[3] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[3].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[4] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[4].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[5] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[5].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[6] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[6].getX() - 8) *
static_cast<float>(scale_0));
halves_0.val[7] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[7].getX() - 8) *
static_cast<float>(scale_0));
halves_1.val[0] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[0].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[1] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[1].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[2] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[2].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[3] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[3].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[4] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[4].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[5] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[5].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[6] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[6].getY() - 8) *
static_cast<float>(scale_1));
halves_1.val[7] = static_cast<T>(
static_cast<float>((int8_t)signed_chars.val[7].getY() - 8) *
static_cast<float>(scale_1));
halves.val[0] = halves_0;
halves.val[1] = halves_1;
return halves;
}
template <>
__device__ inline aligned_vector<aligned_vector<half, 8>, 2> i42h_convert8_2(
uint4_2_8 signed_chars, half scale_0, half scale_1) {
aligned_vector<aligned_vector<half, 8>, 2> halves;
aligned_vector<half, 8> halves_0;
aligned_vector<half, 8> halves_1;
halves_0.val[0] =
__float2half(static_cast<float>((int8_t)signed_chars.val[0].getX() - 8) *
__half2float(scale_0));
halves_0.val[1] =
__float2half(static_cast<float>((int8_t)signed_chars.val[1].getX() - 8) *
__half2float(scale_0));
halves_0.val[2] =
__float2half(static_cast<float>((int8_t)signed_chars.val[2].getX() - 8) *
__half2float(scale_0));
halves_0.val[3] =
__float2half(static_cast<float>((int8_t)signed_chars.val[3].getX() - 8) *
__half2float(scale_0));
halves_0.val[4] =
__float2half(static_cast<float>((int8_t)signed_chars.val[4].getX() - 8) *
__half2float(scale_0));
halves_0.val[5] =
__float2half(static_cast<float>((int8_t)signed_chars.val[5].getX() - 8) *
__half2float(scale_0));
halves_0.val[6] =
__float2half(static_cast<float>((int8_t)signed_chars.val[6].getX() - 8) *
__half2float(scale_0));
halves_0.val[7] =
__float2half(static_cast<float>((int8_t)signed_chars.val[7].getX() - 8) *
__half2float(scale_0));
halves_1.val[0] =
__float2half(static_cast<float>((int8_t)signed_chars.val[0].getY() - 8) *
__half2float(scale_1));
halves_1.val[1] =
__float2half(static_cast<float>((int8_t)signed_chars.val[1].getY() - 8) *
__half2float(scale_1));
halves_1.val[2] =
__float2half(static_cast<float>((int8_t)signed_chars.val[2].getY() - 8) *
__half2float(scale_1));
halves_1.val[3] =
__float2half(static_cast<float>((int8_t)signed_chars.val[3].getY() - 8) *
__half2float(scale_1));
halves_1.val[4] =
__float2half(static_cast<float>((int8_t)signed_chars.val[4].getY() - 8) *
__half2float(scale_1));
halves_1.val[5] =
__float2half(static_cast<float>((int8_t)signed_chars.val[5].getY() - 8) *
__half2float(scale_1));
halves_1.val[6] =
__float2half(static_cast<float>((int8_t)signed_chars.val[6].getY() - 8) *
__half2float(scale_1));
halves_1.val[7] =
__float2half(static_cast<float>((int8_t)signed_chars.val[7].getY() - 8) *
__half2float(scale_1));
halves.val[0] = halves_0;
halves.val[1] = halves_1;
return halves;
}
template <typename T>
__global__ void int8_weight_only_dequant(int8_t* mat,
T* scales,
T* mat_res,
unsigned int k,
unsigned int k_iteration) {
unsigned int tid = threadIdx.x;
unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
int8_8* mat8 = reinterpret_cast<int8_8*>(mat);
aligned_vector<T, 8>* mat_res8 =
reinterpret_cast<aligned_vector<T, 8>*>(mat_res);
T scale = scales[row];
#pragma unroll
for (unsigned int iteration = 0; iteration < k_iteration; iteration++) {
unsigned int gidx = tid + iteration * blockDim.x;
unsigned int gdatax = NUMPERTHREAD / 8 * gidx;
#pragma unroll
for (unsigned int it = 0; it < NUMPERTHREAD / 8 / 2; it++) {
if (gdatax + 2 * it + 1 < k / 8) {
mat_res8[row * (k / 8) + gdatax + 2 * it] =
i82h_convert8(mat8[row * (k / 8) + gdatax + 2 * it], scale);
mat_res8[row * (k / 8) + gdatax + 2 * it + 1] =
i82h_convert8(mat8[row * (k / 8) + gdatax + 2 * it + 1], scale);
}
}
}
}
template <typename T>
__global__ void int8_weight_only_dequant(int8_t* mat,
T* scales,
T* mat_res,
unsigned int k,
unsigned int groupsize,
unsigned int k_iteration) {
unsigned int tid = threadIdx.x;
unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
int8_8* mat8 = reinterpret_cast<int8_8*>(mat);
aligned_vector<T, 8>* mat_res8 =
reinterpret_cast<aligned_vector<T, 8>*>(mat_res);
#pragma unroll
for (unsigned int iteration = 0; iteration < k_iteration; iteration++) {
unsigned int gidx = tid + iteration * blockDim.x;
unsigned int gdatax = NUMPERTHREAD / 8 * gidx;
T scale = scales[row * (k / groupsize) + gidx * NUMPERTHREAD / groupsize];
#pragma unroll
for (unsigned int it = 0; it < NUMPERTHREAD / 8 / 2; it++) {
if (gdatax + 2 * it + 1 < k / 8) {
mat_res8[row * (k / 8) + gdatax + 2 * it] =
i82h_convert8(mat8[row * (k / 8) + gdatax + 2 * it], scale);
mat_res8[row * (k / 8) + gdatax + 2 * it + 1] =
i82h_convert8(mat8[row * (k / 8) + gdatax + 2 * it + 1], scale);
}
}
}
}
template <typename T>
__global__ void int4_weight_only_dequant(uint4_2* mat,
T* scales,
T* mat_res,
unsigned int k,
unsigned int k_iteration) {
unsigned int tid = threadIdx.x;
unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
uint4_2_8* mat16 = reinterpret_cast<uint4_2_8*>(mat);
aligned_vector<T, 8>* mat_res8 =
reinterpret_cast<aligned_vector<T, 8>*>(mat_res);
aligned_vector<aligned_vector<T, 8>, 2> mat_res16;
T scale_0 = scales[2 * row];
T scale_1 = scales[2 * row + 1];
#pragma unroll
for (unsigned int iteration = 0; iteration < k_iteration; iteration++) {
unsigned int gidx = tid + iteration * blockDim.x;
unsigned int gdatax = NUMPERTHREAD / 8 * gidx;
#pragma unroll
for (unsigned int it = 0; it < NUMPERTHREAD / 8 / 2; it++) {
if (gdatax + 2 * it + 1 < k / 8) {
mat_res16 = i42h_convert8_2(
mat16[row * (k / 8) + gdatax + 2 * it], scale_0, scale_1);
mat_res8[2 * row * (k / 8) + gdatax + 2 * it] = mat_res16.val[0];
mat_res8[(2 * row + 1) * (k / 8) + gdatax + 2 * it] = mat_res16.val[1];
mat_res16 = i42h_convert8_2(
mat16[row * (k / 8) + gdatax + 2 * it + 1], scale_0, scale_1);
mat_res8[2 * row * (k / 8) + gdatax + 2 * it + 1] = mat_res16.val[0];
mat_res8[(2 * row + 1) * (k / 8) + gdatax + 2 * it + 1] =
mat_res16.val[1];
}
}
}
}
template <typename T>
__global__ void int4_weight_only_dequant(uint4_2* mat,
T* scales,
T* mat_res,
unsigned int k,
unsigned int groupsize,
unsigned int k_iteration) {
unsigned int tid = threadIdx.x;
unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
uint4_2_8* mat16 = reinterpret_cast<uint4_2_8*>(mat);
aligned_vector<T, 8>* mat_res8 =
reinterpret_cast<aligned_vector<T, 8>*>(mat_res);
aligned_vector<aligned_vector<T, 8>, 2> mat_res16;
#pragma unroll
for (unsigned int iteration = 0; iteration < k_iteration; iteration++) {
unsigned int gidx = tid + iteration * blockDim.x;
unsigned int gdatax = NUMPERTHREAD / 8 * gidx;
T scale_0 =
scales[2 * row * (k / groupsize) + gidx * NUMPERTHREAD / groupsize];
T scale_1 = scales[(2 * row + 1) * (k / groupsize) +
gidx * NUMPERTHREAD / groupsize];
#pragma unroll
for (unsigned int it = 0; it < NUMPERTHREAD / 8 / 2; it++) {
if (gdatax + 2 * it + 1 < k / 8) {
mat_res16 = i42h_convert8_2(
mat16[row * (k / 8) + gdatax + 2 * it], scale_0, scale_1);
mat_res8[2 * row * (k / 8) + gdatax + 2 * it] = mat_res16.val[0];
mat_res8[(2 * row + 1) * (k / 8) + gdatax + 2 * it] = mat_res16.val[1];
mat_res16 = i42h_convert8_2(
mat16[row * (k / 8) + gdatax + 2 * it + 1], scale_0, scale_1);
mat_res8[2 * row * (k / 8) + gdatax + 2 * it + 1] = mat_res16.val[0];
mat_res8[(2 * row + 1) * (k / 8) + gdatax + 2 * it + 1] =
mat_res16.val[1];
}
}
}
}
template <typename T, typename Context>
void WeightDequantize(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& scale,
const std::string& algo,
const int32_t group_size,
DenseTensor* out) {
using DataType = typename PDDataTypeTraits<T>::DataType;
int64_t n = scale.dims()[0];
int64_t k = x.dims()[1];
// TODO(large-tensor): CUDA grid dims not support int64
PADDLE_ENFORCE_LE_INT_MAX(n, "n");
PADDLE_ENFORCE_LE_INT_MAX(k, "k");
unsigned int grid_y = static_cast<unsigned int>(n);
PADDLE_ENFORCE_EQ(
(k % NUMPERTHREAD == 0),
true,
common::errors::InvalidArgument(
"Currently, WeightDequantize only support k % NUMPERTHREAD == 0."));
unsigned int block_dim_x = 256;
unsigned int kperblock = block_dim_x * NUMPERTHREAD;
unsigned int block_dim_y = 1;
unsigned int k_iteration =
k % kperblock == 0 ? k / kperblock : k / kperblock + 1;
dim3 grid(1, grid_y / block_dim_y);
dim3 block(block_dim_x, block_dim_y);
auto stream = dev_ctx.stream();
if (algo == "weight_only_int8" && group_size == -1) {
int8_weight_only_dequant<DataType><<<grid, block, 0, stream>>>(
const_cast<int8_t*>(x.data<int8_t>()),
const_cast<DataType*>(
reinterpret_cast<const DataType*>(scale.data<T>())),
reinterpret_cast<DataType*>(out->data<T>()),
k,
k_iteration);
} else if (algo == "weight_only_int8" && group_size > 0) {
int8_weight_only_dequant<DataType><<<grid, block, 0, stream>>>(
const_cast<int8_t*>(x.data<int8_t>()),
const_cast<DataType*>(
reinterpret_cast<const DataType*>(scale.data<T>())),
reinterpret_cast<DataType*>(out->data<T>()),
k,
group_size,
k_iteration);
} else if (algo == "weight_only_int4" && group_size == -1) {
grid.y /= 2;
int4_weight_only_dequant<DataType><<<grid, block, 0, stream>>>(
reinterpret_cast<uint4_2*>(const_cast<int8_t*>(x.data<int8_t>())),
const_cast<DataType*>(
reinterpret_cast<const DataType*>(scale.data<T>())),
reinterpret_cast<DataType*>(out->data<T>()),
k,
k_iteration);
} else if (algo == "weight_only_int4" && group_size > 0) {
grid.y /= 2;
int4_weight_only_dequant<DataType><<<grid, block, 0, stream>>>(
reinterpret_cast<uint4_2*>(const_cast<int8_t*>(x.data<int8_t>())),
const_cast<DataType*>(
reinterpret_cast<const DataType*>(scale.data<T>())),
reinterpret_cast<DataType*>(out->data<T>()),
k,
group_size,
k_iteration);
}
}
#endif
template <typename T, typename Context>
void WeightDequantizeKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& scale,
const std::string& algo,
int32_t group_size,
DenseTensor* out) {
#if defined(PADDLE_WITH_CUTLASS)
auto out_dims = out->dims();
dev_ctx.template Alloc<T>(out);
WeightDequantize<T, Context>(dev_ctx, x, scale, algo, true, group_size, out);
out->Resize({out_dims[1], out_dims[0]});
auto out_tmp = Transpose<T, Context>(dev_ctx, *out, {1, 0});
out->ShareDataWith(out_tmp);
#elif defined(PADDLE_WITH_HIP)
DenseTensor scale_trans(scale.type());
if (group_size > 0) {
scale_trans.Resize({scale.dims()[1], scale.dims()[0]});
dev_ctx.template Alloc<T>(&scale_trans);
std::vector<int> axis = {1, 0};
funcs::Transpose<Context, T, 2> trans;
trans(dev_ctx, scale, &scale_trans, axis);
}
auto out_dims = out->dims();
dev_ctx.template Alloc<T>(out);
WeightDequantize<T, Context>(
dev_ctx, x, group_size > 0 ? scale_trans : scale, algo, group_size, out);
out->Resize({out_dims[1], out_dims[0]});
auto out_tmp = Transpose<T, Context>(dev_ctx, *out, {1, 0});
out->ShareDataWith(out_tmp);
#else
PADDLE_THROW(
common::errors::PreconditionNotMet("Not compiled with WITH_CUTLASS=ON"));
#endif
}
} // namespace phi
PD_REGISTER_KERNEL(weight_dequantize,
GPU,
ALL_LAYOUT,
phi::WeightDequantizeKernel,
phi::float16,
phi::bfloat16) {}