/* 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_only_linear_kernel.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/common/datatype_traits.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/funcs/weight_only_gemv.h" #if defined(PADDLE_WITH_CUTLASS) #include "paddle/phi/kernels/fusion/cutlass/cutlass_kernels/fpA_intB_gemm/fpA_intB_gemm_template.h" #endif namespace phi { template void WeightOnlyLinearKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& weight, const optional& bias, const DenseTensor& weight_scale, const std::string& weight_dtype, const int32_t arch, const int32_t group_size, DenseTensor* out) { #if defined(PADDLE_WITH_CUTLASS) PADDLE_ENFORCE_EQ( ((arch == 70) || (arch == 75) || (arch == 80) || (arch == 86) || (arch == 89) || (arch == 90) || (arch == 100)), true, common::errors::InvalidArgument( "Currently, arch only support 70, 75, 80, 86, 89, 90, 100.")); #else PADDLE_THROW(common::errors::Unimplemented( "Please compile with cutlass to make cutlass available")); #endif dev_ctx.template Alloc(out); Full(dev_ctx, out->dims(), 0, out); if (out->numel() == 0 || x.numel() == 0 || weight.numel() == 0) { return; } const T* x_data = x.data(); const int8_t* weight_data = weight.data(); const T* bias_data = bias ? bias.get().data() : nullptr; const T* weight_scale_data = weight_scale.data(); T* out_data = out->data(); const auto x_dims = x.dims(); const auto w_dims = weight.dims(); int n = group_size > 0 ? weight_scale.dims()[1] : weight_scale.dims()[0]; int k = w_dims[1]; int m = x.numel() / k; // m > 3: run gemm. if (m > 3 || (arch == 70)) { /* Note(Zhengzekang): If using arch = 70, we always dispatch to weightonly Gemm, we havenot support sm70 weightonly gemv, because sm70 weight layout is RowMajor. */ #if defined(PADDLE_WITH_CUTLASS) if (weight_dtype == "int8") { auto mixed_gemm_runner = CutlassFpAIntBGemmRunner::DataType, uint8_t>(); int mixgemm_max_size = std::max(m, k); DenseTensor mixgemm_workspace; int64_t mixgemm_workspace_size_bytes = mixed_gemm_runner.getWorkspaceSize( m, mixgemm_max_size, mixgemm_max_size); mixgemm_workspace_size_bytes = 100 * 1024 * 1024; mixgemm_workspace.Resize({mixgemm_workspace_size_bytes}); dev_ctx.template Alloc(&mixgemm_workspace); char* mixgemm_workspace_data = reinterpret_cast(mixgemm_workspace.data()); if (bias_data) { mixed_gemm_runner.gemm_bias_act( reinterpret_cast::DataType*>( x_data), reinterpret_cast(weight_data), reinterpret_cast::DataType*>( weight_scale_data), reinterpret_cast::DataType*>( bias_data), reinterpret_cast::DataType*>(out_data), m, n, k, group_size, "none", mixgemm_workspace_data, mixgemm_workspace_size_bytes, dev_ctx.stream()); } else { mixed_gemm_runner.gemm( reinterpret_cast::DataType*>( x_data), reinterpret_cast(weight_data), reinterpret_cast::DataType*>( weight_scale_data), reinterpret_cast::DataType*>(out_data), m, n, k, group_size, mixgemm_workspace_data, mixgemm_workspace_size_bytes, dev_ctx.stream()); } } else { auto mixed_gemm_runner = CutlassFpAIntBGemmRunner::DataType, cutlass::uint4b_t>(); int mixgemm_max_size = std::max(m, k); DenseTensor mixgemm_workspace; int64_t mixgemm_workspace_size_bytes = mixed_gemm_runner.getWorkspaceSize( m, mixgemm_max_size, mixgemm_max_size); mixgemm_workspace_size_bytes = 100 * 1024 * 1024; mixgemm_workspace.Resize({mixgemm_workspace_size_bytes}); dev_ctx.template Alloc(&mixgemm_workspace); char* mixgemm_workspace_data = reinterpret_cast(mixgemm_workspace.data()); if (bias_data) { mixed_gemm_runner.gemm_bias_act( reinterpret_cast::DataType*>( x_data), reinterpret_cast(weight_data), reinterpret_cast::DataType*>( weight_scale_data), reinterpret_cast::DataType*>( bias_data), reinterpret_cast::DataType*>(out_data), m, n, k, group_size, "none", mixgemm_workspace_data, mixgemm_workspace_size_bytes, dev_ctx.stream()); } else { mixed_gemm_runner.gemm( reinterpret_cast::DataType*>( x_data), reinterpret_cast(weight_data), reinterpret_cast::DataType*>( weight_scale_data), reinterpret_cast::DataType*>(out_data), m, n, k, group_size, mixgemm_workspace_data, mixgemm_workspace_size_bytes, dev_ctx.stream()); } } #else PADDLE_THROW(common::errors::Unimplemented( "Please compile with cutlass to make cutlass available")); #endif } else { // m <= 3: gemv if (weight_dtype == "int8") { WeightOnlyGemvWrapper( dev_ctx, x_data, weight_data, bias_data, weight_scale_data, m, n, k, group_size, "int8", group_size > 0 ? "group_wise" : "per_channel", "None", out->data()); } else if (weight_dtype == "int4") { WeightOnlyGemvWrapper( dev_ctx, x_data, weight_data, bias_data, weight_scale_data, m, n, k, group_size, "int4", group_size > 0 ? "group_wise" : "per_channel", "None", out->data()); } } } } // namespace phi PD_REGISTER_KERNEL(weight_only_linear, GPU, ALL_LAYOUT, phi::WeightOnlyLinearKernel, phi::float16, phi::bfloat16) {}