【问题标题】:Using Rcpp within parallel code via snow to make a cluster通过雪在并行代码中使用 Rcpp 来创建集群
【发布时间】:2011-05-20 15:40:36
【问题描述】:

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

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)

【问题讨论】:

    标签: r rcpp snow


    【解决方案1】:

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

    因此,您尝试了正确的做法,将调用该共享库的 R 前端 发送到另一个进程(该进程有另一个临时目录!!),但这并没有获得 dll / so 文件.

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

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

    【讨论】:

    • 非常有意义。出于某种原因,我认为这是特定于雪的,这就是我在此处发布的原因。谢谢!
    【解决方案2】:

    老问题,但我在查看顶级 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 的 cmets,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)
    

    【讨论】:

    • 请不要使用cxxfunction。请改用cppFunction()
    • 我认为它可以同样工作,我只是想使用原始示例。对于cppFunction()src1 代码块会略有不同。有什么特别的理由不使用cxxfunction
    • 最终的结果是一样的,但是到达那里的方式不同。特别是,cxxfunction() 以非函数形式呈现代码。使用 cppFunction() 我可以以函数形式编写 C++ 代码,例如:Rcpp::NumericMatrix sig(Rcpp::NumericMatrix xbem, Rcpp::NumericVector gv){ int nrows = xbem.nrow(); for (int i = 1; i &lt; nrows; i++) { xbem(i,_) = xbem(i-1,_) * gv[0] + xbem(i,_); } return xbem; }。重点更多地放在实际计算上,而不是跟踪输入或投射对象。
    • 本质上,自从引入 Rcpp 属性以来,很多事情都发生了变化。看到人们不利用他们真是太可惜了。
    • 好点。我个人更喜欢使用单独的源文件并使用sourceCpp(),因此我不必区分这两个内联样式函数。使用cppFunction()更新答案。
    【解决方案3】:

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

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

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

    c_func <- cfunction(...)
    

    【讨论】:

      猜你喜欢
      • 2020-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多