为什么我的 Rcpp 均值函数比 R 慢?
Why is my Rcpp mean function slower than R?
我想创建一个 C++
函数,将 x
中的每个元素提升到 power
并取平均值。我创建了三个版本:
- power_mean_R:
R
解决方案 -- mean(x^power)
- power_mean_C:
C++
解决方案
- power_mean_C_2arg:
C++
带有额外 power
参数的解决方案
额外的 power
参数似乎大大降低了函数的速度,以至于它比 R 实现还慢。这是使用 Rcpp
的现实还是我可以在我的代码中改进什么?
#library(Rcpp)
cppFunction(
'double power_mean_C_2arg(NumericVector x, double power) {
int n = x.size();
double total = 0;
for(int i=0; i<n; ++i) {
total += pow(x[i], power);
}
return total / n;
}'
)
cppFunction(
'double power_mean_C(NumericVector x) {
int n = x.size();
double total = 0;
for(int i=0; i<n; ++i) {
total += pow(x[i], 2);
}
return total / n;
}'
)
power_mean_R <- function(x, power) {
mean(x^power)
}
bench::mark(
R = power_mean_R(1:100, p = 2),
C = power_mean_C(1:100),
C2arg = power_mean_C_2arg(x = 1:100, p = 2)
)
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result memory
<bch:expr> <bch:> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list> <list>
1 R 5.91µs 6.91µs 112386. 1.27KB 0 10000 0 89ms <dbl … <Rpro…
2 C 2.87µs 3.54µs 231281. 3.32KB 23.1 9999 1 43.2ms <dbl … <Rpro…
3 C2arg 6.02µs 6.89µs 116187. 3.32KB 0 10000 0 86.1ms <dbl … <Rpro…
在您提供的示例中,很少有东西会妨碍您的 C++ 函数
1:100
是一个 ALTREP 序列,为此存在高度优化的求和方法,速度要快得多。在下面的极端示例中,它的速度提高了 600 万倍以上。很明显,向量并不是一直都是 altrep,但是在 altrep 序列上进行基准测试是个坏主意。
billion <- c(1L, 2:1e9)
billalt <- 1:1e9
identical(billion, billalt)
#> [1] TRUE
bench::mark(sum(billion), sum(billalt))
#> # A tibble: 2 x 10
#> expression min mean median max
#> <chr> <bch:tm> <bch:tm> <bch:tm> <bch:tm>
#> 1 sum(billi~ 614564900.000ns 614564900.000ns 614564900.000ns 614564.900us
#> 2 sum(billa~ 100.000ns 312.530ns 200.000ns 23.300us
#> # ... with 5 more variables: `itr/sec` <dbl>, mem_alloc <bch:byt>, n_gc <dbl>,
#> # n_itr <int>, total_time <bch:tm>
由 reprex package (v0.3.0)
于 2020-12-11 创建
其次,1:100
是一个整数向量 但是你的 Rcpp 函数接受一个数字向量,所以必须在任何数据之前强制输入 double
操作完成。对于这么小的向量,它很可能是开销的很大一部分。
您的测试向量非常小,因此像 Rcpp 的随机种子保存这样的开销将主导差异。
我想创建一个 C++
函数,将 x
中的每个元素提升到 power
并取平均值。我创建了三个版本:
- power_mean_R:
R
解决方案 --mean(x^power)
- power_mean_C:
C++
解决方案 - power_mean_C_2arg:
C++
带有额外power
参数的解决方案
额外的 power
参数似乎大大降低了函数的速度,以至于它比 R 实现还慢。这是使用 Rcpp
的现实还是我可以在我的代码中改进什么?
#library(Rcpp)
cppFunction(
'double power_mean_C_2arg(NumericVector x, double power) {
int n = x.size();
double total = 0;
for(int i=0; i<n; ++i) {
total += pow(x[i], power);
}
return total / n;
}'
)
cppFunction(
'double power_mean_C(NumericVector x) {
int n = x.size();
double total = 0;
for(int i=0; i<n; ++i) {
total += pow(x[i], 2);
}
return total / n;
}'
)
power_mean_R <- function(x, power) {
mean(x^power)
}
bench::mark(
R = power_mean_R(1:100, p = 2),
C = power_mean_C(1:100),
C2arg = power_mean_C_2arg(x = 1:100, p = 2)
)
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result memory
<bch:expr> <bch:> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list> <list>
1 R 5.91µs 6.91µs 112386. 1.27KB 0 10000 0 89ms <dbl … <Rpro…
2 C 2.87µs 3.54µs 231281. 3.32KB 23.1 9999 1 43.2ms <dbl … <Rpro…
3 C2arg 6.02µs 6.89µs 116187. 3.32KB 0 10000 0 86.1ms <dbl … <Rpro…
在您提供的示例中,很少有东西会妨碍您的 C++ 函数
1:100
是一个 ALTREP 序列,为此存在高度优化的求和方法,速度要快得多。在下面的极端示例中,它的速度提高了 600 万倍以上。很明显,向量并不是一直都是 altrep,但是在 altrep 序列上进行基准测试是个坏主意。
billion <- c(1L, 2:1e9)
billalt <- 1:1e9
identical(billion, billalt)
#> [1] TRUE
bench::mark(sum(billion), sum(billalt))
#> # A tibble: 2 x 10
#> expression min mean median max
#> <chr> <bch:tm> <bch:tm> <bch:tm> <bch:tm>
#> 1 sum(billi~ 614564900.000ns 614564900.000ns 614564900.000ns 614564.900us
#> 2 sum(billa~ 100.000ns 312.530ns 200.000ns 23.300us
#> # ... with 5 more variables: `itr/sec` <dbl>, mem_alloc <bch:byt>, n_gc <dbl>,
#> # n_itr <int>, total_time <bch:tm>
由 reprex package (v0.3.0)
于 2020-12-11 创建其次,1:100
是一个整数向量 但是你的 Rcpp 函数接受一个数字向量,所以必须在任何数据之前强制输入 double
操作完成。对于这么小的向量,它很可能是开销的很大一部分。
您的测试向量非常小,因此像 Rcpp 的随机种子保存这样的开销将主导差异。