Skip to content

Commit 76ee0fc

Browse files
add loading time to plots
1 parent c9ef0dd commit 76ee0fc

7 files changed

Lines changed: 91 additions & 17 deletions

File tree

scripts/ci/combine-render-images

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const fpsSamplesName = requiredEnv('FPS_SAMPLES_NAME');
1919
const sceneMetricsName = requiredEnv('SCENE_METRICS_NAME');
2020
const feCoverageName = requiredEnv('FE_COVERAGE_NAME');
2121
const fpsHistoryName = requiredEnv('FPS_HISTORY');
22+
const loadDurationName = requiredEnv('LOAD_DURATION_NAME');
2223
const renderImageScale = Number(process.env.COMBINED_RENDER_IMAGE_SCALE || 3);
2324
if (!Number.isFinite(renderImageScale) || renderImageScale <= 0) {
2425
console.error('COMBINED_RENDER_IMAGE_SCALE must be a positive number');
@@ -74,7 +75,7 @@ const rows = [
7475
[
7576
['fe coverage', imagePath(feCoverageName)],
7677
['fps history', imagePath(fpsHistoryName)],
77-
[],
78+
['load duration', imagePath(loadDurationName)],
7879
],
7980
];
8081

scripts/ci/render-env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
: "${SCENE_METRICS_NAME:=scene_metrics}"
77
: "${CHOSEN_METRICS:=${SCENE_METRICS_NAME}_${CHOSEN_NAME}}"
88
: "${FPS_HISTORY:=fps_history}"
9+
: "${LOAD_DURATION_NAME:=load_duration}"
910
: "${COMBINED_RENDER_IMAGE:=render_summary}"
1011
: "${COMBINED_RENDER_IMAGE_SCALE:=3}"
1112
: "${FE_COVERAGE_NAME:=fe_coverage}"

scripts/ci/run-playwright-render

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ while IFS=$'\034' read -r -u 3 name mode url actions state roi; do
134134
echo "${fps_output}"
135135

136136
fps_value=$(echo "${fps_output}" | awk -F= '/^FPS_VALUE=/{print $2; exit}')
137+
load_duration=$(echo "${fps_output}" | awk -F= '/^LOAD_DURATION=/{print $2; exit}')
137138
scene_metrics=$(echo "${fps_output}" | awk -F= '/^SCENE_METRICS=/{print $2; exit}')
138139
if ! valid_scene_metrics "$scene_metrics"; then
139140
echo "Missing scene metrics for ${url}"
@@ -146,6 +147,7 @@ while IFS=$'\034' read -r -u 3 name mode url actions state roi; do
146147

147148
if [ "$name" = "${CHOSEN_NAME}" ]; then
148149
echo "FPS_VALUE=${fps_value}" >> "$GITHUB_ENV"
150+
echo "LOAD_DURATION=${load_duration}" >> "$GITHUB_ENV"
149151
echo "SCENE_METRICS=${scene_metrics}" >> "$GITHUB_ENV"
150152
chosen_seen=true
151153
fi

scripts/ci/test_python_scripts.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ def test_previous_fps_value_reads_latest_history_row(self):
6868
history_name = f"fps_history_test_{os.getpid()}"
6969
path = Path("/tmp") / f"{history_name}.csv"
7070
path.write_text(
71-
"fps,% change\n"
72-
"80,0.00\n"
73-
"90,12.50\n"
74-
"100,11.11\n",
71+
"fps,% change,load duration,commit sha\n"
72+
"80,0.00,5.00,old123\n"
73+
"90,12.50,5.00,old123\n"
74+
"100,11.11,5.00,old123\n",
7575
)
7676
try:
7777
code, stdout, stderr = run_script("previous-fps-value", env={
@@ -98,13 +98,14 @@ def test_track_fps_history_appends_csv(self):
9898
path = Path("/tmp") / f"{history_name}.csv"
9999
github_env = Path("/tmp") / f"github_env_track_test_{os.getpid()}"
100100
path.write_text(
101-
"fps,% change,commit sha\n"
102-
"100.00,0.00,old123\n")
101+
"fps,% change,load duration,commit sha\n"
102+
"100.00,0.00,5.00,old123\n")
103103
try:
104104
code, stdout, stderr = run_script("track-fps-history", env={
105105
"FPS_HISTORY": history_name,
106106
"FPS_VALUE": "106.04",
107107
"FALLBACK_FPS_VALUE": "100",
108+
"LOAD_DURATION": "5.00",
108109
"GITHUB_SHA": "0123456789abcdef",
109110
"GITHUB_ENV": str(github_env),
110111
"PATH": os.environ["PATH"],
@@ -117,9 +118,9 @@ def test_track_fps_history_appends_csv(self):
117118
self.assertEqual(stderr, "")
118119
self.assertEqual(
119120
path.read_text(),
120-
"fps,% change,commit sha\n"
121-
"100.00,0.00,old123\n"
122-
"106.04,6.04,0123456789\n")
121+
"fps,% change,load duration,commit sha\n"
122+
"100.00,0.00,5.00,old123\n"
123+
"106.04,6.04,5.00,0123456789\n")
123124
self.assertEqual(github_env.read_text(), "PERCENT_CHANGE=6.04\n")
124125
finally:
125126
path.unlink(missing_ok=True)

scripts/ci/track-fps-history

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import subprocess
77

88
fps_history = os.environ["FPS_HISTORY"]
99
fps_value = os.environ["FPS_VALUE"]
10+
load_duration = os.environ["LOAD_DURATION"]
1011
commit_sha = os.environ["GITHUB_SHA"][:10]
1112
github_env = os.environ["GITHUB_ENV"]
1213
csv_path = Path("/tmp") / f"{fps_history}.csv"
13-
csv_header = ["fps", "% change", "commit sha"]
14+
csv_header = ["fps", "% change", "load duration", "commit sha"]
1415

1516
previous = subprocess.check_output(
1617
["scripts/ci/previous-fps-value"],
@@ -35,4 +36,4 @@ if not csv_path.exists():
3536

3637
with csv_path.open("a", newline="") as csv_file:
3738
writer = csv.writer(csv_file)
38-
writer.writerow([fps_value, percent_change, commit_sha])
39+
writer.writerow([fps_value, percent_change, load_duration, commit_sha])

scripts/fps.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ async function main() {
360360
throw new Error('Average post-load FPS was not a valid value');
361361
}
362362
console.log(`FPS_VALUE=${averagePostLoadSample.toFixed(2)}`);
363+
console.log(`LOAD_DURATION=${sampleValues.find(s => !s.loading)?.elapsedSeconds.toFixed(2) || 'unknown'}`);
363364
const data = await page.evaluate(() => window.__scene_metrics);
364365
if (!data || data === 'undefined') {
365366
throw new Error('window.__scene_metrics was not available');

scripts/metric_plot.js

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ function buildMetricPlotSvg(samples, options = {}) {
6666
const yMaxOverride = Number(options.yMax);
6767
const yMaxBaseline = Number(options.yMaxBaseline);
6868
const yTickInterval = Number(options.yTickInterval);
69+
const xTickInterval = Number(options.xTickInterval);
70+
const xGridInterval = Number(options.xGridInterval);
6971
const rightYMinOverride = Number(options.rightYMin);
7072
const rightYMaxBaseline = Number(options.rightYMaxBaseline);
7173
const rightYTickInterval = Number(options.rightYTickInterval);
@@ -84,6 +86,14 @@ function buildMetricPlotSvg(samples, options = {}) {
8486
if (!Number.isFinite(yTickInterval)) {
8587
throw new Error('yTickInterval is required');
8688
}
89+
if (options.xTickInterval !== undefined
90+
&& (!Number.isFinite(xTickInterval) || xTickInterval <= 0)) {
91+
throw new Error('xTickInterval must be a positive number');
92+
}
93+
if (options.xGridInterval !== undefined
94+
&& (!Number.isFinite(xGridInterval) || xGridInterval <= 0)) {
95+
throw new Error('xGridInterval must be a positive number');
96+
}
8797
if (hasRightAxis && ![
8898
rightYMinOverride,
8999
rightYMaxBaseline,
@@ -230,7 +240,18 @@ function buildMetricPlotSvg(samples, options = {}) {
230240
})
231241
.join('')
232242
: '';
233-
const tickInterval = maxX <= 10 ? 1 : maxX <= 100 ? 10 : 100;
243+
const tickInterval = Number.isFinite(xTickInterval)
244+
? xTickInterval
245+
: maxX <= 10 ? 1 : maxX <= 100 ? 10 : 100;
246+
const xGrid = Number.isFinite(xGridInterval)
247+
? Array.from({ length: Math.floor(maxX / xGridInterval) + 1 },
248+
(_value, index) => {
249+
const gridValue = index * xGridInterval;
250+
const x = formatPoint(xFor(gridValue));
251+
return `<line x1="${x}" y1="${margin.top}" x2="${x}" y2="${height - margin.bottom}" stroke="#d0d7de" />`;
252+
})
253+
.join('')
254+
: '';
234255
const xTicks = Array.from({ length: Math.floor(maxX / tickInterval) + 1 },
235256
(_value, index) => {
236257
const tickValue = index * tickInterval;
@@ -242,10 +263,13 @@ function buildMetricPlotSvg(samples, options = {}) {
242263
})
243264
.join('');
244265
const firstLoaded = series[0]?.samples.find(({ loading }) => loading === false);
266+
const loadedLabel = firstLoaded
267+
? escapeSvgText(`Loaded ${formatStat(firstLoaded.x)}s`)
268+
: '';
245269
const loadedMarker = firstLoaded
246270
? [
247271
`<line x1="${formatPoint(xFor(firstLoaded.x))}" y1="${margin.top}" x2="${formatPoint(xFor(firstLoaded.x))}" y2="${height - margin.bottom}" stroke="#f97316" stroke-width="2" stroke-dasharray="5 4" />`,
248-
`<text x="${formatPoint(xFor(firstLoaded.x) + 6)}" y="${margin.top + 16}" fill="#c2410c" font-family="Arial, sans-serif" font-size="12" font-weight="700">Loaded</text>`,
272+
`<text x="${formatPoint(xFor(firstLoaded.x) + 6)}" y="${margin.top + 16}" fill="#c2410c" font-family="Arial, sans-serif" font-size="12" font-weight="700">${loadedLabel}</text>`,
249273
].join('')
250274
: '';
251275
const averageValue = Number(options.averageValue);
@@ -284,6 +308,7 @@ function buildMetricPlotSvg(samples, options = {}) {
284308
<text x="${margin.left}" y="28" fill="#24292f" font-family="Arial, sans-serif" font-size="20" font-weight="700">${title}</text>
285309
${legend}
286310
${grid}
311+
${xGrid}
287312
<line x1="${margin.left}" y1="${margin.top}" x2="${margin.left}" y2="${height - margin.bottom}" stroke="#8c959f" />
288313
${hasRightAxis ? `<line x1="${width - margin.right}" y1="${margin.top}" x2="${width - margin.right}" y2="${height - margin.bottom}" stroke="${rightAxisColor}" stroke-width="2" />` : ''}
289314
${rightAxisTicks}
@@ -413,7 +438,7 @@ const inferCsvPlot = ({ headers, rows }, filename = '') => {
413438
yMin: 0,
414439
yMaxBaseline: 200,
415440
yTickInterval: 100,
416-
decimalValues: true,
441+
decimalValues: false,
417442
samples: rows.map((row, index) => sampleFor(row, index, 'fps')),
418443
latestCommitSha: latestCommitShaFor(rows, ['fps']),
419444
};
@@ -464,6 +489,8 @@ const inferCsvPlot = ({ headers, rows }, filename = '') => {
464489
return {
465490
title: title(basename, 'FPS samples'),
466491
xLabel: xHeader ? 'Seconds' : 'Samples',
492+
xTickInterval: xHeader ? 1 : undefined,
493+
xGridInterval: xHeader ? 1 : undefined,
467494
yMin: 0,
468495
yMaxBaseline: 200,
469496
yTickInterval: 100,
@@ -478,9 +505,11 @@ const inferCsvPlot = ({ headers, rows }, filename = '') => {
478505
};
479506

480507
const buildCsvPlotSvg = (content, options = {}) => {
481-
const inferred = inferCsvPlot(parseCsv(content), options.filename);
508+
const inferred = options.inferred || inferCsvPlot(parseCsv(content), options.filename);
482509
const { latestCommitSha, ...plotOptions } = inferred;
483-
const svg = buildMetricPlotSvg(inferred.samples, { ...plotOptions, ...options });
510+
const svgOptions = { ...options };
511+
delete svgOptions.inferred;
512+
const svg = buildMetricPlotSvg(inferred.samples, { ...plotOptions, ...svgOptions });
484513
return addLatestCommitSha(svg, latestCommitSha);
485514
};
486515

@@ -502,6 +531,36 @@ async function saveCsvPlot(browser, csvPath, destination, options = {}) {
502531
}
503532
}
504533

534+
async function saveLoadDurationPlot(browser, csvPath, destination) {
535+
const content = fs.readFileSync(csvPath, 'utf8');
536+
const { headers, rows } = parseCsv(content);
537+
if (
538+
!headers.includes('fps')
539+
|| !headers.includes('% change')
540+
|| !headers.includes('load duration')
541+
|| !rows.some(row => Number.isFinite(Number(row['load duration'])))
542+
) {
543+
return undefined;
544+
}
545+
546+
await saveCsvPlot(browser, csvPath, destination, {
547+
inferred: {
548+
title: 'Load duration',
549+
xLabel: 'Runs',
550+
yMin: 0,
551+
yMaxBaseline: 10,
552+
yTickInterval: 1,
553+
decimalValues: true,
554+
samples: rows.map((row, index) => ({
555+
x: index,
556+
value: Number(row['load duration']),
557+
})),
558+
latestCommitSha: latestCommitShaFor(rows, ['load duration']),
559+
},
560+
});
561+
return destination;
562+
}
563+
505564
function printUsage() {
506565
console.log([
507566
'Usage: bun scripts/metric_plot.js <csv_path> [plot_path]',
@@ -538,6 +597,13 @@ async function main() {
538597
try {
539598
await saveCsvPlot(browser, csvPath, destination);
540599
console.log(`CSV_PLOT=${destination}`);
600+
const loadDurationName = process.env.LOAD_DURATION_NAME || 'load_duration';
601+
const loadDurationDestination = path.join('/tmp', `${loadDurationName}.png`);
602+
const loadDurationPlot = await saveLoadDurationPlot(
603+
browser, csvPath, loadDurationDestination);
604+
if (loadDurationPlot) {
605+
console.log(`CSV_PLOT=${loadDurationPlot}`);
606+
}
541607
} finally {
542608
await browser.close();
543609
}
@@ -550,6 +616,7 @@ module.exports = {
550616
inferCsvPlot,
551617
parseCsv,
552618
saveCsvPlot,
619+
saveLoadDurationPlot,
553620
saveMetricPlot,
554621
};
555622

0 commit comments

Comments
 (0)