-
Notifications
You must be signed in to change notification settings - Fork 0
/
Perf-measure.Rmd
160 lines (113 loc) · 4.17 KB
/
Perf-measure.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Measuring performance
```{r Perf-measure-1, include = FALSE}
source("common.R")
```
Attaching the needed libraries:
```{r Perf-measure-2, warning=FALSE, message=FALSE}
library(profvis, warn.conflicts = FALSE)
library(dplyr, warn.conflicts = FALSE)
```
## Profiling (Exercises 23.2.4)
---
**Q1.** Profile the following function with `torture = TRUE`. What is surprising? Read the source code of `rm()` to figure out what's going on.
```{r Perf-measure-3, eval = FALSE}
f <- function(n = 1e5) {
x <- rep(1, n)
rm(x)
}
```
**A1.** Let's source the functions mentioned in exercises.
```{r Perf-measure-4, warning=FALSE}
source("profiling-exercises.R")
```
First, we try without `torture = TRUE`: it returns no meaningful results.
```{r Perf-measure-5, error=TRUE}
profvis(f())
```
As mentioned in the docs, setting `torture = TRUE`
> Triggers garbage collection after every torture memory allocation call.
This process somehow never seems to finish and crashes the RStudio session when it stops!
```{r Perf-measure-7, eval = FALSE}
profvis(f(), torture = TRUE)
```
The question says that documentation for `rm()` may provide clues:
```{r Perf-measure-8}
rm
```
I still couldn't figure out why. I would recommend checking out the [official answer](https://advanced-r-solutions.rbind.io/measuring-performance.html#profiling).
---
## Microbenchmarking (Exercises 23.3.3)
---
**Q1.** Instead of using `bench::mark()`, you could use the built-in function `system.time()`. But `system.time()` is much less precise, so you'll need to repeat each operation many times with a loop, and then divide to find the average time of each operation, as in the code below.
```{r Perf-measure-9, eval = FALSE}
n <- 1e6
system.time(for (i in 1:n) sqrt(x)) / n
system.time(for (i in 1:n) x^0.5) / n
```
How do the estimates from `system.time()` compare to those from `bench::mark()`? Why are they different?
**A1.** Let's benchmark first using these two approaches:
```{r Perf-measure-10}
n <- 1e6
x <- runif(100)
# bench -------------------
bench_df <- bench::mark(
sqrt(x),
x^0.5,
iterations = n,
time_unit = "us"
)
t_bench_df <- bench_df %>%
select(expression, time) %>%
rowwise() %>%
mutate(bench_mean = mean(unlist(time))) %>%
ungroup() %>%
select(-time)
# system.time -------------------
# garbage collection performed immediately before the timing
t1_systime_gc <- system.time(for (i in 1:n) sqrt(x), gcFirst = TRUE) / n
t2_systime_gc <- system.time(for (i in 1:n) x^0.5, gcFirst = TRUE) / n
# garbage collection not performed immediately before the timing
t1_systime_nogc <- system.time(for (i in 1:n) sqrt(x), gcFirst = FALSE) / n
t2_systime_nogc <- system.time(for (i in 1:n) x^0.5, gcFirst = FALSE) / n
t_systime_df <- tibble(
"expression" = bench_df$expression,
"systime_with_gc" = c(t1_systime_gc["elapsed"], t2_systime_gc["elapsed"]),
"systime_with_nogc" = c(t1_systime_nogc["elapsed"], t2_systime_nogc["elapsed"])
) %>%
mutate(
systime_with_gc = systime_with_gc * 1e6, # in microseconds
systime_with_nogc = systime_with_nogc * 1e6 # in microseconds
)
```
Now we can compare results from these alternatives:
```{r Perf-measure-11}
# note that system time columns report time in microseconds
full_join(t_bench_df, t_systime_df, by = "expression")
```
The comparison reveals that these two approaches yield quite similar results. Slight differences in exact values is possibly due to differences in the precision of timers used internally by these functions.
---
**Q2.** Here are two other ways to compute the square root of a vector. Which do you think will be fastest? Which will be slowest? Use microbenchmarking to test your answers.
```{r Perf-measure-12, eval = FALSE}
x^(1 / 2)
exp(log(x) / 2)
```
---
**A2.** Microbenchmarking all ways to compute square root of a vector mentioned in this chapter.
```{r Perf-measure-13}
x <- runif(1000)
bench::mark(
sqrt(x),
x^0.5,
x^(1 / 2),
exp(log(x) / 2),
iterations = 1000
) %>%
select(expression, median) %>%
arrange(median)
```
The specialized primitive function `sqrt()` (written in `C`) is the fastest way to compute square root.
---
## Session information
```{r Perf-measure-14}
sessioninfo::session_info(include_base = TRUE)
```