为什么我的 Rcpp 均值函数比 R 慢?

Why is my Rcpp mean function slower than R?

我想创建一个 C++ 函数,将 x 中的每个元素提升到 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 的随机种子保存这样的开销将主导差异。