使用 Intel Math Kernal Library (MKL) 加速 R
R 语言的一大优点就是其编写效率高,追求极致的运行效率并不是 R 的关注点。使用一些基础的计算库,例如 OpenBLAS 和 Intel MKL,可以一定程度提升 R 的运行速度,主要是矩阵运算方面。前几年微软发布过自带 MKL 的 Microsoft R Open(MRO),但并没有持续更新,最终版本停留在了3.5.3,而目前最新的 R-3 版本为3.6.3,R-4.0.2也已经可以下载了。感谢 MKL 的免费,现在可以手动将其与最新版本的R进行链结了。
下列操作方法来自 stack overflow,我不过是做了一些搬运和翻译的工作。另外,我使用的是 Intel CPU 和 Win 系统,在 github 上有用户提供了 Ubuntu 安装 MKL 以及使得 R 调用 MKL 的方法,不过在 AMD CPU,或是 Mac OS和 Linux 的其他发行版上是否有类似的方法或是起到类似的功效,我并不了解。【毕竟有 MATLAB 调用 MKL 结果对 AMD CPU 进行负优化这一情况的存在,不过听说 MALTAB2020 解决了这一问题?】
1. 具体操作方法
1.1 下载 MKL
首先在 MKL 的官网下载对应的 Windows 版本。下载前可能需要填写并提交一些信息,接着在下拉菜单中选择Intel® Math Kernel Library for Windows,版本可以选择最新的,最终选择 Full Package 开始下载。我也把安装包分享到了百度云,提取码 5g4j 。
1.2 安装 MKL 并与 R 链结
初步下载下来的是一个包装成 .exe 文件的压缩包,先双击执行文件然后选择一个合适的解压目录,下方的勾选框可以勾选上,安装完成后会自动删除解压出的临时文件。解压完成之后就会自动启动安装程序,随意选择合适的安装目录即可。安装完成后,将下列两个目录内部的所有文件
D:\Software\ToolsADV\MKL\compilers_and_libraries_2020.2.254\windows\redist\intel64_win\compiler\ D:\Software\ToolsADV\MKL\compilers_and_libraries_2020.2.254\windows\redist\intel64_win\mkl\
拷贝至下列 R 安装目录内的一个文件夹中
D:\Software\ToolsADV\R\R4\bin\x64
其中蓝色部分因人而异,请根据个人的 MKL 和 R 的安装路径以及 MKL 版本自行调整。接着在目前这个 x64 文件夹中,将文件 mkl_rt.dll 复制两份于当前文件夹,其中一个重命名为 Rblas.dll,另一个重命名为 Rlapack.dll,覆盖当前文件夹中原本存在的两个同名文件。到此就算是完成啦。
Remark. 在从 mkl 和 compiler 文件夹拷贝文件时,是把这两个文件夹内部的文件拷贝进 x64 文件夹,而不是把这两个文件夹拷贝进 x64 。
2. 一些小的示例
有兴趣的同学可以专门另外安装一个不带 MKL 版本的 R,来比较使用 MKL 前后的速度差异。
matrix(0, nr = 4, nc = 5)
my.time <-rownames(my.time) <- paste0('Example ', 1:4)
colnames(my.time) <- names(system.time(NULL))
set.seed(1)
10000
m <- 5000
n <- matrix(runif(m*n), m, n)
A <-
# Matrix multiplication
1,] <- system.time(B <- crossprod(A))
my.time[
# Cholesky Factorization
2,] <- system.time(C <- chol(B))
my.time[
2000
n <- matrix(runif(m*n), m, n)
A <-
# Singular Value Decomposition
3,] <- system.time(S <- svd(A))
my.time[
# Principal Components Analysis
4,] <- system.time(P <- prcomp(A))
my.time[
1:3] %>%
my.time[, kable(format = 'html', escape = F) %>%
kable_styling('striped', full_width = F)
user.self | sys.self | elapsed | |
---|---|---|---|
Example 1 | 6.72 | 1.21 | 1.35 |
Example 2 | 1.37 | 0.03 | 0.23 |
Example 3 | 18.92 | 0.44 | 3.38 |
Example 4 | 22.00 | 0.50 | 4.03 |
这里的用户时间大于流逝时间的原因应该是用户时间是多个 CPU 核心的时间累加得出的结果,MKL 库会利用并行计算实现加速。在安装 MKL 之前,流逝时间一般与用户时间和系统时间之和比较接近,因为都是用单线程在跑。
3. 后记
上文主要介绍了如何将 MKL 与 R 链结起来,以提升 R 的运行速度。不过说个题外话,个人认为 MKL 对于 R 起的是锦上添花的作用,对于初学者,把更多的精力放在优化代码上可能更加重要。在进行统计计算时,R代码运行速度如果非常缓慢,一方面可能是算法本身还需进一步优化,另一方面可能是代码不够高效。比如简单粗暴的 for 循环套 for 循环看似非常直观,但实际运算效率非常低下。优化 R 代码的方式有许多,首先,有些看似繁杂的操作可能使用 R 自带的内建函数就能完成,或是使用 apply 函数家族代替 for 循环就能更快捷地满足要求。在网上也能找到一些优化代码的tips。另外,在提出新方法时,初步跑通的代码不一定是最优化的,运行时的 CPU 占用可能跑不满,甚至只有20-30%,但又想先使用模拟的方式初步验证新方法的统计性能。这时使用并行计算的方式,同时跑多个模拟,能够节省大量的时间。最后,更为高阶的提速方式,是将算法的核心部分写成内嵌的 C 或者是 Fortran 语言。
总的来讲,写出好看又高效的 R 代码不是一件容易的事情,需要一定的学习思考以及阅读积累,有兴趣的同学可以多关注统计之都论坛、谢益辉师兄的博客,以及其它一些书籍等等的资料,比如Efficient R programming.