GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Step 3: Lead each card with what happened on the selected build · fastruby/fast-ruby@18631af

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • script
    • results_site.html
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎script/results_site.html‎

Copy file name to clipboardExpand all lines: script/results_site.html
+94-32Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,14 @@
200200

201201
/* Across Rubies: one card per benchmark, a chart with the Rubies on the x axis. */
202202
.rcard { border-bottom: 1px solid var(--line); padding-block: 18px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
203-
.rcard h3 { margin: 0; font-size: 15.5px; line-height: 1.35; color: var(--ink); text-wrap: balance; }
204-
.rcard h3 code { font-weight: 400; }
203+
.rcard .what { margin: 0; font-size: 13px; color: var(--muted); }
204+
.rcard h3 { margin: 0; font-size: 18px; line-height: 1.35; color: var(--ink); display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px; align-items: start; }
205+
.rcard h3 > span:last-child { text-wrap: pretty; overflow-wrap: anywhere; }
206+
.rcard h3 code { font-weight: 700; font-size: .85em; }
207+
.rcard h3 .ico { margin-top: 1px; }
208+
.ico.mix { background: #fff; color: var(--red); box-shadow: inset 0 0 0 3px var(--red); }
209+
.rcard .say { padding-left: 36px; }
210+
.rcard td .lose { color: var(--red); }
205211
.rcard .say { margin: 0; font-size: 14px; color: var(--text); max-width: 75ch; }
206212
.rcard .say b { color: var(--ink); }
207213
.rlegend { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 13px; color: var(--text); }
@@ -669,34 +675,88 @@ <h2 class="bh">FastRuby<span>.io</span> keeps fast-ruby running</h2>
669675
const day = M.shards.length ? M.shards[0].from.slice(0, 10) : "";
670676
$("rrun").innerHTML = `Results from ${runLink}${at}, ${esc(day)}: ${M.labels.length} builds on ${M.shards.length} machine${M.shards.length === 1 ? "" : "s"}, built ${esc(M.built_at)}.`;
671677

672-
function summary(b) {
673-
const plain = M.labels.filter((l) => !variant(l));
674-
let wins = 0, ties = 0, ran = 0;
675-
const ratios = [];
676-
plain.forEach((l) => {
677-
const r = b.runs[l];
678-
if (!r || !ok(r.entries[0])) return;
679-
const c = r.entries[0];
680-
const others = r.entries.slice(1).filter(ok);
681-
if (!others.length) return;
682-
ran++;
683-
const top = others.reduce((a, o) => (o[0] > a[0] ? o : a));
684-
if (same(c, top)) ties++;
685-
else if (c[0] > top[0]) { wins++; ratios.push([c[0] / top[0], l]); }
678+
// The claimed report against the fastest other report of one build: { s: "ok" | "eq" | "no", ratio, winner }, or null when they cannot be compared.
679+
function versus(r) {
680+
const c = r && r.entries[0];
681+
if (!ok(c)) return null;
682+
let w = -1;
683+
r.entries.forEach((e, i) => { if (i && ok(e) && (w < 0 || e[0] > r.entries[w][0])) w = i; });
684+
if (w < 0) return null;
685+
const o = r.entries[w];
686+
if (same(c, o)) return { s: "eq", winner: w };
687+
return c[0] > o[0] ? { s: "ok", ratio: c[0] / o[0], winner: w } : { s: "no", ratio: o[0] / c[0], winner: w };
688+
}
689+
// Card icons, as in "Does it hold?"; "~" when the claim wins on some Rubies and loses on others.
690+
const RICON = { ok: "✓", eq: "=", no: "✗", na: "–", mix: "~" };
691+
// Ratios as in "Does it hold?": 1.04×, 2.3×, 35×, 12k×, 27M×.
692+
const x2 = (r) => r >= 1e6 ? (r / 1e6).toFixed(1) + "M×" : r >= 1e4 ? Math.round(r / 1e3) + "k×" : r >= 10 ? Math.round(r) + "×" : r < 1.1 ? r.toFixed(2) + "×" : r.toFixed(1) + "×";
693+
// The Ruby without its build: under YJIT the build is already in the heading.
694+
const rb = (l) => nice(base(l));
695+
// Rubies next to each other on the axis, as "Ruby 2.1 to 2.6"; a run never spans two engines.
696+
function runsOf(labels, cols) {
697+
const out = [];
698+
let run = [];
699+
const flush = () => { if (run.length) out.push(run.length === 1 ? rb(run[0]) : `${rb(run[0])} to ${base(run[run.length - 1]).replace(/^[a-z]+_/, "")}`); run = []; };
700+
cols.forEach((l) => {
701+
if (!labels.includes(l)) return flush();
702+
if (run.length && base(run[0]).split("_")[0] !== base(l).split("_")[0]) flush();
703+
run.push(l);
686704
});
687-
let s = `<code>${esc(b.names[0])}</code> is fastest on <b>${wins} of ${ran}</b> builds without a JIT`;
688-
if (ties) s += ` (${ties} tie${ties > 1 ? "s" : ""})`;
689-
if (ratios.length) {
690-
ratios.sort((a, c) => a[0] - c[0]);
691-
const lo = ratios[0], hi = ratios[ratios.length - 1];
692-
s += lo === hi ? `, by ${lo[0].toFixed(1)}×` : `, by ${lo[0].toFixed(1)}× (${esc(nice(lo[1]))}) to ${hi[0].toFixed(1)}× (${esc(nice(hi[1]))})`;
705+
flush();
706+
return out.join(", ");
707+
}
708+
709+
// What happened, for the selected build: a headline (icon, sentence) and one line of numbers under it.
710+
function verdict(b) {
711+
const cols = columns().all;
712+
const claim = `<code>${esc(b.names[0])}</code>`, nm = (i) => `<code>${esc(b.names[i])}</code>`;
713+
const where = mode ? ` with ${esc(mode.toUpperCase())}` : "";
714+
const res = cols.map((l) => [l, versus(b.runs[l])]).filter(([, v]) => v);
715+
const n = res.length, of = (s) => res.filter(([, v]) => v.s === s);
716+
const wins = of("ok"), ties = of("eq"), losses = of("no");
717+
const all = `all ${n} Rubies${where}`;
718+
let icon = "ok", head;
719+
if (!n) { icon = "na"; head = `Did not run on any Ruby${where}`; }
720+
else if (!losses.length && !ties.length) head = b.names.length === 2 ? `${claim} beats ${nm(1)} on <b>${all}</b>` : `${claim} is the fastest on <b>${all}</b>`;
721+
else if (!losses.length && !wins.length) { icon = "eq"; head = b.names.length === 2 ? `${claim} and ${nm(1)} are about the same on <b>${all}</b>` : `${claim} ties with the fastest on <b>${all}</b>`; }
722+
else if (!losses.length) head = `${claim} wins or ties on <b>${all}</b> (${ties.length} tie${ties.length > 1 ? "s" : ""})`;
723+
else {
724+
// Who beat the claim, and on which Rubies: the most frequent winner first, at most two named.
725+
const by = {};
726+
losses.forEach(([l, v]) => { (by[v.winner] = by[v.winner] || []).push(l); });
727+
const top = Object.keys(by).sort((a, c) => by[c].length - by[a].length);
728+
const named = top.slice(0, 2).map((w) => `${nm(+w)} wins on ${esc(runsOf(by[w], cols))}`);
729+
const rest = top.slice(2).reduce((s, w) => s + by[w].length, 0);
730+
if (rest) named.push(`others on ${rest} more`);
731+
if (losses.length === n && top.length === 1) { icon = "no"; head = `${nm(+top[0])} beats ${claim} on <b>${all}</b>`; }
732+
else if (losses.length === n) { icon = "no"; head = `${claim} loses on <b>${all}</b>: ${named.join("; ")}`; }
733+
else { icon = "mix"; head = `${claim} wins on <b>${wins.length} of ${n} Rubies${where}</b>${ties.length ? ` (${ties.length} tie${ties.length > 1 ? "s" : ""})` : ""}; ${named.join("; ")}`; }
693734
}
694-
const line = plain.filter((l) => mri(l) && l !== "ruby_head" && b.runs[l] && ok(b.runs[l].entries[0]));
735+
736+
const facts = [];
737+
const span = (list, verb) => {
738+
if (!list.length) return;
739+
const s = list.slice().sort((a, c) => a[1].ratio - c[1].ratio), lo = s[0], hi = s[s.length - 1];
740+
facts.push(lo === hi || x2(lo[1].ratio) === x2(hi[1].ratio) ? `${verb} by ${x2(hi[1].ratio)}` : `${verb} by ${x2(lo[1].ratio)} (${esc(rb(lo[0]))}) to ${x2(hi[1].ratio)} (${esc(rb(hi[0]))})`);
741+
};
742+
span(wins, "Wins");
743+
span(losses, "Loses");
744+
// Its own speed over the released Rubies of this build; head changes every week, so it is left out.
745+
const line = columns().line.filter((l) => base(l) !== "ruby_head" && b.runs[l] && ok(b.runs[l].entries[0]));
695746
if (line.length > 1) {
696-
const first = line[0], last = line[line.length - 1];
697-
s += `. Its own speed from ${esc(nice(first))} to ${esc(nice(last))}: <b>${pct(b.runs[last].entries[0][0], b.runs[first].entries[0][0])}</b>`;
747+
const f = b.runs[line[0]].entries[0], t = b.runs[line[line.length - 1]].entries[0];
748+
facts.push(`Own speed from ${esc(rb(line[0]))} to ${esc(base(line[line.length - 1]).replace(/^ruby_/, ""))}: ${sameRuby(t, f, b.variation) ? "about the same" : `<b>${pct(t[0], f[0])}</b>`}`);
749+
}
750+
if (mode && line.length) {
751+
const l = line[line.length - 1], j = b.runs[l].entries[0], g = b.runs[base(l)] && b.runs[base(l)].entries[0];
752+
if (ok(g)) facts.push(`${esc(mode.toUpperCase())} vs no JIT on ${esc(rb(l))}: ${sameRuby(j, g, b.variation) ? "about the same" : `<b>${pct(j[0], g[0])}</b>`}`);
698753
}
699-
return s + ".";
754+
// Rubies on the chart that are not in the count.
755+
const left = cols.filter((l) => !res.some(([k]) => k === l));
756+
const susp = left.filter((l) => b.runs[l] && b.runs[l].entries.some(sus)), gone = left.filter((l) => !susp.includes(l));
757+
if (susp.length) facts.push(`Not counted: ${esc(runsOf(susp, cols))} (suspect)`);
758+
if (gone.length) facts.push(`Did not run: ${esc(runsOf(gone, cols))}`);
759+
return { icon, head, sub: facts.join(" · ") };
700760
}
701761

702762
// The y axis is logarithmic, so a 100× gap between two reports and a 10% change between two Rubies are both readable on one chart.
@@ -793,8 +853,8 @@ <h2 class="bh">FastRuby<span>.io</span> keeps fast-ruby running</h2>
793853
const e = r.entries[i];
794854
t += `<div class="r"><i style="background:${color(i)}"></i>${esc(nm)}: ${!e ? "did not run" : fmt(e[0]) + " i/s" + (sus(e) ? " (suspect)" : "")}</div>`;
795855
});
796-
const c = r.entries[0], o = r.entries[1];
797-
if (ok(c) && ok(o)) t += `<div class="n">${same(c, o) ? "Same-ish: the gap is within the error." : `${esc(b.names[0])} is ${(c[0] / o[0]).toFixed(2)}× ${esc(b.names[1])}.`}</div>`;
856+
const c = r.entries[0], v = versus(r);
857+
if (v) t += `<div class="n">${v.s === "eq" ? `Same-ish as ${esc(b.names[v.winner])}: the gap is within the error.` : v.s === "ok" ? `${esc(b.names[0])} is ${v.ratio.toFixed(2)}× faster than ${esc(b.names[v.winner])}.` : `${esc(b.names[v.winner])} is ${v.ratio.toFixed(2)}× faster than ${esc(b.names[0])}.`}</div>`;
798858
const prev = k > 0 && k < cols.line.length && b.runs[cols.line[k - 1]];
799859
const pc = prev && prev.entries[0];
800860
if (ok(pc) && ok(c)) t += `<div class="n">vs ${esc(nice(cols.line[k - 1]))}: ${sameRuby(c, pc, b.variation) ? "same-ish" : pct(c[0], pc[0])}</div>`;
@@ -815,7 +875,7 @@ <h2 class="bh">FastRuby<span>.io</span> keeps fast-ruby running</h2>
815875

816876
function table(b) {
817877
const cols = columns();
818-
let h = `<tr><th>Build</th>${b.names.map((nm, i) => `<th>${esc(nm)}${i ? "" : " (claimed)"}</th>`).join("")}<th>Gap</th></tr>`;
878+
let h = `<tr><th>Build</th>${b.names.map((nm, i) => `<th>${esc(nm)}${i ? "" : " (claimed)"}</th>`).join("")}<th>Claimed vs fastest other</th></tr>`;
819879
cols.all.forEach((l, k) => {
820880
const r = b.runs[l], prev = k > 0 && k < cols.line.length ? b.runs[cols.line[k - 1]] : null;
821881
h += `<tr><td>${esc(nice(l))}${l === REF ? " (reference)" : ""}</td>`;
@@ -828,8 +888,8 @@ <h2 class="bh">FastRuby<span>.io</span> keeps fast-ruby running</h2>
828888
const d = ok(pe) ? (sameRuby(e, pe, b.variation) ? ' <small class="sm">same-ish</small>' : ` <small>${pct(e[0], pe[0])}</small>`) : "";
829889
h += `<td>${fmt(e[0])} <small>±${(e[1] / e[0] * 100).toFixed(1)}%</small>${d}</td>`;
830890
});
831-
const c = r && r.entries[0], o = r && r.entries[1];
832-
h += `<td>${ok(c) && ok(o) ? (same(c, o) ? '<span class="sm">tie</span>' : (c[0] / o[0]).toFixed(2) + "×") : ""}</td></tr>`;
891+
const v = versus(r);
892+
h += `<td>${!v ? "" : v.s === "eq" ? '<span class="sm">tie</span>' : v.s === "ok" ? `${v.ratio.toFixed(2)}× faster` : `<span class="lose">${v.ratio.toFixed(2)}× slower</span>`}</td></tr>`;
833893
});
834894
return `<div class="tb"><table>${h}</table></div>`;
835895
}
@@ -854,7 +914,9 @@ <h2 class="bh">FastRuby<span>.io</span> keeps fast-ruby running</h2>
854914
B.forEach((b, n) => {
855915
if (b.section !== sec) { sec = b.section; h += `<h2>${esc(sec)}<span class="on">${esc(MODES.find(([m]) => m === mode)[1])}</span></h2>`; }
856916
const legend = b.names.map((nm, i) => `<span><i style="background:${color(i)}"></i><code>${esc(nm)}</code>${i ? "" : " (claimed)"}</span>`).join("") + (mode ? '<span class="ghost"><i></i>without the JIT</span>' : "");
857-
h += `<article class="rcard"><h3>${md(b.title)}</h3><p class="say">${summary(b)}</p><div class="rlegend">${legend}</div>` +
917+
const v = verdict(b);
918+
h += `<article class="rcard"><p class="what">${md(b.title)}</p><h3><span class="ico ${v.icon}" aria-hidden="true">${RICON[v.icon]}</span><span>${v.head}</span></h3>` +
919+
(v.sub ? `<p class="say">${v.sub}</p>` : "") + `<div class="rlegend">${legend}</div>` +
858920
`<div class="chart" data-n="${n}"></div>${foot(b)}` +
859921
`<details data-n="${n}"${open.has(n) ? " open" : ""}><summary>Numbers per build</summary>${table(b)}</details>` +
860922
`<div class="links"><a href="${M.repo}/blob/main/${encodeURI(b.file)}">View the code</a></div></article>`;

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.