// 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 cudaDataType_t GetGpuDataType() { if (std::is_same::value) { return CUDA_R_32F; } else if (std::is_same::value) { return CUDA_R_64F; } else if (std::is_same::value) { return CUDA_R_16F; } } template cusparseIndexType_t GetCusparseIndexType() { if (std::is_same::value) { return CUSPARSE_INDEX_32I; } else if (std::is_same::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 inline void CreateCsrDescriptor(const SparseCsrTensor& x, const GPUContext& dev_ctx, cusparseSpMatDescr_t* descriptor) { std::vector 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(); const IntT* cols_data = x.non_zero_cols().data(); const T* values_data = x.non_zero_elements().data(); int64_t batch_nnz = x.nnz() / batch_size; cudaDataType_t gpu_type = GetGpuDataType(); cusparseIndexType_t index_type = GetCusparseIndexType(); dev_ctx.CusparseCall([&](cusparseHandle_t handle) { phi::dynload::cusparseCreateCsr(descriptor, M, N, batch_nnz, const_cast(crows_data), const_cast(cols_data), const_cast(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 inline void CreateCooDescriptor(const SparseCooTensor& x, const GPUContext& dev_ctx, cusparseSpMatDescr_t* descriptor) { std::vector 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(); const T* values_data = x.non_zero_elements().data(); 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(); cusparseIndexType_t index_type = GetCusparseIndexType(); dev_ctx.CusparseCall([&](cusparseHandle_t handle) { phi::dynload::cusparseCreateCoo(descriptor, M, N, batch_nnz, const_cast(rows_data), const_cast(cols_data), const_cast(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 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(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(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 class CuSparseDnMatDescriptor { public: explicit CuSparseDnMatDescriptor(const DenseTensor& x, const GPUContext& dev_ctx) : dev_ctx_(dev_ctx) { std::vector 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(); cudaDataType_t gpu_type = GetGpuDataType(); dev_ctx_.CusparseCall([&](cusparseHandle_t handle) { phi::dynload::cusparseCreateDnMat(&descriptor_, M, N, N, const_cast(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 class CuSparseDnVecDescriptor { public: explicit CuSparseDnVecDescriptor(const DenseTensor& x, const GPUContext& dev_ctx) : dev_ctx_(dev_ctx) { std::vector 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(); cudaDataType_t gpu_type = GetGpuDataType(); dev_ctx_.CusparseCall([&](cusparseHandle_t handle) { phi::dynload::cusparseCreateDnVec( &descriptor_, x.numel(), const_cast(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 void SparseBlas::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(mat_a, dev_ctx_); auto b_descriptor = CuSparseDnMatDescriptor(mat_b, dev_ctx_); auto out_descriptor = CuSparseDnMatDescriptor(*mat_out, dev_ctx_); cudaDataType_t gpu_type = GetGpuDataType(); 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(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 void SparseBlas::SPMV(bool transa, T alpha, const TensorType& mat_a, const DenseTensor& vec_x, T beta, DenseTensor* vec_out) const { auto a_descriptor = CuSparseSpMatDescriptor(mat_a, dev_ctx_); auto x_descriptor = CuSparseDnVecDescriptor(vec_x, dev_ctx_); auto out_descriptor = CuSparseDnVecDescriptor(*vec_out, dev_ctx_); cudaDataType_t gpu_type = GetGpuDataType(); 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(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 void SparseBlas::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(mat_a, dev_ctx_); auto b_descriptor = CuSparseDnMatDescriptor(mat_b, dev_ctx_); auto out_descriptor = CuSparseSpMatDescriptor(*mat_out, dev_ctx_); cudaDataType_t gpu_type = GetGpuDataType(); 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(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 __global__ void GetCsrBatchNnz(const int32_t* crow_data, int64_t rows, int32_t* batch_nnz) { int64_t i = static_cast(threadIdx.x); batch_nnz[i] = crow_data[(i + 1) * (rows + 1) - 1]; } template <> template void SparseBlas::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(mat_out_crows); std::vector 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 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 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(); a_cols_data = mat_a.cols().data(); } else { a_crows_int = std::make_shared(); a_cols_int = std::make_shared(); 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( dev_ctx_, mat_a.crows(), DataType::INT32, a_crows_int.get()); CastKernel( dev_ctx_, mat_a.cols(), DataType::INT32, a_cols_int.get()); a_crows_data = a_crows_int->data(); a_cols_data = a_cols_int->data(); } if (mat_b.crows().dtype() == DataType::INT32) { b_crows_data = mat_b.crows().data(); b_cols_data = mat_b.cols().data(); } else { b_crows_int = std::make_shared(); b_cols_int = std::make_shared(); 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( dev_ctx_, mat_b.crows(), DataType::INT32, b_crows_int.get()); CastKernel( dev_ctx_, mat_b.cols(), DataType::INT32, b_cols_int.get()); b_crows_data = b_crows_int->data(); b_cols_data = b_cols_int->data(); } const T* a_values_data = mat_a.values().data(); const T* b_values_data = mat_b.values().data(); const int32_t* out_crows_data = mat_out->crows().data(); const int batch_size = a_batch_size; std::vector a_batch_nnz_vec(batch_size); std::vector 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(dev_ctx_.stream()))); void* tmp_buffer_ptr = tmp_buffer->ptr(); GetCsrBatchNnz<<<1, batch_size, 0, dev_ctx_.stream()>>>( a_crows_data, a_rows, static_cast(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<<<1, batch_size, 0, dev_ctx_.stream()>>>( b_crows_data, b_rows, static_cast(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 out_batch_cols_vec(batch_size); std::vector out_batch_values_vec(batch_size); cudaDataType_t gpu_type = GetGpuDataType(); 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(a_batch_crows_data), const_cast(a_batch_cols_data), const_cast(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(b_batch_crows_data), const_cast(b_batch_cols_data), const_cast(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(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(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(&out_batch_cols_vec[i]); out_batch_values_vec[i].Resize(common::make_dim(out_num_values)); dev_ctx_.template Alloc(&out_batch_values_vec[i]); dev_ctx_.CusparseCall([&](cusparseHandle_t handle) { phi::dynload::cusparseCsrSetPointers( out_batch_desc, const_cast(out_batch_crows_data), const_cast(out_batch_cols_vec[i].data()), const_cast(out_batch_values_vec[i].data())); }); 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 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(dev_ctx_, cols_vec, 0, mat_out->mutable_cols()); phi::ConcatKernel(dev_ctx_, values_vec, 0, mat_out->mutable_values()); } if (mat_a.crows().dtype() == DataType::INT64 || mat_b.crows().dtype() == DataType::INT64) { CastKernel( dev_ctx_, *mat_out_crows, DataType::INT64, mat_out_crows); CastKernel(dev_ctx_, *mat_out_cols, DataType::INT64, mat_out_cols); } } } // namespace sparse } // namespace funcs } // namespace phi