--- 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.