Skip to content

Re-measure the whole fleet at render v0.21.0 - #24

Merged
tannevaled merged 28 commits into
mainfrom
baseline-render-v0.21.0
Aug 31, 2026
Merged

Re-measure the whole fleet at render v0.21.0#24
tannevaled merged 28 commits into
mainfrom
baseline-render-v0.21.0

Conversation

@tannevaled

@tannevaled tannevaled commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

render v0.21.0 changes the decoded output of every subsampled JPEG in the fleet — Go's image/jpeg replicates a subsampled chroma sample where libjpeg interpolates, and v0.21.0 reproduces libjpeg's filter. The landed records were taken at v0.20.0, so they described a library that no longer exists.

All 23 populations, 3280 documents, re-run with the landed instrument. All 23 exited 0; each is committed on its own so an interruption cost one population rather than the run. Based on #22 and #23; it targets main because CI here is branches: [main], so a PR pointing anywhere else reports no checks at all. Its diff shrinks to the baseline: commits and the two READMEs once those land.

The claim is corrected

The improvement was being quoted as 61.2% → 99.3% corpus-wide. That figure is not reproducible from the landed instrument, and it is not a corpus-wide figure: it is DCTDecode alone, at a gate of 4, over the 559 pictures pdfimages -list calls gray or rgb — a denominator #20 showed admits /CalRGB.

At the landed gate of 2, over all 23 populations:

v0.20.0 v0.21.0
DCTDecode agreement 34.0% (146/430) 43.2% (184/426)
whole fleet 84.5% (3347/3962) 85.5% (3385/3957)

The rate understates it, because the gate is a cliff and the change was one of magnitude. Per-population medians of the worst channel on the direct DCTDecode rows:

v0.20.0   16  18  23  23  26  31  34  47  50  58  62  171  244  255      (14 rows)
v0.21.0    3   3   3   3   3   3   4   4   4    4      171  233  255     (13 rows)

Ten of thirteen rows now sit one or two levels above the gate, where eleven of fourteen used to sit between 16 and 62. The row that vanished is uk-govuk: 11 differing pictures to none, 92.3% → 100.0%. Three did not move — fr-impots 255, gh-pypdf 233, gh-qpdf 171 — and those are not chroma reconstruction.

Four other things the run says

The only filter that moved is the only filter the change touched. (samples) 69.7%, (samples) mask 96.2%, JPXDecode 99.2% and the three lossless filters at 100.0% are unchanged to the picture, as is the fleet's 1990 bit-identical. That is a check on the instrument as much as on the library.

The previous run's argument about the empty band did not survive its own library. It found 2 of 21 direct rows under 1% with peak medians of 18 and 23 — "sparse gross error". At v0.21.0 it is 10 of 20 under 1%, a gap of 64.5 above them, and their peak medians are 3 and 4. It does not put the withdrawn 1% back and nothing here proposes to: with N at 0 the criterion is the peak, so a band in the share is evidence about D. Recorded, not spent.

#20's rule moved exactly five pictures, and it is measured rather than asserted. The per-filter pictures and unmatched counts are identical filter for filter across the two runs, so every difference in the direct/converted split is that rule: fr-cerfa 3, gh-openpdf 1, ia-uscourts 1. Four are DCTDecode — the four /CalRGB pictures the issue named. calibrated, at 1802, is a different quantity, and the documentation that called it "what the rule moved" is corrected in this stack.

hung is 0 everywhere, and the baseline says why that is not "nothing hangs". The document that hangs draws no picture on its first page, so images never asks poppler about it; compare meets it and names it. The bound is unexercised by images on this corpus, and that is stated rather than left to be inferred.

Two things held to, both learned here

No table mixes instruments or library versions. Every record carries its modules block; the prose names v0.21.0, gate 2, the 2m0s bound and gfx v0.19.0, and the v0.20.0 figures appear only labelled as the previous run's.

