静态大小的 Rcpp 空列表会比 list.push_back() 更有效吗?

Would a statically sized Rcpp empty list be more efficient than list.push_back()?

如果希望迭代一个函数并将结果添加到 Rcpp 中的列表中,可以使用 .push_back() 方法,例如:

List appended_list() {
    List emptylist = List::create();
    for (int i = 0; i < 3; i++) {
      emptylist.push_back(i);
    }
    return emptylist;
}

但是,我说的每次都重新定义列表,因此这种方法效率很低,我说得对吗?有没有办法创建一个静态大小的列表(即创建一个 n 元素的列表,其中每个元素是 NULLNA 或一些这样的)?例如,要在 R 中执行此操作,可以这样写:

emptylist <- list()
length(emptylist) <- 3
for (i in 1:3) {
  emptylist[[i]] <- i
}

有没有办法在 Rcpp 中做到这一点?会不会更有效率?

作为一般规则,您总是想要预分配。

是的,您重新发现了一些东西(“push_back 在 R SEXP 类型上很昂贵”)我们可能记录了将近十年。如果您事先不知道自己的尺寸,请改用 STL 中的标准容器(在生长方面有 很多 更好的行为)并在最后转换。

pre-allocated 列表将 快得多。 如果您不确定如何创建 pre-determined 大小的列表, 我的建议是您可能需要花一些时间了解 Rcpp 的介绍材料。 一些好的资源是:

这是一个示例,显示避免 push_back() 的速度有多快。 在 so.cpp 中,我们有

#include <Rcpp.h>

// [[Rcpp::export]]
void test1() {
    Rcpp::List out;
    for ( int i = 0; i < 1000; ++i ) {
        out.push_back(i);
    }
}

// [[Rcpp::export]]
void test2() {
    Rcpp::List out(1000);
    for ( int i = 0; i < 1000; ++i ) {
        out[i] = i;
    }
}

然后我们将函数相互进行基准测试:

Rcpp::sourceCpp("so.cpp")
library(microbenchmark)
microbenchmark(empty_list = test1(), pre_allocated = test2())

Unit: microseconds
          expr      min       lq       mean    median        uq      max
    empty_list 3553.549 3755.405 4337.71591 3894.3075 4106.7500 8790.787
 pre_allocated   22.089   23.689   38.67364   24.6645   26.1165 1339.443
 neval
   100
   100

所以你可以看到那里有很大的不同。 当然,在这个 simplified 案例中,这是人类不会注意到的“相当大”的差异,但在更复杂的用例中,或者多次调用这样的函数的情况下,它实际上可能是“相当大的”。