犰狳+NVBLAS 变成 RcppArmadillo+NVBLAS

Posted

技术标签:

【中文标题】犰狳+NVBLAS 变成 RcppArmadillo+NVBLAS【英文标题】:Armadillo+NVBLAS into RcppArmadillo+NVBLAS 【发布时间】:2015-09-20 12:47:48 【问题描述】:

TLDR;对于那些想要避免阅读整个故事的人:是 有一种方法可以将 RcppArmadillo 与 NVBLAS 接口以利用 GPU,更像是使用 NVBLAS 将犰狳与 NVBLAS 连接起来 没有 R 的纯 c++ 代码?

我正在尝试利用 NVBLAS 库 (http://docs.nvidia.com/cuda/nvblas/) 通过将一些计算转移到 GPU 来加速我的项目中的线性代数部分(主要是计算统计、MCMC、粒子滤波器和所有这些好东西) .

我主要使用 C++ 代码,尤其是 Armadillo 库进行矩阵计算,通过他们的常见问题解答,我知道我可以通过以正确的方式链接 armadillo (http://arma.sourceforge.net/faq.html) 来使用 NVBLAS。

所以我设置了我的库安装并编写了以下虚拟程序:

#include <armadillo>
int main()

arma::mat A = arma::randn<arma::mat>(3000,2000);
arma::mat B = cov(A);
arma::vec V = arma::randn(2000);
arma::mat C; arma::mat D;

for(int i = 0; i<20; ++i) C = solve(V,B); D = inv(B);  

return 0;

编译
g++ arma_try.cpp -o arma_try.so -larmadillo

并使用

对其进行分析
nvprof ./arma_try.so

分析器输出显示:

==11798== Profiling application: ./arma_try.so
==11798== Profiling result:
Time(%)      Time     Calls       Avg       Min       Max  Name
 72.15%  4.41253s       580  7.6078ms  1.0360ms  14.673ms  void magma_lds128_dgemm_kernel<bool=0, bool=0, int=5, int=5, int=3, int=3, int=3>(int, int, int, double const *, int, double const *, int, double*, int, int, int, double const *, double const *, double, double, int)
 20.75%  1.26902s      1983  639.95us  1.3440us  2.9929ms  [CUDA memcpy HtoD]
  4.06%  248.17ms         1  248.17ms  248.17ms  248.17ms  void fermiDsyrk_v2_kernel_core<bool=1, bool=1, bool=0, bool=1>(double*, int, int, int, int, int, int, double const *, double const *, double, double, int)
  1.81%  110.54ms         1  110.54ms  110.54ms  110.54ms  void fermiDsyrk_v2_kernel_core<bool=0, bool=1, bool=0, bool=1>(double*, int, int, int, int, int, int, double const *, double const *, double, double, int)
  1.05%  64.023ms       581  110.19us  82.913us  12.211ms  [CUDA memcpy DtoH]
  0.11%  6.9438ms         1  6.9438ms  6.9438ms  6.9438ms  void gemm_kernel2x2_tile_multiple_core<double, bool=1, bool=0, bool=0, bool=1, bool=0>(double*, double const *, double const *, int, int, int, int, int, int, double*, double*, double, double, int)
  0.06%  3.3712ms         1  3.3712ms  3.3712ms  3.3712ms  void gemm_kernel2x2_core<double, bool=0, bool=0, bool=0, bool=1, bool=0>(double*, double const *, double const *, int, int, int, int, int, int, double*, double*, double, double, int)
  0.02%  1.3192ms         1  1.3192ms  1.3192ms  1.3192ms  void syherk_kernel_core<double, double, int=256, int=4, bool=1, bool=0, bool=0, bool=1, bool=0, bool=1>(cublasSyherkParams<double, double>)
  0.00%  236.03us         1  236.03us  236.03us  236.03us  void syherk_kernel_core<double, double, int=256, int=4, bool=0, bool=0, bool=0, bool=1, bool=0, bool=1>(cublasSyherkParams<double, double>)

我认识 dgemm 和其他人的地方......所以它正在工作!太棒了。

现在我想运行相同的代码但与 R 接口,因为我有时需要用它进行输入/输出和绘图。 RcppArmadillo 一直为我创造奇迹,与 Rcpp 一起提供我需要的所有工具。我因此写了cpp:

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
int arma_call()

  arma::mat A = arma::randn<arma::mat>(3000,2000);
  arma::mat B = cov(A);
  arma::vec V = arma::randn(2000);
  arma::mat C; arma::mat D;

  for(int i = 0; i<20; ++i) C = solve(V,B); D = inv(B);  

  return 0;

和 R 脚本:

Rcpp::sourceCpp('arma_try_R.cpp')
arma_call()

并尝试通过在控制台上运行来执行它

nvprof R CMD BATCH arma_try_R.R 

(编辑:注意使用 Rscript 而不是 R CMD BATCH 会产生相同的结果)但是

[NVBLAS] Cannot open default config file 'nvblas.conf'

奇怪...可能 R 无法访问该文件,因此我将其复制到工作目录并重新运行代码:

==12662== NVPROF is profiling process 12662, command: /bin/sh /usr/bin/R CMD BATCH arma_try_R.R
==12662== Profiling application: /bin/sh /usr/bin/R CMD BATCH arma_try_R.R
==12662== Profiling result: No kernels were profiled.

我不知道是什么原因造成的。 我在一个安装了 Bumblebee 的 linux 系统上,所以最后一次尝试:

nvprof optirun R CMD BATCH arma_try_R.R 

强制 R 使用 Nvidia 卡运行,这次输出是

==10900== Profiling application: optirun R CMD BATCH arma_try_R.R
==10900== Profiling result:
Time(%)      Time     Calls       Avg       Min       Max  Name
100.00%  1.3760us         1  1.3760us  1.3760us  1.3760us  [CUDA memcpy HtoD]

因此,就我对分析器的了解而言,根本没有调用 cuda 库,也没有任何委托给 GPU 的计算。 现在问题实际上很多,而不仅仅是一个。

这只是无法跟踪 R 内部调用的分析器问题吗? (我怀疑)

这是因为代码在 R 中的编译方式吗?详细模式显示

/usr/lib64/R/bin/R CMD SHLIB -o 'sourceCpp_27457.so' --preclean 'arma_try_R.cpp'

g++ -I/usr/include/R/ -DNDEBUG -D_FORTIFY_SOURCE=2 -I"/home/marco/R/x86_64-unknown-linux-gnu-library/3.2/Rcpp/include" -I"/home/marco/R/x86_64-unknown-linux-gnu-library/3.2/RcppArmadillo/include" -I"/home/marco/prova_cuda" -fpic -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong --param=ssp-buffer-size=4 -c arma_try_R.cpp -o arma_try_R.o

g++ -shared -L/usr/lib64/R/lib -Wl,-O1,--sort-common,--as-needed,-z,relro -lblas -llapack -o sourceCpp_27457.so arma_try_R.o -llapack -lblas -lgfortran -lm -lquadmath -L/usr/lib64/R/lib -lR

即使我强制使用 -larmadillo 而不是 -lblas 标志(通过 PKG_LIBS env var),也没有任何变化。

有什么方法可以让它工作吗?我错过了什么吗?

如果您需要更多输出,我可以提供所需的内容,无论如何感谢您阅读本文!

编辑:

ldd /usr/lib/R/lib/libR.so 
[NVBLAS] Using devices :0 
    linux-vdso.so.1 (0x00007ffdb5bd6000)
    /opt/cuda/lib64/libnvblas.so (0x00007f4afaccd000)
    libblas.so => /usr/lib/libblas.so (0x00007f4afa6ea000)
    libm.so.6 => /usr/lib/libm.so.6 (0x00007f4afa3ec000)
    libreadline.so.6 => /usr/lib/libreadline.so.6 (0x00007f4afa1a1000)
    libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f4af9f31000)
    liblzma.so.5 => /usr/lib/liblzma.so.5 (0x00007f4af9d0b000)
    libbz2.so.1.0 => /usr/lib/libbz2.so.1.0 (0x00007f4af9afa000)
    libz.so.1 => /usr/lib/libz.so.1 (0x00007f4af98e4000)
    librt.so.1 => /usr/lib/librt.so.1 (0x00007f4af96dc000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f4af94d7000)
    libgomp.so.1 => /usr/lib/libgomp.so.1 (0x00007f4af92b5000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f4af9098000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f4af8cf3000)
    /usr/lib64/ld-linux-x86-64.so.2 (0x0000556509792000)
    libcublas.so.7.5 => /opt/cuda/lib64/libcublas.so.7.5 (0x00007f4af7414000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f4af7092000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f4af6e7b000)
    libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007f4af6c0e000)

