如果没有根访问权限,当 R 与引用 BLAS 链接时,请使用优化的 BLAS 运行 R



谁能告诉我为什么我不能通过以下方式在R中成功测试OpenBLAS的dgemm性能(在GFLOPs中(?

  1. 将 R 与"参考 BLAS"libblas.so链接
  2. 使用 OpenBLAS 库libopenblas.so编译我的 C 程序mmperf.c
  3. 将生成的共享库mmperf.so加载到 R 中,调用 R 包装器函数mmperf并报告 GFLOP 中的dgemm性能。

第 1 点看起来很奇怪,但我别无选择,因为我在要测试的机器上没有 root 访问权限,因此实际链接到 OpenBLAS 是不可能的。通过"不成功">,我的意思是我的程序最终报告dgemm性能以供参考 BLAS 而不是 OpenBLAS。我希望有人能向我解释:

  1. 为什么我的方式行不通;
  2. 是否有可能让它工作(这很重要,因为如果不可能,我必须编写一个 C main函数并在 C 程序中完成我的工作。

我已经调查了这个问题两天,在这里我将包括各种系统输出来帮助您进行诊断。为了使事情可重现,我还将包括代码,makefile以及shell命令。

第 1 部分:测试前的系统环境

有两种

方法可以调用 R,使用 RRscript 。调用它们时加载的内容存在一些差异:

~/Desktop/dgemm$ readelf -d $(R RHOME)/bin/exec/R | grep "NEEDED"
0x00000001 (NEEDED)         Shared library: [libR.so]
0x00000001 (NEEDED)         Shared library: [libpthread.so.0]
0x00000001 (NEEDED)         Shared library: [libc.so.6]
~/Desktop/dgemm$ readelf -d $(R RHOME)/bin/Rscript | grep "NEEDED"
0x00000001 (NEEDED)         Shared library: [libc.so.6]

这里我们需要选择 Rscript ,因为R加载libR.so,它会自动加载引用 BLAS libblas.so.3

~/Desktop/dgemm$ readelf -d $(R RHOME)/lib/libR.so | grep blas
0x00000001 (NEEDED)         Shared library: [libblas.so.3]
~/Desktop/dgemm$ ls -l /etc/alternatives/libblas.so.3
... 31 May /etc/alternatives/libblas.so.3 -> /usr/lib/libblas/libblas.so.3.0
~/Desktop/dgemm$ readelf -d /usr/lib/libblas/libblas.so.3 | grep SONAME
0x0000000e (SONAME)         Library soname: [libblas.so.3]

相比之下,Rscript提供了更清洁的环境。

第 2 部分:开放BLAS

从 OpenBLAS 下载源文件和简单的make命令后,可以生成表单libopenblas-<arch>-<release>.so-<version>的共享库。请注意,我们将没有root访问权限来安装它;相反,我们将这个库复制到我们的工作目录中 ~/Desktop/dgemm 并将其重命名为 libopenblas.so .同时我们必须制作另一个名称为 libopenblas.so.0 的副本,因为这是运行时加载器将寻找的 SONAME

~/Desktop/dgemm$ readelf -d libopenblas.so | grep "RPATH|SONAME"
0x0000000e (SONAME)         Library soname: [libopenblas.so.0]

请注意,没有给出 RPATH 属性,这意味着该库旨在放在/usr/lib中,我们应该调用 ldconfig 将其添加到 ld.so.cache 中。但同样,我们没有root访问权限来执行此操作。事实上,如果可以做到这一点,那么所有的困难都消失了。然后,我们可以使用update-alternatives --config libblas.so.3有效地将R链接到OpenBLAS。

第 3 部分:C 代码、生成文件和 R 代码

这是一个 C 脚本mmperf.c计算大小为 N 的 2 个平方矩阵的 GFLOP:

#include <R.h>
#include <Rmath.h>
#include <Rinternals.h>
#include <R_ext/BLAS.h>
#include <sys/time.h>
/* standard C subroutine */
double mmperf (int n) {
  /* local vars */
  int n2 = n * n, tmp; double *A, *C, one = 1.0;
  struct timeval t1, t2; double elapsedTime, GFLOPs;
  /* simulate N-by-N matrix A */
  A = (double *)calloc(n2, sizeof(double));
  GetRNGstate();
  tmp = 0; while (tmp < n2) {A[tmp] = runif(0.0, 1.0); tmp++;}
  PutRNGstate();
  /* generate N-by-N zero matrix C */
  C = (double *)calloc(n2, sizeof(double));
  /* time 'dgemm.f' for C <- A * A + C */
  gettimeofday(&t1, NULL);
  F77_CALL(dgemm) ("N", "N", &n, &n, &n, &one, A, &n, A, &n, &one, C, &n);
  gettimeofday(&t2, NULL);
  /* free memory */
  free(A); free(C);
  /* compute and return elapsedTime in microseconds (usec or 1e-6 sec) */
  elapsedTime = (double)(t2.tv_sec - t1.tv_sec) * 1e+6;
  elapsedTime += (double)(t2.tv_usec - t1.tv_usec);
  /* convert microseconds to nanoseconds (1e-9 sec) */
  elapsedTime *= 1e+3;
  /* compute and return GFLOPs */
  GFLOPs = 2.0 * (double)n2 * (double)n / elapsedTime;
  return GFLOPs;
  }
/* R wrapper */
SEXP R_mmperf (SEXP n) {
  double GFLOPs = mmperf(asInteger(n));
  return ScalarReal(GFLOPs);
  }

下面是一个简单的 R 脚本mmperf.R用于报告案例N = 2000的 GFLOP

mmperf <- function (n) {
  dyn.load("mmperf.so")
  GFLOPs <- .Call("R_mmperf", n)
  dyn.unload("mmperf.so")
  return(GFLOPs)
  }
GFLOPs <- round(mmperf(2000), 2)
cat(paste("GFLOPs =",GFLOPs, "n"))

最后有一个简单的生成文件来生成共享库mmperf.so

mmperf.so: mmperf.o
    gcc -shared -L$(shell pwd) -Wl,-rpath=$(shell pwd) -o mmperf.so mmperf.o -lopenblas
mmperf.o: mmperf.c
    gcc -fpic -O2 -I$(shell Rscript --default-packages=base --vanilla -e 'cat(R.home("include"))') -c mmperf.c

将所有这些文件放在工作目录 ~/Desktop/dgemm 下,然后编译它:

~/Desktop/dgemm$ make
~/Desktop/dgemm$ readelf -d mmperf.so | grep "NEEDED|RPATH|SONAME"
0x00000001 (NEEDED)            Shared library: [libopenblas.so.0]
0x00000001 (NEEDED)            Shared library: [libc.so.6]
0x0000000f (RPATH)             Library rpath: [/home/zheyuan/Desktop/dgemm]

输出向我们保证 OpenBLAS 已正确链接,并且运行时加载路径已正确设置。

第 4 部分:在 R 中测试 OpenBLAS

让我们做

~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R

请注意,我们的脚本只需要 R 中的base包,--vanilla用于忽略 R 启动时的所有用户设置。在我的笔记本电脑上,我的程序返回:

GFLOPs = 1.11

哎呀!这是真正的参考 BLAS 性能,而不是 OpenBLAS(大约 8-9 GFLOP(。

第 5 部分:为什么?

老实说,我不知道为什么会这样。每一步似乎都正常工作。调用 R 时是否发生了一些微妙的事情?例如,由于某种原因,OpenBLAS 库在某个时候被引用 BLAS 覆盖的可能性是否可能?有什么解释和解决方案吗?谢谢!

为什么我的方式不起作用

首先,UNIX 上的共享库旨在模仿档案库的工作方式(档案库首先出现(。特别是这意味着如果你有 libfoo.solibbar.so ,两者都定义了符号foo,那么首先加载的库是获胜的库:所有从程序内任何地方(包括来自libbar.so(对foo的引用都将绑定到libfoo.sofoo的定义。

这模拟了将程序与libfoo.alibbar.a链接时会发生什么,其中两个存档库定义了相同的符号foo。有关存档链接的更多信息,请单击此处。

从上面应该很清楚,如果libblas.so.3libopenblas.so.0定义了同一组符号(它们确实如此(,并且如果首先将libblas.so.3加载到进程中,则永远不会调用libopenblas.so.0例程。

其次,你已经正确地决定,既然R直接链接到libR.so,既然libR.so直接链接到libblas.so.3,那么保证libopenblas.so.0会输掉这场战斗。

但是,您错误地认为Rscript更好,但事实并非如此:Rscript是一个很小的二进制文件(在我的系统上为 11K;相比之下,libR.so 为 2.4MB(,大约它所做的只是exec R。这在strace输出中是微不足道的:

strace -e trace=execve /usr/bin/Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["/usr/bin/Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 42 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 43 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89625, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89626, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 51 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89630, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

这意味着,当您的脚本开始执行时,libblas.so.3已经加载,并且将作为mmperf.so依赖项加载的libopenblas.so.0实际上不会用于任何用途。

是否有可能使其工作

可能。我可以想到两种可能的解决方案:

  1. 假装libopenblas.so.0实际上是libblas.so.3
  2. 针对libopenblas.so重建整个R包。

对于#1,您需要ln -s libopenblas.so.0 libblas.so.3然后通过适当设置LD_LIBRARY_PATH,确保在系统副本之前找到libblas.so.3的副本。

这似乎对我有用:

mkdir /tmp/libblas
# pretend that libc.so.6 is really libblas.so.3
cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/libblas/libblas.so.3
LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null
Error in dyn.load(file, DLLpath = DLLpath, ...) :
  unable to load shared object '/usr/lib/R/library/stats/libs/stats.so':
  /usr/lib/liblapack.so.3: undefined symbol: cgemv_
During startup - Warning message:
package ‘stats’ in options("defaultPackages") was not found

请注意我是如何得到一个错误的(我的"假装"libblas.so.3没有定义预期的符号,因为它实际上是libc.so.6的副本(。

您还可以确认以这种方式加载的libblas.so.3版本:

LD_DEBUG=libs LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null |& grep 'libblas.so.3'
     91533: find library=libblas.so.3 [0]; searching
     91533:   trying file=/usr/lib/R/lib/libblas.so.3
     91533:   trying file=/usr/lib/x86_64-linux-gnu/libblas.so.3
     91533:   trying file=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server/libblas.so.3
     91533:   trying file=/tmp/libblas/libblas.so.3
     91533: calling init: /tmp/libblas/libblas.so.3

对于#2,你说:

我在想要测试的机器上没有root访问权限,因此无法实际链接到OpenBLAS。

但这似乎是一个虚假的论点:如果你可以构建libopenblas,当然你也可以构建自己的R版本。

更新:

你在开头提到libblas.so.3和libopenblas.so.0定义了相同的符号,这是什么意思?它们具有不同的SONAME,这是否不足以通过系统区分它们?

符号和SONAME彼此无关

您可以在 readelf -Ws libblas.so.3readelf -Ws libopenblas.so.0 的输出中看到符号。与BLAS相关的符号,如cgemv_,将出现在两个库中。

你对SONAME的困惑可能来自Windows。Windows上的DLL设计完全不同。特别是,当FOO.DLLBAR.DLL导入符号bar时,符号的名称(bar(和导入符号的DLL(BAR.DLL(记录在FOO.DLL的导入表中。

这使得R很容易从BLAS.DLL导入cgemv_,而MMPERF.DLLOPENBLAS.DLL导入相同的符号。

但是,这使得库交互变得困难,并且与存档库的工作方式完全不同(即使在Windows上(。

对于哪种设计总体上更好,意见不一,但这两个系统都不太可能改变其模型。

UNIX 可以通过多种方式模拟 Windows 样式的符号绑定:请参见 dlopen 手册页中的RTLD_DEEPBIND。当心:这些都充满了危险,可能会使UNIX专家感到困惑,没有被广泛使用,并且可能有实现错误。

更新 2:

你的意思是我编译 R 并将其安装在我的主目录下?

是的。

然后,当我想调用它时,我应该显式提供我的可执行程序版本的路径,否则可能会调用系统上的那个?或者,我可以将此路径放在环境变量的第一个位置$PATH以欺骗系统吗?

无论哪种方式都有效。

*******

解决方案 2:

****

***

在这里,我们提供了另一种解决方案,通过利用解决方案1中提到的环境变量LD_PRELOADLD_PRELOAD的使用更加"残酷",因为它强制在任何其他程序之前将给定的库加载到程序中,甚至在 C 库libc.so之前!这通常用于 Linux 开发中的紧急修补。

原始帖子的第 2 部分所示,共享的 BLAS 库libopenblas.so具有 SONAME libopenblas.so.0SONAME 是动态库加载器在运行时会寻找的内部名称,因此我们需要创建一个符号链接来使用此 SONAME 进行libopenblas.so

~/Desktop/dgemm$ ln -sf libopenblas.so libopenblas.so.0

然后我们导出它:

~/Desktop/dgemm$ export LD_PRELOAD=$(pwd)/libopenblas.so.0

请注意,需要将 libopenblas.so.0 的完整路径馈送到 LD_PRELOAD 才能成功加载,即使libopenblas.so.0处于$(pwd) 之下。

现在我们启动Rscript并检查LD_DEBUG会发生什么:

~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
  4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4865: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4868: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
  4870: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4869: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4867: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
  4860: find library=libblas.so.3 [0]; searching
  4860:   trying file=/usr/lib/R/lib/libblas.so.3
  4860:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
  4860:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
  4860:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
  4860:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
  4860:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
  4860:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
  4860:   trying file=/usr/lib/libblas.so.3
  4860: calling init: /usr/lib/libblas.so.3
  4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4874: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4876: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
  4860: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
  4860: calling fini: /usr/lib/libblas.so.3 [0]

我们在解决方案1中看到的相比,我们将R与我们自己的libblas.so.3版本作弊,我们可以看到

  • libopenblas.so.0首先加载,因此首先由Rscript找到;
  • 找到libopenblas.so.0后,Rscript继续搜索和加载libblas.so.3。但是,这不会受到原始答案中解释的"先到先得">规则的影响。

很好,一切正常,所以我们测试我们的mmperf.c程序:

~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 9.62

结果 9.62 大于我们在早期解决方案中看到的 8.77 只是偶然的。作为使用 OpenBLAS 的测试,我们不会多次运行实验以获得更精确的结果。

然后像往常一样,我们最后取消设置环境变量:

~/Desktop/dgemm$ unset LD_PRELOAD

*******

解决方案 1:

****

***

多亏了俄语,我的问题终于解决了。调查需要Linux系统调试和修补方面的重要技能,我相信这是我学到的一笔巨大的财富。在这里,我将发布一个解决方案,并纠正我原始帖子中的几点。

1 关于调用 R

在我的原始帖子中,我提到有两种方法可以通过RRscript启动R。但是,我错误地夸大了它们的差异。现在让我们通过一个重要的 Linux 调试工具strace来研究它们的启动过程(见man strace(。我们在 shell 中键入命令后,实际上会发生很多有趣的事情,我们可以使用

strace -e trace=process [command]

跟踪涉及进程管理的所有系统调用。因此,我们可以观察进程的分叉、等待和执行步骤。虽然手册页中没有说明,但@Employed俄语表明可以只指定process的子类,例如,execve执行步骤。

对于R我们有

~/Desktop/dgemm$ time strace -e trace=execve R --vanilla < /dev/null > /dev/null
execve("/usr/bin/R", ["R", "--vanilla"], [/* 70 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5777, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--vanilla"], [/* 79 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5778, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real    0m0.345s
user    0m0.256s
sys     0m0.068s

而对于Rscript我们有

~/Desktop/dgemm$ time strace -e trace=execve Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 70 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 71 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5822, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5823, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 80 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5827, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real    0m0.063s
user    0m0.020s
sys     0m0.028s

我们还使用time来测量启动时间。请注意,

  1. RscriptR快约5.5倍。一个原因是R将在启动时加载 6 个默认包,而Rscript仅通过控制加载一个base包:--default-packages=base 。但即使没有此设置,它仍然要快得多。
  2. 最后两个启动过程都指向$(R RHOME)/bin/exec/R,在我的原始帖子中,我已经利用readelf -d来表明该可执行文件将加载与libblas.so.3链接的libR.so。根据@Employed俄语的解释,首先加载的 BLAS 库将获胜,所以我的原始方法不可能起作用。
  3. 为了成功运行strace,我们在必要时使用惊人的文件/dev/null作为输入文件和输出文件。例如,Rscript需要输入文件,而R需要两者。我们将空设备提供给两者,以使命令平稳运行并且输出干净。空设备是一个物理存在的文件,但效果惊人。从中读取时,它不包含任何内容;在写入它时,它会丢弃所有内容。

2. 作弊 R

现在,由于无论如何都会加载libblas.so,我们唯一能做的就是提供我们自己的这个库版本。正如我在原始帖子中所说,如果我们有root访问权限,这真的很容易,通过使用 update-alternatives --config libblas.so.3 ,以便系统Linux将帮助我们完成此切换。但是@Employed俄语提供了一种很棒的方法来欺骗系统而无需root访问权限:让我们检查R如何在启动时找到BLAS库,并确保在找到系统默认值之前提供我们的版本!要监视如何查找和加载共享库,请使用环境变量 LD_DEBUG

有许多带有前缀LD_的Linux环境变量,如man ld.so中所述。这些变量可以在可执行文件之前分配,以便我们可以更改程序的运行功能。一些有用的变量包括:

  • LD_LIBRARY_PATH用于设置运行时库搜索路径;
  • LD_DEBUG用于跟踪共享库的查找和加载;
  • LD_TRACE_LOADED_OBJECTS用于显示程序加载的所有库(行为类似于ldd(;
  • LD_PRELOAD用于在
  • 一开始就强制将库注入程序,然后再查找所有其他库;
  • LD_PROFILELD_PROFILE_OUTPUT,用于分析一个指定的共享库。阅读过编写 R 扩展的第 3.4.1.1 节 sprof 的 R 用户应该记得,这用于从 R 中分析编译的代码。

LD_DEBUG的使用可以通过以下方式查看:

~/Desktop/dgemm$ LD_DEBUG=help cat
Valid options for the LD_DEBUG environment variable are:
  libs        display library search paths
  reloc       display relocation processing
  files       display progress for input file
  symbols     display symbol table processing
  bindings    display information about symbol binding
  versions    display version dependencies
  scopes      display scope information
  all         all previous options combined
  statistics  display relocation statistics
  unused      determined unused DSOs
  help        display this help message and exit
  To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable.

在这里,我们对使用LD_DEBUG=libs特别感兴趣。例如

~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
  5974: find library=libblas.so.3 [0]; searching
  5974:   trying file=/usr/lib/R/lib/libblas.so.3
  5974:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
  5974:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
  5974:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
  5974:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
  5974:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
  5974:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
  5974:   trying file=/usr/lib/libblas.so.3
  5974: calling init: /usr/lib/libblas.so.3
  5974: calling fini: /usr/lib/libblas.so.3 [0]

显示了 R 程序尝试查找和加载libblas.so.3的各种尝试。因此,如果我们可以提供我们自己的libblas.so.3版本,并确保R首先找到它,那么问题就解决了。

让我们首先在工作路径中创建一个符号链接libblas.so.3到 OpenBLAS 库libopenblas.so,然后使用我们的工作路径展开默认LD_LIBRARY_PATH(并将其导出(:

~/Desktop/dgemm$ ln -sf libopenblas.so libblas.so.3
~/Desktop/dgemm$ export LD_LIBRARY_PATH = $(pwd):$LD_LIBRARY_PATH  ## put our working path at top

现在让我们再次检查库加载过程:

~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
  6063: find library=libblas.so.3 [0]; searching
  6063:   trying file=/usr/lib/R/lib/libblas.so.3
  6063:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
  6063:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
  6063:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
  6063:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
  6063:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
  6063:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
  6063:   trying file=/home/zheyuan/Desktop/dgemm/libblas.so.3
  6063: calling init: /home/zheyuan/Desktop/dgemm/libblas.so.3
  6063: calling fini: /home/zheyuan/Desktop/dgemm/libblas.so.3 [0]

伟大!我们成功地欺骗了R。

3. 使用OpenBLAS进行实验

~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 8.77

现在,一切按预期工作!

4. 未设置LD_LIBRARY_PATH(为安全起见(

使用后取消设置LD_LIBRARY_PATH是一种很好的做法。

~/Desktop/dgemm$ unset LD_LIBRARY_PATH

最新更新