Files
2026-07-13 12:40:42 +08:00

840 lines
34 KiB
C++

// Copyright (c) 2018 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 "glog/logging.h"
#include "paddle/common/ddim.h"
#include "paddle/phi/backends/dynload/cusparse.h"
#ifdef PADDLE_WITH_CUDA
#include "paddle/phi/backends/gpu/cuda/cuda_graph_with_memory_pool.h"
#endif
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/core/sparse_coo_tensor.h"
#include "paddle/phi/core/sparse_csr_tensor.h"
#include "paddle/phi/core/visit_type.h"
#include "paddle/phi/kernels/cast_kernel.h"
#include "paddle/phi/kernels/concat_kernel.h"
#include "paddle/phi/kernels/empty_kernel.h"
namespace phi {
namespace funcs {
namespace sparse {
template <typename T>
cudaDataType_t GetGpuDataType() {
if (std::is_same<T, float>::value) {
return CUDA_R_32F;
} else if (std::is_same<T, double>::value) {
return CUDA_R_64F;
} else if (std::is_same<T, phi::float16>::value) {
return CUDA_R_16F;
}
}
template <typename T>
cusparseIndexType_t GetCusparseIndexType() {
if (std::is_same<T, int32_t>::value) {
return CUSPARSE_INDEX_32I;
} else if (std::is_same<T, int64_t>::value) {
return CUSPARSE_INDEX_64I;
}
}
inline cusparseOperation_t GetTransposeOperation(const bool trans) {
if (trans) {
return CUSPARSE_OPERATION_TRANSPOSE;
} else {
return CUSPARSE_OPERATION_NON_TRANSPOSE;
}
}
inline cusparseSpMMAlg_t GetSpMMAlgorithm(const SparseCsrTensor& x) {
// TODO(zhouwei): will change to 'CUSPARSE_SPMM_CSR_ALG2' when support batch
return CUSPARSE_SPMM_CSR_ALG2;
}
inline cusparseSpMMAlg_t GetSpMMAlgorithm(const SparseCooTensor& x) {
return CUSPARSE_SPMM_ALG_DEFAULT;
}
/************* SPARSE MATRIX DESCRIPTOR (COO/CSR) ************/
template <typename T, typename IntT>
inline void CreateCsrDescriptor(const SparseCsrTensor& x,
const GPUContext& dev_ctx,
cusparseSpMatDescr_t* descriptor) {
std::vector<int64_t> xdim_vec = vectorize(x.dims());
auto x_ndims = xdim_vec.size();
PADDLE_ENFORCE_GE(
x_ndims,
2,
common::errors::InvalidArgument("the dim size of SparseCsrTensor must be "
"greater than or equal to 2."));
int64_t M = xdim_vec[x_ndims - 2];
int64_t N = xdim_vec[x_ndims - 1];
int batch_size = 1;
for (int i = 0; i < x_ndims - 2; i++) {
batch_size *= xdim_vec[i];
}
PADDLE_ENFORCE_EQ(x.non_zero_crows().numel(),
batch_size * (M + 1),
common::errors::PreconditionNotMet(
"the length of SparseCsrTensor crows is not right."));
const IntT* crows_data = x.non_zero_crows().data<IntT>();
const IntT* cols_data = x.non_zero_cols().data<IntT>();
const T* values_data = x.non_zero_elements().data<T>();
int64_t batch_nnz = x.nnz() / batch_size;
cudaDataType_t gpu_type = GetGpuDataType<T>();
cusparseIndexType_t index_type = GetCusparseIndexType<IntT>();
dev_ctx.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateCsr(descriptor,
M,
N,
batch_nnz,
const_cast<IntT*>(crows_data),
const_cast<IntT*>(cols_data),
const_cast<T*>(values_data),
index_type,
index_type,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
});
if (batch_size > 1) {
#if CUDA_VERSION >= 11080
dev_ctx.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCsrSetStridedBatch(
*descriptor, batch_size, M + 1, batch_nnz);
});
#else
PADDLE_THROW(common::errors::Unimplemented(
"Batch Sparse matmul use 'cusparseCsrSetStridedBatch', which is "
"supported from CUDA 11.8"));
#endif
}
}
template <typename T, typename IntT>
inline void CreateCooDescriptor(const SparseCooTensor& x,
const GPUContext& dev_ctx,
cusparseSpMatDescr_t* descriptor) {
std::vector<int64_t> xdim_vec = vectorize(x.dims());
auto x_ndims = xdim_vec.size();
PADDLE_ENFORCE_GE(
x_ndims,
2,
common::errors::InvalidArgument("the dim size of SparseCsrTensor must be "
"greater than or equal to 2."));
int64_t M = xdim_vec[x_ndims - 2];
int64_t N = xdim_vec[x_ndims - 1];
int batch_size = 1;
for (int i = 0; i < x_ndims - 2; i++) {
batch_size *= xdim_vec[i];
}
int64_t nnz = x.nnz();
const IntT* indices_data = x.non_zero_indices().data<IntT>();
const T* values_data = x.non_zero_elements().data<T>();
auto rows_data = indices_data + (x_ndims - 2) * nnz;
auto cols_data = indices_data + (x_ndims - 1) * nnz;
int64_t batch_nnz = nnz / batch_size;
cudaDataType_t gpu_type = GetGpuDataType<T>();
cusparseIndexType_t index_type = GetCusparseIndexType<IntT>();
dev_ctx.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateCoo(descriptor,
M,
N,
batch_nnz,
const_cast<IntT*>(rows_data),
const_cast<IntT*>(cols_data),
const_cast<T*>(values_data),
index_type,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
});
if (batch_size > 1) {
#if CUDA_VERSION >= 11080
dev_ctx.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCooSetStridedBatch(
*descriptor, batch_size, batch_nnz);
});
#else
PADDLE_THROW(common::errors::Unimplemented(
"Batch Sparse matmul use 'cusparseCooSetStridedBatch', which is "
"supported from CUDA 11.8"));
#endif
}
}
template <typename T>
class CuSparseSpMatDescriptor {
public:
explicit CuSparseSpMatDescriptor(const SparseCsrTensor& x,
const GPUContext& dev_ctx)
: dev_ctx_(dev_ctx) {
PD_VISIT_BASE_INTEGRAL_TYPES(
x.non_zero_crows().dtype(), "Csr CuSparseSpMatDescriptor", ([&] {
CreateCsrDescriptor<T, data_t>(x, dev_ctx_, &descriptor_);
}));
VLOG(6) << "Create csr cusparseSpMatDescr_t " << &descriptor_;
}
explicit CuSparseSpMatDescriptor(const SparseCooTensor& x,
const GPUContext& dev_ctx)
: dev_ctx_(dev_ctx) {
PD_VISIT_BASE_INTEGRAL_TYPES(
x.non_zero_indices().dtype(), "Coo CuSparseSpMatDescriptor", ([&] {
CreateCooDescriptor<T, data_t>(x, dev_ctx_, &descriptor_);
}));
VLOG(6) << "Create coo cusparseSpMatDescr_t " << &descriptor_;
}
~CuSparseSpMatDescriptor() {
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseDestroySpMat(descriptor_);
});
VLOG(6) << "Destroy cusparseSpMatDescr_t " << &descriptor_;
}
const cusparseSpMatDescr_t& descriptor() const { return descriptor_; }
private:
const GPUContext& dev_ctx_;
cusparseSpMatDescr_t descriptor_;
};
/************* DENSE MATRIX DESCRIPTOR ************/
template <typename T>
class CuSparseDnMatDescriptor {
public:
explicit CuSparseDnMatDescriptor(const DenseTensor& x,
const GPUContext& dev_ctx)
: dev_ctx_(dev_ctx) {
std::vector<int64_t> xdim_vec = vectorize(x.dims());
auto x_ndims = xdim_vec.size();
PADDLE_ENFORCE_GE(
x_ndims,
2,
common::errors::InvalidArgument("the dim size of DenseTensor must be "
"greater than or equal to 2."));
int64_t M = xdim_vec[x_ndims - 2];
int64_t N = xdim_vec[x_ndims - 1];
int batch_size = 1;
for (int i = 0; i < x_ndims - 2; i++) {
batch_size *= xdim_vec[i];
}
const T* x_data = x.data<T>();
cudaDataType_t gpu_type = GetGpuDataType<T>();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateDnMat(&descriptor_,
M,
N,
N,
const_cast<T*>(x_data),
gpu_type,
CUSPARSE_ORDER_ROW);
});
PADDLE_ENFORCE_EQ(x.numel(), batch_size * M * N);
if (batch_size > 1) {
#if CUDA_VERSION >= 11080
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseDnMatSetStridedBatch(
descriptor_, batch_size, M * N);
});
#else
PADDLE_THROW(common::errors::Unimplemented(
"Batch Sparse matmul use 'cusparseDnMatSetStridedBatch', which is "
"supported from CUDA 11.8"));
#endif
}
VLOG(6) << "Create cusparseDnMatDescr_t " << &descriptor_;
}
~CuSparseDnMatDescriptor() {
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseDestroyDnMat(descriptor_);
});
VLOG(6) << "Destroy cusparseDnMatDescr_t " << &descriptor_;
}
const cusparseDnMatDescr_t& descriptor() const { return descriptor_; }
private:
const GPUContext& dev_ctx_;
cusparseDnMatDescr_t descriptor_;
};
/************* DENSE VECTOR DESCRIPTOR ************/
template <typename T>
class CuSparseDnVecDescriptor {
public:
explicit CuSparseDnVecDescriptor(const DenseTensor& x,
const GPUContext& dev_ctx)
: dev_ctx_(dev_ctx) {
std::vector<int64_t> xdim_vec = vectorize(x.dims());
auto x_ndims = xdim_vec.size();
PADDLE_ENFORCE_GE(x_ndims,
1,
common::errors::InvalidArgument(
"the dim size of Vec must be equal to 1."));
const T* x_data = x.data<T>();
cudaDataType_t gpu_type = GetGpuDataType<T>();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateDnVec(
&descriptor_, x.numel(), const_cast<T*>(x_data), gpu_type);
});
VLOG(6) << "Create cusparseDnVecDescr_t " << &descriptor_;
}
~CuSparseDnVecDescriptor() {
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseDestroyDnVec(descriptor_);
});
VLOG(6) << "Destroy cusparseDnVecDescr_t " << &descriptor_;
}
const cusparseDnVecDescr_t& descriptor() const { return descriptor_; }
private:
const GPUContext& dev_ctx_;
cusparseDnVecDescr_t descriptor_;
};
/************* SPARSE*DENSE->DENSE MATMUL ************/
template <>
template <typename T, typename TensorType>
void SparseBlas<GPUContext>::SPMM(bool transa,
bool transb,
T alpha,
const TensorType& mat_a,
const DenseTensor& mat_b,
T beta,
DenseTensor* mat_out) const {
auto a_descriptor = CuSparseSpMatDescriptor<T>(mat_a, dev_ctx_);
auto b_descriptor = CuSparseDnMatDescriptor<T>(mat_b, dev_ctx_);
auto out_descriptor = CuSparseDnMatDescriptor<T>(*mat_out, dev_ctx_);
cudaDataType_t gpu_type = GetGpuDataType<T>();
size_t buffer_size = 0;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpMM_bufferSize(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_descriptor.descriptor(),
b_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
GetSpMMAlgorithm(mat_a),
&buffer_size);
});
phi::Allocator::AllocationPtr tmp_buffer = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
buffer_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_ptr = tmp_buffer->ptr();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpMM(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_descriptor.descriptor(),
b_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
GetSpMMAlgorithm(mat_a),
tmp_buffer_ptr);
});
}
/************* SPARSE*DENSE->DENSE MV ************/
template <>
template <typename T, typename TensorType>
void SparseBlas<GPUContext>::SPMV(bool transa,
T alpha,
const TensorType& mat_a,
const DenseTensor& vec_x,
T beta,
DenseTensor* vec_out) const {
auto a_descriptor = CuSparseSpMatDescriptor<T>(mat_a, dev_ctx_);
auto x_descriptor = CuSparseDnVecDescriptor<T>(vec_x, dev_ctx_);
auto out_descriptor = CuSparseDnVecDescriptor<T>(*vec_out, dev_ctx_);
cudaDataType_t gpu_type = GetGpuDataType<T>();
size_t buffer_size = 0;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpMV_bufferSize(handle,
GetTransposeOperation(transa),
&alpha,
a_descriptor.descriptor(),
x_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
CUSPARSE_SPMV_ALG_DEFAULT,
&buffer_size);
});
phi::Allocator::AllocationPtr tmp_buffer = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
buffer_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_ptr = tmp_buffer->ptr();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpMV(handle,
GetTransposeOperation(transa),
&alpha,
a_descriptor.descriptor(),
x_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
CUSPARSE_SPMV_ALG_DEFAULT,
tmp_buffer_ptr);
});
}
/************* DENSE*DENSE->SPARSE MATMUL ************/
template <>
template <typename T, typename TensorType>
void SparseBlas<GPUContext>::SDDMM(bool transa,
bool transb,
T alpha,
const DenseTensor& mat_a,
const DenseTensor& mat_b,
T beta,
TensorType* mat_out) const {
auto a_descriptor = CuSparseDnMatDescriptor<T>(mat_a, dev_ctx_);
auto b_descriptor = CuSparseDnMatDescriptor<T>(mat_b, dev_ctx_);
auto out_descriptor = CuSparseSpMatDescriptor<T>(*mat_out, dev_ctx_);
cudaDataType_t gpu_type = GetGpuDataType<T>();
size_t buffer_size = 0;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSDDMM_bufferSize(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_descriptor.descriptor(),
b_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
CUSPARSE_SDDMM_ALG_DEFAULT,
&buffer_size);
});
phi::Allocator::AllocationPtr tmp_buffer = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
buffer_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_ptr = tmp_buffer->ptr();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSDDMM_preprocess(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_descriptor.descriptor(),
b_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
CUSPARSE_SDDMM_ALG_DEFAULT,
tmp_buffer_ptr);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSDDMM(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_descriptor.descriptor(),
b_descriptor.descriptor(),
&beta,
out_descriptor.descriptor(),
gpu_type,
CUSPARSE_SDDMM_ALG_DEFAULT,
tmp_buffer_ptr);
});
}
/************* SPARSE*SPARSE->SPARSE MATMUL ************/
template <typename T>
__global__ void GetCsrBatchNnz(const int32_t* crow_data,
int64_t rows,
int32_t* batch_nnz) {
int64_t i = static_cast<int64_t>(threadIdx.x);
batch_nnz[i] = crow_data[(i + 1) * (rows + 1) - 1];
}
template <>
template <typename T>
void SparseBlas<GPUContext>::SPGEMM(bool transa,
bool transb,
T alpha,
const SparseCsrTensor& mat_a,
const SparseCsrTensor& mat_b,
T beta,
SparseCsrTensor* mat_out) const {
DenseTensor* mat_out_crows = mat_out->mutable_crows();
DenseTensor* mat_out_cols = mat_out->mutable_cols();
DenseTensor* mat_out_values = mat_out->mutable_values();
MetaTensor out_crows_meta(mat_out_crows);
out_crows_meta.set_dtype(DataType::INT32);
out_crows_meta.set_dims(mat_a.crows().dims());
dev_ctx_.template Alloc<int32_t>(mat_out_crows);
std::vector<int64_t> a_dim_vec = vectorize(mat_a.dims());
auto a_ndims = a_dim_vec.size();
const int64_t a_rows = a_dim_vec[a_ndims - 2];
const int64_t a_cols = a_dim_vec[a_ndims - 1];
int a_batch_size = 1;
for (int i = 0; i < a_ndims - 2; i++) {
a_batch_size *= a_dim_vec[i];
}
std::vector<int64_t> b_dim_vec = vectorize(mat_b.dims());
auto b_ndims = b_dim_vec.size();
const int64_t b_rows = b_dim_vec[b_ndims - 2];
const int64_t b_cols = b_dim_vec[b_ndims - 1];
// cusparseSpGEMM only support 32-bit indices.
const int32_t *a_crows_data = nullptr, *a_cols_data = nullptr,
*b_crows_data = nullptr, *b_cols_data = nullptr;
std::shared_ptr<DenseTensor> a_crows_int = nullptr, a_cols_int = nullptr,
b_crows_int = nullptr, b_cols_int = nullptr;
if (mat_a.crows().dtype() == DataType::INT32) {
a_crows_data = mat_a.crows().data<int32_t>();
a_cols_data = mat_a.cols().data<int32_t>();
} else {
a_crows_int = std::make_shared<DenseTensor>();
a_cols_int = std::make_shared<DenseTensor>();
MetaTensor crows_meta(a_crows_int.get());
crows_meta.set_dims(mat_a.crows().dims());
MetaTensor cols_meta(a_cols_int.get());
cols_meta.set_dims(mat_a.cols().dims());
CastKernel<int64_t>(
dev_ctx_, mat_a.crows(), DataType::INT32, a_crows_int.get());
CastKernel<int64_t>(
dev_ctx_, mat_a.cols(), DataType::INT32, a_cols_int.get());
a_crows_data = a_crows_int->data<int32_t>();
a_cols_data = a_cols_int->data<int32_t>();
}
if (mat_b.crows().dtype() == DataType::INT32) {
b_crows_data = mat_b.crows().data<int32_t>();
b_cols_data = mat_b.cols().data<int32_t>();
} else {
b_crows_int = std::make_shared<DenseTensor>();
b_cols_int = std::make_shared<DenseTensor>();
MetaTensor crows_meta(b_crows_int.get());
crows_meta.set_dims(mat_b.crows().dims());
MetaTensor cols_meta(b_cols_int.get());
cols_meta.set_dims(mat_b.cols().dims());
CastKernel<int64_t>(
dev_ctx_, mat_b.crows(), DataType::INT32, b_crows_int.get());
CastKernel<int64_t>(
dev_ctx_, mat_b.cols(), DataType::INT32, b_cols_int.get());
b_crows_data = b_crows_int->data<int32_t>();
b_cols_data = b_cols_int->data<int32_t>();
}
const T* a_values_data = mat_a.values().data<T>();
const T* b_values_data = mat_b.values().data<T>();
const int32_t* out_crows_data = mat_out->crows().data<int32_t>();
const int batch_size = a_batch_size;
std::vector<int32_t> a_batch_nnz_vec(batch_size);
std::vector<int32_t> b_batch_nnz_vec(batch_size);
if (batch_size == 1) {
a_batch_nnz_vec[0] = mat_a.nnz();
b_batch_nnz_vec[0] = mat_b.nnz();
} else {
phi::Allocator::AllocationPtr tmp_buffer = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
batch_size * sizeof(int32_t),
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_ptr = tmp_buffer->ptr();
GetCsrBatchNnz<T><<<1, batch_size, 0, dev_ctx_.stream()>>>(
a_crows_data, a_rows, static_cast<int32_t*>(tmp_buffer_ptr));
#ifdef PADDLE_WITH_CUDA
PADDLE_ENFORCE_EQ(
phi::backends::gpu::IsCUDAGraphCapturing(),
false,
common::errors::InvalidArgument(
"SparseBlas CsrMM does not support CUDA Graph capture: async D2H "
"copy to local vectors 'a_batch_nnz_vec' / 'b_batch_nnz_vec' will "
"bake the destination addresses into the graph; on replay the "
"vectors are re-created at different addresses, causing "
"dangling-pointer writes."));
#endif
phi::backends::gpu::GpuMemcpyAsync(a_batch_nnz_vec.data(),
tmp_buffer_ptr,
batch_size * sizeof(int32_t),
gpuMemcpyDeviceToHost,
dev_ctx_.stream());
GetCsrBatchNnz<T><<<1, batch_size, 0, dev_ctx_.stream()>>>(
b_crows_data, b_rows, static_cast<int32_t*>(tmp_buffer_ptr));
phi::backends::gpu::GpuMemcpyAsync(b_batch_nnz_vec.data(),
tmp_buffer_ptr,
batch_size * sizeof(int32_t),
gpuMemcpyDeviceToHost,
dev_ctx_.stream());
}
std::vector<DenseTensor> out_batch_cols_vec(batch_size);
std::vector<DenseTensor> out_batch_values_vec(batch_size);
cudaDataType_t gpu_type = GetGpuDataType<T>();
const int32_t* a_batch_crows_data = a_crows_data;
const int32_t* a_batch_cols_data = a_cols_data;
const T* a_batch_values_data = a_values_data;
const int32_t* b_batch_crows_data = b_crows_data;
const int32_t* b_batch_cols_data = b_cols_data;
const T* b_batch_values_data = b_values_data;
const int32_t* out_batch_crows_data = out_crows_data;
for (int i = 0; i < batch_size; ++i) {
int32_t a_batch_nnz = a_batch_nnz_vec[i];
int32_t b_batch_nnz = b_batch_nnz_vec[i];
cusparseSpMatDescr_t a_batch_desc, b_batch_desc, out_batch_desc;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateCsr(&a_batch_desc,
a_rows,
a_cols,
a_batch_nnz,
const_cast<int32_t*>(a_batch_crows_data),
const_cast<int32_t*>(a_batch_cols_data),
const_cast<T*>(a_batch_values_data),
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateCsr(&b_batch_desc,
b_rows,
b_cols,
b_batch_nnz,
const_cast<int32_t*>(b_batch_crows_data),
const_cast<int32_t*>(b_batch_cols_data),
const_cast<T*>(b_batch_values_data),
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCreateCsr(&out_batch_desc,
a_rows,
b_cols,
0,
nullptr,
nullptr,
nullptr,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_32I,
CUSPARSE_INDEX_BASE_ZERO,
gpu_type);
});
size_t buffer_a_size = 0, buffer_b_size = 0;
cusparseSpGEMMDescr_t spgemm_desc;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_createDescr(&spgemm_desc);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_workEstimation(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_batch_desc,
b_batch_desc,
&beta,
out_batch_desc,
gpu_type,
CUSPARSE_SPGEMM_DEFAULT,
spgemm_desc,
&buffer_a_size,
nullptr);
});
phi::Allocator::AllocationPtr tmp_buffer_a = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
buffer_a_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_a_ptr = tmp_buffer_a->ptr();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_workEstimation(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_batch_desc,
b_batch_desc,
&beta,
out_batch_desc,
gpu_type,
CUSPARSE_SPGEMM_DEFAULT,
spgemm_desc,
&buffer_a_size,
tmp_buffer_a_ptr);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_compute(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_batch_desc,
b_batch_desc,
&beta,
out_batch_desc,
gpu_type,
CUSPARSE_SPGEMM_DEFAULT,
spgemm_desc,
&buffer_b_size,
nullptr);
});
phi::Allocator::AllocationPtr tmp_buffer_b = phi::memory_utils::Alloc(
dev_ctx_.GetPlace(),
buffer_b_size,
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx_.stream())));
void* tmp_buffer_b_ptr = tmp_buffer_b->ptr();
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_compute(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_batch_desc,
b_batch_desc,
&beta,
out_batch_desc,
gpu_type,
CUSPARSE_SPGEMM_DEFAULT,
spgemm_desc,
&buffer_b_size,
tmp_buffer_b_ptr);
});
int64_t out_num_crows, out_num_cols, out_num_values;
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpMatGetSize(
out_batch_desc, &out_num_crows, &out_num_cols, &out_num_values);
});
out_batch_cols_vec[i].Resize(common::make_dim(out_num_values));
dev_ctx_.template Alloc<int32_t>(&out_batch_cols_vec[i]);
out_batch_values_vec[i].Resize(common::make_dim(out_num_values));
dev_ctx_.template Alloc<T>(&out_batch_values_vec[i]);
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseCsrSetPointers(
out_batch_desc,
const_cast<int32_t*>(out_batch_crows_data),
const_cast<int32_t*>(out_batch_cols_vec[i].data<int32_t>()),
const_cast<T*>(out_batch_values_vec[i].data<T>()));
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_copy(handle,
GetTransposeOperation(transa),
GetTransposeOperation(transb),
&alpha,
a_batch_desc,
b_batch_desc,
&beta,
out_batch_desc,
gpu_type,
CUSPARSE_SPGEMM_DEFAULT,
spgemm_desc);
});
dev_ctx_.CusparseCall([&](cusparseHandle_t handle) {
phi::dynload::cusparseSpGEMM_destroyDescr(spgemm_desc);
});
a_batch_crows_data += a_rows + 1;
a_batch_cols_data += a_batch_nnz;
a_batch_values_data += a_batch_nnz;
b_batch_crows_data += b_rows + 1;
b_batch_cols_data += b_batch_nnz;
b_batch_values_data += b_batch_nnz;
out_batch_crows_data += a_rows + 1;
}
if (batch_size == 1) {
*(mat_out->mutable_cols()) = std::move(out_batch_cols_vec[0]);
*(mat_out->mutable_values()) = std::move(out_batch_values_vec[0]);
} else {
std::vector<const DenseTensor*> cols_vec, values_vec;
for (int i = 0; i < batch_size; ++i) {
cols_vec.push_back(&out_batch_cols_vec[i]);
values_vec.push_back(&out_batch_values_vec[i]);
}
phi::ConcatKernel<int32_t>(dev_ctx_, cols_vec, 0, mat_out->mutable_cols());
phi::ConcatKernel<T>(dev_ctx_, values_vec, 0, mat_out->mutable_values());
}
if (mat_a.crows().dtype() == DataType::INT64 ||
mat_b.crows().dtype() == DataType::INT64) {
CastKernel<int32_t>(
dev_ctx_, *mat_out_crows, DataType::INT64, mat_out_crows);
CastKernel<int32_t>(dev_ctx_, *mat_out_cols, DataType::INT64, mat_out_cols);
}
}
} // namespace sparse
} // namespace funcs
} // namespace phi