近期在解决一个解大型稀疏矩阵方程的问题,用到了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格式是坐标系格式,有三个数组表示,三个数组分别记录元素的行坐标,列坐标和数值,排列方式为行优先。
CSR是将COO格式的行坐标向量进行压缩,压缩后行向量的长度变为m+1,m为稀疏矩阵的行数。行坐标向量的前m位的值为当前行第一个非零元素的列坐标,行坐标向量的第m+1位为非零元素总数加上行坐标向量的第一位。
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的解法要慢。需要注意,这些时间数据只代表我的运行结果,不一定具有普遍性。