Home > OS >  How to correctly pass the dot, dot, dot argument
How to correctly pass the dot, dot, dot argument

Time:10-12

I'm doing something like this

library(microbenchmark)
library(tidyverse)

f1 = function(n=100) rnorm(n)

f2 = function(n=100, shape1=1, shape2=0.1) rbeta(n, shape1, shape2)

f3 = function(n=100, shape=1) rgamma(n, shape)

maxdif = 1e6
mb = microbenchmark(f1(100), f2(100), f3(100), times=1)
if(max(abs(mb$time - lead(mb$time)), na.rm=TRUE)<maxdif) mb = 
  microbenchmark(f1(100), f2(100), f3(100), times=10)
mb$time
# [1] 1927396 1876079 1660118   32074   20528   42338   25659   23094   35923   37633
# [11]   16251   39344   27797   38061   13258   12830   38061   13685   13258   23094
# [21]   13686   25232   37633   24377   20528   12830   38061   14113   12402   35495

So far, everything is clear to me and as expected. However, if I enclose it with a function

fComp = function(..., times, maxdif){
  mb = microbenchmark(..., times=1)
  if(max(abs(mb$time - lead(mb$time)), na.rm=TRUE)<maxdif) mb = 
      microbenchmark(..., times=times)
  mb
}

mb = fComp(f1(100), f2(100), f3(100), times=10, maxdif = 1e6)
mb$time
# [1]  5988 29508   856   429   429     1     1     1   428     0   428   428     0
# [14]   428     1     1   428     0     1     0     1   429     1     1     0   428
# [27]     0   428     1   429

this is something weird going on.

My guess is that it's about passing the ... parameter. Unfortunately, I don't know how to properly decode ... to get the desired (as above) effect.

I will be grateful for any hint.

CodePudding user response:

It seems the problem comes from using the ... twice. I think It's trying to prevent the expressions from being evaluated multiple times which is normally perhaps what you want, but in the case of benchmarking you actually do want to evaluate the same expression multiple times. Here's a work around

fComp = function(..., times, maxdif){
  exprs <- match.call(expand.dots = FALSE)$...
  mb = microbenchmark(list=exprs, times=1)
  if(max(abs(mb$time - lead(mb$time)), na.rm=TRUE)<maxdif) mb = 
    microbenchmark(list=exprs, times=times)
  mb
}

We use match.call to get the unevaluated expressions passed in the .... We then pass those parameters to microbenchmark() via the list= parameter rather than using .... This seems to avoid that optimization.

This method also preserved the expression names in the output

fComp(f1(100), f2(100), f3(100), times=10, maxdif = 1e6)
Unit: microseconds
    expr  min   lq  mean median   uq  max neval
 f1(100)  8.4  8.7  9.29   9.05  9.6 11.2    10
 f2(100) 25.1 25.7 28.40  25.95 27.5 46.9    10
 f3(100) 13.5 16.3 18.09  17.85 20.3 23.5    10

CodePudding user response:

One way is to change the order of ... as the last argument and it works

fComp = function(times, maxdif, ...){
  mb = microbenchmark(times=1, ...)
  if(max(abs(mb$time - lead(mb$time)), na.rm=TRUE)<maxdif) mb = 
      microbenchmark(times=times, ...)
  mb
}

-testing

fComp(times=10, maxdif = 1e6, f1(100), f2(100), f3(100))
Unit: microseconds
 expr      min       lq     mean   median       uq      max neval
  ..1 1957.235 1957.235 1957.235 1957.235 1957.235 1957.235     1
  ..2  101.801  101.801  101.801  101.801  101.801  101.801     1
  ..3  170.926  170.926  170.926  170.926  170.926  170.926     1
  • Related