Dirk Eddelbuettel — written Jan 6, 2013 — updated Dec 30, 2016 — source
Sine the 0.10.2 release, Rcpp contains an internal class Timer
which can be used for fine-grained benchmarking. Romain motivated
Timer
in a
post to the mailing list
where Timer
is used to measure the different components of the costs of
random number generation.
A slightly modified version of that example follows below.
#include <Rcpp.h>
#include <Rcpp/Benchmark/Timer.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector useTimer() {
int n = 1000000;
// start the timer
Timer timer;
timer.step("start"); // record the starting point
for (int i=0; i<n; i++) {
GetRNGstate();
PutRNGstate();
}
timer.step("get/put"); // record the first step
for (int i=0; i<n; i++) {
GetRNGstate();
rnorm(10, 0.0, 1.0);
PutRNGstate();
}
timer.step("g/p+rnorm()"); // record the second step
for (int i=0; i<n; i++) {
// empty loop
}
timer.step("empty loop"); // record the final step
NumericVector res(timer); //
for (int i=0; i<res.size(); i++) {
res[i] = res[i] / n;
}
return res;
}
We get the following result, each expressing the cost per iteration in nanoseconds, both cumulative (default) and incrementally (by taking differences).
res <- useTimer()
res # normal results: cumulative
start get/put g/p+rnorm() empty loop 0.000156 1571.057133 3920.082384 3920.085223
diff(res) # simple difference
get/put g/p+rnorm() empty loop 1571.056977 2349.025251 0.002839
The interesting revelation is that repeatedly calling
GetRNGstate()
and PutRNGstate()
can amount to about 60% of the
cost of RNG draws. Luckily, we usually only have to call these
helper functions once per subroutine called from R (rather than
repeatedly as shown here) so this is not really a permanent cost to
bear when running simulations with R.
It also show the usefulness of a fine-grained timer at the code level.
Tweet