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

933 lines
35 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 <math.h>
#include <limits>
#include <string>
#include <vector>
#include "paddle/phi/common/memory_utils.h"
#if defined(PADDLE_WITH_CUDA)
#include "paddle/phi/backends/dynload/cusparse.h"
#endif
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/utils/data_type.h"
#include "paddle/utils/optional.h"
namespace phi {
#if defined(PADDLE_WITH_CUDA)
template <typename T>
__forceinline__ __device__ T CudaShuffleXorSync(unsigned mask,
T val,
int width = warpSize) {
return __shfl_xor_sync(mask, val, width);
}
template <typename T, int batch_size, int warp_size>
__device__ __forceinline__ void WarpReduceSum(T* sum) {
#pragma unroll
for (int offset = warp_size / 2; offset > 0; offset /= 2) {
#pragma unroll
for (int i = 0; i < batch_size; ++i) {
T sum_val = CudaShuffleXorSync(0xFFFFFFFF, sum[i], offset);
sum[i] = sum[i] + sum_val;
}
}
}
template <typename T, int batch_size, int warp_size>
__device__ __forceinline__ void WarpReduceMax(T* sum) {
#pragma unroll
for (int offset = warp_size / 2; offset > 0; offset /= 2) {
#pragma unroll
for (int i = 0; i < batch_size; ++i) {
T max_val = CudaShuffleXorSync(0xFFFFFFFF, sum[i], offset);
sum[i] = max(sum[i], max_val);
}
}
}
template <typename T, int BlockSize, int BlockNnzMax>
__global__ void BlockSparseSoftmaxForward(T* softmax,
const T* src,
T scale,
const T* kp_mask,
const T* attn_mask,
const int* layout_rowptr,
const int* layout_colindex,
int num_rows) {
// current thread related info
const int WarpSize = 32;
const int cur_row = blockIdx.x * blockDim.y + threadIdx.y;
if (cur_row < num_rows) {
const int cur_block_row = cur_row / BlockSize;
const int cur_block_nnz =
layout_rowptr[cur_block_row + 1] - layout_rowptr[cur_block_row];
T srcdata[(BlockSize * BlockNnzMax + WarpSize - 1) / WarpSize] = {0};
T attndata[(BlockSize * BlockNnzMax + WarpSize - 1) / WarpSize] = {0};
// read tensor data, attn mask
const int iter = (cur_block_nnz + WarpSize - 1) / WarpSize;
const T* srcptr = src + layout_rowptr[cur_block_row];
const T* attnptr = (attn_mask == nullptr)
? nullptr
: (attn_mask + cur_block_row * num_rows);
// the column start index in current row
const int* colindex = layout_colindex + layout_rowptr[cur_block_row];
for (int j = 0; j < iter; j++) {
int cur_block_col = j * WarpSize + threadIdx.x;
int cur_reg_index = j;
if (cur_block_col < cur_block_nnz) {
// read kp mask
T cur_kp_mask;
if ((kp_mask != nullptr) && std::abs(kp_mask[colindex[cur_block_col]]) <
std::numeric_limits<T>::epsilon()) {
cur_kp_mask = -std::numeric_limits<T>::infinity();
} else {
cur_kp_mask = 0;
}
// do mask operation
if ((attnptr != nullptr) && std::abs(attnptr[colindex[cur_block_col]]) <
std::numeric_limits<T>::epsilon()) {
srcdata[cur_reg_index] =
-std::numeric_limits<T>::infinity() * scale + cur_kp_mask;
} else {
srcdata[cur_reg_index] = scale * srcptr[cur_block_col] + cur_kp_mask;
}
} else {
srcdata[cur_reg_index] = -std::numeric_limits<T>::infinity();
}
}
// max value
T max_value = srcdata[0];
const int kIteration =
(cur_block_nnz * BlockSize + WarpSize - 1) / WarpSize;
#pragma unroll
for (int it = 1; it < kIteration; ++it) {
max_value = (max_value > srcdata[it]) ? max_value : srcdata[it];
}
WarpReduceMax<T, 1, WarpSize>(&max_value);
// exp sum
T sum = 0;
#pragma unroll
for (int it = 0; it < kIteration; ++it) {
srcdata[it] = std::exp(srcdata[it] - max_value);
sum += srcdata[it];
}
WarpReduceSum<T, 1, WarpSize>(&sum);
// compute softmax and write out
T* softmaxptr = softmax + layout_rowptr[cur_block_row];
for (int j = 0; j < iter; j++) {
int cur_block_col = j * WarpSize + threadIdx.x;
int cur_reg_index = j;
if (cur_block_col < cur_block_nnz) {
softmaxptr[cur_block_col] = srcdata[cur_reg_index] / sum;
}
}
}
}
template <typename T, int BlockSize, int BlockNnzMax>
__global__ void BlockSparseSoftmaxBackward(T* dst,
const T* grad,
const T* src,
T scale,
const int* layout_rowptr,
const int* layout_colindex,
int num_rows) {
// current thread related info
const int WarpSize = 32;
const int cur_row = blockIdx.x * blockDim.y + threadIdx.y;
if (cur_row < num_rows) {
const int cur_block_row = cur_row / BlockSize;
const int cur_block_nnz =
layout_rowptr[cur_block_row + 1] - layout_rowptr[cur_block_row];
T srcdata[(BlockSize * BlockNnzMax + WarpSize - 1) / WarpSize];
T graddata[(BlockSize * BlockNnzMax + WarpSize - 1) / WarpSize];
// read tensor data, attn mask
const int iter = (cur_block_nnz + WarpSize - 1) / WarpSize;
const T* srcptr = src + layout_rowptr[cur_block_row];
const T* gradptr = grad + layout_rowptr[cur_block_row];
for (int j = 0; j < iter; j++) {
int cur_block_col = j * WarpSize + threadIdx.x;
int cur_reg_index = j;
if (cur_block_col < cur_block_nnz) {
srcdata[cur_reg_index] = srcptr[cur_block_col];
graddata[cur_reg_index] = gradptr[cur_block_col];
} else {
srcdata[cur_reg_index] = 0;
graddata[cur_reg_index] = 0;
}
}
T sum = 0;
const int kIteration =
(cur_block_nnz * BlockSize + WarpSize - 1) / WarpSize;
#pragma unroll
for (int it = 0; it < kIteration; ++it) {
sum += srcdata[it] * graddata[it];
}
WarpReduceSum<T, 1, WarpSize>(&sum);
// compute softmax and write out
T* dstptr = dst + layout_rowptr[cur_block_row];
for (int j = 0; j < iter; j++) {
int cur_block_col = j * WarpSize + threadIdx.x;
int cur_reg_index = j;
if (cur_block_col < cur_block_nnz) {
dstptr[cur_block_col] =
scale * srcdata[cur_reg_index] * (graddata[cur_reg_index] - sum);
}
}
}
}
/*
input: sparse C in CSR format (num_rows,num_rows)
output: sparse C after softmax operation
*/
template <typename DeviceContext, typename T>
void SparseSoftmaxForward(const GPUContext& dev_ctx,
const DenseTensor* offset,
const DenseTensor* columns,
DenseTensor* input,
DenseTensor* output,
const int blocksize,
const int num_rows,
const int num_cols,
const DenseTensor* key_padding_mask,
const DenseTensor* attn_mask) {
const int* offset_data = offset->data<int>();
const int* columns_data = columns->data<int>();
T* input_data = input->data<T>();
T* output_data = output->data<T>();
// Add mask
const T* key_padding_mask_data =
(key_padding_mask != nullptr) ? key_padding_mask->data<T>() : nullptr;
const T* attn_mask_data =
(attn_mask != nullptr) ? attn_mask->data<T>() : nullptr;
const int block_size = 1;
dim3 blocks(32, 4, 1);
int grid = (num_rows * block_size + 3) / 4;
T scaling = static_cast<T>(1.0) / sqrt(static_cast<T>(num_cols));
if (num_cols <= 4) {
BlockSparseSoftmaxForward<T, block_size, 4>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 4 && num_cols <= 8) {
BlockSparseSoftmaxForward<T, block_size, 8>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 8 && num_cols <= 16) {
BlockSparseSoftmaxForward<T, block_size, 16>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 16 && num_cols <= 32) {
BlockSparseSoftmaxForward<T, block_size, 32>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 32 && num_cols <= 64) {
BlockSparseSoftmaxForward<T, block_size, 64>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 64 && num_cols <= 128) {
BlockSparseSoftmaxForward<T, block_size, 128>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 128 && num_cols <= 256) {
BlockSparseSoftmaxForward<T, block_size, 256>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 256 && num_cols <= 512) {
BlockSparseSoftmaxForward<T, block_size, 512>
<<<grid, blocks>>>(output_data,
input_data,
scaling,
key_padding_mask_data,
attn_mask_data,
offset_data,
columns_data,
num_rows);
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"The head_dim of query in sparse_attention op should less or equal "
"512"));
}
}
template <typename DeviceContext, typename T>
void SparseSoftmaxBackward(const GPUContext& dev_ctx,
const DenseTensor* offset,
const DenseTensor* columns,
DenseTensor* dx,
const DenseTensor* dout,
const DenseTensor* out,
const int blocksize,
const int num_rows,
const int num_cols) {
const int* offset_data = offset->data<int>();
const int* columns_data = columns->data<int>();
T* dx_data = dx->data<T>();
const T* dout_data = dout->data<T>();
const T* out_data = out->data<T>();
const int block_size = 1;
dim3 blocks(32, 4, 1);
int grid = (num_rows * block_size + 3) / 4;
T scaling = static_cast<T>(1.0) / sqrt(static_cast<T>(num_cols));
if (num_cols <= 4) {
BlockSparseSoftmaxBackward<T, block_size, 4><<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 4 && num_cols <= 8) {
BlockSparseSoftmaxBackward<T, block_size, 8><<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 8 && num_cols <= 16) {
BlockSparseSoftmaxBackward<T, block_size, 16>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 16 && num_cols <= 32) {
BlockSparseSoftmaxBackward<T, block_size, 32>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 32 && num_cols <= 64) {
BlockSparseSoftmaxBackward<T, block_size, 64>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 64 && num_cols <= 128) {
BlockSparseSoftmaxBackward<T, block_size, 128>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 128 && num_cols <= 256) {
BlockSparseSoftmaxBackward<T, block_size, 256>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else if (num_cols > 256 && num_cols <= 512) {
BlockSparseSoftmaxBackward<T, block_size, 512>
<<<grid, blocks>>>(dx_data,
dout_data,
out_data,
scaling,
offset_data,
columns_data,
num_rows);
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"The head_dim of query in sparse_attention op should less or equal "
"512"));
}
}
inline cudaDataType_t GetGpuType(const DataType data_type) {
if (data_type == DataType::FLOAT32) {
return CUDA_R_32F;
} else if (data_type == DataType::FLOAT64) {
return CUDA_R_64F;
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"Not support tensor type in sparse_attention OP: %s",
phi::DataTypeToString(data_type)));
}
}
inline cusparseOperation_t GetTransposeOperation(const bool transpose) {
if (transpose) {
return CUSPARSE_OPERATION_TRANSPOSE;
} else {
return CUSPARSE_OPERATION_NON_TRANSPOSE;
}
}
void CusparseDestroy(cusparseDnMatDescr_t* dn_mat_first,
cusparseDnMatDescr_t* dn_mat_second,
cusparseSpMatDescr_t* sp_mat) {
phi::dynload::cusparseDestroyDnMat(*dn_mat_first);
phi::dynload::cusparseDestroyDnMat(*dn_mat_second);
phi::dynload::cusparseDestroySpMat(*sp_mat);
}
#endif
/*
input: dense A (num_rows,num_cols), dense B (num_rows,num_cols)
output: sparse C in CSR format (num_rows,num_rows)
*/
template <typename DeviceContext, typename T>
void DotSdd(const GPUContext& dev_ctx,
const DenseTensor* a,
const DenseTensor* b,
const DenseTensor* c_offset,
const DenseTensor* c_columns,
DenseTensor* c_value,
const int num_rows,
const int num_cols,
const bool a_transpose,
const bool b_transpose) {
#if defined(PADDLE_WITH_CUDA)
const T* a_data = a->data<T>();
const T* b_data = b->data<T>();
const int* c_offset_data = c_offset->data<int>();
const int* c_columns_data = c_columns->data<int>();
T* c_value_data = c_value->data<T>();
cudaDataType_t gpu_type = GetGpuType(c_value->dtype());
cusparseHandle_t handle = nullptr;
cusparseDnMatDescr_t mat_a, mat_b;
cusparseSpMatDescr_t mat_c;
phi::dynload::cusparseCreate(&handle);
// Create dense matrix A
phi::dynload::cusparseCreateDnMat(&mat_a,
num_rows,
num_cols,
num_cols,
const_cast<T*>(a_data),
gpu_type,
CUSPARSE_ORDER_ROW);
// Create dense matrix B
phi::dynload::cusparseCreateDnMat(&mat_b,
num_rows,
num_cols,
num_cols,
const_cast<T*>(b_data),
gpu_type,
CUSPARSE_ORDER_ROW);
// Create sparse matrix C in CSR format
int64_t c_nnz = c_columns->numel();
phi::dynload::cusparseCreateCsr(&mat_c,
num_rows,
num_rows,
c_nnz,
const_cast<int*>(c_offset_data),
const_cast<int*>(c_columns_data),
c_value_data,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
T alpha = 1;
T beta = 0;
size_t buffer_size = 0;
phi::dynload::cusparseSDDMM_bufferSize(handle,
GetTransposeOperation(a_transpose),
GetTransposeOperation(b_transpose),
&alpha,
mat_a,
mat_b,
&beta,
mat_c,
gpu_type,
CUSPARSE_SDDMM_ALG_DEFAULT,
&buffer_size);
auto d_buffer_ptr = phi::memory_utils::Alloc(
dev_ctx.GetPlace(),
buffer_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx.stream())));
void* d_buffer = static_cast<void*>(d_buffer_ptr->ptr());
phi::dynload::cusparseSDDMM(handle,
GetTransposeOperation(a_transpose),
GetTransposeOperation(b_transpose),
&alpha,
mat_a,
mat_b,
&beta,
mat_c,
gpu_type,
CUSPARSE_SDDMM_ALG_DEFAULT,
d_buffer);
CusparseDestroy(&mat_a, &mat_b, &mat_c);
phi::dynload::cusparseDestroy(handle);
#else
PADDLE_THROW(common::errors::Unimplemented(
"DotSdd use cusparseSDDMM, which is supported "
"from CUDA 11.3"));
#endif
}
/*
input: sparse A in CSR format (num_rows,num_rows), dense B (num_rows,num_cols)
output: dense C (num_rows,num_cols)
*/
template <typename DeviceContext, typename T>
void DotDsd(const GPUContext& dev_ctx,
const DenseTensor* a_offset,
const DenseTensor* a_columns,
const DenseTensor* a_value,
const DenseTensor* b,
DenseTensor* c,
const int num_rows,
const int num_cols,
const bool a_transpose,
const bool b_transpose) {
#if defined(PADDLE_WITH_CUDA)
const int* a_offset_data = a_offset->data<int>();
const int* a_columns_data = a_columns->data<int>();
const T* a_value_data = a_value->data<T>();
const T* b_data = b->data<T>();
T* c_data = c->data<T>();
cudaDataType_t gpu_type = GetGpuType(c->dtype());
cusparseHandle_t handle = nullptr;
cusparseSpMatDescr_t mat_a;
cusparseDnMatDescr_t mat_b, mat_c;
phi::dynload::cusparseCreate(&handle);
// Create sparse matrix A in CSR format
int64_t a_nnz = a_columns->numel();
phi::dynload::cusparseCreateCsr(&mat_a,
num_rows,
num_rows,
a_nnz,
const_cast<int*>(a_offset_data),
const_cast<int*>(a_columns_data),
const_cast<T*>(a_value_data),
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
// Create dense matrix B
phi::dynload::cusparseCreateDnMat(&mat_b,
num_rows,
num_cols,
num_cols,
const_cast<T*>(b_data),
gpu_type,
CUSPARSE_ORDER_ROW);
// Create dense matrix C
phi::dynload::cusparseCreateDnMat(&mat_c,
num_rows,
num_cols,
num_cols,
c_data,
gpu_type,
CUSPARSE_ORDER_ROW);
T alpha = 1;
T beta = 0;
size_t buffer_size = 0;
// allocate an external buffer if needed
phi::dynload::cusparseSpMM_bufferSize(handle,
GetTransposeOperation(a_transpose),
GetTransposeOperation(b_transpose),
&alpha,
mat_a,
mat_b,
&beta,
mat_c,
gpu_type,
CUSPARSE_SPMM_ALG_DEFAULT,
&buffer_size);
auto d_buffer_ptr = phi::memory_utils::Alloc(
dev_ctx.GetPlace(),
buffer_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx.stream())));
void* d_buffer = static_cast<void*>(d_buffer_ptr->ptr());
phi::dynload::cusparseSpMM(handle,
GetTransposeOperation(a_transpose),
GetTransposeOperation(b_transpose),
&alpha,
mat_a,
mat_b,
&beta,
mat_c,
gpu_type,
CUSPARSE_SPMM_ALG_DEFAULT,
d_buffer);
CusparseDestroy(&mat_b, &mat_c, &mat_a);
phi::dynload::cusparseDestroy(handle);
#else
PADDLE_THROW(common::errors::Unimplemented(
"DotDsd use cusparseSpMM, which is supported "
"from CUDA 11.0"));
#endif
}
std::vector<DenseTensor> GetSplitTensor(DenseTensor* input) {
auto dims = input->dims();
int batch_size = dims[0];
int num_heads = dims[1];
std::vector<int> new_dims(dims.size() - 1);
new_dims[0] = batch_size * num_heads;
for (int i = 1; i < new_dims.size(); i++) {
new_dims[i] = dims[i + 1];
}
input->Resize(new_dims);
return input->Split(1, 0);
}
template <typename T, typename Context>
void SparseAttentionCUDAKernel(const Context& dev_ctx,
const DenseTensor& q,
const DenseTensor& k,
const DenseTensor& v,
const DenseTensor& offset,
const DenseTensor& columns,
const optional<DenseTensor>& key_padding_mask,
const optional<DenseTensor>& attn_mask,
DenseTensor* out,
DenseTensor* sparse_dot_sdd,
DenseTensor* softmax) {
#if defined(PADDLE_WITH_CUDA)
auto query = q;
auto key = k;
auto value = v;
auto output_ptr = out;
dev_ctx.template Alloc<T>(out);
auto sparse_dot_sdd_ptr = sparse_dot_sdd;
dev_ctx.template Alloc<T>(sparse_dot_sdd);
auto softmax_ptr = softmax;
dev_ctx.template Alloc<T>(softmax);
auto output = *output_ptr;
auto result_sdd = *sparse_dot_sdd_ptr;
auto result_softmax = *softmax_ptr;
auto query_dims = query.dims();
int batch_size = query_dims[0];
int num_heads = query_dims[1];
int M = query_dims[2];
int N = query_dims[3];
DenseTensor q2 = q;
DenseTensor k2 = k;
DenseTensor v2 = v;
DenseTensor offset2 = offset;
DenseTensor columns2 = columns;
std::vector<DenseTensor> query_lists = GetSplitTensor(&q2);
std::vector<DenseTensor> key_lists = GetSplitTensor(&k2);
std::vector<DenseTensor> value_lists = GetSplitTensor(&v2);
std::vector<DenseTensor> offset_lists = GetSplitTensor(&offset2);
std::vector<DenseTensor> columns_lists = GetSplitTensor(&columns2);
std::vector<DenseTensor> result_sdd_lists = GetSplitTensor(&result_sdd);
std::vector<DenseTensor> result_softmax_lists =
GetSplitTensor(&result_softmax);
std::vector<DenseTensor> output_lists = GetSplitTensor(&output);
const int iter_num = batch_size * num_heads;
for (int i = 0; i < iter_num; i++) {
DotSdd<Context, T>(dev_ctx,
&query_lists[i],
&key_lists[i],
&offset_lists[i],
&columns_lists[i],
&result_sdd_lists[i],
M,
N,
false,
true);
if (key_padding_mask && attn_mask) {
SparseSoftmaxForward<Context, T>(
dev_ctx,
&offset_lists[i],
&columns_lists[i],
&result_sdd_lists[i],
&result_softmax_lists[i],
1,
M,
N,
key_padding_mask.get_ptr() + (i / num_heads) * M,
attn_mask.get_ptr());
} else if (key_padding_mask && !attn_mask.is_initialized()) {
SparseSoftmaxForward<Context, T>(
dev_ctx,
&offset_lists[i],
&columns_lists[i],
&result_sdd_lists[i],
&result_softmax_lists[i],
1,
M,
N,
key_padding_mask.get_ptr() + (i / num_heads) * M,
nullptr);
} else if (!key_padding_mask.is_initialized() && attn_mask) {
SparseSoftmaxForward<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&result_sdd_lists[i],
&result_softmax_lists[i],
1,
M,
N,
nullptr,
attn_mask.get_ptr());
} else {
SparseSoftmaxForward<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&result_sdd_lists[i],
&result_softmax_lists[i],
1,
M,
N,
nullptr,
nullptr);
}
DotDsd<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&result_softmax_lists[i],
&value_lists[i],
&output_lists[i],
M,
N,
false,
false);
}
#endif
}
template <typename T, typename Context>
void SparseAttentionGradCUDAKernel(const Context& dev_ctx,
const DenseTensor& q,
const DenseTensor& k,
const DenseTensor& v,
const DenseTensor& offset,
const DenseTensor& columns,
const DenseTensor& sparse_dot_sdd,
const DenseTensor& softmax,
const DenseTensor& out_grad,
DenseTensor* q_grad,
DenseTensor* k_grad,
DenseTensor* v_grad) {
#if defined(PADDLE_WITH_CUDA)
auto query = q;
auto key = k;
auto value = v;
auto dout = out_grad;
auto* dquery_ptr = q_grad;
auto* dkey_ptr = k_grad;
auto* dvalue_ptr = v_grad;
dev_ctx.template Alloc<T>(q_grad);
dev_ctx.template Alloc<T>(k_grad);
dev_ctx.template Alloc<T>(v_grad);
auto dquery = *dquery_ptr;
auto dkey = *dkey_ptr;
auto dvalue = *dvalue_ptr;
auto query_dims = query.dims();
int batch_size = query_dims[0];
int num_heads = query_dims[1];
int M = query_dims[2];
int N = query_dims[3];
DenseTensor q2 = q;
DenseTensor k2 = k;
DenseTensor v2 = v;
DenseTensor offset2 = offset;
DenseTensor columns2 = columns;
DenseTensor sparse_dot_sdd2 = sparse_dot_sdd;
DenseTensor softmax2 = softmax;
DenseTensor dout2 = out_grad;
std::vector<DenseTensor> query_lists = GetSplitTensor(&q2);
std::vector<DenseTensor> key_lists = GetSplitTensor(&k2);
std::vector<DenseTensor> value_lists = GetSplitTensor(&v2);
std::vector<DenseTensor> offset_lists = GetSplitTensor(&offset2);
std::vector<DenseTensor> columns_lists = GetSplitTensor(&columns2);
std::vector<DenseTensor> sparse_dot_sdd_lists =
GetSplitTensor(&sparse_dot_sdd2);
std::vector<DenseTensor> softmax_lists = GetSplitTensor(&softmax2);
std::vector<DenseTensor> dout_lists = GetSplitTensor(&dout2);
std::vector<DenseTensor> dquery_lists = GetSplitTensor(&dquery);
std::vector<DenseTensor> dkey_lists = GetSplitTensor(&dkey);
std::vector<DenseTensor> dvalue_lists = GetSplitTensor(&dvalue);
const int iter_num = batch_size * num_heads;
for (int i = 0; i < iter_num; i++) {
// dValue = transpose(result_softmax) * dOut
DotDsd<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&softmax_lists[i],
&dout_lists[i],
&dvalue_lists[i],
M,
N,
true,
false);
// dSoftmax = dOut * transpose(Value)
int64_t nnz_num = columns_lists[i].numel();
DenseTensor dsoftmax;
dsoftmax.Resize({nnz_num});
dev_ctx.template Alloc<T>(&dsoftmax);
DotSdd<Context, T>(dev_ctx,
&dout_lists[i],
&value_lists[i],
&offset_lists[i],
&columns_lists[i],
&dsoftmax,
M,
N,
false,
true);
// dSparseDotSdd = dSoftmax * softmax'(SparseDotSdd)
DenseTensor dsparse_dot_sdd;
dsparse_dot_sdd.Resize({nnz_num});
dev_ctx.template Alloc<T>(&dsparse_dot_sdd);
SparseSoftmaxBackward<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&dsparse_dot_sdd,
&dsoftmax,
&softmax_lists[i],
1,
M,
N);
// dQuery = dSparseDotSdd * Key
DotDsd<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&dsparse_dot_sdd,
&key_lists[i],
&dquery_lists[i],
M,
N,
false,
false);
// dKey = transpose(dSparseDotSdd) * Query
DotDsd<Context, T>(dev_ctx,
&offset_lists[i],
&columns_lists[i],
&dsparse_dot_sdd,
&query_lists[i],
&dkey_lists[i],
M,
N,
true,
false);
}
#endif
}
} // namespace phi
PD_REGISTER_KERNEL(sparse_attention,
GPU,
ALL_LAYOUT,
phi::SparseAttentionCUDAKernel,
float,
double) {
kernel->InputAt(3).SetDataType(phi::DataType::INT32);
kernel->InputAt(4).SetDataType(phi::DataType::INT32);
}
PD_REGISTER_KERNEL(sparse_attention_grad,
GPU,
ALL_LAYOUT,
phi::SparseAttentionGradCUDAKernel,
float,
double) {
kernel->InputAt(3).SetDataType(phi::DataType::INT32);
kernel->InputAt(4).SetDataType(phi::DataType::INT32);
}