13.3 Report how much time each chunk takes to run

By default, knitr provides a text-based progress bar to show you the knitting progress. If you want more precise timing information about the chunks, you may register a custom chunk hook to record the time for each chunk. Here is an example hook:

knitr::knit_hooks$set(time_it = local({
  now <- NULL
  function(before, options) {
    if (before) {
      # record the current time before each chunk
      now <<- Sys.time()
    } else {
      # calculate the time difference after a chunk
      res <- difftime(Sys.time(), now, units = "secs")
      # return a character string to show the time
      paste("Time for this code chunk to run:", round(res,
        2), "seconds")
    }
  }
}))

Then you can time a chunk with the chunk option time_it, e.g.,

```{r, time_it = TRUE}
Sys.sleep(2)
```

If you want to time all code chunks, you can certainly set the option globally: knitr::opts_chunk$set(time_it = TRUE).

In the above hook function, you can also output more information from the chunk options (i.e., the options argument of the function). For example, you may print out the chunk label in the returned value:

paste("Time for the chunk", options$label, "to run:", res)

Or you may record the time without printing it out in the hook:

all_times <- list()  # store the time for each chunk
knitr::knit_hooks$set(time_it = local({
  now <- NULL
  function(before, options) {
    if (before) {
      now <<- Sys.time()
    } else {
      res <- difftime(Sys.time(), now)
      all_times[[options$label]] <<- res
    }
  }
}))

Then you can access all the time information in the object all_times. This object is a named list with the names being chunk labels, and element values being the execution time for each chunk.

Lastly, as a technical note, we want to explain the use of the local() function in the previous hooks because some readers may not be familiar with it. This function allows you to run code in a “local” environment. The main benefit is that variables created in the code are local to that environment, so they will not pollute the outer environment (usually the global environment). For example, we created a variable now in local(), and used it in the time_it hook function. In the hook function, we update the value of now via the double arrow <<- instead of the normal assignment operator <-. This is because <<- assigns a value to a variable in the parent environment (which is the environment in local() in this case), and <- can only assign values to variables in the current environment. Before each code chunk is evaluated, the local variable now records the current time. After each code chunk is evaluated, we calculate the time difference between the current time and now. Note that local() returns the last value in the expression passed to it, which is a (hook) function in this case. In short, local() can make your workspace cleaner by not exposing variables that are only used locally but unused in the global environment. If you do not mind creating a variable now in the global environment, you can choose not to use local().