PEGO

Benchmark results

The results of the benchmarks in bench/: every PEGO backend (closure, recursive and iterative bytecode VMs, Go parsers generated by pego gen) and, where a comparable parser exists, the Go standard library. What the benchmarks measure, the analysis of these results, and the history of the optimizations behind them are in performance.md.

This file is generated: go run ./bench/report runs the benchmarks, saves the raw results to bench/results.txt and writes this file from them; go run ./bench/report -in bench/results.txt rewrites it from saved results. Do not edit it by hand: a test checks that it matches the results.

Run

Measured on 2026-10-08 at commit b03fd59 with go1.27.1 on Apple M3 Max (darwin/arm64, 16 cores), with a load average of 3.88 3.81 4.15 (1, 5 and 15 minutes) when the run started. Times and memory are per operation, the median of 3 runs. Factors in parentheses are relative to the closure backend (lower is faster), except where a table says otherwise.

Results

Parsing time (position unit: code points)

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go Generated Go, typed (ParseAST) Standard library
JSON 11.8 ms 14.8 ms (1.25×) 18.1 ms (1.53×) 6.65 ms (0.56×) 3.10 ms (0.26×) 3.62 ms (encoding/json)
CSV 4.66 ms 6.19 ms (1.33×) 6.73 ms (1.45×) 2.57 ms (0.55×) 2.57 ms (0.55×) 754 µs (encoding/csv)
XML 13.4 ms 16.9 ms (1.26×) 19.7 ms (1.47×) 7.45 ms (0.56×) 3.97 ms (0.30×) 3.91 ms (encoding/xml)
Arith_Pratt 9.76 ms 12.7 ms (1.30×) 18.9 ms (1.94×) 6.44 ms (0.66×) 5.71 ms (0.59×) 2.26 ms (go/parser)
Arith_LeftRec 25.6 ms 27.3 ms (1.07×) 35.2 ms (1.38×) 15.2 ms (0.60×) 9.46 ms (0.37×) 2.32 ms (go/parser)
Minilang 14.6 ms 18.2 ms (1.25×) 23.2 ms (1.59×) 9.73 ms (0.67×) 10.7 ms (0.73×) –
Recovery 13.2 ms 17.6 ms (1.33×) 22.0 ms (1.67×) 9.48 ms (0.72×) 10.4 ms (0.79×) –
Outline 5.70 ms 6.88 ms (1.21×) 7.27 ms (1.28×) 3.79 ms (0.67×) 2.01 ms (0.35×) –

Parsing time (position unit: bytes)

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go
JSON 12.4 ms 15.1 ms (1.22×) 18.0 ms (1.45×) 6.96 ms (0.56×)
CSV 4.52 ms 6.00 ms (1.33×) 6.85 ms (1.52×) 2.82 ms (0.62×)
XML 13.2 ms 17.1 ms (1.30×) 20.7 ms (1.57×) 7.27 ms (0.55×)
Arith_Pratt 10.3 ms 13.0 ms (1.27×) 18.8 ms (1.83×) 6.80 ms (0.66×)
Arith_LeftRec 24.3 ms 29.0 ms (1.19×) 35.7 ms (1.47×) 14.6 ms (0.60×)
Minilang 14.9 ms 18.0 ms (1.21×) 23.3 ms (1.57×) 10.2 ms (0.69×)
Recovery 14.0 ms 17.2 ms (1.23×) 22.5 ms (1.61×) 10.0 ms (0.72×)
Outline 6.22 ms 7.18 ms (1.15×) 7.63 ms (1.23×) 4.12 ms (0.66×)

Memory per parse (position unit: code points)

