• MIT
  • Rust
  • Linux · macOS · Windows
  • One binary

pito-compare

The charts tool behind PITO MD's numbers. You write the numbers in a small JSON or TOML file, and pcmp draws them as dark, crisp PNGs in a hand-drawn pixel font: two or more parties side by side on one axis, how many times faster or slower one is than another, or plain bars.

5 to 7 ms a chart, 63× to 77× faster than matplotlib Start to PNG on disk, a new process per chart, median of 50 runs: 5.5 to 7.1 ms against matplotlib 3.11's 408 to 447 ms on the same data. Measured 2026-10-15 on an AMD Ryzen 9 9950X, load 4 on 32 threads, for pito-compare v1.1.1's README.

A small TOML file in, paired, ratio and single-bar PNGs out, 1080 px wide. Recorded by prec.

Speed: every number and how it was measured

Numbers people read at a glance

Benchmark results read best as a picture, and a picture that looks the same every time.

What it's for

  • Benchmark charts for a README, release notes, docs or a site, from the numbers your bench prints.
  • Two or more versions, tools or settings on one axis, with how many times faster or slower printed beside every row.
  • Charts in a script or CI: one file in, PNGs out, exit codes you can trust, nothing else installed.

Who it's for

  • Library and tool authors who publish numbers and want them read in a second.
  • Projects whose charts, from every run, should sit side by side in one look, 1080 px wide.
  • Rust programs that draw charts from their own data through the library, with no command line and no clap.

There is no plotting library: the canvas, the 5x7 pixel font and the PNG writer are a few hundred lines of Rust, so a chart is drawn the same, pixel for pixel, on every machine.

Speed

pcmp draws its own benchmark: a new process per chart, 50 runs each, then matplotlib drawing the same data the same way.

Paired bars, milliseconds per chart, lower is better: paired bars pcmp 7.1 against matplotlib 446.9, balanced ratios 5.5 against 421.9, single bars 5.7 against 408.1.
Start to PNG on disk pcmp 1.1.0 against Python 3.14 with matplotlib 3.11 drawing the same data as a similar dark 1080 px chart. Median of 50, load 4 on 32 threads, 2026-10-15.

Every number, and how

The ratios, peak memory, binary size and build time, with the bench that took them, are on the Speed page.

See pito-compare's speed

Use it

pcmp runs on Linux (x86_64), Windows (x86_64) and macOS (Apple Silicon). Each release carries a binary for all three, with a SHA256SUMS file.

