Repository navigation
Conversation
Duel passed: none of 20 benchmarks slower
20 benchmarks in 12s. A timing is slower when a 99.0% confidence interval for its ratio, the candidate's to the baseline's, is above 1.02. Across the board
20 benchmarks unchanged
|
Benchmark the hot paths with Duel, so that a pull request that makes them slower fails CI: urlencoding small, large, nested and multibyte params, building multipart bodies with text fields and in-memory files, and reading bodies with to_s, in 16 KB chunks as http.rb streams them, rewind and content length. Every multipart body uses a fixed boundary. Duel requires Ruby 3.4 or later, so the Gemfile adds it only there and the Rake task is loaded only when Duel is. The Perf workflow compares each pull request with master, each push with the commit before it, and the last release on a schedule. RuboCop now checks .duel.rb too. The Duel job only reads, and a Comment job of its own, which checks nothing out and runs none of the branch's code, comments on the pull request with the report. A change to only the benchmarks, .duel.rb or the Perf workflow is compared too.
|
I updated the Perf workflow here after a review of the same workflow in another project. The Duel job now only reads, and its checkout keeps no credentials. A separate Comment job, which checks nothing out and runs none of the branch's code, posts the report. |
The Perf workflow now comments on a pull request only when Duel fails, as c.comment says by default, and posts a new comment instead of editing the last one. Duel 0.1.1 watches the benchmarks and .duel.rb itself, so .duel.rb only adds the workflow.
|
I updated this to Duel 0.1.1. The Perf workflow now only comments when Duel fails, posts a new comment instead of editing the old one, and the report is much shorter. |
🟢 Duel: 100% of benchmarks passedCompared 79752ea with 664bf90 (where HEAD left Details: 20 benchmarks in 17s, 2 unsettled
An unsettled timing was too close to the limit to settle. It doesn't fail the run, but one that's unsettled in run after run is noisy. Across the board:
A timing is slower when a 99.0% confidence interval for its ratio, the candidate's to the baseline's, is above 1.02. How Duel decides |
This adds Duel, a gem I wrote to catch performance regressions in CI. On every pull request it runs the benchmarks in
duel/againstmasterand the branch side by side, and fails the build if the branch is slower or allocates more.The 20 benchmarks cover what http.rb does with form data on every request that has a body: urlencoding small, large, nested and multibyte params, building multipart bodies with text fields and in-memory files, and reading them back with
to_s, in 16 KB chunks the way http.rb streams them, withrewindand with the content length. Every multipart body uses a fixed boundary.To check that it actually catches things, I added a
.duptoDefaultEncoder.escape, and Duel failed all five urlencoding benchmarks. Some allocation-free work in theCompositeIO#read_chunksloop failed the six reading benchmarks at 2.3 to 3.7 times the CPU time. Comparingmasterwith itself passed three times in a row.You can run it locally with
bundle exec rake duel.Duel requires Ruby 3.4, so the Gemfile only adds it on 3.4 and newer, and the Rake task is skipped when it isn't installed. Everything else still works on older Rubies.