Bytes allocated / number of allocations.

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go Generated Go, typed (ParseAST) Standard library
JSON 10.6 MB / 767 11.6 MB / 945 11.6 MB / 984 10.4 MB / 743 2.40 MB / 231 2.19 MB / 53 k
CSV 5.48 MB / 319 6.95 MB / 549 6.95 MB / 561 5.48 MB / 308 5.48 MB / 308 1.20 MB / 10 k
XML 12.8 MB / 870 14.0 MB / 1.0 k 13.9 MB / 1.1 k 12.7 MB / 853 3.19 MB / 274 2.54 MB / 80 k
Arith_Pratt 9.01 MB / 569 9.01 MB / 568 9.04 MB / 724 8.90 MB / 545 3.12 MB / 258 2.41 MB / 69 k
Arith_LeftRec 17.4 MB / 1.7 k 18.0 MB / 1.8 k 18.3 MB / 2.2 k 17.1 MB / 1.7 k 4.24 MB / 496 2.41 MB / 69 k
Minilang 7.85 MB / 830 8.26 MB / 922 8.30 MB / 1.0 k 7.79 MB / 802 9.87 MB / 936 –
Recovery 7.64 MB / 1.7 k 8.04 MB / 1.8 k 8.08 MB / 1.9 k 7.59 MB / 1.7 k 9.68 MB / 1.8 k –
Outline 10.2 MB / 2.2 k 10.5 MB / 2.3 k 10.5 MB / 2.4 k 10.1 MB / 2.2 k 2.62 MB / 1.9 k –

Memory per parse (position unit: bytes)

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go
JSON 10.6 MB / 767 11.6 MB / 945 11.6 MB / 984 10.4 MB / 743
CSV 5.48 MB / 320 6.94 MB / 549 6.94 MB / 561 5.47 MB / 309
XML 12.8 MB / 870 14.0 MB / 1.0 k 14.0 MB / 1.1 k 12.7 MB / 853
Arith_Pratt 9.00 MB / 570 9.00 MB / 568 9.03 MB / 724 8.90 MB / 545
Arith_LeftRec 17.4 MB / 1.7 k 18.1 MB / 1.8 k 18.3 MB / 2.2 k 17.1 MB / 1.7 k
Minilang 7.84 MB / 830 8.24 MB / 922 8.28 MB / 1.0 k 7.79 MB / 802
Recovery 7.63 MB / 1.7 k 8.02 MB / 1.8 k 8.07 MB / 1.9 k 7.59 MB / 1.7 k
Outline 10.2 MB / 2.2 k 10.5 MB / 2.3 k 10.5 MB / 2.4 k 10.1 MB / 2.2 k

Typed values (ParseAST of pego gen -types)

ParseAST builds the grammar's Go types directly when every value it can return has a Go type of its own ("direct"); otherwise (the values include CST nodes, as in CSV and minilang) it runs Parse and converts the tree ("converted"; for CSV, whose start rule's value is itself a list node, the tree is returned as it is). See code generation. The factor is relative to the generated parser's Parse (code points).

Workload ParseAST Parse ParseAST Memory (Parse → ParseAST)
JSON direct 6.65 ms 3.10 ms (0.47×) 10.4 MB / 743 → 2.40 MB / 231
CSV converted 2.57 ms 2.57 ms (1.00×) 5.48 MB / 308 → 5.48 MB / 308
XML direct 7.45 ms 3.97 ms (0.53×) 12.7 MB / 853 → 3.19 MB / 274
Arith_Pratt direct 6.44 ms 5.71 ms (0.89×) 8.90 MB / 545 → 3.12 MB / 258
Arith_LeftRec direct 15.2 ms 9.46 ms (0.62×) 17.1 MB / 1.7 k → 4.24 MB / 496
Minilang converted 9.73 ms 10.7 ms (1.10×) 7.79 MB / 802 → 9.87 MB / 936
Recovery converted 9.48 ms 10.4 ms (1.10×) 7.59 MB / 1.7 k → 9.68 MB / 1.8 k
Outline direct 3.79 ms 2.01 ms (0.53×) 10.1 MB / 2.2 k → 2.62 MB / 1.9 k

