Repository navigation
Give the module benchmarks a clock that can see them, and say where each module runs - #748
Merged
Merged
Conversation
…ach runs
`bench/modules/run.sh` timed with `/usr/bin/time -p`, which prints two decimals
of a second. Its resolution is 10 ms, and anything faster than one tick reads
zero:
$ { /usr/bin/time -p /usr/bin/true >/dev/null; } 2>&1 | awk '/real/{print $2}'
0.00
Of the 171 timing cells in the three per-module sections of BENCHMARKS.md, all
171 are multiples of 10 ms; 36 are ten ticks or fewer and 11 are five or fewer.
`matrix | 20ms | 50ms | 0.40x` was a two-tick measurement. `tsort | 80ms | 90ms
| 0.89x` claimed an 11% win over MRI from a difference of one tick — re-taken,
it is 1.42x, a loss.
Three changes to the harness:
Time::HiRes instead of /usr/bin/time -p, so the figures have the precision the
ratios were already being quoted to.
A run that exits non-zero is an error, not a fast run. `best()` kept the
smallest time of N without ever looking at the exit status, so a command that
died immediately won its own series and got published. It now marks that arm
and carries on with the others rather than timing a failure.
The runtimes are interleaved, one run each in rotation with the order rotating
every round, instead of each taking its own block of wall-clock time. A machine
whose load moves during a block charges the movement to whichever runtime was
running, and nothing in the output says so. See go-crdt/crdt#131: the same
defect put a published ratio 14% out, and contention does not fall on every arm
alike — it falls hardest on the shortest-running one, which is the fastest.
The table is re-taken in a new dated section; the June ones are kept as they were
taken. MRI is the control and reproduces within a few percent across all 24 rows
the two runs share, so what moved in the rbgo column is rbgo's. Five of those 24
change which side of parity they sit on: cmath, prettyprint and tsort were
published as wins over MRI and are losses; prime and ipaddr were losses and are
wins.
`set` reads 38.2x where June published 10.09x, and that one is not the clock.
#56 bound Set to go-ruby-set/set on 2026-06-29; #330 reimplemented it here on
2026-08-08 for MRI 4.0 conformance, with the bulk of the Ruby-observable API in
the prelude; #333's `go mod tidy` dropped the now-unused require the next day.
The benchmark exercises exactly that algebra, so it measures prelude Ruby where
it used to measure Go. Nothing about the trade was wrong. What was missing was
anything that would say it had happened — a page in another org went on
attributing this number to a library we had stopped using.
So: bench/modules/BACKING.md now records, beside the scripts, which modules run
on an external go-ruby library and which run on code in this repository. It is
generated, and TestBenchmarkedModulesSayWhereTheirCodeLives fails when it and
go.mod disagree. complex, rational and set are in-tree today.
Refs #747.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
git hands BACKING.md to a Windows checkout with CRLF endings, and the test compared bytes, so the first CI run that saw it reported the file as no longer matching go.mod — over carriage returns. A guard about which module runs where must not depend on how the working tree was written. Compared with carriage returns dropped from both sides. Checked on a CRLF copy that the correct table passes and that flipping `set` back to github.com/go-ruby-set/set still fails and still names the line: the point was to stop it failing for the wrong reason, not to stop it failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tannevaled
added a commit
to go-ruby-set/docs
that referenced
this pull request
Sep 30, 2026
Three pages here state in the present tense that `rbgo` binds this module. It did, from rbgo#56 on 2026-06-29 until rbgo#330 on 2026-08-08, which reimplemented `Set` inside the interpreter for MRI 4.0 conformance with the bulk of the Ruby-observable API in its prelude. rbgo#333 dropped the now-unused requirement the next day. `github.com/go-ruby-set/set` has not been in rbgo's build list since. That makes the rbgo row on the performance page a historical figure rather than a measurement of this library. It also moved underneath the claim: re-taken on 2026-09-30 with a clock finer than the 10 ms one that produced it, that row reads 8212 ms and 38.2x rather than 2220 ms and 10.09x — because it now times prelude Ruby rather than this library. MRI reproduces at 215 ms against the published 220, which is what says the machine is not the explanation. The library-level benchmark further down the page is untouched and unaffected: it drives this library through its own Go API and never went through rbgo. Refs go-embedded-ruby/ruby#747, go-embedded-ruby/ruby#748. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the first two parts of #747 and gives the third one a guard.
The clock
bench/modules/run.shtimed with/usr/bin/time -p, which prints two decimalsof a second:
10 ms resolution, and anything under one tick reads zero. Of the 171 timing
cells in the three per-module sections of
BENCHMARKS.md, all 171 aremultiples of 10 ms; 36 are ten ticks or fewer.
That is where the bolded rows lived.
matrix | 20ms | 50ms | 0.40×was atwo-tick measurement.
tsort | 80ms | 90ms | 0.89×claimed an 11% win overMRI from a difference of one tick — re-taken, it is 1.42×, a loss.
Three changes to the harness
Time::HiResinstead of/usr/bin/time -p, so the figures carry theprecision the ratios were already quoted to.
best()kept thesmallest time of N without looking at the exit status, so a command that died
immediately won its own series and got published. It now marks that arm and
carries on with the others.
rotating every round, instead of each taking its own block of wall-clock time.
A machine whose load moves during a block charges the movement to whichever
runtime was running, and nothing in the output says so. See Compare implementations, not moments: the Fugue row was 14% too high go-crdt/crdt#131:
the same defect put a published ratio 14% out, and contention does not fall on
every arm alike — it falls hardest on the shortest-running one, which is
the fastest one.
The table, re-taken
A new dated section; the June ones are kept as they were taken. All 35 modules
passed the byte-identical-to-MRI gate under every installed runtime, no row was
dropped and no run exited non-zero.
MRI is the control. Across the 24 rows the two runs share it reproduces
within a few percent — 220→215 ms on
set, 1450→1546 ondid-you-mean,570→567 on
scanf, 330→329 onsecurerandom— so the machine and the workloadsare the same, and what moved in the rbgo column is rbgo's.
Five of those 24 rows change which side of parity they sit on:
cmathprettyprinttsortprimeipaddrThree of the eight modules the June text lists as "rbgo beats MRI outright" are
not beating MRI, and all three sat on one or two ticks.
Other rows moved because the code did, which is the larger part:
strscan10720 → 262 ms and
json1520 → 701 ms are #89 and #88 landing the day afterthose tables were taken.
regexp1530 → 4968 ms moved that far the other way.Three months separate the runs; the new section does not try to attribute each
row, only to say which comparisons the old clock could not support.
set, and the guardsetreads 38.2× where June published 10.09×, and that one is not the clockeither. #56 bound Set to
go-ruby-set/seton 2026-06-29; #330 reimplemented ithere on 2026-08-08 for MRI 4.0 conformance, with the bulk of the Ruby-observable
API in the prelude; #333's
go mod tidydropped the now-unused require the nextday. The benchmark exercises exactly that algebra, so it now measures prelude Ruby
where it used to measure Go.
Nothing about that trade was wrong — conformance was the point. What was missing
was anything that would say it had happened, and a page in another org went on
attributing this number to a library we had stopped using, for seven weeks, with
a figure that had moved 3.7× underneath it.
bench/modules/BACKING.mdnow records which modules run on an externalgo-ruby-<mod>library and which run on code in this repository. It is generated,and
TestBenchmarkedModulesSayWhereTheirCodeLivesfails when it andgo.moddisagree — I checked that it does, both by flipping one row back to
github.com/go-ruby-set/set(it names the line) and by deleting the file. Todaycomplex,rationalandsetare in-tree.The four documentation pages in the other orgs are untouched; #747 records which
they are.
🤖 Generated with Claude Code