使用openMP和Rcpp设置RNG状态

时间:2013-09-23 19:58:47

标签: openmp rcpp

我有一个澄清问题。 据我所知,sourceCpp自动传递RNG状态,因此set.seed(123)在调用Rcpp代码时给出了可重现的随机数。编译包时,我必须添加一组RNG语句。 现在,如何在sourceCpp或包中使用openMP?

考虑以下Rcpp代码

#include <Rcpp.h>
#include <omp.h>

// [[Rcpp::depends("RcppArmadillo")]]

// [[Rcpp::export]]
Rcpp::NumericVector rnormrcpp1(int n, double mu, double sigma  ){
  Rcpp::NumericVector out(n);
        for (int i=0; i < n; i++) {
          out(i) =R::rnorm(mu,sigma);
        }

  return(out);
}


// [[Rcpp::export]]
Rcpp::NumericVector rnormrcpp2(int n, double mu, double sigma, int cores=1  ){
  omp_set_num_threads(cores);
  Rcpp::NumericVector out(n);
    #pragma omp parallel for schedule(dynamic)   
        for (int i=0; i < n; i++) {
          out(i) =R::rnorm(mu,sigma);
        }

  return(out);
}

然后运行

   set.seed(123)
    a1=rnormrcpp1(100,2,3,2)
    set.seed(123)
    a2=rnormrcpp1(100,2,3,2)
    set.seed(123)
    a3=rnormrcpp2(100,2,3,2)
    set.seed(123)
    a4=rnormrcpp2(100,2,3,2)
    all.equal(a1,a2)
    all.equal(a3,a4)

虽然a1和a2相同,但a3和a4不相同。如何使用openMP循环调整RNG状态?我可以吗?

2 个答案:

答案 0 :(得分:2)

为了扩展Dirk Eddelbuettel已经说过的内容,几乎不可能同时生成相同的PRN序列并且具有所需的加速。其根源在于PRN序列的生成本质上是一个顺序过程,其中每个状态依赖于前一个状态,这会创建一个向后依赖链,该链向后延伸到初始播种状态。

此问题有两种基本解决方案。其中一个需要大量内存,另一个需要大量CPU时间,实际上两者都比真正的解决方案更像是变通方法:

预生成的PRN序列:一个线程按顺序生成大量PRN,然后所有线程以与顺序情况一致的方式访问此数组。此方法需要大量内存才能存储序列。另一种选择是将序列存储到稍后存储器映射的磁盘文件中。后一种方法的优点是节省了一些计算时间,但通常I / O操作很慢,因此只有处理能力有限或RAM少的机器才有意义。

prewound PRNGs:这种方法适用于在线程之间静态分配工作的情况,例如:与schedule(static)。每个线程都有自己的PRNG,所有PRNG都使用相同的初始种子播种。然后每个线程绘制与其开始迭代一样多的虚拟PRN,基本上将其PRNG预绕到正确的位置。例如:

  • 线程0:绘制0个虚拟PRN,然后绘制100个PRN并填充out(0:99)
  • 主题1:绘制100个虚拟PRN,然后绘制100个PRN并填充out(100:199)
  • 线程2:绘制200个虚拟PRN,然后绘制100个PRN并填充out(200:299)

等等。除了绘制PRN之外,当每个线程进行大量计算时,此方法也能正常工作,因为在某些情况下(例如,多次迭代),PRNG前置的时间可能很长。(

除了绘制PRN之外还有大量数据处理的情况下存在第三种选择。这个使用OpenMP有序循环(注意迭代块大小设置为1):

#pragma omp parallel for ordered schedule(static,1)
for (int i=0; i < n; i++) {
  #pragma omp ordered
  {
     rnum = R::rnorm(mu,sigma);
  }
  out(i) = lots of processing on rnum
}

虽然循环排序本质上是序列化计算,但它仍然允许lots of processing on rnum并行执行,因此可以观察到并行加速。有关原因,请参阅this answer以获得更好的解释。

答案 1 :(得分:1)

是,sourceCpp()等以及RNGScope的实例化,以便RNG保持适当的状态。

是的,可以做OpenMP。但是在OpenMP段内部,您无法控制线程执行的顺序 - 因此您需要更长的相同序列。我正在开发一个包有同样的问题,我希望有可重复的绘图,但仍然使用OpenMP。但似乎你不能。