Recognition only (RecognizeOnly, no tree)

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go (Recognize) Standard library
JSON 8.15 ms 10.7 ms (1.31×) 13.1 ms (1.61×) 3.78 ms (0.46×) 326 µs (json.Valid)
CSV 3.22 ms 4.05 ms (1.26×) 4.61 ms (1.43×) 1.53 ms (0.47×) –
XML 10.9 ms 14.3 ms (1.32×) 17.5 ms (1.61×) 5.29 ms (0.49×) –
Arith_Pratt 6.88 ms 9.82 ms (1.43×) 15.6 ms (2.26×) 4.34 ms (0.63×) –
Arith_LeftRec 17.3 ms 20.8 ms (1.20×) 25.9 ms (1.49×) 9.79 ms (0.56×) –
Minilang 10.5 ms 14.6 ms (1.40×) 18.9 ms (1.81×) 7.24 ms (0.69×) –
Recovery 10.1 ms 14.1 ms (1.40×) 18.3 ms (1.80×) 7.01 ms (0.69×) –
Outline 3.50 ms 4.47 ms (1.28×) 4.95 ms (1.42×) 1.72 ms (0.49×) –

Memory per recognition (bytes allocated / number of allocations):

Workload Closure Bytecode (recursive) Bytecode (iterative) Generated Go (Recognize) Standard library
JSON 10.9 KB / 7 17.3 KB / 15 6.88 KB / 39 7.52 KB / 0 0 B / 0
CSV 3.97 KB / 5 5.71 KB / 12 6.79 KB / 21 2.09 KB / 0 –
XML 4.91 MB / 248 5.52 MB / 341 5.67 MB / 382 3.84 MB / 237 –
Arith_Pratt 5.34 KB / 7 6.95 KB / 9 35.7 KB / 165 4.20 KB / 0 –
Arith_LeftRec 979 KB / 249 1.44 MB / 265 1.61 MB / 590 1.14 MB / 241 –
Minilang 573 KB / 135 595 KB / 150 628 KB / 219 539 KB / 123 –
Recovery 698 KB / 414 719 KB / 429 750 KB / 498 664 KB / 403 –
Outline 1.95 MB / 1.8 k 2.05 MB / 1.8 k 2.07 MB / 1.9 k 1.63 MB / 1.8 k –

Incremental parsing (minilang, one-character edit followed by a parse)

Backend Document: time Document: memory Full parse each time: time Full parse each time: memory
Closure 154 µs 169 KB / 33 13.7 ms 7.84 MB / 830
Bytecode (recursive) 144 µs 177 KB / 50 18.2 ms 8.26 MB / 922
Bytecode (iterative) 143 µs 179 KB / 72 22.6 ms 8.30 MB / 1.0 k

Incremental parsing of a long document (CSV, 50,000 records, one-character edit followed by a parse)

Backend Time Memory
Closure 2.48 ms 2.71 MB / 23
Bytecode (recursive) 2.83 ms 7.07 MB / 64
Bytecode (iterative) 2.82 ms 7.08 MB / 77

Streaming (CSV, 50,000 rows, 2.17 MB)

Backend Time Throughput Memory
Closure 86.0 ms 25 MB/s 152 MB / 310 k
Bytecode (recursive) 101 ms 21 MB/s 161 MB / 310 k
Bytecode (iterative) 114 ms 19 MB/s 161 MB / 310 k
encoding/csv (Read loop) 7.12 ms 300 MB/s 6.98 MB / 100 k

Preparation (time until a grammar is ready to use)

Loading a .pegoc file includes the execution setup performed on the first parse after loading (assembling closures or preparing the VM).

Grammar Compile from source .pegoc → closure .pegoc → bytecode .pegoc (no AST) → bytecode
json 178 µs 107 µs 141 µs 50.6 µs
minilang 566 µs 222 µs 280 µs 125 µs

Memory (bytes allocated / number of allocations) and file sizes:

Grammar Source Compile from source .pegoc (with the AST) .pegoc → closure .pegoc → bytecode .pegoc (no AST) .pegoc (no AST) → bytecode
json 1.51 KB 288 KB / 2.7 k 2.88 KB 110 KB / 1.3 k 146 KB / 1.6 k 2.22 KB 80.7 KB / 538
minilang 4.42 KB 927 KB / 7.7 k 6.75 KB 317 KB / 2.9 k 392 KB / 3.6 k 4.90 KB 233 KB / 1.2 k

All results

Every benchmark of the run, by its name under bench/ (median of the runs; the throughput is in MB/s of input).

