Eigen 与 CUDA cusolver 解大规模稀疏矩阵方程的实现与比较

近期在解决一个解大型稀疏矩阵方程的问题,用到了Eigen库和cuSolver库,并对二者的不同算法进行了时间上的比较。

1.Eigen解稀疏矩阵方程

1.1 解法流程

调用c++的eigen库解方程的参考资料比较多,这里只是简单地总结一下大致流程。给项目配置好eigen环境后,
首先是引用头文件和命名空间,

#include"Eigen/Sparse"
...

using namespace Eigen;
...

这样就可以在后面调用eigen sparse的一些功能。
我们要解的方程Ax=b,接下来就是根据自己的需求构造稀疏系数矩阵A,和向量b,最后调用一些solver来解方程。

typedef Eigen::SparseMatrix SpMat;
typedef Eigen::Triplet T;

//1.构造A矩阵
int nx = 10000,ny=10000;
int estimateNZN = nx*5;//矩阵中非零项的大致个数
SpMat A(nx,ny);//10000*10000的稀疏矩阵
A.reserve(estimateNZN);//提前为A分配estimateNZN个元素的内存

std::vector tripletList;//为矩阵A批量赋值的三元组
tripletList.reserve(estimateNZN);

//根据需要构造A
for(...){
...
tripletList.push_back(T(rowId, colId,value));//push每个三元组(行,列,值)
...
}
//给A批量赋值
A.setFromTriplets(tripletList.begin(), tripletList.end());

//2.根据自己的需求构造向量b
Eigen::VectorXf bx(interior_num);
bx.setZero();
...

//3.构造系数方程的solver,解方程
Eigen::SimplicialCholesky solver(A);
//Eigen::SparseLU solver(A);
//Eigen::SparseQR> solver(A);

Eigen::VectorXf x = solver.solve(bx);//最后的结果为x

1.2稀疏矩阵不同的赋值方法

为A矩阵的赋值方法,这里用的是通过三元组来批量赋值,但也可用A.coeffRef(rowId, colId) = value;来逐一赋值,值得一提的是,当元素个数较多时,批量赋值的速度远远快于逐一插入,因此推荐使用批量的赋值。

1.3 不同的求解器与reordering 类型

几种不同的solver:Eigen提供了很多方程求解器,上面代码中标注了几个常用的,如cholesky分解,qr分解和lu分解,他们针对不同属性的矩阵,且运算速度不同,详细的求解器说明参考这里(Eigen:Solving Sparse Linear Systems)。
运算速度也受不同的reordering参数影响(Eigen:OrderingMethods module),QR分解需要显式指出reordering类型,另外两种则有默认的类型。

2.cuSolver解稀疏矩阵方程

2.1 CUDA环境配置

我的这篇文章介绍了如何在windows系统配置CUDA10环境,有需要的可以参考一下:(vs+CUDA10.2环境配置)

2.2 cuSparse中稀疏矩阵格式

cuSparse中支持多种稀疏矩阵的格式,这里只介绍比较常见的COO和CSR两种。

COO格式是坐标系格式,有三个数组表示,三个数组分别记录元素的行坐标,列坐标和数值,排列方式为行优先。


coo

CSR是将COO格式的行坐标向量进行压缩,压缩后行向量的长度变为m+1,m为稀疏矩阵的行数。行坐标向量的前m位的值为当前行第一个非零元素的列坐标,行坐标向量的第m+1位为非零元素总数加上行坐标向量的第一位。


csr

2.2 解法流程

这篇文章(利用cuda的cusparse模块计算超大型稀疏矩阵方程的解)仿照cuda提供的cusolver的示例代码讲解了如何构造求解稀疏方程,这里不再赘述。


需要注意的是:

  • 示例代码中用的是low level的函数,即将解方程的每一步都单独作为一个函数,来逐一运行;
  • 我们往往不需要那么繁琐的细节,可以直接调用cusolver提供的high level的函数如cusolverSpScsrlsvchol()(choleskey解法),cusolverSpScsrlsvqr()(QR解法)等等,关于这些函数的详细解释请参考cusolver的官方文档(https://docs.nvidia.com/cuda/cusolver/index.html)。

3. 两类方法的时间比较

笔者测试了eigen和cusolver的不同解法的时间,将比较结果记录一下。


设备信息:intel 酷睿i7处理器;8g内存;NVIDIA GTX 960M 的显卡;
测试的稀疏矩阵维度为10000*10000,记录一下累计解100次Ax=b方程的时间:

Eigen cpu解法 cuSolver gpu解法
QR分解 非常慢,不予考虑 31s
LU分解 3.5s 不支持gpu,只有cpu版本
choleskey分解 1.3s 11s

P.S.以上都是使用默认的reordering类型。


结果很出乎意料,对于我构造的矩阵,cuda gpu的choleskey分解要比Eigen在cpu的解法要慢。需要注意,这些时间数据只代表我的运行结果,不一定具有普遍性。






你可能感兴趣的:(Eigen 与 CUDA cusolver 解大规模稀疏矩阵方程的实现与比较)