Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance

Fatou is a compiled Rust formatter competing with two Julia-native tools, Runic and JuliaFormatter. This page compares their formatting throughput.

Methodology

We measure each tool in a warm loop: the tool is loaded once, run through a few warmup calls, and then timed over many iterations. This deliberately excludes process startup and first-call JIT compilation for the Julia tools, which would otherwise dominate and obscure the actual formatting cost. In other words, these numbers reflect a long-lived editor or language-server session, not the cold julia -e ... command-line invocation. That cold path is measured separately in Cold start below.

Because each tool runs in its own runtime, we report throughput in MB/s, which normalizes for byte count and stays comparable even when tools cover different files. Each tool formats with its own default style; we are measuring speed, not comparing output. A file counts for a tool only if that tool formats it without error, and any skips are reported.

Corpora

Two real-world projects, both pinned to a tag, picked to pull in opposite directions:

  • JuliaSyntax.jl, the parser Fatou targets for parity: dense branching, large token tables, and the code Fatou is best equipped to handle. Home turf.
  • DataFrames.jl, ordinary library code of the kind users actually format: docstring-heavy, macro-heavy, built around a large indexing DSL, and roughly 2.6x the size of the JuliaSyntax tree.

Scenarios

  • Single file (three of them), through each tool’s pure String -> String formatter (fatou::formatter::format, Runic.format_string, JuliaFormatter.format_text). The three targets span both size and shape: parse_stream.jl (42 KB of dense parser internals), kinds.jl (24 KB that is almost entirely one flat macro/data table), and abstractdataframe.jl (100 KB of docstring- and macro-heavy application code).
  • Project (one per corpus): the whole src/ tree, driven through each tool’s own directory entry point, so file discovery, IO, and the tool’s internal parallel scheduling all count. This is the “format my whole project” path. Fatou uses fatou::formatter::check_paths (glob/directory discovery plus rayon-parallel formatting, read-only); JuliaFormatter uses format(dir; overwrite = false) (recursive, thread-parallel, read-only). Runic is excluded from these scenarios by design: it has no in-process directory API (its format_file is single-file only, and directory walking lives solely in its CLI), so there is nothing to measure on the same terms.

Reproduce with task bench (after reloading the devenv shell so Runic is on the Julia path). Results are written to bench/results.json.

Setup

  • Corpora: JuliaSyntax.jl v0.4.10 (09576ca), DataFrames.jl v1.8.2 (946c72a)
  • Versions: Fatou 0.10.0, Runic 1.5.1, JuliaFormatter 2.4.0, Julia 1.12.6
  • Host: AMD Ryzen 9 7900 12-Core Processor (Linux x86_64)
  • Machine: terra
  • Warm-loop iterations: 50 single, 20 project; 3 warmup
  • Cold-start iterations: 5 fresh-process runs (single file)

Results

Single files and whole projects get a chart each: they measure different work at different sizes, and sharing one axis buries that. In both, Fatou is the baseline on the dashed line at 1, and every other tool’s time is plotted relative to it, so faster tools fall below the line and slower tools rise above it.

Single files

Formatting time relative to Fatou on a log scale (lower is faster). One dot per file, grouped at each tool and colored by file; Fatou sits on the dashed baseline at 1 and slower tools appear above it. Each file goes through the tool's pure String -> String formatter, in the tool's own default style. Hover a dot for the exact figures.
Data table

parse_stream.jl (JuliaSyntax/src/parse_stream.jl)

ToolFilesBytesMedian (ms)Throughput (MB/s)Relative
Fatou141,9376.26.75baseline
Runic141,93726.51.584.27x
JuliaFormatter141,9379.34.491.50x

kinds.jl (JuliaSyntax/src/kinds.jl)

ToolFilesBytesMedian (ms)Throughput (MB/s)Relative
Fatou124,4423.08.23baseline
Runic124,44217.71.385.97x
JuliaFormatter124,4423.76.641.24x

abstractdataframe.jl (DataFrames/src/abstractdataframe/abstractdataframe.jl)

ToolFilesBytesMedian (ms)Throughput (MB/s)Relative
Fatou1100,3888.212.30baseline
Runic1100,38831.93.153.91x
JuliaFormatter1100,38812.28.221.50x

Projects

Formatting time relative to Fatou on a log scale (lower is faster). One dot per project, grouped at each tool and colored by project; Fatou sits on the dashed baseline at 1 and slower tools appear above it. Each tool walks the whole source tree through its own directory entry point, so file discovery, IO, and internal parallelism all count; Runic is absent because it has no in-process directory API. Hover a dot for the exact figures.
Data table

JuliaSyntax (JuliaSyntax/src)

ToolFilesBytesMedian (ms)Throughput (MB/s)Relative
Fatou15332,78317.618.86baseline
JuliaFormatter14332,78323.414.251.32x

JuliaFormatter skipped parser.jl: processed, but the tool's own output failed its parse check, so the file was left unchanged

DataFrames (DataFrames/src)

ToolFilesBytesMedian (ms)Throughput (MB/s)Relative
Fatou36870,58115.157.80baseline
JuliaFormatter36870,58137.223.382.47x

Cold start

The warm loop above is the right model for an editor or language server that stays resident, but it hides the cost a command-line user pays on the very first run. This section measures that cold start directly: each tool is invoked as a fresh process that starts up, formats the single file once, and exits. For the Julia tools that means paying Julia’s startup, package loading, and first-call JIT compilation every time, through the same julia -e 'using ...' path a shell user would take; Fatou, a compiled binary, pays only process startup through fatou format. Only one file (parse_stream.jl) is measured, since the numbers are dominated by fixed startup and compilation cost, not by the file’s size.

Median cold-start time relative to Fatou on a logarithmic scale (lower is faster). Fatou is the dashed baseline at 1; each Julia tool sits above at its slowdown factor. Each run is a brand-new process that starts up, formats once, and exits. Fatou runs through fatou format; the Julia tools run through the same julia -e 'using ...' path a shell user takes, so Julia startup, package load, and first-call compilation all count. Hover a dot for the exact figures.
Data table
Cold start: parse_stream.jl, one fresh process per run
ToolMedian timeThroughput (MB/s)vs Fatou
Fatou7.8 ms5.38baseline
Runic192.0 ms0.2224.64x
JuliaFormatter237.7 ms0.1830.50x