Module: Vicary::LatencyBaseline
- Defined in:
- lib/vicary/latency_baseline.rb
Overview
Is this build slower than the last release, and is that a fair question here?
The gate has asked this three ways. The first two are worth keeping in view, because each looked correct until it decided a release.
An absolute bar — 10 ms. A claim about the machine as much as about the code. It passed on a laptop and failed on the CI runner enforcing it, so v0.2.3 published to PyPI and npm and was refused by RubyGems on the same commit. This gem is the one that caught it.
A stored baseline — record each release's number and compare the next run
against it, refusing unless the run claims the profile the baseline was
recorded on. Better, and still wrong, for a reason no estimator fixes: the
profile github-ubuntu-latest is not a machine. Thirty-six processes across
six runners per port, on identical code, spread 67% in THIS port — 6.53 ms on
an Intel Xeon 6973P-C against 10.63 ms on an EPYC 7763 — 26% in Python and
21% in TypeScript, against an 8% bar. One probe run drew five CPU models from
that one label, and two runners of the same model still differed by 26%.
A pair, measured here. The previous release's code and this checkout,
measured on the SAME machine, interleaved and counterbalanced, by
tools/latency_pair.py. Every property of the machine is common to both
sides and cancels; what is left is within-process noise, 1.7% in this port.
Which leaves this module the job it has always had: REFUSING to compare when the two sides would not be like for like. What changed is that the refusals are about the pair record — is there one, is it this port's, was it measured on these essays, was it measured for this commit — rather than about the profile of a machine somewhere else.
This port reaches its own verdict from the shared record. It does not read Python's answer.
Defined Under Namespace
Classes: Comparison
Constant Summary collapse
- SPEC_FILENAME =
The tolerance and the protocol, in the repository. Not a measurement: nothing is recorded at release time any more, because the comparison point is the previous release's code, which the repository already has.
"latency_baseline.json"- PAIR_ENV_VAR =
Where
tools/latency_pair.pyleft the paired measurement. Set by CI in the same job, seconds before the gate runs. Absent on a laptop unless the harness was run there by hand, and that absence is a refusal to compare rather than a pass — measuring one side of a comparison is not a gate. "VICARY_LATENCY_PAIR"- PAIR_DOCUMENT_VERSION =
What this reader understands. A record from a future shape is refused rather than half-read: a partly-understood record still yields a number, and a number is exactly what must not be invented here.
1- IMPLEMENTATION =
"ruby"- DEFAULT_TOLERANCE_PCT =
The bar, chosen rather than derived — 8% is what a reviewer is willing to call a regression. What the noise decides is whether the bar is USABLE, and it is: the gate statistic holds sigma 0.46% in this port (95% CI 0.34-0.72%, sixteen runs across eight CI runners, fixed head and tag), putting 8% at 17.2 sigma — the widest margin of the three. It was about a third of a sigma under the stored baseline, which is how that one red-lit
mainon unchanged code, and how it refused this port's 0.2.3 while the other two took the same commit.This is also the port where the pairing earns the most. Across three CPU models its absolute figure spreads 31.8% — the same axis, and nearly the same size, as the 67% that killed the stored baseline — while its ratio spreads 0.36 pp. Same runs, same data. See
tools/latency_pair.py.It does not catch drift: +5% a release passes every time and compounds. That is deliberate — this gate is for the step change, not the trend.
8.0
Class Method Summary collapse
-
.compare(measured_ms, corpus_id, dir: nil, implementation: IMPLEMENTATION, pair_path: nil, building_sha: nil) ⇒ Object
Compare the pair measured on this machine, for this port.
-
.gate_fields(measured_ms, corpus_id, **opts) ⇒ Object
The keyword arguments Gates.measure wants.
- .load(dir = nil) ⇒ Object
-
.load_pair(path = nil) ⇒ Object
The paired measurement, or why there is none to read.
- .render(comparison) ⇒ Object
- .spec_path(dir = nil) ⇒ Object
Class Method Details
.compare(measured_ms, corpus_id, dir: nil, implementation: IMPLEMENTATION, pair_path: nil, building_sha: nil) ⇒ Object
Compare the pair measured on this machine, for this port.
measured_ms is this process's own figure. It is reported either way and
it is never the verdict: the verdict comes from the two numbers in the
pair record, taken back to back on one machine. Mixing this process's
measurement with the pair's other side would reintroduce exactly the
machine difference the pair exists to cancel.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/vicary/latency_baseline.rb', line 134 def compare(measured_ms, corpus_id, dir: nil, implementation: IMPLEMENTATION, pair_path: nil, building_sha: nil) doc = load(dir) || {} tolerance = (doc["tolerance_pct"] || DEFAULT_TOLERANCE_PCT).to_f declined = lambda do |reason| Comparison.new(measured_ms: measured_ms, previous_ms: nil, current_ms: nil, regression_pct: nil, tolerance_pct: tolerance, against: nil, comparable: false, reason: reason) end record, why = load_pair(pair_path) return declined.call(why || "no paired measurement") if record.nil? unless record["document_version"] == PAIR_DOCUMENT_VERSION return declined.call( "the pair record is document_version #{record['document_version']} " \ "and this reader knows #{PAIR_DOCUMENT_VERSION}" ) end unless record["implementation"] == implementation return declined.call( "the pair record measures #{record['implementation'].inspect}, " \ "not #{implementation.inspect}" ) end unless record["corpus"] == corpus_id return declined.call( "the pair was measured on corpus #{record['corpus'].inspect} and this " \ "run is #{corpus_id.inspect}; latency scales with essay length" ) end # Only where there is something to check against. `GITHUB_SHA` names the # commit the job is building, so a record left over from an earlier # commit is caught here rather than being read as this build's verdict. # Locally there is no such witness and no such risk: the harness is run # by hand, minutes before, on the tree in front of you. building = (building_sha || ENV["GITHUB_SHA"] || "").strip head = record["head_sha"].to_s if !building.empty? && !head.empty? && building != head return declined.call( "the pair was measured for commit #{head[0, 12]} and this job is " \ "building #{building[0, 12]}; the record is stale" ) end previous = record["previous_ms"] current = record["current_ms"] unless previous.is_a?(Numeric) && current.is_a?(Numeric) return declined.call("the pair record carries no pair of measurements") end if previous <= 0 return declined.call( "the previous release measured #{previous} ms, which is not positive" ) end Comparison.new( measured_ms: measured_ms, previous_ms: previous.to_f, current_ms: current.to_f, regression_pct: (current.to_f / previous.to_f - 1.0) * 100.0, tolerance_pct: tolerance, against: (record["against"] || {})["ref"], comparable: true, reason: nil ) end |
.gate_fields(measured_ms, corpus_id, **opts) ⇒ Object
The keyword arguments Gates.measure wants. Returns the detail rather than a value when the comparison was declined, so the gate reports NOT MEASURED with the reason attached instead of quietly passing.
218 219 220 221 222 223 224 225 |
# File 'lib/vicary/latency_baseline.rb', line 218 def gate_fields(measured_ms, corpus_id, **opts) c = compare(measured_ms, corpus_id, **opts) if c.comparable { latency_regression_pct: c.regression_pct } else { latency_regression_detail: render(c) } end end |
.load(dir = nil) ⇒ Object
98 99 100 101 102 103 |
# File 'lib/vicary/latency_baseline.rb', line 98 def load(dir = nil) path = spec_path(dir) return nil if path.nil? JSON.parse(path.read) end |
.load_pair(path = nil) ⇒ Object
The paired measurement, or why there is none to read.
An unreadable file and an absent one stay distinguishable: the first is a broken harness and the second is an ordinary laptop, and they should not report the same thing.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/vicary/latency_baseline.rb', line 110 def load_pair(path = nil) given = (path || ENV[PAIR_ENV_VAR] || "").strip if given.empty? return [nil, "#{PAIR_ENV_VAR} is unset, so no paired measurement was taken on " \ "this machine; the gate compares this build against the last " \ "release measured HERE, and one side of a comparison is not a gate"] end return [nil, "#{PAIR_ENV_VAR}=#{given.inspect} does not exist"] unless File.exist?(given) begin [JSON.parse(File.read(given)), nil] rescue StandardError => e [nil, "the pair record at #{given} could not be read: #{e.}"] end end |
.render(comparison) ⇒ Object
201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/vicary/latency_baseline.rb', line 201 def render(comparison) c = comparison unless c.comparable return format("latency %.3f ms — NOT COMPARED against the last release: %s", c.measured_ms, c.reason) end sign = c.regression_pct >= 0 ? "+" : "" format("latency %.3f ms here; paired on this machine, %.3f ms against " \ "%s's %.3f ms — %s%.2f%% against a %d%% bar", c.measured_ms, c.current_ms, c.against || "the last release", c.previous_ms, sign, c.regression_pct, c.tolerance_pct) end |
.spec_path(dir = nil) ⇒ Object
90 91 92 93 94 95 96 |
# File 'lib/vicary/latency_baseline.rb', line 90 def spec_path(dir = nil) root = dir || Conformance.directory return nil if root.nil? path = Pathname.new(root).join(SPEC_FILENAME) path.exist? ? path : nil end |