A population that did not complete would be named in the table with its reason. None was: all 23 completed, with peak memory ia-americana 5.9 GB, ia-biodiversity 3.6 GB, ia-medical 3.2 GB. The refused count is 4, the same four ia-biodiversity documents, and they are render's own 256-megapixel budget rather than documents we cannot read.

No timings. Another job shared the machine throughout, so every duration measured would have measured that job as much as this one. Peak memory is reported because it is not a timing.

Gate

gofmt, go vet, -race, exact 100% statement coverage and the nine cross-compile targets, all under GOTOOLCHAIN=go1.27.0, GOWORK=off, CGO_ENABLED=0.

@tannevaled
tannevaled force-pushed the baseline-render-v0.21.0 branch from 0483582 to 40977d4 Compare August 31, 2026 16:50
@tannevaled
tannevaled marked this pull request as ready for review August 31, 2026 16:55
@tannevaled
tannevaled force-pushed the baseline-render-v0.21.0 branch from 287c3f8 to 119214b Compare August 31, 2026 17:09
pdfimages lists a /CalRGB picture as "rgb" and a /CalGray one as "gray"
(utils/ImageOutputDev.cc:159-164), while the pixels it writes went through
colorMap->getRGB (:451) — for a GfxCalRGBColorSpace, the gamma, the matrix and
the chromatic adaptation. So the direct bucket admitted pictures poppler HAD
converted, and measured every pixel of them against a colour conversion we did
not make. All four pictures still differing by more than four levels after
render v0.21.0's chroma fix were CalRGB.

The bucket is now decided by both sides: a picture is converted when the
listing says so, and also when its own /ColorSpace resolves to CalRGB, CalGray,
ICCBased or Lab, whatever the listing says. Only the first two can move
anything — poppler lists ICCBased as "icc" and Lab as "lab", both converted
already — and all four are named so the rule is a statement about the document
rather than a patch over one judge's table.

The space is read from the picture's own dictionary, through the page's forms
as render.Images walks them and to the same depth, and through the resource
dictionary when a picture names its space rather than writing it out.

Where it cannot decide it does not decide quietly. A name is unique within one
resource dictionary and not across the several a page reaches, so a page whose
two forms each name their own Im1 marks both when either is CalRGB. That
over-counts converted by at most those pictures; the other direction credits a
colour conversion to a codec, which is what this is about.

Bucket.Calibrated and the record's "calibrated" say how many pictures the
second half of the rule moved, per filter and per bucket, because a rule that
changes a bucket has to be able to say what it changed. On the four documents
issue #20 names, it moves 4 of their 5 DCTDecode pictures and leaves the fifth
where it was.

Closes #20
v0.21.0 puts a JPEG's chroma back the way every other reader does: Go's
image/jpeg replicates a subsampled chroma sample and libjpeg interpolates, and
render now reproduces libjpeg's filter. That changes the decoded output of
every subsampled JPEG in both corpora, so the records under baseline/ describe
a library that no longer exists and the whole run is retaken.

github.com/go-gfx/gfx also moved from v0.16.0 to v0.19.0 since those records
were taken. Every record carries its own modules block; the prose beside them
names the versions, and no table mixes two.
Every one of the 23 populations was re-run with the landed instrument at render
v0.21.0, gate 2, the colour-space rule of #20 and the bound of #21. All 23
exited 0 and every one is in the tables.

The improvement was being quoted as 61.2% to 99.3% corpus-wide. That figure is
DCTDecode alone, at a gate of 4, over the 559 pictures pdfimages -list calls
gray or rgb — a denominator #20 showed admits CalRGB, so it no longer exists
here. At gate 2 across all 23 populations, DCTDecode goes 34.0% to 43.2% and
the fleet 84.5% to 85.5%.

