没有 root 访问权限,运行 R 与参考 BLAS 链接时调整了 BLAS

Without root access, run R with tuned BLAS when it is linked with reference BLAS

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

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

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

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

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

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

有两种调用 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,会自动加载引用BLASlibblas.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 部分:OpenBLAS

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把它加到[=44=里面].但是同样,我们没有 root 访问权限来执行此操作。其实,如果能做到这一点,那么一切困难都迎刃而解了。然后我们可以使用 update-alternatives --config libblas.so.3 有效地 link R 到 OpenBLAS。

第 3 部分:C 代码、Makefile 和 R 代码

这是一个 C 脚本 mmperf.c 计算乘以 2 个大小为 N:

的方阵的 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"))

终于有了生成共享库的简单makefile 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 已正确 linked,并且 运行 时间加载路径已正确设置。

第 4 部分:在 R 中测试 OpenBLAS

开始吧

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

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

GFLOPs = 1.11

糟糕!这是真正参考 BLAS 性能而不是 OpenBLAS(大约 8-9 GFLOPs)。

第 5 部分:为什么?

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

why my way does not work

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

这模拟了将程序链接到 libfoo.alibbar.a 时会发生的情况,其中两个存档库都定义了相同的符号 foo。有关存档链接的更多信息 here.

从上面应该很清楚,如果libblas.so.3libopenblas.so.0定义了相同的符号集(它们),如果libblas.so.3 首先加载到进程中,然后来自 libopenblas.so.0 的例程将 永远不会 被调用。

其次,您已经正确地决定,由于 R 直接链接到 libR.so,并且由于 libR.so 直接链接到 libblas.so.3,因此可以保证 libopenblas.so.0会输掉战斗

但是,您错误地 认为 Rscript 更好,但事实并非如此:Rscript 是一个 tiny 二进制文件(在我的系统上为 11K;与 libR.so 的 2.4MB 相比),它所做的大约是 Rexec。这在 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 不会 实际上可用于任何事情。

is it possible at all to make it work

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

  1. 假设 libopenblas.so.0 实际上是 libblas.so.3
  2. 根据 libopenblas.so.
  3. 重建整个 R

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

这似乎对我有用:

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

请注意我是如何出错的(我的 "pretend" 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,您说:

I have no root access on machines I want to test, so actual linking to OpenBLAS is impossible.

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

版本

更新:

You mentioned in the beginning that libblas.so.3 and libopenblas.so.0 define the same symbol, what does this mean? They have different SONAME, is that insufficient to distinguish them by the system?

符号和 SONAME 彼此没有任何关系

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

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

这使得 RBLAS.DLL 导入 cgemv_ 变得容易,而 MMPERF.DLLOPENBLAS.DLL.[=82= 导入相同的符号]

然而,这使得 library interpositioning 变得困难,并且与存档库的工作方式完全不同(即使在 Windows 上)。

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

UNIX 有多种方法可以模拟 Windows 式符号绑定:请参阅 dlopen man page 中的 RTLD_DEEPBIND。当心:这些充满危险,可能会让 UNIX 专家感到困惑,没有被广泛使用,并且可能存在实现错误。

更新二:

you mean I compile R and install it under my home directory?

是的。

Then when I want to invoke it, I should explicitly give the path to my version of executable program, otherwise the one on the system might be invoked instead? Or, can I put this path at the first position of environment variable $PATH to cheat the system?

两种方法都行。

*********************

解决方案 1:

*********************

感谢Employed Russian,我的问题终于解决了。调查需要 Linux 系统调试和修补 方面的重要技能,我相信这是我学到的宝贵财富。在这里我会post一个解决方案,同时更正我原来的几个地方post。

1 关于调用R

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

strace -e trace=process [command]

跟踪所有涉及进程管理的系统调用。因此,我们可以观察进程的派生、等待和执行步骤。尽管手册页中没有说明,@Employed Russian 表明可以仅指定 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,而在我原来的post中,我已经利用readelf -d表明这个可执行文件将加载libR.so,用 libblas.so.3 编辑 link。按照@Employed Russian的解释,先加载的BLAS库会获胜,所以我原来的方法是行不通的。
  3. 为了成功 运行 strace,我们在必要时使用了 amazing 文件 /dev/null 作为输入文件和输出文件。例如,Rscript 需要输入文件,而 R 需要两者。我们将 null 设备提供给两者,以使命令 运行 顺畅且输出干净。空设备是一个物理上存在的文件,但效果惊人。从中读取时,它什么也不包含;在写入时,它会丢弃所有内容。

2。作弊R

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

有多个 Linux environment variables with prefix LD_, as documented in man ld.so。这些变量可以在可执行文件之前赋值,这样我们就可以改变程序的 运行ning 特性。一些有用的变量包括:

  • LD_LIBRARY_PATH用于设置运行时间库搜索路径;
  • LD_DEBUG 用于跟踪共享库的查找和加载;
  • LD_TRACE_LOADED_OBJECTS 用于显示程序加载的所有库(行为类似于 ldd);
  • LD_PRELOAD 用于在一开始就强制将库注入程序,然后再查找所有其他库;
  • LD_PROFILELD_PROFILE_OUTPUT 用于分析 one 指定的共享库。阅读过 Writing R extensions3.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先找到它,那么问题就解决了。

让我们首先在我们的工作路径中创建一个符号 link 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

*********************

解决方案 2:

*********************

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

的第 2 部分所示,共享 BLAS 库 libopenblas.so 具有 SONAME libopenblas.so.0SONAME 是动态库加载器将在 运行 时查找的内部名称,因此我们需要使用此符号 link 到 libopenblas.so SONAME:

~/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]

与我们在 中看到的通过用我们自己的 libblas.so.3 版本欺骗 R 进行比较,我们可以看到

  • 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