'Rcpp' Tic-Toc Timer with 'OpenMP' Support.
RcppTimeR - Rcpp Tic-Toc Timer with OpenMP Support
This R Package provides Rcpp bindings for cpptimer, a simple tic-toc timer class for benchmarking C++ code. It's not just simple, it's blazing fast! This sleek tic-toc timer class supports overlapping timers as well as OpenMP parallelism. It boasts a microsecond-level time resolution. We did not find any overhead of the timer itself at this resolution. Results (with summary statistics) are automatically passed back to R as a data frame.
Install
Install rcpptimer from CRAN.
install.packages("rcpptimer")
The Rcpp side of things
Link it in your DESCRIPTION
file or with //[[Rcpp::depends(rcpptimer)]]
, and load the header library into individual .cpp
files with #include <rcpptimer.h>
. Then create an instance of the Rcpp::Clock class and use:
.tic(std::string)
to start a new timer. .toc(std::string)
to stop the timer.
//[[Rcpp::depends(rcpptimer)]]
#include <rcpptimer.h>
std::vector<int> fibonacci(std::vector<int> n)
{
Rcpp::Timer timer; // Or Rcpp::Timer timer("my_name"); to assign a custom name
// to the returned dataframe (default is 'times')
timer.tic("fib_body"); // Start timer measuring the whole function
std::vector<int> results = n;
for (int i = 0; i < n.size(); ++i)
{
// Start a timer for each fibonacci number
timer.tic("fib_" + std::to_string(n[i]));
results[i] = fib(n[i]);
// Stop the timer for each fibonacci number
timer.toc("fib_" + std::to_string(n[i]));
}
// Stop the timer measuring the whole function
timer.toc("fib_body");
return (results);
}
Multiple timers with the same name (i.e. in a loop) will be grouped and we report the Mean and Standard Deviation for them. The results will be automatically passed to R as the timer
instance goes out of scope. You don't need to worry about return statements.
The R side of things
On the R end, we can now observe the times
object that was silently passed to the global environment:
[R] fibonacci(n = rep(10 * (1:4), 10))
[R] times
Name Milliseconds SD Count
1 fib_10 0.002 0.001 10
2 fib_20 0.048 0.011 10
3 fib_30 5.382 0.070 10
4 fib_40 658.280 1.520 10
5 fib_body 6637.259 0.000 1
OpenMP Support
Since we added OpenMP support, we also have an OpenMP version of the fibonacci
function:
std::vector<int> fibonacci_omp(std::vector<int> n)
{
Rcpp::Timer timer;
timer.tic("fib_body");
std::vector<int> results = n;
#pragma omp parallel for
for (int i = 0; i < n.size(); ++i)
{
timer.tic("fib_" + std::to_string(n[i]));
results[i] = fib(n[i]);
timer.toc("fib_" + std::to_string(n[i]));
}
timer.toc("fib_body");
return (results);
}
Nothing has to be changed with respect to your timer
instance. The timings show that the OpenMP version is significantly faster (fib_body):
Name Milliseconds SD Count
1 fib_10 0.022 0.031 10
2 fib_20 0.132 0.057 10
3 fib_30 8.728 2.583 10
4 fib_40 779.942 91.569 10
5 fib_body 908.919 0.000 1
Scoped Timer
We also added a new Rcpp::CppTimer::ScopedTimer
. This can be used to time the lifespan of an object until it goes out of scope. This is useful for timing the duration of a function or a loop. Below is the fibonacci
example from above. However, we replace the "fib_body" tic-toc timer with the scoped version.
std::vector<int> fibonacci(std::vector<int> n)
{
Rcpp::Timer timer;
// This scoped timer measures the total execution time of 'fibonacci'
Rcpp::Timer::ScopedTimer scpdtmr(timer, "fib_body");
std::vector<int> results = n;
for (unsigned int i = 0; i < n.size(); ++i)
{
timer.tic("fib_" + std::to_string(n[i]));
results[i] = fib(n[i]);
timer.toc("fib_" + std::to_string(n[i]));
}
return (results);
}
Note that you can name your object (in this example scpdtmr
) however you like. Rcpp::CppTimer::ScopedTimer
acts as a wrapper, so it will call .tic
upon construction and .toc
will be called automatically upon destruction.
Rcpp::CppTimer::ScopedTimer
is useful to time the duration of a function or a loop.
Limitations
Processes taking less than a microsecond cannot be timed.
Acknowledgments
This package (and the underlying cpptimer class) was inspired by zdebruine's RcppClock. I used that package a lot and wanted to add OpenMP support, alter the process of calculating summary statistics, and apply a series of other small adjustments. I hope you find it useful.