---
title: "benchmarks"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{benchmarks}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set (
collapse = TRUE,
comment = "#>"
)
```
```{r setup}
library (uaengine)
```
Urban Transport Analyst aims to be the fastest and most scalable open source
multi-modal routing engine. Other notable options include:
1. ["valhalla", for single-mode routing](https://github.com/valhalla/valhalla)
2. ["r5" for multi-modal routing](https://github.com/conveyal/r5)
The following benchmarks were generated by running a local docker image of the
`valhalla` engine. First generate some origin and destination coordinates. Note
that `valhalla` queries are limited to a total of 2,000 pairwise comparisons,
whereas `m4ra` is only limited by memory, and will generally extend to millions
if not billions of comparisons.
```{r sources-targets, eval = FALSE}
city <- "mannheim"
mode <- "motorcar"
nsources <- 10L
ntargets <- 200L
graph <- m4ra::m4ra_load_cached_network (city = "mannheim", mode = "motorcar", contracted = TRUE)
v <- m4ra::m4ra_vertices (graph, city)
vxy <- data.frame (lat = v$y, lon = v$x)
set.seed (1L)
index_s <- sample (nrow (v), nsources)
index_t <- sample (nrow (v), ntargets)
sources <- vxy [index_s, ]
targets <- vxy [index_t, ]
from <- v$id [index_s]
to <- v$id [index_t]
```
`valhalla` then needs the source and target data converted to a list, along
with a specified "costing", in this case as automobile.
```{r valhalla-data, eval = FALSE}
dat <- list (
sources = sources,
targets = targets,
costing = "auto"
)
```
A `valhalla` query is performed as a HTTP request to the server running in the
docker container. This function defines the request as a single function:
```{r valhalla-query, eval = FALSE}
valhalla <- function (dat) {
q <- httr2::request ("http://localhost:8002/sources_to_targets") |>
httr2::req_method ("POST") |>
httr2::req_body_json (data = dat) |>
httr2::req_perform ()
httr2::resp_body_json (q, simplifyVector = TRUE)
}
```
That suffices to generate the benchmark comparisons:
```{r benchmark, eval = FALSE}
bench::mark (
valhalla = valhalla (dat),
m4ra = m4ra::m4ra_times_single_mode (graph, from = from, to = to),
check = FALSE
) [, 1:5]
```
## # A tibble: 2 × 5
## expression min median `itr/sec` mem_alloc
##
## 1 valhalla 2.52s 2.52s 0.397 3.55MB
## 2 m4ra 767.77ms 767.77ms 1.30 70.84MB
And `m4ra` is around three times faster than `valhalla`. Moreover, `m4ra` is
optimised for very large many-to-many queries, and so generally performs larger
queries even more efficiently. These can't be benchmarked against `valhalla`
because of the intrinsic restriction to small queries of 2,000 or less
origin/destination pairs.
### Accuracy of estimated motorcar travel times
Automobile travel times in `m4ra` are calibrated using a [separate empirical
procedure](https://github.com/UrbanAnalyst/ttcalib) to ensure realistic travel
times are generated, and not just "optimal" travel times reflecting unhibited
travel at maximum permissible speed limits along all ways.
```{r travel-times, echo = FALSE, eval = FALSE}
times_val <- valhalla (dat)
times_val <- lapply (times_val$sources_to_targets, function (i) i$time)
times_val <- do.call (rbind, times_val)
times_m4ra <- m4ra::m4ra_times_single_mode (graph, from = from, to = to)
times_m4ra2val <- as.vector (times_m4ra) / as.vector (times_val)
times_m4ra2val <- data.frame (ratio = times_m4ra2val [which (!is.na (times_m4ra2val))])
```
```{r times_m4ra2val, echo = FALSE}
times_m4ra2val <- data.frame (ratio = 1.38)
```
```{r valhalla-png, eval = FALSE, echo = FALSE}
library (ggplot2)
ggplot (times_m4ra2val, aes (x = ratio)) +
geom_freqpoly () +
xlab ("ratio: m4ra / valhalla")
ggsave ("man/figures/m4ra-valhalla-time-ratio.png")
```
![](m4ra-valhalla-time-ratio.png)
The `m4ra` times are considerably slower than the "optimal" `valhalla` times,
with `m4ra` times being on average `r round(100 * mean(times_m4ra2val$ratio -
1))`% slower. Perhaps more importantly, the distribution is highly skewed,
indicating that `m4ra` clearly generates a few travel times far greater than
equivalent `valhalla` times. The `m4ra` calibration procedure attempts to
capture realistic effects of congestion on actual travel times, and this skewed
distribution is precisely what would be expected if `m4ra` indeeds captures
congestion effects which are not reflected in equivalent `valhalla` times.