所以除了奇怪的 [NVBLAS] Using devices :0 之外,至少 R 似乎知道 cuda nvblas 库...

【问题讨论】:

见the gcbd vignette and package -- 这正是每个 R 包都从R 获得其BLAS 和LAPACK 的确切问题。 @DirkEddelbuettel 感谢您的快速回答/评论!我已经阅读了小插图,您在第 3.4 节中列出了 alternative approach could consist of locally installing the dierent libraries into sub-directories of, say, /opt/blas . One could then use the environment variable LD_LIBRARY_PATH to select one of these directories at a time 我的 LD_LIBRARY_PATH 设置为 /opt/cuda/lib64/ 并且在我上次编辑中,我表明 R 知道 nvblas 库。请原谅我的专业知识,我仍然不明白我应该如何使它工作...... 我不知道它是否真的有帮助,但 gputools 已安装并在 R 中工作:&gt; library(gputools) &gt; N &lt;- 1e3 &gt; m &lt;- matrix(sample(100, size = N*N, replace = T), nrow = N) &gt; system.time(dist(m)) user system elapsed 4.000 0.000 3.998 &gt; &gt; system.time(gpuDist(m)) user system elapsed 0.174 0.010 0.185 【参考方案1】:

回答我自己的问题:是的,有可能并且使 R 指向正确的 (NV)BLAS 库就足够了,RcppArmadillo 将在正确的位置获取例程(您可能想阅读 Dirk Eddelbuettel 对问题来看看为什么)


