From 5e7e88fc8421f11ab8ff77b7f1c5005c360b8a2a Mon Sep 17 00:00:00 2001 From: digua Date: Sat, 8 Aug 2026 20:41:39 +0800 Subject: [PATCH] fix(startup): benchmark full presentation samples Create the CDP target on a blank page and reset the session playback marker before retained samples so warmups cannot turn splash timing into reload timing. --- scripts/bench-cli-web-startup.mjs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/bench-cli-web-startup.mjs b/scripts/bench-cli-web-startup.mjs index 9edfa78a..7a4bfef4 100644 --- a/scripts/bench-cli-web-startup.mjs +++ b/scripts/bench-cli-web-startup.mjs @@ -11,6 +11,8 @@ const DEFAULT_URL = 'http://127.0.0.1:3100/' const DEFAULT_RUNS = 10 const DEFAULT_WARMUPS = 1 const DEFAULT_TIMEOUT_MS = 15_000 +// Keep aligned with src/bootstrap/startup-playback.ts so retained samples exercise the full first-start presentation. +const STARTUP_PRESENTATION_SESSION_KEY = 'chatlab:startup-presentation-shown:v1' function parsePositiveInteger(value, flag) { const parsed = Number.parseInt(value, 10) @@ -58,8 +60,8 @@ headless Chrome profile and reads timing-only data from the page. Options: --url CLI Web URL (default: ${DEFAULT_URL}) - --runs Recorded reloads (default: ${DEFAULT_RUNS}) - --warmups Unrecorded warm-up reloads (default: ${DEFAULT_WARMUPS}) + --runs Recorded startup navigations (default: ${DEFAULT_RUNS}) + --warmups Unrecorded warm-up navigations (default: ${DEFAULT_WARMUPS}) --timeout Per-reload startup timeout (default: ${DEFAULT_TIMEOUT_MS}) --chrome Chrome/Chromium executable (or CHATLAB_CHROME_PATH) --json Print the full result as JSON @@ -215,6 +217,16 @@ async function navigateAndMeasure(client, url, timeoutMs) { return readStartupSnapshot(client, timeoutMs) } +async function resetStartupPresentation(client) { + const evaluation = await client.send('Runtime.evaluate', { + expression: `sessionStorage.removeItem(${JSON.stringify(STARTUP_PRESENTATION_SESSION_KEY)})`, + returnByValue: true, + }) + if (evaluation.exceptionDetails) { + throw new Error(`Unable to reset startup presentation state: ${evaluation.exceptionDetails.text}`) + } +} + function percentile(values, fraction) { const sorted = [...values].sort((left, right) => left - right) const index = Math.max(0, Math.ceil(sorted.length * fraction) - 1) @@ -252,7 +264,8 @@ async function main() { try { await waitForJson(`http://127.0.0.1:${port}/json/version`, options.timeoutMs) - const targetResponse = await fetch(`http://127.0.0.1:${port}/json/new?${encodeURIComponent(options.url)}`, { + // Create a blank target so an unmeasured navigation cannot consume the one-time startup presentation. + const targetResponse = await fetch(`http://127.0.0.1:${port}/json/new?${encodeURIComponent('about:blank')}`, { method: 'PUT', }) if (!targetResponse.ok) throw new Error(`Unable to create Chrome target: HTTP ${targetResponse.status}`) @@ -263,8 +276,11 @@ async function main() { await client.send('Page.enable') await client.send('Runtime.enable') const snapshots = [] + let hasNavigatedToTarget = false for (let index = 0; index < options.warmups + options.runs; index += 1) { + if (index >= options.warmups && hasNavigatedToTarget) await resetStartupPresentation(client) const snapshot = await navigateAndMeasure(client, options.url, options.timeoutMs) + hasNavigatedToTarget = true if (index >= options.warmups) snapshots.push(snapshot) }