That understates what happened, because the gate is a cliff and the change was
a change of magnitude. The per-population medians of the worst channel on the
direct DCTDecode rows were 16 18 23 23 26 31 34 47 50 58 62 171 244 255; they
are now 3 3 3 3 3 3 4 4 4 4 171 233 255, and uk-govuk's row is gone entirely at
100.0%. Ten of thirteen rows sit one or two levels above the gate. Three did
not move and are not chroma reconstruction.

The previous run's finding that the empty band was gone, and that what sat
under 1% was worse than what used to, was true of v0.20.0 and is not true of
v0.21.0: ten of twenty direct rows are under 1% with a gap of 64.5 above them,
and their peak medians are 3 and 4 rather than 18 and 23. It does not put the
1% back and nothing here proposes to — with N at 0 the criterion is the peak,
so a band in the share is evidence about D, and it is recorded rather than
spent.

The colour-space rule moved five pictures, measurable exactly because the
per-filter picture and unmatched counts are identical across the two runs: four
DCTDecode and one (samples), and the four are the four #20 named. calibrated is
1802 and is a different quantity, which the document now says.

And the only filter that moved is the only one the change touched: every other
filter's agreement and the fleet's 1990 identical are unchanged to the picture.
The sections quoting the landed record were quoting v0.20.0, including the
DCTDecode rate, the band analysis and the claim that what sat under 1% was
worse than what used to. All three are properties of a library that no longer
exists. What replaces them names the version, corrects the 61.2%-to-99.3%
figure to what the landed instrument measures, and says why the rate understates
a change that was one of magnitude.
@tannevaled
tannevaled force-pushed the baseline-render-v0.21.0 branch from 119214b to a6ca43d Compare August 31, 2026 17:45
Three precision defects in the prose, all of the kind that produced the figure
this run exists to correct.

The two ordered lists of peak medians are sorted, not paired, and have 14 and 13
rows; a reader pairing their columns gets the wrong population every time. They
are now given paired as well.

"Three did not move at all" was wrong about gh-pypdf, which went 244 to 233. It
moved, just not to anywhere near rounding.

And the whole-fleet figure spans three changes, not one: the denominators differ
by the five pictures #20 moved, and gfx went v0.16.0 to v0.19.0 in between. All
five moved pictures differ at v0.21.0, so the bucketing can be held fixed
arithmetically: DCTDecode 42.8% and the fleet 85.4% are what is attributable to
the library. Quoting 43.2% as v0.21.0's would credit a bucketing change to a
library change, which is a smaller version of the same error.
@tannevaled

Copy link
Copy Markdown
Contributor Author

Rebased onto main after #22 merged. #23 is now a single commit against main (mergeable_state: clean), and this PR sits on top of it.

Three precision defects in this PR's own prose are fixed in f9f10f7, all of the kind that produced the figure this run exists to correct:

  • The two ordered lists of peak medians are sorted, not paired — 14 rows and 13 rows, in different population order. Pairing their columns gives the wrong population every time. baseline/README.md now gives the paired table as well.
  • "Three did not move at all" was wrong about gh-pypdf, which went 244 → 233. It moved; just not to anywhere near rounding. The three that stayed gross are gh-qpdf 171 → 171, fr-impots 255 → 255, gh-pypdf 244 → 233.
  • The whole-fleet figure spans three changes, not one. The denominators are 3962 and 3957 because The direct bucket admits CalRGB and ICCBased, because pdfimages calls them rgb #20's rule moved five pictures out, and go-gfx/gfx went v0.16.0 → v0.19.0 between the runs. All five moved pictures differ at v0.21.0, so the bucketing can be held fixed arithmetically: DCTDecode 184/430 = 42.8% and the fleet 3385/3962 = 85.4% are what is attributable to the library. Quoting 43.2% as v0.21.0's own would credit a bucketing change to a library change — a smaller version of the error being corrected.

The cleanest single number attributable to render v0.21.0 alone is uk-govuk: 92.3% → 100.0% (131/142 → 142/142), because that population moved no pictures between buckets.

@tannevaled
tannevaled merged commit 976a516 into main Aug 31, 2026
1 check 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