繁体   English   中英

在 R 中并行化非平凡的 Gibbs 采样器:RcppThread 与 RcppParallel

[英]Parallelizing a non-trivial Gibbs Sampler in R: RcppThread vs. RcppParallel

概述:我对并行化(跨链)一个非平凡回归问题的 Gibbs 采样器很感兴趣,我已经通过 Rcpp/RcppEigen 串行实现了这个问题。 我已经阅读了RcppParallelRcppThread的文档,我想知道我对并行化此代码所涉及的挑战的理解是否准确,以及我提出的使用RcppThread伪代码是否可行。

编程挑战:这个回归问题需要在 Gibbs 采样器的每次迭代中反转更新的设计矩阵。 因此,任何新矩阵(每个链一个)都需要是“线程安全的”。 也就是说,不存在一个线程写入另一个线程也可能尝试访问的内存的危险。 如果这样做,然后我可以通过给Rcpp::parallelFor一个唯一索引来绘制和存储回归系数样本(beta),用于分配样本。 我想知道在哪里/如何最好初始化这些线程特定的矩阵 请参阅下文以了解我的整体概念理解,并首先猜测我如何基本上使用并行分配样本的样本原则来并行分配 X。 注意这是假设 Eigen 对象可以进行并发索引访问,就像我在RcppThread文档中看到 std::vector<> 的内存访问RcppThread

#include "RcppEigen.h>
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::depends(RcppThread)]] 
// [[Rcpp::depends(RcppEigen)]] 

// Sampler class definition
#include "Sampler.h" 
#include "RcppThread.h"

// [[Rcpp::export]]
Eigen::ArrayXXd fancyregression(const Eigen::VectorXd &y, // outcome vector
                                const Eigen::MatrixXd &Z, // static sub-matrix of X
                                const int &num_iterations,
                                const int &num_chains_less_one,
                                const int &seed,
                                ...)
{ 
   std::mt19937 rng;
   rng(seed);
   const int dim_X = get_dim_X(Z,...);
   const int n = y.rows();
   const int num_chains = num_chains_less_one + 1;

   Eigen::ArrayXXd beta_samples;
   beta_samples.setZero(num_iterations,num_chains*dim_X);

   Eigen::MatrixXd shared_X(n,dim_X*num_chains);

   // sampler object only has read access to its arguments
   SamplerClass sampler(y,Z,...);
    
   //chain for loop
    RcppThread::parallelFor(0, num_chains_less_one,[&beta, &shared_X, &n,&sampler, &dim_X, &rng](unsigned int chain){
        // chain specific iteration for loop
        for(unsigned int iter_ix = 0; iter_ix < num_iterations ; iter_ix ++){
            X.block(0,dim_X*chain,n,dim_X) = sampler.create_X(rng);
            beta_samples(iter_ix,dim_X*chain) = sampler.get_beta_sample(X,rng); 
        }
    });

    return(beta_samples);

}

“在哪里/如何最好初始化这些线程特定的矩阵?”

您正在寻找线程特定的资源。 这是一个准系统示例:

#include <Rcpp.h>
#include <RcppParallel.h>
using namespace Rcpp;
using namespace RcppParallel;

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

struct Test : public Worker {
  tbb::enumerable_thread_specific<bool> printonce;
  Test() : printonce(false) {}
  
  void operator()(std::size_t begin, std::size_t end) {
    tbb::enumerable_thread_specific<bool>::reference p = printonce.local();
    if(!p) { // print once per thread
      std::cout << 1;
      p= true;
    }
  }
};

// [[Rcpp::export(rng = false)]]
void test() {
  Test x{};
  parallelFor(0, 10000, x);
}

RcppParallel 在底层使用 TBB(适用于大多数操作系统),因此您可以在 TBB 中使用和查找任何内容。

请注意,由于它是必须在某处分配的线程本地资源,因此您需要使用类/函子而不是 lambda。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM