Skip to content

Benchmark against nativewebp, the other pure-Go encoder - #12

Merged
SeriousBug merged 4 commits into
mainfrom
benchmark-nativewebp
Aug 7, 2026
Merged

Benchmark against nativewebp, the other pure-Go encoder#12
SeriousBug merged 4 commits into
mainfrom
benchmark-nativewebp

Conversation

@SeriousBug

Copy link
Copy Markdown
Owner

Adds HugoSmits86/nativewebp to the
benchmark as a fifth engine.

It writes VP8L and nothing else, so it joins the lossless mode of the encode and
peak-RSS passes and appears in neither lossy mode. It gets no decode row either:
its Decode wraps golang.org/x/image/webp, which is already its own engine
here, so a row would be a second measurement of the same decoder.

What it shows

It lands at the opposite end of the tradeoff from us. Files 8-23% larger than
ours (11-33% larger than libwebp's), encoded in 0.41-0.71x our time on arm64 and
0.47-0.94x on amd64, at 0.41-0.55x our peak memory.

That last figure makes it the lightest lossless encoder in the tables, level with
libwebp. results.md previously claimed that spot for us, so that note is
corrected.

The speed comes from not searching. Its compression level picks histogram and
transform block sizes and nothing else: the cross-color transform is never
applied, the palette transform runs only for an already-*image.Paletted input,
the color cache size is fixed, and no candidate encode is ever tried and
discarded. libwebp at level 6 searches those axes and keeps the smallest result,
which is where its time goes and why its files are smaller. On arm64 that leaves
nativewebp ahead of even the C reference on the geometric mean (969 ms against
1.2 s); on amd64 it does not (1.6 s against 1.2 s).

Also here

  • benchmark/compat gains the one testable direction: nativewebp encodes, our
    decoder has to reproduce the pixels exactly, opaque and with alpha.
  • The bar figures grow with their footnote instead of clipping the last line,
    which the added engine's note was the first to need.
  • Charts regenerated. The engine is placed last in the bar order so its empty
    slot in the lossy panels sits at the edge of the group rather than as a hole in
    the middle.

On the numbers

The nativewebp rows were measured on the same two machines as the existing
tables but on a later date, and merged in rather than captured with them. The
pass that produced them re-ran the other engines as a check: every output size
came back byte-identical on both machines, and the timings landed within 8% of
the rows already in the table (arm64 about 6% slower across the board, amd64
about 2% faster). So the sizes are exact and the times carry that much
systematic error against neighbouring rows.

The webp-rust stage of that pass was cut short deliberately, since nothing about
it changed; its existing rows are reused as they were.

HugoSmits86/nativewebp writes VP8L and nothing else, so it joins the
lossless mode of the encode and peak-RSS passes and appears in neither
lossy mode. It gets no decode row either: its Decode wraps
golang.org/x/image/webp, which is already its own engine here.

It lands at the opposite end of the tradeoff from us, 8-23% larger files
in roughly half the time and under half the peak memory, because its
compression level only picks block sizes: no cross-color transform, no
palette detection, no candidate encode tried and discarded. That makes
it the lightest lossless encoder in the tables, so the note claiming
that spot for us is now wrong and is corrected.

compat gains the one testable direction, nativewebp encodes and our
decoder has to reproduce the pixels exactly.

The bar figures grow with their footnote instead of clipping the last
line, which the added engine's note was the first to need.
@SeriousBug
SeriousBug merged commit 95907a8 into main Aug 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant