--- title: "A Short Introduction to rcpptimer" author: Jonathan Berrisch date: "`r Sys.Date()`" output: rmarkdown::html_vignette: number_sections: no toc: no vignette: > %\VignetteIndexEntry{A Short Introduction to rcpptimer} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, # dev = "svg", warning = TRUE, message = FALSE, comment = "#>" ) Sys.setenv("OMP_THREAD_LIMIT" = 2) ``` This package provides a simple timer for `Rcpp` code. The interface resembles the [tictoc](https://CRAN.R-project.org/package=tictoc) R package. The package wraps [cpptimer](https://github.com/BerriJ/cpptimer), a header-only library that contains a class called `CppTimer`. rcpptimer adds this class as `Timer` to the `Rcpp` namespace. This introduction explains how to use `Rcpp::Timer` with `Rcpp::cppFunction` and how: - You can use multiple (potentially nested) timers - You can time scopes using `Rcpp::Timer::ScopedTimer` - You can turn off Warnings Check out the other vignettes for: - Using rcpptimer together with `Rcpp::sourceCpp` `vignette("sourceCpp")` - Adding rcpptimer to a Package `vignette("packages")` - Automatic and Manual Return of the Timings `vignette("autoreturn")` - Accessing unprocessed Timings, Resetting and Updating the Timer Results `vignette("advanced")` ## Initialize a Timer Initializing a timer is simple. There are four constructors available. The default constructor initializes a timer with warnings enabled that will write the results as data.frame called "times" to the R environment: ```c++ Rcpp::Timer timer; // default constructor Rcpp::Timer timer("my_timer"); // Set a custom name for the results Rcpp::Timer timer(false); // Disable warnings Rcpp::Timer timer("my_timer", false); // Set a custom name and disable warnings ``` Below and throughout other vignettes, we will use all four as needed. ## Straightforward Example With `Rcpp::cppFunction`, we must add the `depends` argument to the function to tell the compiler we want to link the 'rcpptimer' library to the C++ code. Then, we can construct an instance of the `Timer` class and use the `tic` and `toc` methods to measure the time it takes to execute a code block. Here, we allocate some memory to have something to measure: ```{r, eval = TRUE} Rcpp::cppFunction(" double add(double &x, double &y) { Rcpp::Timer timer; timer.tic(); double z = x + y; timer.toc(); return(z); }", depends = "rcpptimer" ) add(rnorm(1), runif(1)) ``` This function will automatically write a data frame called "times" to the R environment. Read more about that `autoreturn` feature (i.e., how to assign a custom variable name and how to manually handle the results) in `vignette("autoreturn")`. The resulting `times` object has two classes: `data.frame` and `rcpptimer`. We provide a custom S3 method for printing the results. If it is registered, it may scale the results to improve readability (see `rcpptimer::print.rcpptimer`). Otherwise, it will print the results using `base::print.data.frame`. ```{r, eval = TRUE} print(times) ``` ## Multiple Timers You can also use multiple timers in the same function. The Timers can be nested and overlapping. Just pass a string to the `tic` and `toc` methods to distinguish the timers: ```{r, eval = TRUE} Rcpp::cppFunction(' double add(double &x, double &y) { Rcpp::Timer timer; timer.tic("body"); timer.tic("add_1"); timer.tic("add_2"); double z = x + y; timer.toc("add_1"); timer.toc("add_2"); timer.toc("body"); return(z); }', depends = "rcpptimer" ) add(rnorm(1), runif(1)) print(times) ``` `rcpptimer` will group multiple timers with the same name and calculate summary statistics for them. Consider this more advanced example, which also uses OpenMP: ```c++ // fibonacci.cpp std::vector fibonacci_omp(std::vector n) { Rcpp::Timer timer; // This scoped timer measures the total execution time of 'fibonacci' Rcpp::Timer::ScopedTimer scpdtmr(timer, "fib_body"); std::vector results = n; #pragma omp parallel for 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); } ``` This function is included in rcpptimer, so we can execute it right away: ```{r, eval = TRUE} results <- rcpptimer::fibonacci_omp(n = rep(20:25, 10)) print(times) ``` ## Timing Scopes with Rcpp::Timer::ScopedTimer The `ScopedTimer` lets you measure the execution time of scopes. It will call .`.tic()` upon creation and `.toc()` upon destruction. Consider the simple example below: ```{r, eval = TRUE} Rcpp::cppFunction(' double add(double &x, double &y) { Rcpp::Timer timer; Rcpp::Timer::ScopedTimer scoped_timer(timer, "add"); double z = x + y; return(z); }', depends = "rcpptimer" ) add(rnorm(1), runif(1)) print(times) ``` Note that you only need to initialize the `ScopedTimer`. Once it goes out of scope, the timer will automatically be stopped. ## Warnings and how to Disable them The default setting will warn about timers that have been started using `.tic` but never stopped using `.toc()` and vice versa. This is useful to catch unmatched `.tic()` and `.toc()` calls that may be unmatched due to missing statements or typos. For example, the following code will produce two warnings: ```{r, eval = TRUE} Rcpp::cppFunction(' double add(double &x, double &y) { Rcpp::Timer timer; Rcpp::Timer::ScopedTimer scoped_timer(timer, "add"); timer.tic("add"); double z = x + y; timer.toc("ad"); return(z); }', depends = "rcpptimer" ) add(rnorm(1), runif(1)) ``` Note that this does not affect terminated timers such as 'mem'. ```{r, eval = TRUE} print(times) ``` These warnings occur at runtime. Unfortunately, we can't check for this at compile time. However, you can turn off these warnings by passing `false` to the constructor. This is useful if you need `.toc()` calls in code blocks that may not get executed, e.g. in conditional statements. The example below will not produce any warnings: ```{r, eval = TRUE} Rcpp::cppFunction(' double add(double &x, double &y) { Rcpp::Timer timer(false); Rcpp::Timer::ScopedTimer scoped_timer(timer, "add"); timer.tic("add"); double z = x + y; timer.toc("ad"); return(z); }', depends = "rcpptimer" ) add(rnorm(1), runif(1)) print(times) ```