A complete benchmarking toolkit for Elm. Define benchmarks with automatic correctness verification, run them headlessly across multiple JS engines, and get rich terminal output with scaling charts.
Define benchmarks with a clean API:
module MyBenchmarks exposing (suite)
import Bench exposing (Benchmark)
suite : Benchmark
suite =
Bench.describe "Array.Extra"
[ Bench.rank "mapToList"
(\mapToList -> mapToList negate myArray)
[ ( "with foldr", MapToList.withFoldr )
, ( "with toIndexedList", MapToList.withListMap )
]
, Bench.scale "mapToList across sizes"
[ 10, 100, 1000, 10000 ]
(\n -> Array.fromList (List.range 1 n))
[ ( "with foldr", MapToList.withFoldr negate )
, ( "with toIndexedList", MapToList.withListMap negate )
]
]Run with a single command:
$ elm-bench run --filter mapToListGet verification first, then results:
Verifying benchmark correctness...
✓ All implementations produce consistent results
Running benchmarks in node...
Benchmark Results
Array.Extra / mapToList
───────────────────────────────────────────────────────
with foldr 980,124 runs/s (99.9%) fastest
with Array.toIndexedList 535,891 runs/s (99.8%) 1.83x slowerSee how algorithms scale with input size:
Array.Extra / mapToList across sizes
30.2M ┼──╮───╮
17.5M ┤ ╰─────╮─────╮
10.1M ┤ ╰─────╮────╮
5.8M ┤ ╰────╮────╮
3.4M ┤ ╰────╮────╮
1.9M ┤ ╰────╮────╮
1.1M ┤ ╰────╮────╮
651.3K ┤ ╰────╮───╮
376.5K ┤ ╰───╮───╮
217.6K ┤ ╰───╮───╮
125.8K ┤ ╰───╮──╮
72.7K ┤ ╰──╮──╮
42.0K ┤ ╰──╮──╮
24.3K ┤ ╰──╮──╮
14.0K ┤ ╰──╮──
8.1K ┤ ╰──╮
4.7K ┤ ╰
(log scale)
n= 1 10 500 1000 10000
━━ with foldr (30.2M → 9.8K runs/s)
━━ with Array.toIndexedList (23.0M → 4.7K runs/s)In terminals with inline image support (iTerm2, WezTerm, Ghostty, VS Code), a full-color PNG chart is rendered instead.
Compare across JavaScript engines:
$ elm-bench run -t node -t chromium -t firefox --filter mapToList
Array.Extra / indexedMapToList
node chromium firefox
────────────────────────────────────────────────────────────────────
with Array.foldr 608,609 runs/s 712,445 runs/s 838,702 runs/s
with Array.indexedMap 527,083 runs/s 634,221 runs/s 671,752 runs/s
with List.indexedMap 384,174 runs/s 445,890 runs/s 339,777 runs/s
with toIndexedList 302,367 runs/s 356,112 runs/s 330,534 runs/sWhen rankings differ across targets, entries are highlighted in green (ranked higher than first target) or red (ranked lower) — immediately surfacing cross-engine performance characteristics.
cd my-project/benchmarks
npm install elm-bench
elm-json install gampleman/elm-benchBench.rank, Bench.compare, and Bench.scale automatically verify that all implementations produce the same result before benchmarking begins. This runs via elm-test-rs in seconds and catches broken optimizations before you waste time on misleading benchmark results.
Verifying benchmark correctness...
✗ Benchmark verification failed!
Some implementations produce different results:
↓ Array.Extra / reverse
✗ with List.reverse should match with push
Array.fromList [100,99,...,1]
╷
│ Expect.equal
╵
Array.fromList [1,2,...,100]Use Bench.skipEqualityCheck for cases where implementations intentionally differ (e.g. different valid orderings).
Only run what you need:
elm-bench run --filter mapToListFiltering happens at the Elm level before benchmarks execute — so even with hundreds of benchmarks in your suite, filtered runs are fast.
Use elm-explorations/test fuzzers for randomized benchmark input:
Bench.rankFuzz "mapToList"
(Fuzz.array (Fuzz.intRange 1 1000))
(\mapToList array -> mapToList negate array)
[ ( "with foldr", MapToList.withFoldr )
, ( "with toIndexedList", MapToList.withListMap )
]The same fuzzer is used for both the correctness check (fuzz test) and to generate the benchmark input (seeded deterministically via --seed).
Compare across JavaScript engines to ensure optimizations are consistent:
elm-bench run -t node -t chromium -t firefoxRequires Playwright for browser targets: npm install playwright.
Understand algorithmic complexity with Bench.scale:
Bench.scale "sorting"
[ 10, 100, 1000, 10000 ]
(\n -> List.range 1 n |> List.reverse)
[ ( "List.sort", List.sort )
, ( "mergeSort", mergeSort )
]Results are rendered as a chart with log-scale detection.
elm-bench run [options] [globs...]
Options:
-f, --filter <pattern> Only run benchmarks matching pattern
-t, --target <name> Execution target (repeatable): node, chromium, firefox, webkit
-r, --reporter <format> Output: console (default), json
--compiler <path> Path to elm binary
--project <path> Path to benchmarks elm.json
--seed <number> Random seed for fuzz-based inputs
--skip-test Skip correctness verification
--no-optimize Disable --optimize (for debugging)npm install playwright