简体   繁体   English

rcpp function [实际上不是] package 内部比外部慢很多

[英]Rcpp function [actually not] much slower inside of package than outside

I recently wrote a compute intense function in Rcpp.我最近在 Rcpp 中写了一个计算密集型 function。 Now, I would like to port this code to an R package. However, I notice that the code is much (~100x) slower when run inside an R package.现在,我想将这段代码移植到 R package。但是,我注意到当在 R package 中运行时,代码要慢很多(~100 倍)。

I already read here , that this may have to do with how the function is called.我已经在这里读到,这可能与 function 的调用方式有关。 However, this is not a one-time cost.但是,这不是一次性成本。 Instead, it scaled with the number of iterations in the Rcpp function (only a single call to Rcpp is made).相反,它与 Rcpp function 中的迭代次数成比例(仅对 Rcpp 进行了一次调用)。

Please find a minimally complete verifiable example below.请在下面找到一个最低限度的完整可验证示例。 The function below doesn't do anything useful but shows the behavior I am worried about.下面的 function 没有做任何有用的事情,但显示了我担心的行为。

How can I troubleshoot this issue?我该如何解决这个问题?

Steps to recreate package.重新创建 package 的步骤。

  • Use Rcpp.package.skeleton to create a new package skeleton with Rcpp.使用Rcpp.package.skeleton用 Rcpp 创建一个新的 package 骨架。

  • Add the following example.cpp file to \src .将以下example.cpp文件添加到\src

    example.cpp例子.cpp

     #include <Rcpp.h> // [[Rcpp::export]] int example_cpp(Rcpp::IntegerMatrix mat, int iters) { for(int i = 0; i < iters; ++i) { std::vector<int> vec; std::iota(std::begin(vec), std::end(vec), 0); } return 0; }
  • Add the following example.R file to \R .将以下example.R文件添加到\R

    example.R例子.R

     # @export example <- function(mat, iters) { example_cpp(mat, iters) }
  • Test the Rcpp function inside/outside the package using the following script.使用以下脚本测试 package 内部/外部的 Rcpp function。

     library(examplePackage) Rcpp::sourceCpp('src/example.cpp') exampleOutside <- function(mat, iters) { example_cpp(mat, iters) } set.seed(42) mat <- replicate(n=1000, sample(1:10)) for(iters in c(1e4, 1e5, 1e6)) { res <- microbenchmark::microbenchmark( example(mat, iters), exampleOutside(mat, iters), times=10 ) print(iters) print(res) }

Output. Output。

[1] 10000
Unit: microseconds
                       expr     min      lq     mean  median      uq      max neval
        example(mat, iters) 629.550 630.977 696.1131 686.488 719.399  858.081    10
 exampleOutside(mat, iters)   3.143   4.203 239.7205   5.021   6.981 2340.719    10
[1] 1e+05
Unit: microseconds
                       expr      min       lq      mean    median       uq      max neval
        example(mat, iters) 6512.453 6625.420 6717.6595 6713.2375 6843.519 6921.158    10
 exampleOutside(mat, iters)    2.637    3.226    7.6473    4.1205   12.647   16.489    10
[1] 1e+06
Unit: microseconds
                       expr       min        lq       mean    median        uq       max neval
        example(mat, iters) 64091.144 66392.745 67491.8759 68001.405 68609.006 69028.736    10
 exampleOutside(mat, iters)     2.885     3.574    10.6664     4.792    17.653    35.927    10

Edit: See below for the full discussion.编辑:完整讨论见下文。 You are measuring noise as the functions are empty returning immediately.您正在测量噪声,因为函数为空并立即返回。 For a real problem I replace your function and it unused argument and data with a simple summation of the log of the loop value:对于一个真正的问题,我将您的 function 及其未使用的参数和数据替换为循环值日志的简单总和:

#include <Rcpp.h>

// [[Rcpp::export]]
double nontrivial(int iters) {
    double sum = 0;
    for (int i = 0; i < iters; ++i) {
        sum += log(i * 1.0);
    }
    return sum;
}

Running over 10 million elements the default one hundred takes just under five seconds, and post approaches are equally fast:运行超过 1000 万个元素,默认的 100 个元素只需要不到 5 秒,post 方法也同样快:

edd@rob:~/git/stackoverflow/74688018(master)$ Rscript nontrivial.R
     test replications elapsed relative
1  inside          100   4.743    1.000
2 outside          100   4.920    1.037
edd@rob:~/git/stackoverflow/74688018(master)$ 

Updated files are in the repo linked below.更新的文件在下面链接的 repo 中。 The original reply from yesterday follows.昨天的原始回复如下。


Thanks for making the problem smaller.感谢您使问题变小。 There are numerous problems here:这里有很多问题:

  • you risk naming collission by calling the function you source the same as the one the package when you load the package, I would avoid that您通过调用 function 来冒命名冲突的风险,当您加载 package 时,您的来源与 package 相同,我会避免这种情况
  • the matrix creations矩阵创作
    • nonsensical: what is replicate doing there荒谬: replicate在那里做什么
    • pointless: the matrix is not used in the code毫无意义:代码中未使用矩阵
  • after I made some rearrangements I set up a simpler test (see below) and it made it pretty clear that neither function is doing anything as the compiler looked through your empty loop.在我做了一些重新安排之后,我设置了一个更简单的测试(见下文)并且它非常清楚地表明编译器查看你的空循环时 function 都没有做任何事情 You can return zero faster by just returning zero, what is happening here:您可以通过仅返回零来更快地返回零,这里发生了什么:

Simpler Test更简单的测试

Rcpp::sourceCpp("exampleOutside.cpp")

set.seed(42)
mat <- matrix(sample(1:10, 100, replace=TRUE), 10, 10)

rbenchmark::benchmark(examplePackage::example_cpp(mat, 10),
                      exampleOutside(mat, 10),
                      replications = 10)[,1:4]

Outcome结果

> rbenchmark::benchmark(examplePackage::example_cpp(mat, 10), 
+                       exampleOutside(mat, 10), replications = 10)[,1:4]
                                  test replications elapsed relative
2              exampleOutside(mat, 10)           10   0.001       NA
1 examplePackage::example_cpp(mat, 10)           10   0.000       NA
> 

Modified 'outside' function修改后的“外部” function

#include <Rcpp.h>

// [[Rcpp::export]]
int exampleOutside(Rcpp::IntegerMatrix mat, int iters) {
  for(int i = 0; i < iters; ++i) {
    std::vector<int> vec;
    std::iota(std::begin(vec), std::end(vec), 0);
  }
  return 0;
}

Overall, this is a fairly good "teachable moment" about why minimal examples are recommended.总的来说,这是一个关于为什么建议使用最少示例的相当好的“教学时刻”。 You were chasing a mirage, and you didn't notice because you put up too many barriers for yourself to see that maybe your comparison setup was not quite right.你在追逐海市蜃楼,但你没有注意到,因为你为自己设置了太多障碍,以至于你看不到你的比较设置可能不太正确。

In short, "when something cannot be true" (as in 'massive difference between same function compiled two different ways') it often is not.简而言之,“当某事不可能是真的时”(如“相同的 function 以两种不同方式编译的巨大差异”)通常不是。

(And for reproducibility all my files are in this directory on github .) (为了重现性,我的所有文件都在 github 的这个目录中。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM