在R foreach下并行运行时无法识别动态库依赖项

问题描述

我正在使用Rfast软件包,它将导入软件包RcppZiggurat。我在Linux群集(Red Hat 6.1)上运行R 3.6.3。这些软件包安装在我的本地目录中,但R安装在系统范围内。

我直接调用Rfast函数(例如colsums())时效果很好。但是,当我像下面这样在foreach()循环中调用它们时(编辑:我添加代码注册集群,正如Rui Barradas指出的那样,但这不能解决问题)。

library(Rfast)
library(doParallel)
library(foreach)

cores <- detectCores()
cl <- makeCluster(cores)
registerDoParallel(cl)

A <- matrix(rnorm(1e6),1000,1000)
cm <- foreach(n = 1:4,.packages = 'Rfast') %dopar% colmeans(A)

stopCluster(cl)

然后我得到一个错误

unable to load shared object '/home/users/sutd/R/x86_64-pc-linux-gnu-library/3.6/RcppZiggurat/libs/RcppZiggurat.so':
  libgsl.so.0: cannot open shared object file: No such file or directory

以某种方式,在直接调用动态库时会被识别,而在foreach()调用时则无法识别。

我知道libgsl.so位于/usr/lib64/中,因此我在R脚本的开头添加了以下行

Sys.setenv(LD_LIBRARY_PATH=paste("/usr/lib64/",Sys.getenv("LD_LIBRARY_PATH"),sep = ":"))

但这没用。

我也尝试做dyn.load('/usr/lib64/libgsl.so'),但出现以下错误

Error in dyn.load("/usr/lib64/libgsl.so") : unable to load shared object '/usr/lib64/libgsl.so': 
/usr/lib64/libgsl.so: undefined symbol: cblas_ctrmv

如何在foreach()并行循环中使用依赖项?

注意

在实际使用案例中,我使用的是遗传算法软件包GA,并且有GA::ga()处理foreach()循环,并且在循环中我在自己的软件包中使用了一个函数调用Rfast函数。因此,我希望有一个无需修改foreach()调用解决方案。

解决方法

以下内容没有问题。与问题中的代码不同,它首先检测可用核心的数量,创建集群并使其对foreach可用。

library(Rfast)
library(doParallel)
library(foreach)

cores <- detectCores()
cl <- makeCluster(cores)
registerDoParallel(cl)

set.seed(2020)
A <- matrix(rnorm(1e6),1000,1000)
cm <- foreach(n = 1:4,.combine = rbind,.packages = "Rfast") %dopar% {
  colmeans(A)
}

stopCluster(cl)

str(cm)
#num [1:4,1:1000] -0.02668 -0.02668 -0.02668 -0.02668 0.00172 ...
# - attr(*,"dimnames")=List of 2
#  ..$ : chr [1:4] "result.1" "result.2" "result.3" "result.4"
#  ..$ : NULL
,

foreach软件包非常适合当时。但是,现在,应该仅使用future进行并行计算,以便进行静态代码分析以正确地导出到工作程序。结果,在future方法下,不需要向.packages=注册软件包。此外,future反映了通常的 R 代码,只是将输出变量设置为listenv略有变化。例如,我们有:

library("future")
library("listenv")
library("Rfast")

plan(tweak(multiprocess,workers = 2L))
# For all cores,directly use:
# plan(multiprocess)

# Generate matrix once
A <- matrix(rnorm(1e6),1000)
# Setup output
x <- listenv()

# Iterate 4 times
for(i in 1:4) {
  # On each core,compute the colmeans()
  x[[i]] %<-% {
    colmeans(A)
    # For better control over function applies,use a namespace call
    # e.g. Rfast::colmeans(A)
  }
}

# Switch from listenv to list
output <- as.list(x)
,

由于@RuiBarradas和@coatless的回答,我意识到问题不在于foreach(),因为(1)当我也使用future运行代码时也出现了问题,并且( 2)当我没有注册集群时,即使使用了错误的调用,它也发生在foreach()代码中。如果没有注册集群,foreach()将发出警告并改为以顺序模式运行。但这没有发生。

因此,我意识到问题甚至在foreach()调用之前就已经发生了。在日志中,它出现在消息Loading package RcppZiggurat之后。加载此软件包时,肯定出了点问题。

然后,我检查了RcppZiggurat的依赖关系,发现它依赖于另一个名为RcppGSL的软件包,该软件包将R和GSL库连接。宾果游戏,这是调用RcppZiggurat时需要libgsl.so.0的地方。

因此,我制作了一个名为test-gsl.R的R脚本,该脚本具有以下两行。

library(RcppZiggurat)
print(‘OK’)

现在,我在头节点上运行以下

$ module load R/3.6.3
$ Rscript test-gsl.R

一切正常。会打印出“确定”。

但是,如果我在计算节点上提交作业,此操作将无效。首先,称为test.sh的PBS脚本如下

### Resources request
#PBS -l select=1:ncpus=1:mem=1GB

### Walltime
#PBS -l walltime=00:01:00

echo Working directory is $PBS_O_WORKDIR
cd $PBS_O_WORKDIR

### Run R
module load R/3.6.3
Rscript test-gsl.R

然后我跑了

qsub test.sh

错误弹出。这意味着我的系统上的计算节点和头节点之间存在某些差异,与程序包无关。我联系了系统管理员,他向我解释说GSL库在默认路径的头节点上可用,但在计算节点上不可用。因此,在我的Shell脚本中,我需要在运行R脚本之前添加module load gsl/2.1。我测试了一下,一切正常。

该解决方案似乎很简单,但是我对Linux管理了解得很少。经过询问并尝试(而不是盲目地)进行了很多事情之后,我才终于找到了这种解决方案。因此,感谢那些提供帮助的人,以及Mea culpa不能在一开始就准确地描述问题。