Linux
v=$(basename "$(curl -fsSLo /dev/null -w '%{url_effective}' https://github.com/gmrdad82/pito-compare/releases/latest)")
mkdir -p ~/.local/bin && curl -fsSL "https://github.com/gmrdad82/pito-compare/releases/download/$v/pcmp-$v-linux-x86_64.tar.gz" | tar -xz -C ~/.local/bin pcmp
macOS
v=$(basename "$(curl -fsSLo /dev/null -w '%{url_effective}' https://github.com/gmrdad82/pito-compare/releases/latest)")
mkdir -p ~/.local/bin && curl -fsSL "https://github.com/gmrdad82/pito-compare/releases/download/$v/pcmp-$v-macos-arm64.tar.gz" | tar -xz -C ~/.local/bin pcmp
Windows, PowerShell
$v = (Invoke-RestMethod https://api.github.com/repos/gmrdad82/pito-compare/releases/latest).tag_name
Invoke-WebRequest "https://github.com/gmrdad82/pito-compare/releases/download/$v/pcmp-$v-windows-x86_64.zip" -OutFile "$env:TEMP\pcmp.zip"
Expand-Archive "$env:TEMP\pcmp.zip" "$env:LOCALAPPDATA\pcmp" -Force
[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ";$env:LOCALAPPDATA\pcmp", 'User')

On Linux and macOS, ~/.local/bin needs to be on your PATH; on Windows, open a new terminal afterwards. From source, on any of the three, with Rust 1.97 or newer: cargo install --locked --git https://github.com/gmrdad82/pito-compare --tag v1.1.2 pito-compare. pcmp needs nothing else to run.

Your first chart

demo.toml
title = "PARSE SPEED"
unit = "MEGABYTES PER SECOND"
better = "higher"
suffix = " MB/S"
parties = [{ name = "PARSER V2" }, { name = "PARSER V1" }]
rows = [
  { label = "CONFIG FILES", values = [612, 240] },
  { label = "LOG LINES", values = [455, 420] },
  { label = "DATA DUMPS", values = [1210, 388] },
]
Draw it
$ pcmp demo.toml --out charts
charts/demo-paired.png
charts/demo-ratio.png
The command line
pcmp <DATA> [--out DIR] [--kind all|paired|ratio|bars]

--kind all, the default, draws single bars for one party, and the paired and ratio charts for two or more. A JSON file with the same keys works the same.

In a Rust project

Cargo.toml
[dependencies]
pito-compare = { git = "https://github.com/gmrdad82/pito-compare", tag = "v1.1.2", default-features = false }
Draw from your own data
use pito_compare::{Better, Chart, Group, Kind, Row};

let mut chart = Chart::new("DECODE SPEED", "FRAMES PER SECOND", Better::Higher, &["OURS", "THEIRS"]);
chart.suffix = " FPS".into();
chart.groups.push(Group { name: "CPU".into(), rows: vec![Row::new("H.264 1080P", &[412.0, 298.0])] });
for (kind, canvas) in pito_compare::render(&chart, Kind::All)? {
    canvas.save(format!("decode-{kind}.png").as_ref())?;
}

default-features = false leaves out the command line and its clap.

The charts it draws

One dark look, 1080 px wide, so charts from different runs and projects sit side by side. The examples in this section are pito-compare's own, drawn from its examples/ folder, whose numbers are made up.

  1. 01

    Paired bars

    Every party's value on one shared linear axis from zero, the value beside each bar, and per row how many times faster or slower the first party is than the one it is compared with.

    Rows fall under group headings, a row can carry a mark such as * or †, and notes under the chart explain them.

    More on charts

    Paired bars, sample data: parse speed of parser v2 against parser v1 in two groups, with 2.5× faster, same speed and 3.2× slower tags, a marked row and two notes.
    Sample data from pito-compare's examples/speed.toml.
  2. 02

    Three or more parties

    Up to 12 parties on the same axis. versus names the one the first party's tags compare with, so a third party can stand there for scale; better = "lower" turns the axis for times and latencies.

    Paired bars with three parties, lower is better, sample data: request latency of cache v2, cache v1 and no cache, each row tagged against cache v1.
    Sample data from pito-compare's examples/latency.json.
  3. 03

    Balanced ratios

    1× in the middle, slower to the left and faster to the right on a symmetric log scale, so 3× slower is as long as 3× faster. pcmp draws it next to the paired bars for any data with two or more parties.

    The balanced ratio chart of the sample parse speed data: green bars to the right of 1× for faster rows, red to the left for slower ones.
    The same sample data as a balanced ratio chart.
  4. 04

    Single bars

    One party's values, when there is nothing to compare. --kind bars with several parties draws one chart per party.

    Single bars, sample data: cold build time per platform for one release build, in three groups.
    Sample data from pito-compare's examples/build.toml.
  5. 05

    Your colours

    A party's colour is any #RRGGBB or one of the chart's roles (ours, theirs, dim, text, faster, slower); the legend and the bars follow it. The charts on these pages wear each project's own colour.

    Paired bars with four parties in custom colours, sample data: frame rate of Vulkan, Metal, DirectX 12 and OpenGL in three scenes, each row tagged Vulkan against OpenGL.
    Sample data from pito-compare's examples/frames.toml.
  6. 06

    Never a wrong chart

    Any character the font can't draw, an unknown key, a negative value or text too long for its line is refused with one line naming the file and the field, and no PNG is written.

    Exit 0 when every chart was drawn, 1 when the data couldn't be read or drawn, 2 when the command line is wrong. pcmp's tests draw every chart in its README and compare it with the image there.

    More on never wrong

    A refused file
    $ pcmp bad.toml
    pcmp: bad.toml: groups[0].rows[0].values holds -612: values are finite and zero or more

Where PITO MD runs it

Every chart on PITO MD's open-source pages, and in the READMEs behind them, is drawn by pcmp.

  • pito-codecs

    Where its look comes from

    pito-compare is the chart drawer of pito-codecs' benchmarks made into a tool, with its exact look. It draws every chart on pito-codecs' page and README.

    See pito-codecs' speed
  • prec

    prec against VHS

    The frames, re-render and size charts on prec's page come from pcmp, drawn from prec's bench data.

    See prec's speed
  • pito-tui

    Frame times

    pito-tui's frame-time charts, average and worst of 1,000 frames, are pcmp's too.

    See pito-tui's speed
Paired bars from pito-codecs' page, milliseconds, lower is better: time to first frame at 1440p, pito-codecs 3.2 to 7.4 ms against ffmpeg's 85.2 to 88.9 ms, 12× to 28× faster.
On pito-codecs' page: time to first frame Drawn by pcmp in pito-codecs' gold.
Paired bars from prec's page, higher is better: frames in a 10-second clip, prec 299, 598 and 1196 against VHS's 239, 228 and 214 at 30, 60 and 120 fps.
On prec's page: frames in the clip Drawn by pcmp in prec's cyan.

Read it. Run it. It's MIT.

The code is MIT licensed, © Catalin Ilinca: read it, fork it, ship it in your own work. The pito-compare, pcmp and PITO MD names and their logos are © Catalin Ilinca, all rights reserved, and are not covered by the MIT licence.

Issues are welcome on GitHub: a chart that comes out wrong, with the data file that draws it, or an idea for a chart pcmp should draw. Pull requests aren't taken.

Credits

The clip is pito-compare's own shot, recorded by prec and made a GIF; the example charts are pito-compare's README images, drawn by pcmp from its examples/, whose numbers are made up, and its bench/, whose numbers are measured. pito-compare's drawing, colours and pixel font come from pito-codecs' benchmark charts.

Set in Archivo by Omnibus-Type and Atkinson Hyperlegible Mono by the Braille Institute, both under the SIL Open Font License.