更新
BUG :rev2
中存在错误,现在已修复。
rev1
和Rcpprev1
已删除。
我还包含了不同的功能,并根据评论和答案进行了修改。
结论:rev4
最适合我的目的,RcppRev3
对于长矢量更好。
我有一个程序需要多次反转整数向量(固定长度)。我分析了代码,rev
函数占用了总时间的30%左右。我对R中函数rev
的缓慢性能感到非常惊讶。所以我决定将基数R rev
与其他一些选择进行比较
library(Rcpp)
library(microbenchmark)
rev2 = function(x){
nx = length(x)
y = vector("numeric", nx)
for(i in 1:nx) y[i] = x[nx-i+1]
y
}
rev3 = function(x){
x[length(x):1]
}
rev4 = function(x){
.subset(x, length(x):1)
}
Rcpp等价:
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector Rcpprev2(IntegerVector x){
int nx = x.size();
IntegerVector y(nx);
for(int i = 0; i<nx; i++){
y[i] = x[nx-i-1];
}
return(y);
}
// [[Rcpp::export]]
IntegerVector RcppRev3(IntegerVector x) {
return rev(x);
}
// [[Rcpp::export]]
std::vector<int> CppRev(std::vector<int> & x) {
std::reverse(x.begin(), x.end());
return x;
}
')
微基准:
> x = c(1L,2L,3L,4L)
> microbenchmark(rev(x), rev2(x), rev3(x), rev4(x), Rcpprev2(x), RcppRev3(x), CppRev(x))
Unit: nanoseconds
expr min lq median uq max neval
rev(x) 4614 5501.0 6086.5 8208.0 32860 100
rev2(x) 5765 6965.0 7803.0 8784.5 28637 100
rev3(x) 1013 1351.0 1521.5 1985.5 9977 100
rev4(x) 961 1317.0 1485.5 1728.5 16492 100
Rcpprev2(x) 2008 2360.0 2558.5 3075.5 17342 100
RcppRev3(x) 2045 2377.5 2607.5 3718.0 9184 100
CppRev(x) 2279 2668.0 2947.0 3381.0 35614 100
事实证明,最快的方法是纯R方法rev4
,它甚至比Rcpp
函数更快。
让我们考虑更长的向量。
> x = rep(c(1L,2L,3L,4L), 1000)
> microbenchmark(rev(x), rev2(x), rev3(x), rev4(x), Rcpprev2(x), RcppRev3(x), CppRev(x))
Unit: microseconds
expr min lq median uq max neval
rev(x) 26.887 29.9425 39.2165 50.4080 71.614 100
rev2(x) 3899.577 4195.3530 4370.5410 4743.3585 6117.387 100
rev3(x) 22.092 24.4405 25.8340 27.8230 51.602 100
rev4(x) 22.346 24.3150 25.8800 28.9425 90.666 100
Rcpprev2(x) 6.039 7.6405 8.4530 11.6720 31.054 100
RcppRev3(x) 5.384 6.1425 6.7395 8.3295 47.981 100
CppRev(x) 10.462 12.0375 13.4130 17.6725 58.012 100
对于长向量,Rcpp函数要好得多。
答案 0 :(得分:3)
你正在混合用于在副本中分配新记忆的方法,以及那些不分配新记忆的方法。
我会尝试从对象指针实例化std::vector<int>
,并调用C ++向量的相应反向方法。当然还有Rcpp糖方法......
另外两个补充:
R> microbenchmark(rev(x), rev1(x), rev2(x), rev3(x), Rcpprev1(x), Rcpprev2(x),
+ RcppRev3(x), CppRev(x))
Unit: microseconds
expr min lq median uq max neval
rev(x) 7.676 9.1070 9.9870 11.1445 71.559 100
rev1(x) 1.446 1.9565 2.1580 2.4280 23.310 100
rev2(x) 4.333 5.2875 5.9305 6.4335 17.458 100
rev3(x) 1.229 1.6875 1.9310 2.1115 12.841 100
Rcpprev1(x) 1.949 2.5440 2.8875 3.3655 7313.028 100
Rcpprev2(x) 1.686 2.0840 2.6260 3.1960 2632.929 100
RcppRev3(x) 1.544 2.0770 2.6130 2.9330 15.722 100
CppRev(x) 1.986 2.6100 3.0530 3.6035 14.091 100
R>
但我怀疑n=4
你在这里几乎无法衡量。
编辑:这是一个更新版本,反映了Kevin关于()
与[]
的优点,并添加了Randy的新{{1} - 并在有意义的向量上运行它。
编辑2:还有一个rev4
,我需要()
。 []
接近Rcpp糖的表现,但并不完全存在。
[]
糖版本对我来说仍然很好。
为了完整起见,我的[更新,两次]文件位于下方。
R> microbenchmark(rev(x), rev2(x), rev3(x), rev4(x), Rcpprev2r(x),
+ Rcpprev2s(x), RcppRev3(x), CppRev(x))
Unit: microseconds
expr min lq median uq max neval
rev(x) 3505.205 3603.892 4307.269 4581.047 45761.91 100
rev2(x) 273.258 294.350 341.571 1258.093 42992.72 100
rev3(x) 3489.653 3573.588 4462.155 4545.285 46253.93 100
rev4(x) 3481.903 3575.505 4409.817 4567.506 48873.70 100
Rcpprev2r(x) 3433.918 3482.274 3507.504 3554.779 4519.62 100
Rcpprev2s(x) 364.481 379.853 403.149 484.366 1684.71 100
RcppRev3(x) 269.145 283.750 290.870 346.462 42867.46 100
CppRev(x) 907.719 963.175 991.787 1126.783 2313.97 100
R>
R部分如下:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector Rcpprev2r(IntegerVector x){
int nx = x.size();
IntegerVector y(nx);
for(int i = 0; i<nx; i++){
y(i) = x(nx-i-1);
}
return(y);
}
// [[Rcpp::export]]
IntegerVector Rcpprev2s(IntegerVector x){
int nx = x.size();
IntegerVector y(nx);
for(int i = 0; i<nx; i++){
y[i] = x[nx-i-1];
}
return(y);
}
// [[Rcpp::export]]
IntegerVector RcppRev3(IntegerVector x) {
return rev(x);
}
// [[Rcpp::export]]
std::vector<int> CppRev(std::vector<int> & x) {
std::reverse(x.begin(), x.end());
return x;
}
答案 1 :(得分:3)
仅供参考:在索引operator()
向量时,使用operator[]
代替Rcpp
需要付费。这是因为operator()
使用offset函数进行边界检查,与operator[]
不同。参见:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector RcppRev(IntegerVector x) {
int n = x.size();
IntegerVector output = no_init(n);
for (int i=0; i < n; ++i) {
output[i] = x[n - i - 1];
}
return output;
}
// [[Rcpp::export]]
IntegerVector RcppRev2(IntegerVector x){
int nx = x.size();
IntegerVector y(nx);
for(int i = 0; i<nx; i++){
y(i) = x(nx-i-1);
}
return(y);
}
/*** R
library(microbenchmark)
x <- as.integer(rnorm(1E7))
identical( rev(x), RcppRev(x) )
identical( rev(x), RcppRev2(x) )
microbenchmark( times=5,
rev(x),
RcppRev(x),
RcppRev2(x)
)
*/
sourceCpp
就此给了我:
> Rcpp::sourceCpp('~/Desktop/rev.cpp')
> library(microbenchmark)
> x <- as.integer(rnorm(1E7))
> identical( rev(x), RcppRev(x) )
[1] TRUE
> identical( rev(x), RcppRev2(x) )
[1] TRUE
> microbenchmark( times=5,
+ rev(x),
+ RcppRev(x),
+ RcppRev2(x)
+ )
Unit: milliseconds
expr min lq median uq max neval
rev(x) 54.987311 55.261393 55.48561 56.083582 70.88035 5
RcppRev(x) 8.375551 8.458457 8.70446 8.800677 63.12635 5
RcppRev2(x) 75.398164 75.733779 75.74356 76.027000 76.59727 5
答案 2 :(得分:2)
你的结论是错误的。对于大型向量rev1
,R和RCpp的版本都是不切实际的。 rev2
速度很快。
x = rep(c(1L, 2L, 3L, 4L), 1e+05)
microbenchmark(rev(x), rev2(x), rev3(x), Rcpprev1(x), Rcpprev2(x))
## Unit: microseconds
## expr min lq median uq max neval
## rev(x) 2159.888 2202.9270 2235.3715 2515.2895 32350.315 100
## rev2(x) 201.621 221.3185 229.9265 255.9155 1577.871 100
## rev3(x) 2139.362 2191.5050 2212.6935 2907.7710 32202.328 100
## Rcpprev1(x) 1.655 3.8075 9.6010 13.5740 18.871 100
## Rcpprev2(x) 4784.596 4857.7615 4892.3580 4957.4130 5777.800 100
更新
以下是使用Dirk&n CppRev
和Rcpprev3
函数的更新基准。
RcppRev3
最好。
microbenchmark(rev(x), rev2(x), rev3(x), Rcpprev2(x), RcppRev3(x), CppRev(x))
## Unit: microseconds
## expr min lq median uq max neval
## rev(x) 2157.902 2227.2605 2502.874 2975.143 32623.777 100
## rev2(x) 202.945 231.7475 250.453 829.822 1042.865 100
## rev3(x) 2127.112 2205.9070 2321.946 2947.996 32239.076 100
## Rcpprev2(x) 4805.453 4878.6190 4924.637 5018.495 5846.661 100
## RcppRev3(x) 189.040 206.0900 222.478 797.874 1135.232 100
## CppRev(x) 1368.304 1410.6810 1432.035 1489.972 31546.151 100