静态大小的 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 元素的列表,其中每个元素是 NULL
或 NA
或一些这样的)?例如,要在 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 的介绍材料。
一些好的资源是:
Rcpp
提供的小插图,特别是简介和常见问题小插图。除了从 R 访问它们之外,您还可以在网络上找到它们 here
- Rcpp for everyone
- Hadley's chapter on 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 案例中,这是人类不会注意到的“相当大”的差异,但在更复杂的用例中,或者多次调用这样的函数的情况下,它实际上可能是“相当大的”。
如果希望迭代一个函数并将结果添加到 Rcpp 中的列表中,可以使用 .push_back()
方法,例如:
List appended_list() {
List emptylist = List::create();
for (int i = 0; i < 3; i++) {
emptylist.push_back(i);
}
return emptylist;
}
但是,我说的每次都重新定义列表,因此这种方法效率很低,我说得对吗?有没有办法创建一个静态大小的列表(即创建一个 n 元素的列表,其中每个元素是 NULL
或 NA
或一些这样的)?例如,要在 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 的介绍材料。 一些好的资源是:
Rcpp
提供的小插图,特别是简介和常见问题小插图。除了从 R 访问它们之外,您还可以在网络上找到它们 here- Rcpp for everyone
- Hadley's chapter on 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 案例中,这是人类不会注意到的“相当大”的差异,但在更复杂的用例中,或者多次调用这样的函数的情况下,它实际上可能是“相当大的”。