Benchmark Time Throughput Memory Allocations Other
Parse/JSON/closure/codepoints 11.8 ms 22.2 10.6 MB 767 –
Parse/JSON/closure/bytes 12.4 ms 21.2 10.6 MB 767 –
Parse/JSON/bytecode/codepoints 14.8 ms 17.7 11.6 MB 945 –
Parse/JSON/bytecode/bytes 15.1 ms 17.3 11.6 MB 945 –
Parse/JSON/iterative/codepoints 18.1 ms 14.5 11.6 MB 984 –
Parse/JSON/iterative/bytes 18.0 ms 14.6 11.6 MB 984 –
Parse/JSON/generated/codepoints 6.65 ms 39.4 10.4 MB 743 –
Parse/JSON/generated/bytes 6.96 ms 37.7 10.4 MB 743 –
Parse/JSON/generated_ast 3.10 ms 84.5 2.40 MB 231 –
Parse/JSON/encoding_json 3.62 ms 72.3 2.19 MB 53 k –
Parse/CSV/closure/codepoints 4.66 ms 45.4 5.48 MB 319 –
Parse/CSV/closure/bytes 4.52 ms 46.8 5.48 MB 320 –
Parse/CSV/bytecode/codepoints 6.19 ms 34.2 6.95 MB 549 –
Parse/CSV/bytecode/bytes 6.00 ms 35.2 6.94 MB 549 –
Parse/CSV/iterative/codepoints 6.73 ms 31.4 6.95 MB 561 –
Parse/CSV/iterative/bytes 6.85 ms 30.9 6.94 MB 561 –
Parse/CSV/generated/codepoints 2.57 ms 82.3 5.48 MB 308 –
Parse/CSV/generated/bytes 2.82 ms 75.0 5.47 MB 309 –
Parse/CSV/generated_ast 2.57 ms 82.3 5.48 MB 308 –
Parse/CSV/encoding_csv 754 µs 280 1.20 MB 10 k –
Parse/XML/closure/codepoints 13.4 ms 19.6 12.8 MB 870 –
Parse/XML/closure/bytes 13.2 ms 19.9 12.8 MB 870 –
Parse/XML/bytecode/codepoints 16.9 ms 15.5 14.0 MB 1.0 k –
Parse/XML/bytecode/bytes 17.1 ms 15.3 14.0 MB 1.0 k –
Parse/XML/iterative/codepoints 19.7 ms 13.3 13.9 MB 1.1 k –
Parse/XML/iterative/bytes 20.7 ms 12.7 14.0 MB 1.1 k –
Parse/XML/generated/codepoints 7.45 ms 35.2 12.7 MB 853 –
Parse/XML/generated/bytes 7.27 ms 36.1 12.7 MB 853 –
Parse/XML/generated_ast 3.97 ms 66.0 3.19 MB 274 –
Parse/XML/encoding_xml 3.91 ms 67.1 2.54 MB 80 k –
Parse/Arith_Pratt/closure/codepoints 9.76 ms 13.7 9.01 MB 569 –
Parse/Arith_Pratt/closure/bytes 10.3 ms 13.0 9.00 MB 570 –
Parse/Arith_Pratt/bytecode/codepoints 12.7 ms 10.5 9.01 MB 568 –
Parse/Arith_Pratt/bytecode/bytes 13.0 ms 10.3 9.00 MB 568 –
Parse/Arith_Pratt/iterative/codepoints 18.9 ms 7.04 9.04 MB 724 –
Parse/Arith_Pratt/iterative/bytes 18.8 ms 7.10 9.03 MB 724 –
Parse/Arith_Pratt/generated/codepoints 6.44 ms 20.7 8.90 MB 545 –
Parse/Arith_Pratt/generated/bytes 6.80 ms 19.6 8.90 MB 545 –
Parse/Arith_Pratt/generated_ast 5.71 ms 23.4 3.12 MB 258 –
Parse/Arith_Pratt/go_parser 2.26 ms 59.2 2.41 MB 69 k –
Parse/Arith_LeftRec/closure/codepoints 25.6 ms 5.22 17.4 MB 1.7 k –
Parse/Arith_LeftRec/closure/bytes 24.3 ms 5.48 17.4 MB 1.7 k –
Parse/Arith_LeftRec/bytecode/codepoints 27.3 ms 4.89 18.0 MB 1.8 k –
Parse/Arith_LeftRec/bytecode/bytes 29.0 ms 4.60 18.1 MB 1.8 k –
Parse/Arith_LeftRec/iterative/codepoints 35.2 ms 3.79 18.3 MB 2.2 k –
Parse/Arith_LeftRec/iterative/bytes 35.7 ms 3.74 18.3 MB 2.2 k –
Parse/Arith_LeftRec/generated/codepoints 15.2 ms 8.76 17.1 MB 1.7 k –
Parse/Arith_LeftRec/generated/bytes 14.6 ms 9.13 17.1 MB 1.7 k –
Parse/Arith_LeftRec/generated_ast 9.46 ms 14.1 4.24 MB 496 –
Parse/Arith_LeftRec/go_parser 2.32 ms 57.5 2.41 MB 69 k –
Parse/Minilang/closure/codepoints 14.6 ms 6.09 7.85 MB 830 –
Parse/Minilang/closure/bytes 14.9 ms 5.98 7.84 MB 830 –
Parse/Minilang/bytecode/codepoints 18.2 ms 4.88 8.26 MB 922 –
Parse/Minilang/bytecode/bytes 18.0 ms 4.95 8.24 MB 922 –
Parse/Minilang/iterative/codepoints 23.2 ms 3.83 8.30 MB 1.0 k –
Parse/Minilang/iterative/bytes 23.3 ms 3.82 8.28 MB 1.0 k –
Parse/Minilang/generated/codepoints 9.73 ms 9.14 7.79 MB 802 –
Parse/Minilang/generated/bytes 10.2 ms 8.67 7.79 MB 802 –
Parse/Minilang/generated_ast 10.7 ms 8.30 9.87 MB 936 –
Parse/Recovery/closure/codepoints 13.2 ms 6.77 7.64 MB 1.7 k –
Parse/Recovery/closure/bytes 14.0 ms 6.40 7.63 MB 1.7 k –
Parse/Recovery/bytecode/codepoints 17.6 ms 5.07 8.04 MB 1.8 k –
Parse/Recovery/bytecode/bytes 17.2 ms 5.19 8.02 MB 1.8 k –
Parse/Recovery/iterative/codepoints 22.0 ms 4.06 8.08 MB 1.9 k –
Parse/Recovery/iterative/bytes 22.5 ms 3.98 8.07 MB 1.9 k –
Parse/Recovery/generated/codepoints 9.48 ms 9.43 7.59 MB 1.7 k –
Parse/Recovery/generated/bytes 10.0 ms 8.90 7.59 MB 1.7 k –
Parse/Recovery/generated_ast 10.4 ms 8.57 9.68 MB 1.8 k –
Parse/Outline/closure/codepoints 5.70 ms 14.6 10.2 MB 2.2 k –
Parse/Outline/closure/bytes 6.22 ms 13.4 10.2 MB 2.2 k –
Parse/Outline/bytecode/codepoints 6.88 ms 12.1 10.5 MB 2.3 k –
Parse/Outline/bytecode/bytes 7.18 ms 11.6 10.5 MB 2.3 k –
Parse/Outline/iterative/codepoints 7.27 ms 11.5 10.5 MB 2.4 k –
Parse/Outline/iterative/bytes 7.63 ms 10.9 10.5 MB 2.4 k –
Parse/Outline/generated/codepoints 3.79 ms 22.0 10.1 MB 2.2 k –
Parse/Outline/generated/bytes 4.12 ms 20.2 10.1 MB 2.2 k –
Parse/Outline/generated_ast 2.01 ms 41.5 2.62 MB 1.9 k –
Recognize/JSON/closure 8.15 ms 32.2 10.9 KB 7 –
Recognize/JSON/bytecode 10.7 ms 24.5 17.3 KB 15 –
Recognize/JSON/iterative 13.1 ms 20.0 6.88 KB 39 –
Recognize/JSON/generated 3.78 ms 69.3 7.52 KB 0 –
Recognize/JSON/json_Valid 326 µs 803 0 B 0 –
Recognize/CSV/closure 3.22 ms 65.6 3.97 KB 5 –
Recognize/CSV/bytecode 4.05 ms 52.3 5.71 KB 12 –
Recognize/CSV/iterative 4.61 ms 45.9 6.79 KB 21 –
Recognize/CSV/generated 1.53 ms 139 2.09 KB 0 –
Recognize/XML/closure 10.9 ms 24.1 4.91 MB 248 –
Recognize/XML/bytecode 14.3 ms 18.3 5.52 MB 341 –
Recognize/XML/iterative 17.5 ms 15.0 5.67 MB 382 –
Recognize/XML/generated 5.29 ms 49.6 3.84 MB 237 –
Recognize/Arith_Pratt/closure 6.88 ms 19.4 5.34 KB 7 –
Recognize/Arith_Pratt/bytecode 9.82 ms 13.6 6.95 KB 9 –
Recognize/Arith_Pratt/iterative 15.6 ms 8.57 35.7 KB 165 –
Recognize/Arith_Pratt/generated 4.34 ms 30.7 4.20 KB 0 –
Recognize/Arith_LeftRec/closure 17.3 ms 7.70 979 KB 249 –
Recognize/Arith_LeftRec/bytecode 20.8 ms 6.41 1.44 MB 265 –
Recognize/Arith_LeftRec/iterative 25.9 ms 5.16 1.61 MB 590 –
Recognize/Arith_LeftRec/generated 9.79 ms 13.6 1.14 MB 241 –
Recognize/Minilang/closure 10.5 ms 8.48 573 KB 135 –
Recognize/Minilang/bytecode 14.6 ms 6.07 595 KB 150 –
Recognize/Minilang/iterative 18.9 ms 4.69 628 KB 219 –
Recognize/Minilang/generated 7.24 ms 12.3 539 KB 123 –
Recognize/Recovery/closure 10.1 ms 8.83 698 KB 414 –
Recognize/Recovery/bytecode 14.1 ms 6.33 719 KB 429 –
Recognize/Recovery/iterative 18.3 ms 4.90 750 KB 498 –
Recognize/Recovery/generated 7.01 ms 12.8 664 KB 403 –
Recognize/Outline/closure 3.50 ms 23.9 1.95 MB 1.8 k –
Recognize/Outline/bytecode 4.47 ms 18.7 2.05 MB 1.8 k –
Recognize/Outline/iterative 4.95 ms 16.9 2.07 MB 1.9 k –
Recognize/Outline/generated 1.72 ms 48.5 1.63 MB 1.8 k –
Incremental/closure/document 154 µs – 169 KB 33 –
Incremental/closure/fresh 13.7 ms – 7.84 MB 830 –
Incremental/bytecode/document 144 µs – 177 KB 50 –
Incremental/bytecode/fresh 18.2 ms – 8.26 MB 922 –
Incremental/iterative/document 143 µs – 179 KB 72 –
Incremental/iterative/fresh 22.6 ms – 8.30 MB 1.0 k –
IncrementalLong/closure 2.48 ms – 2.71 MB 23 –
IncrementalLong/bytecode 2.83 ms – 7.07 MB 64 –
IncrementalLong/iterative 2.82 ms – 7.08 MB 77 –
Stream/closure 86.0 ms 25.2 152 MB 310 k –
Stream/bytecode 101 ms 21.5 161 MB 310 k –
Stream/iterative 114 ms 19.1 161 MB 310 k –
Stream/encoding_csv 7.12 ms 305 6.98 MB 100 k –
Prepare/json/source 178 µs – 288 KB 2.7 k –
Prepare/json/pegoc/closure 107 µs – 110 KB 1.3 k 2880 file-bytes
Prepare/json/pegoc/bytecode 141 µs – 146 KB 1.6 k 2880 file-bytes
Prepare/json/pegoc-noast/bytecode 50.6 µs – 80.7 KB 538 2220 file-bytes
Prepare/minilang/source 566 µs – 927 KB 7.7 k –
Prepare/minilang/pegoc/closure 222 µs – 317 KB 2.9 k 6750 file-bytes
Prepare/minilang/pegoc/bytecode 280 µs – 392 KB 3.6 k 6750 file-bytes
Prepare/minilang/pegoc-noast/bytecode 125 µs – 233 KB 1.2 k 4900 file-bytes