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

378 lines
14 KiB
Plaintext

// 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/fused_seqpool_cvm_kernel.h"
#include <string>
#include "paddle/common/enforce.h"
#include "paddle/phi/backends/gpu/cuda/cuda_graph_with_memory_pool.h"
#include "paddle/phi/backends/gpu/gpu_info.h"
#include "paddle/phi/backends/gpu/gpu_launch_config.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/mixed_vector.h"
namespace phi {
namespace fusion {
#define CUDA_KERNEL_LOOP(i, n) \
for (auto i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \
i += blockDim.x * gridDim.x)
// normal
template <typename T>
__global__ void FusedSeqpoolKernelNormal(const size_t N,
T **input_values,
T **seqpool_output_values,
size_t **lods_values,
const int batch_size,
const int embedding_size,
const float pad_value) {
CUDA_KERNEL_LOOP(i, N) {
int key = i / embedding_size;
int offset = i % embedding_size;
int x = key / batch_size; // slot id
int y = key % batch_size; // ins id
auto &start = *(lods_values[x] + y);
auto &end = *(lods_values[x] + y + 1);
T val = static_cast<T>(pad_value);
for (auto k = start; k < end; ++k) {
val += *(input_values[x] + k * embedding_size + offset);
}
*(seqpool_output_values[x] + y * embedding_size + offset) = val;
}
}
// join need show click input
template <typename T>
__global__ void FusedCVMKernelWithCVM(const size_t N,
T **output_values,
T **seqpool_output_values,
const int batch_size,
const int embedding_size,
const int cvm_offset) {
CUDA_KERNEL_LOOP(i, N) {
int key = i / embedding_size;
int offset = i % embedding_size;
int x = key / batch_size; // slot id
int y = key % batch_size; // ins id
if (offset == 0) { // show
*(output_values[x] + y * embedding_size) =
log(*(seqpool_output_values[x] + y * embedding_size) + 1);
} else if (offset == 1) { // click
*(output_values[x] + y * embedding_size + offset) =
log(*(seqpool_output_values[x] + y * embedding_size + 1) + 1) -
log(*(seqpool_output_values[x] + y * embedding_size) + 1);
} else {
*(output_values[x] + y * embedding_size + offset) =
*(seqpool_output_values[x] + y * embedding_size + offset);
}
}
}
// update not need show click input
template <typename T>
__global__ void FusedCVMKernelNoCVM(const size_t N,
T **output_values,
T **seqpool_output_values,
const int batch_size,
const int no_cvm_embedding_size,
const int cvm_offset) {
CUDA_KERNEL_LOOP(i, N) {
int key = i / no_cvm_embedding_size;
int offset = i % no_cvm_embedding_size;
int x = key / batch_size; // slot id
int y = key % batch_size; // ins id
// no cvm
*(output_values[x] + y * no_cvm_embedding_size + offset) =
*(seqpool_output_values[x] + y * (no_cvm_embedding_size + cvm_offset) +
offset + cvm_offset);
}
}
template <typename T>
void FusedSeqpoolCVM(
const GPUContext &dev_ctx, // const paddle::phi::Place &place,
const std::vector<const T *> &input_data,
const std::vector<T *> &output_data,
const std::vector<T *> &seqpool_output_data,
std::vector<const size_t *> lods,
const int batch_size,
const int slot_num,
const int embedding_size,
const float padding_value,
const bool use_cvm,
const int cvm_offset) {
auto stream = dev_ctx.stream();
size_t total_ptr_len = input_data.size() + output_data.size() +
seqpool_output_data.size() + lods.size();
auto temp_ptr = phi::memory_utils::AllocShared(
dev_ctx.GetPlace(), total_ptr_len * sizeof(void *));
void *ptr = temp_ptr->ptr();
#ifdef PADDLE_WITH_HIP
T **gpu_input_values = reinterpret_cast<T **>(temp_ptr->ptr());
backends::gpu::GpuMemcpyAsync(
gpu_input_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(input_data.data()), input_data.size()),
input_data.size() * sizeof(T *),
hipMemcpyHostToDevice,
stream);
T **gpu_output_values =
reinterpret_cast<T **>(&gpu_input_values[input_data.size()]);
backends::gpu::GpuMemcpyAsync(
gpu_output_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(output_data.data()), output_data.size()),
output_data.size() * sizeof(T *),
hipMemcpyHostToDevice,
stream);
T **gpu_seqpool_output_values =
reinterpret_cast<T **>(&gpu_output_values[output_data.size()]);
backends::gpu::GpuMemcpyAsync(
gpu_seqpool_output_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(seqpool_output_data.data()),
seqpool_output_data.size()),
seqpool_output_data.size() * sizeof(T *),
hipMemcpyHostToDevice,
stream);
size_t **lods_values = reinterpret_cast<size_t **>(
&gpu_seqpool_output_values[seqpool_output_data.size()]);
backends::gpu::GpuMemcpyAsync(
lods_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<size_t **>(lods.data()), lods.size()),
lods.size() * sizeof(size_t *),
hipMemcpyHostToDevice,
stream);
#else
T **gpu_input_values = reinterpret_cast<T **>(temp_ptr->ptr());
backends::gpu::GpuMemcpyAsync(
gpu_input_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(input_data.data()), input_data.size()),
input_data.size() * sizeof(T *),
cudaMemcpyHostToDevice,
stream);
T **gpu_output_values =
reinterpret_cast<T **>(&gpu_input_values[input_data.size()]);
backends::gpu::GpuMemcpyAsync(
gpu_output_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(output_data.data()), output_data.size()),
output_data.size() * sizeof(T *),
cudaMemcpyHostToDevice,
stream);
T **gpu_seqpool_output_values =
reinterpret_cast<T **>(&gpu_output_values[output_data.size()]);
backends::gpu::GpuMemcpyAsync(
gpu_seqpool_output_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<T **>(seqpool_output_data.data()),
seqpool_output_data.size()),
seqpool_output_data.size() * sizeof(T *),
cudaMemcpyHostToDevice,
stream);
size_t **lods_values = reinterpret_cast<size_t **>(
&gpu_seqpool_output_values[seqpool_output_data.size()]);
backends::gpu::GpuMemcpyAsync(
lods_values,
backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
const_cast<size_t **>(lods.data()), lods.size()),
lods.size() * sizeof(size_t *),
cudaMemcpyHostToDevice,
stream);
#endif
size_t N = static_cast<size_t>(batch_size * slot_num * embedding_size);
backends::gpu::GpuLaunchConfig config =
backends::gpu::GetGpuLaunchConfig1D(dev_ctx, N);
// first sum pool
FusedSeqpoolKernelNormal<<<config.block_per_grid.x,
config.thread_per_block.x,
0,
stream>>>(N,
gpu_input_values,
gpu_seqpool_output_values,
lods_values,
batch_size,
embedding_size,
padding_value);
// second log
if (use_cvm) {
FusedCVMKernelWithCVM<<<config.block_per_grid.x,
config.thread_per_block.x,
0,
stream>>>(N,
gpu_output_values,
gpu_seqpool_output_values,
batch_size,
embedding_size,
cvm_offset);
} else {
// not need show click input
N = static_cast<size_t>(batch_size * slot_num *
(embedding_size - cvm_offset));
backends::gpu::GpuLaunchConfig config =
backends::gpu::GetGpuLaunchConfig1D(dev_ctx, N);
FusedCVMKernelNoCVM<<<config.block_per_grid.x,
config.thread_per_block.x,
0,
stream>>>(N,
gpu_output_values,
gpu_seqpool_output_values,
batch_size,
(embedding_size - cvm_offset),
cvm_offset);
}
}
template <typename T, typename Context>
void FusedSeqpoolCVMCUDAKernel(const Context &dev_ctx,
const std::vector<const DenseTensor *> &x,
const DenseTensor &cvm,
const std::string &pooltype,
float pad_value,
bool use_cvm,
int cvm_offset,
std::vector<DenseTensor *> out) {
// from InferShape
const size_t num_inputs = x.size();
std::vector<DDim> outs_dims;
outs_dims.resize(num_inputs);
int batch_size_tmp = -1;
for (size_t i = 0; i < num_inputs; ++i) {
const auto dims = x[i]->dims();
int rank = dims.size();
int cur_batch_size = 0;
const auto &x_lod = x[0]->lod();
if (!x_lod.empty()) {
cur_batch_size = static_cast<int>(x_lod[0].size() - 1);
} else {
cur_batch_size = static_cast<int>(x[0]->dims()[0]);
}
if (batch_size_tmp == -1) {
batch_size_tmp = cur_batch_size;
} else {
PADDLE_ENFORCE_EQ(batch_size_tmp,
cur_batch_size,
common::errors::PreconditionNotMet(
"The batch size of all input should be same, "
"please check, last batch_size is %d, current "
"batch_size is %d",
batch_size_tmp,
cur_batch_size));
}
std::vector<int64_t> out_dim;
if (use_cvm) {
out_dim = {batch_size_tmp, dims[rank - 1]};
} else {
out_dim = {batch_size_tmp, dims[rank - 1] - cvm_offset};
}
outs_dims[i] = make_ddim(out_dim);
}
for (size_t i = 0; i < out.size(); ++i) {
out[i]->Resize(outs_dims[i]);
}
auto &inputs = x;
auto &outputs = out;
const auto slot_size = inputs.size();
std::vector<const float *> input_data(slot_size);
std::vector<const size_t *> lods_data(slot_size);
std::vector<T *> output_data(slot_size);
std::vector<DenseTensor> seqpool_outputs(slot_size);
std::vector<T *> seqpool_output_data(slot_size);
auto padding_value = pad_value;
int64_t embedding_size_64 = inputs[0]->numel() / inputs[0]->dims()[0];
PADDLE_ENFORCE_LE_INT_MAX(embedding_size_64, "embedding_size");
int embedding_size = static_cast<int>(embedding_size_64);
int batch_size = -1;
std::vector<phi::MixVector<size_t> *> mix_lods_v(slot_size);
for (size_t i = 0; i < slot_size; ++i) {
const auto *input = inputs[i];
Vector<size_t> lods;
if (input->lod().size() != 0) {
auto lod = input->lod();
lods = lod[0];
} else {
lods.push_back(0);
for (int i = 0; i < input->dims()[0]; i++) {
lods.push_back(i + 1);
}
}
int cur_batch_size =
input->lod().size() ? input->lod()[0].size() - 1 : input->dims()[0];
if (batch_size == -1) {
batch_size = cur_batch_size;
} else {
PADDLE_ENFORCE_EQ(batch_size,
cur_batch_size,
common::errors::PreconditionNotMet(
"The batch size of all input should be same, "
"please cheack, last batchsize is %d, current "
"batchsize is %d",
batch_size,
cur_batch_size));
}
input_data[i] = reinterpret_cast<const T *>(input->data<T>());
auto *output = outputs[i];
if (use_cvm) {
output->Resize({batch_size, embedding_size});
} else {
output->Resize({batch_size, embedding_size - cvm_offset});
}
output_data[i] = reinterpret_cast<T *>(
dev_ctx.template Alloc<T>(output, output->numel() * sizeof(T)));
mix_lods_v[i] = new phi::MixVector<size_t>(&lods);
lods_data[i] = mix_lods_v[i]->CUDAData(dev_ctx.GetPlace());
seqpool_outputs[i].Resize({batch_size, embedding_size});
seqpool_output_data[i] = reinterpret_cast<T *>(dev_ctx.template Alloc<T>(
&seqpool_outputs[i], seqpool_outputs[i].numel() * sizeof(T)));
}
FusedSeqpoolCVM(dev_ctx,
input_data,
output_data,
seqpool_output_data,
lods_data,
batch_size,
slot_size,
embedding_size,
padding_value,
use_cvm,
cvm_offset);
for (int i = 0; i < slot_size; i++) {
delete mix_lods_v[i];
}
}
} // namespace fusion
} // namespace phi
PD_REGISTER_KERNEL(fused_seqpool_cvm,
GPU,
ALL_LAYOUT,
phi::fusion::FusedSeqpoolCVMCUDAKernel,
float) {}