【问题标题】:Calling 'mypackage' function within public worker在公共工作者中调用“mypackage”函数
【发布时间】:2018-08-22 23:08:20
【问题描述】:

我知道我遇到的问题是线程安全问题。因为我现在拥有的代码将使用“seThreadOptions(1)”执行。我的问题是什么是克服这个问题的好习惯。

我知道这一点:Threadsafe function pointer with Rcpp and RcppParallel via std::shared_ptr 会以某种方式发挥作用。而且我也一直在思考/尝试将内部功能作为并行工作者结构的一部分。实际上,我正在调用两个内部函数,我希望一个是可变的,另一个是恒定的,这让我倾向于认为我需要两个解决方案。

错误是 rstudio 中的 R 会话崩溃。 这里有两点需要注意: 1. 如果我 'setThreadOptions(1)' 这运行良好。 2. 如果我将 'myfunc' 移动到主 cpp 文件中并简单地调用 'myfunc' 这也可以正常运行。

这是一个详细的例子:

第一个 cpp 文件:

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::interfaces(cpp)]]
// [[Rcpp::plugins(cpp11)]]
#include "RcppArmadillo.h"
using namespace arma;
using namespace std;

// [[Rcpp::export]]
double myfunc(arma::vec vec_in){

  int Len = arma::size(vec_in)[0];
  return (vec_in[0] +vec_in[1])/Len;
}

二、cpp文件:

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::depends(RcppParallel)]]
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::depends(ParallelExample)]]

#include "RcppArmadillo.h"
#include "RcppParallel.h"
#include "ParallelExample.h"
#include <random>
#include <memory>
#include <math.h>

using namespace Rcpp;
using namespace arma;
using namespace RcppParallel;
using namespace std;

struct PARALLEL_WORKER : public Worker{

  const arma::vec &input;
  arma::vec &output;

  PARALLEL_WORKER(const arma::vec &input, arma::vec &output) : input(input), output(output) {}

  void operator()(std::size_t begin, std::size_t end){


    std::mt19937 engine(1);

    // Create a loop that runs through a selected section of the total Boot_reps
    for( int k = begin; k < end; k ++){
      engine.seed(k);
      arma::vec index = input;
      std::shuffle( index.begin(), index.end(), engine);

      output[k] = ParallelExample::myfunc(index);
  }
}

};

// [[Rcpp::export]]
arma::vec Parallelfunc(int Len_in){

  arma::vec input = arma::regspace(0, 500);
  arma::vec output(Len_in);

  PARALLEL_WORKER  parallel_woker(input, output);
  parallelFor( 0, Len_in, parallel_woker);
  return output;
}

Makevars,因为我使用的是 Macintosh:

CXX_STD = CXX11

PKG_CXXFLAGS +=  -I../inst/include

和命名空间:

exportPattern("^[[:alpha:]]+")
importFrom(Rcpp, evalCpp)
importFrom(RcppParallel,RcppParallelLibs)
useDynLib(ParallelExample, .registration = TRUE)

export(Parallelfunc)

【问题讨论】:

  • 错误信息? output 是什么数据类型? i 是如何定义的?你用random:那是怎么用的?这样的问题还有很多。它们都归结为:minimal reproducible example 在哪里?
  • 由于该示例有多个文件,因此运行起来并不容易。还有这个错误,我认为这是一个线程安全问题,因为它使工作室崩溃,类似于我已经解决的其他线程安全问题。同样,问题仅在于我通过 cpp 标头调用辅助函数时。
  • 我去看看。第二个 cpp 文件也是包的一部分吗?我会忽略所有这些using namespace ...

标签: rcpp armadillo rcppparallel


【解决方案1】:

当您调用ParallelExample::myfunc 时,您正在调用inst/include/ParallelExample_RcppExport.h 中定义的函数,该函数使用R API。这是一个不能在并行上下文中做的事情。我看到了两种可能性:

  1. myfunc 转换为仅标题并将其包含在int/include/ParallelExample.h 中。
  2. 如果第二个 cpp 文件在同一个包中,请将 myfunc 的适当声明放入 src/first.h,将该文件包含在 src/first.cppsrc/second.cpp 中,并调用 myfunc 而不是 ParallelExample::myfunc .毕竟,如果您只想在同一个包中调用它,则无需向 R 注册函数。使用 R 注册是针对从外部调用的函数。

【讨论】:

    【解决方案2】:

    在某些方面,这有点违背了 Rcpp 的内置接口 cpp 功能的目的。

    首先,cpp保存为'ExampleInternal.h':

    // [[Rcpp::depends(RcppArmadillo)]]
    // [[Rcpp::plugins(cpp11)]]
    #include "RcppArmadillo.h"
    using namespace arma;
    using namespace std;
    
    namespace ExampleInternal
    {
    
      double myfunc3(arma::vec vec_in){
    
        int Len = arma::size(vec_in)[0];
        return (vec_in[0] +vec_in[1])/Len;
      }
    
    
    }
    

    第二个:

    #include "ParallelExample.h"
    #include "ExampleInternal.h"
    #include <random>
    #include <memory>
    #include <math.h>
    
    using namespace Rcpp;
    using namespace arma;
    using namespace RcppParallel;
    using namespace ExampleInternal;
    using namespace std;
    
    struct PARALLEL_WORKER : public Worker{
    
      const arma::vec &input;
      arma::vec &output;
    
      PARALLEL_WORKER(const arma::vec &input, arma::vec &output) : input(input), output(output) {}
    
      void operator()(std::size_t begin, std::size_t end){
    
    
        std::mt19937 engine(1);
    
        // Create a loop that runs through a selected section of the total Boot_reps
        for( int k = begin; k < end; k ++){
          engine.seed(k);
          arma::vec index = input;
          std::shuffle( index.begin(), index.end(), engine);
    
          output[k] = ExampleInternal::myfunc3(index);
      }
    }
    
    };
    
    // [[Rcpp::export]]
    arma::vec Parallelfunc(int Len_in){
    
      arma::vec input = arma::regspace(0, 500);
      arma::vec output(Len_in);
    
      PARALLEL_WORKER  parallel_woker(input, output);
      parallelFor( 0, Len_in, parallel_woker);
      return output;
    }
    

    【讨论】:

    • 什么“违背了 Rcpp 的内置接口 cpp 功能的目的”?该功能适用​​于可以通过 R 提供的接口从外部包调用的函数。由于它是 R 接口,因此它不是线程安全的。但是,没有必要使用包的R接口。并且必须编写头文件以使代码可用于不同的源文件对于 C++ 来说是正常的。
    • 说得好,我没想到会从包外调用 c++ 函数。
    • 其次,在研究头文件时,我遇到了我认为最好的解决方案。即添加'double myfunc(arma::vec vec_in);'到第二个 cpp 文件,如果我没记错的话,这是一个函数声明?并告诉 c++ 这个函数存在。因此,除非您需要大量内部函数,否则无需创建头文件或命名空间。
    • 只要你小心保持这个一致,你可以这样做。如果您在多个地方使用多个功能,就会出现问题。在这种情况下,通常的做法是创建带有函数声明的头文件(不需要命名空间或函数定义),并将该文件包含在使用该函数的所有文件中。
    猜你喜欢
    • 2018-01-10
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    相关资源
    最近更新 更多