如果没有root访问权限,当与参考BLAS链接时,运行具有调谐BLAS的R

任何人都可以告诉我为什么我无法通过以下方式成功testingR中OpenBLAS的dgemm性能(在dgemm中)?

  1. 链接R与“参考BLAS” libblas.so
  2. 用OpenBLAS库libopenblas.so编译我的C程序libopenblas.so
  3. 将生成的共享库mmperf.so到R中,调用R wrapper函数mmperf并报告dgemm性能。

第1点看起来很奇怪,但我别无select,因为我想在我想testing的机器上没有root权限,所以实际连接到OpenBLAS是不可能的。 通过“不成功”,我的意思是我的程序最终报告dgemm性能参考BLAS而不是OpenBLAS。 我希望有人能向我解释:

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

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

第1部分:testing前的系统环境

有两种方法可以调用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] 

这里我们需要selectRscript ,因为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部分:OpenBLAS

从OpenBLAS下载源文件和一个简单的make命令后,就可以生成libopenblas-<arch>-<release>.so-<version>forms的共享库。 请注意,我们将不具有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代码,Makefile和R代码

这里是一个C脚本mmperf.c计算mmperf.c乘以2平方的matrix大小为N

 #include <Rh> #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 mmperf.R

 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已正确链接,并且正确设置了运行时加载path。

第4部分:在R中testingOpenBLAS

让我们做

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

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

 GFLOPs = 1.11 

哎呀! 这是真正的参考BLAS性能不OpenBLAS(这是约8-9 GFLOPs)。

第5部分:为什么?

说实话,我不知道为什么会发生这种情况。 每一步似乎都能正常工作。 当调用R时是否会有微妙的变化? 例如,出于某种原因某种程度上,OpenBLAS库被BLAS忽略的可能性? 任何解释和解决scheme? 谢谢!

为什么我的方式不起作用

首先,UNIX上的共享库被设计为模仿归档库的工作方式(归档库在那里)。 特别是,如果你有libfoo.solibbar.so ,都定义了符号foo ,那么无论哪个库首先被加载的都是获胜的:从程序中的任何地方(包括来自libbar.so )的所有对foo引用将绑定到libfoo.sofoo的定义。

这模仿了如果你将程序与libfoo.alibbar.a联系起来的libbar.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),大概所有它是R exec 。 在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已经被加载了, libopenblas.so.0将作为mmperf.so一个依赖被加载, 实际上不会被用于任何事情。

是否有可能使其工作

大概。 我可以想到两个可能的解决方案:

  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.DLLFOO.DLL导入符号bar时,符号( bar )的名称导入该符号的DLLBAR.DLL )都将记录在FOO.DLL的导入表中。

这样可以很容易让RBLAS.DLL导入cgemv_ ,而MMPERF.DLLMMPERF.DLL导入相同的符号。

但是,这使得图书馆很难进行插入 ,并且与存档库的工作方式(即使在Windows上)完全不同。

对于哪个设计总体来说,意见不一,但是这两个系统都不可能改变它的模型。

UNIX可以模拟Windows风格的符号绑定:请参阅dlopen 手册页中的 RTLD_DEEPBIND 。 注意:这些是充满危险的,可能会混淆UNIX专家,没有被广泛使用,并可能有实施错误。

更新2:

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

是。

那么当我想调用它的时候,我应该明确地给出我的版本的可执行程序的路径,否则系统上的那个可能会被调用呢? 或者,我可以把这个路径放在环境变量$ PATH的第一个位置来欺骗系统吗?

无论哪种方式工作。

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

解决方案1:

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

谢谢俄罗斯人 ,我的问题终于解决了。 调查需要Linux系统调试和补丁方面的重要技能,我相信这是我学到的一笔宝贵的财富。 在这里,我会张贴一个解决方案,以及纠正我原来的职位的几点。

1关于调用R

在我原来的文章中,我提到有两种方法可以通过RRscript来启动R。 但是,我错误地夸大了他们的差异。 现在让我们通过一个重要的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 ,在我原来的文章中,我已经利用readelf -d来显示这个可执行文件会加载libR.so ,与libblas.so.3 。 根据@Employed俄罗斯的解释,首先加载的BLAS库会赢,所以我的原始方法将无法工作。
  3. 为了成功运行strace ,我们使用了惊人的文件/dev/null作为输入文件并在必要时输出文件。 例如, Rscript需要一个输入文件,而R需要这两个文件。 我们将null设备提供给这两者,以使命令顺利运行并且输出干净。 空设备是一个物理上存在的文件,但令人惊讶的。 读它时,它什么也没有; 写信给它,它丢弃一切。

2.作弊R

既然libblas.so会被加载,我们唯一能做的就是提供我们自己的这个库的版本。 正如我在原文中所说的,如果我们拥有root权限,通过使用update-alternatives --config libblas.so.3 ,这非常容易,这样系统Linux将帮助我们完成这个转换。 但@Employed俄罗斯提供了一个非常好的方式来欺骗没有root访问权限的系统: 让我们来看看R如何在启动时发现BLAS库,并确保在发现系统默认值之前提供我们的版本! 要监视如何找到并加载共享库,请使用环境变量LD_DEBUG

有许多Linux LD_ 环境变量 ,如man ld.so 。 这些变量可以在可执行文件之前分配,这样我们就可以改变程序的运行特性。 一些有用的变量包括:

  • LD_LIBRARY_PATH用于设置运行时库的搜索路径;
  • 用于跟踪查找和加载共享库的LD_DEBUG ;
  • LD_TRACE_LOADED_OBJECTS用于显示程序所有加载的库(行为类似于ldd );
  • LD_PRELOAD用于在所有其他库被查找之前,在开始时强制将库注入到程序;
  • 用于分析一个指定的共享库的LD_PROFILELD_PROFILE_OUTPUT 。 R用户谁已经阅读第3.4.1.1 写入R扩展 sprof应该记得,这是用来分析从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先找到它,那么问题就解决了。

首先在我们的OpenBLAS库libopenblas.so工作路径中创建一个符号链接libblas.so.3 ,然后展开与我们的工作路径(并导出它)的默认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:

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

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

原文的第2部分所示,共享的BLAS库libopenblas.so具有SONAME libopenblas.so.0SONAME是动态库加载程序在运行时要查找的内部名称,因此我们需要使用此SONAMElibopenblas.so进行符号链接:

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

那么我们出口它:

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

请注意,即使libopenblas.so.0位于$(pwd)下, libopenblas.so.0完整路径 libopenblas.so.0需要被提供给LD_PRELOAD才能成功加载。

现在我们启动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中看到的与我们自己版本的libblas.so.3作弊的R libblas.so.3 ,我们可以看到这一点

  • libopenblas.so.0被加载,因此首先被Rscript找到;
  • 找到Rscript后, 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