由于笔者在学习工作中需要大量用到c++线性运算库进行科学计算,在配置环境的摸索过程中趟了不少坑,所以写下常用工具以及配置心得,以备日后查阅,也方便新来者快速上手。
c++中可用的线性运算库有很多种,笔者也见过很多做计算机的同仁不满意现有的库,自己实现线性运算库。然而笔者并不打算自己重新造轮子,毕竟计算机领域的一大优势就在于我们能通过开源的优秀代码,站在别人的肩膀上更进一步。在做过一些调查以后,笔者主要介绍两个c++线性运算库,其中 Eigen 是笔者工作生产中常用的库。
Eigen 是一款十分著名的线性运算模板库, Eigen 支持稠密矩阵(Dense Matrix)和稀疏矩阵(Sparse Matrix)运算,并内置实现了对两种矩阵的求解器(Solver)。使用 Eigen 十分简单,因为是模板库,所以只需要将头文件拷入 include 目录即可,缺点是编译时比较费时。Eigen 的稠密矩阵运算在某些平台上的性能甚至接近 Intel ® MKL [1]。虽然 Eigen 的求解器从效率来讲并不算最好,但将其作为矩阵容器是一个很好的选择,在求解大型问题时再通过连向诸如 SuiteSparse 等顶级求解器即可。TensorFlow 即使用了Eigen。在 3.3 之后,Eigen 引入了对其他 BLAS 的接口,支持了包括 MKL,OpenBLAS 等高性能库,使得其性能还可能更进一步提高。
Armadillo 是一款年轻的线性运算模板库,拥有友好的交互接口,十分贴近 matlab 风格,迭代速度也很快,笔者十分喜欢。并且可以使用其他 BLAS 实现如 OpenBLAS 作为计算后端,实现稠密矩阵运算的加速,在某些评测下甚至超过 Eigen。但是笔者之所以没有选择它,是因为笔者的项目对 Cholmod 分解要求很高,需要连接 SuiteSparse 进行求解,而可惜 Armadillo 的稀疏矩阵求解只能连向 SuperLU,因此笔者只能转向 Eigen。
Eigen 与 Armadillo 提供了较好的矩阵容器以及 C++ 调用接口,也实现了稠密矩阵和稀疏矩阵的简单求解,但是对于一些特定问题的求解并不是最优,比如高性能计算界研究了很多年的稀疏矩阵求解(Sparse Matrix Solver)。对于标准的 LU/QR/Cholesky 分解,高性能计算界已经有成熟高效的计算库如 SuiteSparse。所以在追求计算速度的科研领域,还是需要掌握这些更复杂的求解库的使用。
SuiteSparse 是现在科学界和工业界最强大的基于 BLAS/LAPACK 的多种稀疏矩阵求解算法合集,其中包括了LU分解算法(UMFPACK), Cholesky分解(CHOLMOD), QR分解算法(SPQR)等算法的实现。Matlab 的矩阵求解就是使用的 SuiteSparse 的实现,可以说是相当的强大。笔者工作中就极度依赖 SuiteSparse 提供的加速,比如使用 Eigen 的自带稀疏矩阵求解器就要比使用 SuiteSparse 慢一两个数量级。
SuperLU 针对LU分解所写的库,和 UMFPACK 具有相同的使用范围,在各测试集上表现也各有优劣,笔者较为推荐 UMFPACK,因为可以在 SuiteSparse 中一并安装。
在涉及线性运算的领域,几个术语BLAS, LAPACK是绕不过的,笔者最开始时是各种懵逼,在安装线性运算库时各种踩坑,之后在实践中才慢慢摸索清楚它们之间的关系。笔者就将我的理解总结在下面,如有笔误也欢迎指正。
BLAS 全称为 Basic Linear Algebra Subprograms,是一个API标准,其设计目的是为了提供对向量和矩阵计算的基础支持,具体分为BLAS1(向量间运算),BLAS2(矩阵与向量计算),BLAS3(矩阵与矩阵计算)。设计BLAS的原因是由于不同的计算机硬件架构(总线、各级缓存、CPU寄存器数量),逻辑上相同的运算在写为机器码过程中有很大的优化空间,所以通过这样一个标准来分离底层运算及更高级的调用,达到较好的跨平台性及运算速度。现在较为知名的BLAS实现是 Netlib 的 cBLAS, 第一个做到跨平台可伸缩的 ATLAS,boost 提供的ublas,以及 GotoBLAS2 的继承改进版 OpenBLAS,还有Intel专门为Intel处理器开发的MKL。
LAPACK 全称 Linear Algebra Package,是一个基于 Level 3 BLAS 的线性代数数值计算库,其继承了LINPACK的线性方程求解和最小二乘求解,EISPACK的特征值求解,以及实现了多种矩阵分解算法(LU, Cholesky, QR, SVD, Schur, generalized Schur)。最新的 LAPACK 是由Fortran 90写成的。LAPACKE 是 LAPACK 的 C 语言API。
比较全的介绍可以在 BLAS 的 wiki 中找到,以下主要谈一谈笔者的体会。
uBLAS 是 boost 包提供的 C 语言 BLAS 实现,性能不敢恭维。
cuBLAS 是 Nvidia 为其 GPU 提供的线性运算库,包含于 CUDA 中,也可以单独下载安装。由于稠密矩阵运算是高度并行的任务,所以 GPU 更擅长做基础向量运算。在 Nvidia 官方给的测试中,使用 cuBLAS 的 Tesla P100 比使用 MKL 的 Xeon Dual E5-2699 v4 快了5倍。
Linux 下安装和使用 OpenBLAS 是比较方便的,在此就不再赘述。笔者主要记录一下 Win 下安装使用的步骤。
1. 下载预编译包(通常选择x64)binary package
2. 解压,并将包中.h, .lib, .dll分别放在合适的位置。.lib包中只有 libopenblas.dll.a 是 Visual Studio 可以调用的,另一个是只有 mingw-gcc 才能调用的。
3. 编译时包含头文件目录,链接libopenblas.dll.a。
4. 运行时需要若干 mingw dll 文件,按照提示去下载。
SuiteSparse 不提供 Windows 下编译安装的官方支持,但是 Github 上有大神做了一个出来 suitesparse-metis-for-windows,按照说明一步一步安装再编译就不会有什么问题。在配置 CMake 时可以手动指定使用的 BLAS 库,比如 OpenBLAS。
Windows Kit corcert_math rint issue
[1] 矩阵运算库blas, cblas, openblas, atlas, lapack, mkl之间有什么关系,在性能上区别大吗? - 蝙蝠果的回答 - 知乎