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

Dynamic library dependencies not recognized when run in parallel under 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, 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) 当我 运行 使用 [=15] 的代码时出现问题=] 也是,并且 (2) 当我没有注册集群时,即使调用错误,它也会与 foreach() 代码一起出现。当没有集群注册时,foreach() 将抛出警告并 运行s 改为顺序模式。但那并没有发生。

因此,我意识到问题一定是在 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

一切正常。打印了‘OK’。

但是如果我在计算节点上提交作业,这不起作用。一、PBS脚本,名为test.sh,如下

### 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 脚本中,我需要在 运行 之前添加 module load gsl/2.1 我的 R 脚本。我测试了一下,一切正常。

这个解决方案看起来很简单,但我对 Linux 管理知之甚少,无法实现它。只有在四处询问并尝试(相当盲目地)很多事情之后,我才终于找到了这个解决方案。所以感谢那些提供帮助的人,并感谢在开始时无法准确描述问题。