现在谈谈我的问题的细节和自我回答的原因:

我认为问题不在我想的地方。

当在另一个终端上运行 nvidia-smi 而不是运行 Rscript arma_try_R.R 的终端时,例如

+------------------------------------------------------+                       
| NVIDIA-SMI 352.41     Driver Version: 352.41         |                       
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 860M    Off  | 0000:01:00.0     Off |                  N/A |
| N/A   64C    P0    N/A /  N/A |    945MiB /  2047MiB |     21%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     20962    C   /usr/lib64/R/bin/exec/R                         46MiB |
|    0     21598    C   nvidia-smi                                      45MiB |
+-----------------------------------------------------------------------------+

表示 GPU 确实在工作!

因此问题出在 nvprof 例程中,它无法检测到它,有时会冻结我的 Rscript。但这是另一个完全不相关的问题。

(我会等待接受它作为答案,看看是否有其他人来更聪明地解决它......)

【讨论】:

【参考方案2】:

如果您想在R 会话期间使用RcppArmadillonvbals(而不是批量运行R)来分享我自己的经验,在终端中打开R 之前LD_PRELOAD 就足够了,

export LD_PRELOAD=/usr/local/cuda-8.0/lib64/libnvblas.so

设置系统环境为

Sys.setenv("PKG_CXXFLAgs"="-I/usr/local/cuda-8.0/include")
Sys.setenv("PKG_LIBS"="-L/usr/local/cuda-8.0/lib64 -lnvblas")

使用 sourceCpp 编译并运行。希望这对某人有所帮助。

【讨论】:

以上是关于犰狳+NVBLAS 变成 RcppArmadillo+NVBLAS的主要内容,如果未能解决你的问题,请参考以下文章

什么是犰狳+Atlas、犰狳+OpenBLAS、犰狳+uBLAS、犰狳+MKL?

犰狳中的并行化

我怎么知道,犰狳正在我的 ARM 上使用 openblas?

PCHIP 犰狳函数

如何将向量转换为犰狳矩阵?

用犰狳加载大型矩阵