在我的两台计算机上,我尝试了这段代码:
N <- 10e3
M <- 2000
X <- matrix(rnorm(N * M), N)
system.time(crossprod(X))
Run Code Online (Sandbox Code Playgroud)
第一个是标准笔记本电脑,此操作需要1.7秒.
> sessionInfo()
R version 3.4.4 (2018-03-15)
Platform: x86_64-redhat-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)
Matrix products: default
BLAS/LAPACK: /usr/lib64/R/lib/libRblas.so
Run Code Online (Sandbox Code Playgroud)
第二个是相当不错的台式电脑,花了17秒.
> sessionInfo()
R version 3.4.4 (2018-03-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Linux Mint 18.3
Matrix products: default
BLAS: /usr/lib/libblas/libblas.so.3.6.0
LAPACK: /usr/lib/lapack/liblapack.so.3.6.0
Run Code Online (Sandbox Code Playgroud)
台式计算机比笔记本电脑更高性能,但这种矩阵计算需要10倍的时间.
问题来自默认的BLAS/LAPACK吗?
tldr: CentOS使用单线程OpenBLAS,Linux Mint默认使用Reference BLAS,但可以使用其他BLAS版本.
EPEL提供的CentOS R套件依赖于openblas-Rblas.这似乎是为BL提供BLAS的OpenBLAS构建.因此,虽然看起来使用了R的BLAS,但它实际上是OpenBLAS.LAPACK版本始终是R提供的版本.
在Debian和衍生的发行版上,如Mint,r-base-core取决于
默认情况下,这些由参考实现提供libblas3和 liblapack3.这些并不是特别快,但您可以通过安装类似的软件包轻松更换它们libopenblas-base.您可以通过控制系统上使用的BLAS和LAPACK update-alternatives.
为了控制OpenBLAS的线程数,我通常使用RhpcBLASctl:
N <- 20000
M <- 2000
X <- matrix(rnorm(N * M), N)
RhpcBLASctl::blas_set_num_threads(2)
system.time(crossprod(X))
#> User System verstrichen
#> 2.492 0.331 1.339
RhpcBLASctl::blas_set_num_threads(1)
system.time(crossprod(X))
#> User System verstrichen
#> 2.319 0.052 2.316
Run Code Online (Sandbox Code Playgroud)
由于某种原因设置环境变量 OPENBLAS_NUM_THREADS,GOTO_NUM_THREADS或OMP_NUM_THREADS从R没有预期的效果.在CentOS上甚至RhpcBLASctl没有帮助,因为使用的OpenBLAS是单线程的.
R是使用默认的BLAS实现分发的,但可能未针对您的计算机进行优化。按照R 的安装指南中的建议,在 R之前通过ATLAS或OpenBLAS 安装 BLAS的优化版本是可行的方法。如果您轻按Download R for Linux,然后再轻按debian/。据说:
您可能需要安装自动调整的Atlas或多线程OpenBlas库,以便为线性代数运算获得更高的性能
R的源代码可以在此处下载,而BLAS实现则位于R-3.5.0/src/extra/blas。例如,矩阵矩阵乘法的Fortran源代码dgemm位于blas.f中,以及大多数BLAS例程(在单个文件中!)。
该函数的注释指定:
-- Written on 8-February-1989.
Jack Dongarra, Argonne National Laboratory.
Iain Duff, AERE Harwell.
Jeremy Du Croz, Numerical Algorithms Group Ltd.
Sven Hammarling, Numerical Algorithms Group Ltd.
Run Code Online (Sandbox Code Playgroud)
在例程dgemm的netlib实现中可以找到相同的代码行。
相反,OpenBLAS提供了不同的实现方式,每种实现都针对一种处理器。例如,参见该文件专用于dgemm的haswell微体系结构。有对prefetcht0的调用以进行预取,有对vfmadd231pd的调用,vfmadd231pd是矢量FMA SIMD指令,它一次执行4次双精度d = a * b + c。
使用优化的BLAS可以节省时间。例如,参见此基准测试,其中netlib的dgemm()持续64秒,而MKL,OpenBLAS或ATLAS dgemm花费不到4秒。
R内部BLAS的情况可能比经典的Netlib库更糟。实际上,如附录A.3.1.5 Shared BLAS中所指定R Installation and Administration:
R提供了将BLAS编译到存储在R_HOME / lib中的动态库libRblas中的选项,并且可以将R本身和所有附加软件包链接到该库。....使用共享的BLAS可能会有性能上的缺点。...但是,实验表明,在许多情况下,只要使用了高级编译器优化,使用共享BLAS的速度就一样快。
查看R的config.site文件,可以看到,对于g77 / gfortran,优化级别为“ -O2”。因此,如果fortran编译器不是g77 / gfortran,调整FFLAGS选项可能会很有用。在配置步骤中,应该有一行checking whether we are using the GNU Fortran 77 compiler... yes(配置文件的7521行)。