16

我已经编写了一个函数Rcpp并用inline. 现在,我想在不同的内核上并行运行它,但我遇到了一个奇怪的错误。这是一个最小的示例,该函数funCPP1可以自行编译并运行良好,但不能被snow'sclusterCall函数调用。该函数作为单个进程运行良好,但在并行运行时会出现以下错误:

Error in checkForRemoteErrors(lapply(cl, recvResult)) : 
  2 nodes produced errors; first error: NULL value passed as symbol address

这是一些代码:

## Load and compile
library(inline)
library(Rcpp)
library(snow)
src1 <- '
     Rcpp::NumericMatrix xbem(xbe);
     int nrows = xbem.nrow();
     Rcpp::NumericVector gv(g);
     for (int i = 1; i < nrows; i++) {
      xbem(i,_) = xbem(i-1,_) * gv[0] + xbem(i,_);
     }
     return xbem;
'
funCPP1 <- cxxfunction(signature(xbe = "numeric", g="numeric"),body = src1, plugin="Rcpp")

## Single process
A <- matrix(rnorm(400), 20,20)
funCPP1(A, 0.5)

## Parallel
cl <- makeCluster(2, type = "SOCK") 
clusterExport(cl, 'funCPP1') 
clusterCall(cl, funCPP1, A, 0.5)
4

3 回答 3

19

想一想——内联是做什么的?它为您创建一个 C/C++ 函数,然后将其编译并链接到一个可动态加载的共享库中。那一个坐在哪里?在 R 的临时目录中。

因此,您通过将调用该共享库的 R 前端发送到另一个进程(它有另一个临时目录!!)尝试了正确的事情,但这并没有在那里获得 dll / so 文件。

因此建议是创建一个本地包,安装它并让两个雪进程加载并调用它。

(和往常一样:rcpp-devel 列表中可能有质量更好的答案,该列表被更多的 Rcpp 贡献者阅读,而不是 SO。)

于 2011-05-20T15:47:35.073 回答
1

老问题,但是我在浏览顶部的 Rcpp 标签时偶然发现了它,所以也许这个答案仍然有用。

我认为当您编写的代码完全调试并执行您想要的操作时,Dirk 的答案是正确的,但是为示例中的一小段代码编写新包可能会很麻烦。您可以做的是导出代码块,导出编译源代码并运行帮助程序的“帮助程序”函数。这将使 CXX 函数可用,然后使用另一个辅助函数来调用它。例如:

# Snow must still be installed, but this functionality is now in "parallel" which ships with base r.
library(parallel)

# Keep your source as an object
src1 <- '
     Rcpp::NumericMatrix xbem(xbe);
     int nrows = xbem.nrow();
     Rcpp::NumericVector gv(g);
     for (int i = 1; i < nrows; i++) {
      xbem(i,_) = xbem(i-1,_) * gv[0] + xbem(i,_);
     }
     return xbem;
'
# Save the signature
sig <- signature(xbe = "numeric", g="numeric")

# make a function that compiles the source, then assigns the compiled function 
# to the global environment
c.inline <- function(name, sig, src){
    library(Rcpp)
    funCXX <- inline::cxxfunction(sig = sig, body = src, plugin="Rcpp")
    assign(name, funCXX, envir=.GlobalEnv)
}
# and the function which retrieves and calls this newly-compiled function 
c.namecall <- function(name,...){
    funCXX <- get(name)
    funCXX(...)
}

# Keep your example matrix
A <- matrix(rnorm(400), 20,20)

# What are we calling the compiled funciton?
fxname <- "TestCXX"

## Parallel
cl <- makeCluster(2, type = "PSOCK") 

# Export all the pieces
clusterExport(cl, c("src1","c.inline","A","fxname")) 

# Call the compiler function
clusterCall(cl, c.inline, name=fxname, sig=sig, src=src1)

# Notice how the function now named "TestCXX" is available in the environment
# of every node?
clusterCall(cl, ls, envir=.GlobalEnv)

# Call the function through our wrapper
clusterCall(cl, c.namecall, name=fxname, A, 0.5)
# Works with my testing

我编写了一个包ctools(无耻的自我推销),它包含了用于集群计算的并行和 Rhpc 包中的许多功能,包括 PSOCK 和 MPI。我已经有一个名为“c.sourceCpp”的函数,它在每个节点上调用“Rcpp::sourceCpp”,其方式与上述方式大致相同。我将添加一个“c.inlineCpp”,现在我看到了它的用处。

编辑:

鉴于 Coatless 的评论,Rcpp::cppFunction()事实上c.inline这里否定了对助手的需要,尽管c.namecall仍然需要。

src2 <- '
 NumericMatrix TestCpp(NumericMatrix xbe, int g){
        NumericMatrix xbem(xbe);
        int nrows = xbem.nrow();
        NumericVector gv(g);
        for (int i = 1; i < nrows; i++) {
            xbem(i,_) = xbem(i-1,_) * gv[0] + xbem(i,_);
        }
        return xbem;
 }
'

clusterCall(cl, Rcpp::cppFunction, code=src2, env=.GlobalEnv)

# Call the function through our wrapper
clusterCall(cl, c.namecall, name="TestCpp", A, 0.5)
于 2016-09-05T15:45:14.873 回答
0

我通过在每个集群集群节点上采购一个具有所需 C 内联函数的 R 文件来解决它:

clusterEvalQ(cl, 
    {
     library(inline)
     invisible(source("your_C_func.R"))
    })

你的文件 your_C_func.R 应该包含 C 函数定义:

c_func <- cfunction(...)
于 2019-03-22T21:38:34.610 回答