v0.2.1
Everything a chart is, except the picture.
The chart engine is being built in two halves and this is the first one finished. What goes on each of the 154 charts and where it goes are both settled, both taken from the original rather than from reading the original, and both checked bit for bit in CI on a checkout with nothing measured in it. 154 titles, 11088 bars, 1872 ticks and 6098 gridlines, all of them the original's.
That was worth doing before the renderer rather than after. The original's chart layer leaves nothing behind but a PNG, so every decision it makes about what a chart says and where the axis starts is invisible once it has run. Both fixtures were taken by standing in for the thing it hands its work to, Python in one case and matplotlib in the other, which means neither is a transcription that could quietly drift from what the original does. Once the renderer exists, a wrong pixel will be a rendering bug and nothing else, because everything upstream of the pixel is already pinned.
The fonts are in the binary too, so nothing depends on what happens to be installed. Three faces, three licences, three digests checked against the embedded bytes.
M3 is not finished. The renderer, the hash manifest, the provenance stamp and the determinism job are what is left, and the exit gate needs two machines to agree on the bytes.
Added
- The series layer, which is the half of the chart engine with no pixels in it. A results file goes in and what comes out is a title, both axis labels, a thread count for each group of bars, and one number per bar, for each of the 154 charts. Everything a reader could disagree with is decided here, so it is a pure function and it is tested before anything is drawn.
testdata/golden/series.json, which is all 154 charts as the original worked them out. Itsgraphtool pastes the numbers into a Python script and deletes the script after drawing, sotools/series-vectorsstands in for Python, keeps the script and throws the picture away. The fixture is the original's answer rather than a description of it.- Two levels of check on that fixture. The filenames, titles, axes, legend order and colours are checked in
cargo testagainst a results file with the original's shape and none of its numbers, so it runs in CI on a checkout with nothing measured in it. The bar heights go throughcache-bench verify --against, where they come out of our own reduction of the original's run files, which makes a matching chart one where every bar survived the run files, the selection, the combining and the extraction. - The three faces the charts are drawn with, in the binary. Jost Book and Jost Bold for the original's Futura and its bold, DejaVu Sans for the Verdana it names on the quarter decade labels. Each licence sits in the directory next to the font it covers,
assets/fonts/README.mdrecords the exact release each file came from, and the SHA-256 of all three is written intofont.rswhere a test checks it against the bytes it embedded. - The geometry layer. Where the y axis starts and stops, which ticks get a label, where the gridlines between them go, what number sits beside each one, how wide a bar is and where in its group it starts. All of it is the original's arithmetic, including the two different logarithms it uses for the two ends of a log axis and the eighth of a decade it calls a quarter.
testdata/golden/axes.json, which is that geometry for all 154 charts as the original produced it. Its layout lives in about forty lines of Python inside two Go string constants and leaves nothing behind but a PNG, sotools/axis-vectorsslices those constants out ofcmd/graph/main.goand runs them with matplotlib replaced by something that records what it was told. Every bound, tick, gridline and label is compared bit for bit incargo test, andcache-bench verifynow prints anaxesline making the same check from the command line. Like the series fixture it needs no measurements, because an axis is a function of the bars.
Fixed
Kindnow formats throughpad, so a width in a format string does what it says.verifyprints the four aggregates in a column and asks for eight characters, and the old implementation threw the width away without complaining, which is why that column was never a column.
Notes
- All 154 charts and all 11088 bars come back as the original's. That is the first half of the M3 gate met, and it is met before a single pixel has been drawn, which was the point of splitting the layer in two.
- Running the original's own layout code turned up three things that reading it did not. The thread count under each group of bars is placed at a hardcoded offset that is the middle of the group for six bars and for no other number, so our seventh engine would have knocked every x label off centre. That is D13 and the offset is now computed. The minor gridlines step by an eighth of a decade rather than the quarter their variable is named after, so there are seven between labelled ticks. And the only two charts with a zero on them are linear, which is the whole reason a zero has never reached the original's logarithmic path, where it has no answer.
- A bar can now be absent rather than zero. The original has no way to say that a cell was never measured or that the machine could not count cycles, so it says zero, and a zero bar claims an engine scored nothing rather than that it was not tested. On a logarithmic chart one of those takes the whole y axis with it.
--compat=upstreamstill writes the zero.