3 Measuring and comparing execution times
The first step before optimizing a code is to measure its execution time, in order to compare timings between different implementations.
For this section and the following we refer to the book Advanced R by Hadley Wickham 2, freely available online.
3.1 Measuring execution times with system.time()
To measure the execution time of an command, you can use the native system.time() function like this:
obs <- matrix(rep(1.96, 2), nrow=2, ncol=1)
system.time(mvnpdf(x=obs, Log=FALSE))## user system elapsed
## 0.001 0.001 0.002
The problem that appears in this example is that the execution is so fast that system.time() displays 0 (or a very close value) that will be impossible to compare to an hopefully faster implementation. Also, we see that there is some variability when we run the command several times.
Thus if we want to compare our code with the mvtnorm::dmvnorm() function, we can’t use system.time():
obs <- rep(1.96, 2)
system.time(mvtnorm::dmvnorm(obs))## user system elapsed
## 0.002 0.001 0.004
We could imagine that we need to increase the complexity of our calculation to be able to compare them, but there is actually a better way: the use of the microbenchmark package!
NB: It can be convenient to add an .R file in your package folder to add those commands (or testing your functions more generally), but do not forget to ignore it from the package building and also optionnally from the git. For that, your can use usethis::use_build_ignore("name_of_file.R") and usethis::use_git_ignore("name_of_file.R").
3.2 Compare execution times with microbenchmark()
As its name indicates, this package allows to compare execution times even when they are very fast. Moreover, each benchmarked expression will be repeatedly evaluated several times, thus stabilizing its timing estimations.
library(microbenchmark)
mb <- microbenchmark(mvtnorm::dmvnorm(rep(1.96, 2)),
mvnpdf(x = matrix(rep(1.96,2)), Log = FALSE),
times = 1000)## Warning in microbenchmark(mvtnorm::dmvnorm(rep(1.96, 2)), mvnpdf(x =
## matrix(rep(1.96, : less accurate nanosecond times to avoid potential integer
## overflows
mb## Unit: microseconds
## expr min lq mean median
## mvtnorm::dmvnorm(rep(1.96, 2)) 10.414 11.3980 12.73325 12.341
## mvnpdf(x = matrix(rep(1.96, 2)), Log = FALSE) 9.799 10.7215 12.88388 11.480
## uq max neval
## 13.3865 38.827 1000
## 12.4640 1023.688 1000

Both mvnpdf() and dmnvorm() functions being able to take a matrix as input, we can also compare their speeds in this setting:
n <- 100
mb <- microbenchmark(mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2)),
mvnpdf(x=matrix(1.96, nrow = 2, ncol = n),
Log=FALSE),
times=100)
mb## Unit: microseconds
## expr min lq
## mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2)) 15.703 17.3840
## mvnpdf(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 135.546 140.0355
## mean median uq max neval
## 19.08468 18.2450 19.5775 66.994 100
## 148.45977 146.3085 154.7340 190.732 100

Something happened… And we will find out what exactly is causing this issue in what comes next.