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

Posted

技术标签:

【中文标题】在 R foreach() 下并行运行时无法识别动态库依赖项【英文标题】:Dynamic library dependencies not recognized when run in parallel under R foreach() 【发布时间】:2021-02-09 13:03:18 【问题描述】:

我正在使用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,并拥有处理foreach() 循环的GA::ga(),在循环中我使用我自己的包中的一个函数,它调用Rfast职能。所以我希望有一个解决方案,我不必修改foreach() 调用

【问题讨论】:

我也尝试过export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib64/,并在foreach() 调用中添加了.packages = c('Rfast', 'RcppZiggurat')。都失败了。 您是否尝试过重新安装软件包Rfast 你为什么要在as a GitHub issue 和这里重复发帖? 对不起,@DirkEddelbuettel。我在虚弱和沮丧的时刻发布了 GitHub 问题。当我在一个月内提交论文时,我迫切需要代码才能工作。几天来我一直在尝试解决依赖关系,但它们只是一个接一个地弹出,我想放弃。但是在发布 GitHub 问题几个小时后,冷静下来后,我做了一些测试,意识到这是因为并行处理。所以我认为在这里发帖是合适的。我没有意识到交叉发布是不合适的。学过的知识。我道歉。 @RuiBarradas 是的,我尝试重新安装,首先只是RcppZiggurat,然后是Rfast,但并没有解决问题。 【参考方案1】:

以下工作没有问题。与问题中的代码不同,它首先检测可用内核的数量,创建一个集群并将其提供给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() 是从GA::ga() 调用的,它已经自己进行集群注册。在 Windows 上我不需要单独注册,代码就可以了。当我移植到 Linux 时,它失败了。 如果您的问题 (@Drumy) 是 GA::ga 没有成功导出库(我认为这是包中的错误),您是否尝试过 1) 更新包? 2)覆盖函数定义,手动添加导出。这可以使用函数定义的复制粘贴或使用edit(GA::ga) 手动完成。函数本身只有 1 次对 foreach 的调用,因此很容易修复。 谢谢奥利弗。是的,GA 包已更新(实际上是全新安装)。但我认为不只是GA,因为按照上面Rui的代码手动导出也不起作用。【参考方案2】:

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)

【讨论】:

感谢您的建议。我刚刚尝试了您的代码,但仍然出现同样的错误。 @Drumy 我删除了futureOf() 部分。如果你运行上面的代码,会出现什么错误?【参考方案3】:

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

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

然后我检查了RcppZiggurat的依赖关系,发现它依赖于另一个名为RcppGSL的包,它连接了R和GSL库。 Bingo,这就是调用 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 脚本中,我需要在运行我的 R 脚本之前添加module load gsl/2.1。我对此进行了测试,一切正常。

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

【讨论】:

以上是关于在 R foreach() 下并行运行时无法识别动态库依赖项的主要内容,如果未能解决你的问题,请参考以下文章

R foreach:并行读取和操作多个文件

R中的并行foreach共享内存

使用 foreach 进行并行处理时出错:“找不到函数“%dopar%””

与 foreach 并行预测 nnet 输出时 R 内存爆炸

在 R doParallel foreach 循环中运行 ovun.sample

使用 foreach 函数和 doParallel 库在 R 中嵌套 for 循环