我正在try 为R优化一个算法.最初,我使用Rcpp(和Rcpp向量等)编写了该算法,但随后使用标准C++向量重写了该算法,并仅在最后阶段将其转换为Rcpp.然而,涉及shuffle的C++算法组件似乎相当慢.事实上,来回转换为Rcpp向量以便使用Rcpp/R sample函数要快得多.这让我很惊讶.

下面是一个可重复性最低的示例:

#include <Rcpp.h>
#include <random>
#include <algorithm>

// [[Rcpp::export]]

List test_cpp(int n, int x)  {

  List return_list(n);

  std::vector<int> v;
  v.reserve(x);

  for(int i = 0; i < x; ++i) {
    v.push_back(i);
  }

  std::random_device rd;
  std::mt19937 g(rd());

  for(int i = 0; i < n; ++i)  {
    std::shuffle(v.begin(), v.end(), g);
    return_list(i) = v;
  }

  return return_list;
}


// [[Rcpp::export]]

List test_r(int n,
            int x)  {

  List return_list(n);

  std::vector<int> v;
  v.reserve(x);

  for(int i = 0; i < x; ++i){
      v.push_back(i);
    }

  IntegerVector vs = wrap(v);

  for(int i = 0; i < n; ++i)  {
    IntegerVector s_v = sample(vs, v.size());
    std::vector<int> s_v_c = as<std::vector<int>>(s_v);
    return_list(i) = s_v_c;
  }

  return return_list;
}

使用C++shuffle的第一个函数要比使用Rcpp sample的版本慢得多,直到你洗牌了大约50000个元素的向量.对于一个更接近我的大多数用例的示例,下面生成的Rcpp sample的中值时间约为13 ms,而C++shuffle的中值时间约为20 ms.

n <- 1000
x <- 999

speed <- bench::mark(min_iterations = 100, 
                       check = FALSE,
                       cpp = test_cpp(n, x),
                       rcpp = test_r(n, x)
                       )

  ggplot2::autoplot(speed) +
    ggplot2::theme_minimal() +
    ggplot2::xlab(NULL) +
    ggplot2::ylab(NULL) 

enter image description here

很可能我把C++代码弄糟了.如果是的话,有人能告诉我我的错误吗?还是shuffle太慢了,我应该使用不同的C++算法?或者,在R/Rcpp之外调用一个算法/随机数生成器来解释性能上的差异,会有什么损失吗?感谢您的建议.

Edit为了说明C++版本的效率低下并不是因为必须将标准向量转换为整数,我修改了Rcpp版本,以便在采样后将整数多余地转换为标准向量(然后再转换回整数).

Update

我try 了一些替代的伪随机数生成器.This post表明我上面使用的Mersenne Twister伪随机数生成器与一些备选方案相比相对较慢.我try 了编码为in this post的伪随机数生成器,它们确实更快,但并没有显著提高性能.下面是我的简化测试函数.

// [[Rcpp::export]]

void test_pcg(int x)  {
  std::vector<int> v;   
  v.reserve(x);
  for(int i = 0; i < x; ++i) {
    v.push_back(i);
  }
  std::random_device rd;   
  pcg g(rd);
  std::shuffle(v.begin(), v.end(), g);
}


  // [[Rcpp::export]]

  void test_mt(int x)  {
    std::vector<int> v;
    v.reserve(x);
    for(int i = 0; i < x; ++i) {
      v.push_back(i);
    }
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(v.begin(), v.end(), g);
  }


// [[Rcpp::export]]

void test_splitmix(int x)  {
  std::vector<int> v;   
  v.reserve(x);
  for(int i = 0; i < x; ++i) {
    v.push_back(i);
  }
  std::random_device rd;   
  splitmix g(rd);   
  std::shuffle(v.begin(), v.end(), g);
}



// [[Rcpp::export]]

void test_xorshift(int x)  {
  std::vector<int> v;   
  v.reserve(x);
  for(int i = 0; i < x; ++i) {
    v.push_back(i);
  }
  std::random_device rd;   
  xorshift g(rd);
  std::shuffle(v.begin(), v.end(), g);
}


// [[Rcpp::export]]

void test_rcpp(int x)  {
  IntegerVector v = seq(0, x);   
  IntegerVector s_v = sample(v, x);
}

对于1000的向量,Rcpp版本仍然要快很多,大约13毫秒,而对于使用C++shuffle的速度最快的RNG,则要快20毫秒.

据我所知,C++shuffle实现了Fisher-Yates(Knuth)shuffle.我现在的猜测是,当所有元素都在不替换的情况下采样时,Rcpp样本函数不会实现Fisher-Yates洗牌,而是使用排序算法?对于我的应用程序来说,也许C++中有一种类似的算法比shuffle更快?

推荐答案

正如我在 comments 中提到的,你的职能可能"做得太多".这里是一个简化的示例(这也是毫无意义的,因为我们可能每次都会更改输入向量),但它将您的问题归结为"采样速度是否比从标准库中shuffle 快".事实并非如此.

enter image description here

下面是我修改过的代码.

Code

#include <Rcpp.h>
#include <random>
#include <algorithm>

// [[Rcpp::export]]
Rcpp::IntegerVector shuffle_cpp(Rcpp::IntegerVector x)  {
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(x.begin(), x.end(), g);
    return x;
}

// [[Rcpp::export]]
Rcpp::IntegerVector sample_rcpp(Rcpp::IntegerVector x)  {
    return sample(x, x.size());
}

/*** R
v <- seq(1, 1e6)
res <- bench::mark(min_iterations = 100, check = FALSE, shuffle_cpp(v), sample_rcpp(v))
res
ggplot2::autoplot(res) + ggplot2::theme_minimal() + ggplot2::ylab(NULL)
*/

R相关问答推荐

如何以编程方式将X轴勾号上的希腊符号合并到R图中?

使用na.locf在长格式数据集中输入具有多个时间点的数据集

如何在区分不同条件的同时可视化跨时间的连续变量?

如何直接从R中的风险分数计算c指数?

在R中,如何将变量(A,B和C)拟合在同一列中,如A和B,以及A和C在同一面板中?

如何调整曲线图中的y轴标签?

根据现有列的名称和字符串的存在进行变异以创建多个新列

R中1到n_1,2到n_2,…,n到n_n的所有组合都是列表中的向量?

如何在ggplot2中绘制具有特定 colored颜色 的连续色轮

如何在科学记数法中显示因子

按多列统计频次

如何删除R中除数字元素以外的所有元素

减少雨云面之间的间距并绘制所有统计数据点

SHILINY中DT列的条件着色

对R中的列表列执行ROW Mean操作

R没有按顺序显示我的有序系数?

将某个阈值以下的列中的值分类到不同的列中,否则保持该列的原样

动态统计函数在ShinyApp内部更改

如何准确地指出Read_delim所面临的问题?

如何根据每个子框架中分类因子的唯一计数来过滤子框架列表?