尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Sanity Studio 密闭式性能基准套件(bench)实战指南:从架构设计到本地运行、场景扩展与 CI 集成

Sanity Studio 密闭式性能基准套件(bench)实战指南:从架构设计到本地运行、场景扩展与 CI 集成 Sanity Studio 密闭式性能基准套件bench实战指南从架构设计到本地运行、场景扩展与 CI 集成【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity本文围绕 Sanity 仓库中perf/bench这套 hermetic密闭式Studio 性能基准套件展开它取代了已退役的 eFPS 套件用于测量构建产物在本地 mock Content Lake 上的编辑响应性、加载指标与资源占用并以真实统计手段对比一个 PR 的构建experiment与 merge-base 构建reference。读完本文你将掌握这套基准的架构设计原理、指标口径、本地运行的全部命令、如何新增一个基准场景以及它与 CI 工作流的集成方式。为什么需要一套全新的基准套件旧 eFPS 套件的三大结构性缺陷在深入了解新套件之前先看它要解决的三个问题。旧套件dev/efps已从仓库移除见 git 历史存在三个无法靠加固修复的结构性缺陷跨进程计时按键时间戳记录在 Node 进程中渲染时间戳记录在浏览器进程中二者依靠 marker 字符串匹配对齐由此产生了经典的 No matching event flake 类问题。远程、实时环境旧套件把 Studio 部署到 Vercel 上对着真实的 Sanity API 跑所谓 reference 只是main分支最近一次部署的产物而不是当前 PR 的 merge-base对比基准天然失真。隐藏方差的统计采用 best-of-3 合并、仅展示用的 20% 阈值、没有 gate闸门——数据看起来有结论实际上方差被掩盖且没有任何机制阻止回归合入。新套件的三大设计反转perf/bench核心文档见 perf/bench/README.md将上述三点逐一反转浏览器内测量通过 Event Timing APIinteractionId、input delay / processing / presentation 分解与 Long Animation Frames含脚本归因在页面内部完成测量。全程使用页面自身的单一单调时钟runner 只负责编排不再做跨进程时间戳对齐。密闭环境两侧构建都在本地以 HTTP/2TLS 提供服务后端是构建在repo/debug-proxy之上的进程内 Content Lake mock。PR 路径上零真实密钥Studio 的 auth token 是一个假字符串见 perf/bench/constants.ts 中的FAKE_TOKEN。交错 A/B 采样 cluster bootstraptachometer 风格在同一浏览器上交替运行 reference/experiment 会话重采样单位是会话而不是把按键事件池化当两个中位数之差的 95% 置信区间足够窄、足以做出裁决时停止采样。裁决结果四类 regression回归/ improvement改进/ ✅ neutral中性/ ⚪inconclusive预算耗尽时 CI 仍太宽——绝不掷硬币式地下结论。架构总览从 perf/bench/README.md 的架构图可以看出整体链路runner (tsx CLI, runs from HEAD) ├── static servers (h2TLS) one per side: perf/bench/dist .reference/dist ├── mock Content Lake (h2TLS) one per side: mock-api/* on repo/debug-proxy │ actions/mutate/listen(SSE)/doc/query(groq-js)/acl/auth/… request ledger ├── Chromium (SPKI-trusted cert; CDP CPU throttle; hermeticity route guard) │ injected collector (instrumentation/): event timing, LoAF, paints, bench:* marks └── stats (cluster bootstrap, gating) → report (markdown PR comment BenchRunDocument JSON)对应到仓库目录CLI 入口perf/bench/cli/index.ts 及其下的commands/run、runImpl、scenarios、dev、report、store、prepareReference、prepareBackfill、certPath、buildDistAtCommit。Mock Content Lakeperf/bench/mock-api/createServer.ts、data.ts、sse.ts、store.ts、ledger.ts、tls.ts等。浏览器编排perf/bench/runner/orchestrator.ts、browser.ts、servers.ts、staticServer.ts、smoke.ts以及session/下的interaction.ts、pageLoad.ts、settle.ts、inp.ts、navigation.ts。页面内采集器perf/bench/instrumentation/index.tsevent timing、LoAF、paint、LCP、layout-shift、bench:*marks、resource与 perf/bench/instrumentation/settle.ts仅 settle 会话注入。统计与裁决perf/bench/stats/bootstrap.ts、gate.ts、quantiles.ts、inp.ts、settle.ts、rng.ts、sparkline.ts。场景定义perf/bench/scenarios/types.ts、index.ts及各场景文件。被测 Studioperf/bench/studio/schema 与 workspace与 perf/bench/studio-customizations/定制化场景专用构建。固定端口与两侧配置统一收敛在 perf/bench/constants.tsexperiment 侧为benchexp/ API 端口 4311 / Studio 端口 3411reference 侧为benchref/ 4312 / 3412dataset 固定为bench。两侧都使用假 tokenbench-fake-token和假用户bench-user。mock 的契约对照 Studio 源码验证Mock 不只是假装是 Content Lake它的行为契约是对照 Studio 真实源码验证过的见 perf/bench/README.md写入走 Actions APIPOST /data/actionssanity.action.document.edit/.create并在 SSE listener 上以resultRev transactionId回显。对应 Studio 源码见 packages/sanity/src/core/store/document/document-pair/checkoutPair.ts。listener 是 rev 链式的每个 mutation 事件必须从快照的_rev链上previousRev → resultRevappear事件则必须没有previousRev。对应实现见 packages/sanity/src/core/store/document/document-pair/getPairListener.ts 与 packages/sanity/src/core/store/document/document-pair/utils/sequentializeListenerEvents.ts。契约测试 perf/bench/mock-api/tests/contract.test.ts 用真实的sanity/client驱动走完这一切。未知端点 404 且被记录请求 ledgerperf/bench/mock-api/ledger.ts会记录每一个意外端点一旦出现意外端点整个会话判失败这是 mock-drift 检测器。也就是说如果你的 Studio 改动新增了一个 API 调用就必须在同一个 PR 里扩展 mock——就像更新类型一样。runner 侧还会给出修复提示见 perf/bench/runner/session/interaction.ts 的UNEXPECTED_ENDPOINT_HINT在createServer.ts中实现该端点或如果 Studio 能优雅降级则将其加入ledger.ts的UNIMPLEMENTED_BUT_GRACEFUL白名单。Mock 还实现了控制平面端点/_bench/reset、/_bench/seed、/_bench/requests让 runner 能在进程内重置 store、播种 fixture、读取请求 ledger全程零网络回环。指标体系套件把指标分成五个桶见 perf/bench/README.md 的 Metrics 表格下表为完整继承并标注是否被 gate 门禁桶指标是否门禁编辑响应性逐按键的 keydown→paint 延迟中位数作为 eFPS 等价物同时上报 p75/p90/p99、快速连击fast burst次级指标、LoAF 阻塞时间与归因中位数基于 CI 门禁INPCore Web Vital在真实交互混合下点字段 → 打一段字 → 离开的 Interaction to Next Paint按 web-vitals 自身的按数量百分位规则在 ≥50 次交互上计算交互数作为置信上下文一并输出仅报告加载time to editable导航开始 → 表单可接受按键头条指标、TTFB/FCP/LCP/CLS web vitals、冷启动与打开文档两种条件、bundle 大小精确 gzip 差值、auth 启动路径里程碑可编辑前的往返次数、首次请求时间、in-flight 窗口——仅报告time to editable资源仅报告每端点类别的请求数/字节数精确、主线程 CPU-msTaskDuration 等、GC 后 heap/DOM 节点/监听器数量尚未只读中断打字中途瞬态data-read-only翻转的次数与持续时间上报编辑响应性会话的默认参数从 perf/bench/runner/session/interaction.ts 的DEFAULT_SESSION_CONFIG可以看到单次交互会话的默认节奏预热按键warmupKeystrokes: 8输入后丢弃实测按键measuredKeystrokes: 32隔离节奏isolatedCadenceMs: 100ms单键节奏必须大于最坏预期延迟快速连击burstKeystrokes: 24、burstCadenceMs: 40ms仅报告的次级指标CPU 节流cpuThrottleRate: 4就绪超时readinessTimeoutMs: 60s回读超时readbackTimeoutMs: 20s。场景可以覆盖按键数量BenchScenario.keystrokes例如 synthetic 场景按键速度约为其他场景的 10 倍默认计数会让每个会话白白多花几分钟因此该场景会调低计数——中位数需要的是样本量不是马拉松。INP 的按数量百分位规则perf/bench/stats/inp.ts 完整复刻了 web-vitals 库的规则INP 取totalInteractionCount / 50位最差的交互estimateP98LongestInteraction而不是按观测条目数取百分位——因为快于 Event Timing 可观测下限的交互不产生条目按观测条目数取百分位会让 INP 在回归把原本低于下限的交互推过下限时反而下降。总交互数 50 时官方认为不可报告套件仍返回最差值并标记reportable: false方便本地短跑。Settle 模式渲染循环检测器--mode settle针对的是渲染循环这一类 bug每次渲染时 observable 身份漂移例如useDocumentValues传入内联paths数组已在 PR #14241 修复打开场景、等待就绪然后测量达到静止quiescence的时间以及页面是否真的能到达静止。该模式不打字全部活动信号都挂在页面时钟上React commits主信号仅 settle 会话注入的 init 脚本 perf/bench/instrumentation/settle.ts 在 React 加载前安装一个最小化的 React DevTools hook 桩——生产版 react-dom 会把每次 commit 上报给它因此即使每一帧都很便宜不足以触发 LoAF循环也会现形。该脚本仅由 settle 会话注入perf/bench/instrumentation/index.ts 在其余所有模式下保持字节级一致这是刻意设计新增 settle 逻辑绝不能扰动既有指标。hook 桩对缺失信号采取优雅降级hookInstalled: false或零桶都不会判会话失败而是回退到 LoAF render-mark 活动。Long Animation Frames带脚本归因昂贵的循环会暴露它的元凶。bench:render:componentmarks由被插桩的 workspace 组件发出提供逐组件归因。CPUCDP TaskDuration每个轮询周期采样并报告但不参与静止判定——GC 与后台定时器噪声太大。静止判定逻辑纯函数computeSettleperf/bench/stats/settle.ts定义settled 整整一个安静窗口默认 3s500ms 轮询内没有任何活动从就绪算起超过 30s 上限仍未安静则判timedOut。默认窗口参数为quietWindowMs: 3000、maxSettleMs: 30000、activityFloor: 0后者是给合法周期性 ticker 的场景逃生阀。关键语义did not settle未静止和 never became ready从未就绪是报告结果不是会话失败——真正的循环绝不能当作 flake 重试。场景通过expectedToSettle声明预期默认true声明为false的场景是刻意标红的场景用于持续暴露已知且未修复的坑——它的settled: false会话是活证据退出码保持 0报告会对预期 vs 观测的任何方向失配发出警告一旦某个红场景开始静止说明加固已落地须在同一 PR 中翻转该标志。绿场景的失配则在结果 JSON 写出后以非零退出码结束。注意一个内置 caveatmock 总是发出visibility: transaction这会让 preview store 走上慢速拉取路径——settle 时间是 bench 内部的趋势数值不能当作 UX 声明。定制化场景与 read-only 中断指标定制化场景previewHeavy、customInputs、documentActions、structurePane、listenQueryPane、wrappedForm见 perf/bench/scenarios/customizations.ts以贴近真实客户调用的形态覆盖公开定制 API——自定义 preview/input、document actions/badges、S.componentpane、配置级form.components。其中documentActions和listenQueryPane是刻意标红已知未修复的坑useTemplatePermissions内联templateItems、内联 observable 的useLoadable。每个场景都有自己的 workspace 与 basePath而这些 workspace只存在于定制化构建中pnpm --filter bench build:customizations # → perf/bench/dist-customizations ( bench-build-flags.json) pnpm bench run --mode settle --scenario previewHeavy --dist perf/bench/dist-customizations默认的pnpm build:benchdist 永远不包含它们Studio 启动时会热编译每个 workspace 的 schema而 bundle 大小是上报指标——所有门禁数字测量的纯净 dist 必须保持字节级一致。runner 在目标 dist 缺少该 flag 时会跳过定制化场景并提示上面的命令。read-only 中断指标刻画的是套件真正表征过的一个 Studio bugpackages/sanity/src/core/store/document/document-pair/editState.ts 派生ready: !fromCache而 editState observable 是publishReplay(1)refCount()——提交附近发生订阅者 churn 会拆掉它SWR 缓存以ready: false重新发出表单在约 1 秒内静默吞掉按键。打字循环此时暂停并计数而不是对着虚空打字。从 perf/bench/runner/session/interaction.ts 的waitUntilEditable可见每次按键前检查[data-testidform-view]的data-read-only属性翻转期间暂停并累计count与totalMs。本地运行指南一切都可以通过benchCLI 触达——pnpm bench help列出全部命令pnpm bench run --help查看某条命令的 flags。根目录的 scripts 映射见 package.jsonbench、bench:dev、bench:report、bench:store、bench:test、bench:unit。pnpm build:bench # 构建 packages bench studioexperiment 配置 pnpm bench scenarios # 列出可用场景 # 绝对模式单一构建无对比 pnpm bench run --scenario singleString --sessions 4 # 自测构建与自身对比同一份源码两侧配置—— 必须全部 neutral pnpm --filter bench build:reference-config pnpm bench run --scenario singleString --reference-dist perf/bench/.reference/dist # 加载指标 bundle 大小 pnpm bench run --mode pageload --scenario singleString # 浸泡检查长时间会话中的退化 泄漏斜率 pnpm bench run --mode soak --scenario singleString --minutes 5 # settle打开文档并测量静止时间渲染循环检测器 pnpm bench run --mode settle --scenario singleString --sessions 5 # INPCore Web Vital需要 50 次交互 pnpm bench run --mode inp --scenario singleString --sessions 3 # 交互式调试mock sanity dev自己往种子文档里打字 pnpm bench dev # 同上但服务的是带全部定制化场景种子的定制化 Studio pnpm bench dev --customizations pnpm bench:unit # mock 契约 stats 单元测试注意bench:test、bench:dev、bench:report、bench:store根脚本只是对应 CLI 命令的别名。仅 CI 使用的命令bench prepare-reference、bench cert-path通过--help查看文档。常用的bench runflags从 perf/bench/cli/commands/run.ts 的参数定义使用optique命令行解析库可以看到完整参数表--scenario NAME可重复指定场景默认全部--mode interaction|pageload|soak|inp|settle默认interaction--sessions N绝对模式每场景会话数默认 6/ A/B 模式每侧最少会话数--max-sessions NA/B 模式每侧最大会话数默认 20--budget seconds每场景墙钟上限默认 8 分钟--dist DIRexperiment 构建路径默认perf/bench/dist--reference-dist DIR传入即启用 A/B 模式--seed 42PRNG 种子同一种子 → 完全可复现的 bootstrap 区间默认 1--headed有头浏览器--throttle 1关闭 CPU 节流默认 4×--json-out path把结果写成 BenchRunDocument JSON 文件--fail-on-verdict任何比较 gate 出 regression/improvement 即以非零退出自测用inconclusive 不算失败--no-network-emulationpageLoad 模式下禁用 Fast-4G 网络模拟。绝对数值是宿主相关的CPU 节流是对宿主机性能的倍增每次结果都会记录标定分数固定负载越大越慢让不同机器的运行可以诚实对比。在负载高的笔记本上预期会有噪声——A/B 裁决能吸收它绝对数值不能。添加一个场景新增场景是四步流程详见 perf/bench/README.mdSchema在 perf/bench/studio/schemas/ 写name.ts导出一个 workspace partial并在 perf/bench/sanity.config.ts 注册。Scenario在 perf/bench/scenarios/ 写name.ts用defineScenario定义——fixture 必须确定性用 perf/bench/scenarios/fixtures/prng.ts绝不使用Math.randominteractions声明要打字的字段kind: pte表示 Portable Text如果字段路径上的值不是普通字符串提供readbackText需要种子图片资源时用cdn.sanity.io/images/*路由守卫会把它替换为一张恒定 PNG。注册在 perf/bench/scenarios/index.ts 注册并把场景加入 .github/workflows/bench.yml 的bench-interaction矩阵。验证一个绝对模式会话无失败通过——回读、console 错误、密闭性、端点漂移全部是硬性会话失败。defineScenario字段全解从 perf/bench/scenarios/types.ts 可以拿到字段契约name场景唯一 idsourceFile仓库根相对路径写进报告以便仪表盘链接到定义不能从name推导——syntheticLarge就住在synthetic.ts里workspace要打开的 workspace basePath 段默认name多个场景共享一个 workspace 时设置如syntheticLargepath相对于 workspace 的路由如自定义 structure pane 的structure/itemIdfixture 仍会被种子化readySelector导航后等待的就绪选择器默认是表单可编辑perf/bench/runner/session/navigation.ts 的DEFAULT_READY_SELECTOR[data-testidform-view]:not([data-read-onlytrue])自定义 pane 等不渲染表单的路由需显式设置expectedToSettle默认truerequiresCustomizations声明场景 workspace 只在定制化构建中存在documentType/documentId被测文档的类型与已发布 iddraft 为drafts.idfixture每个会话前种子化的文档数组必须确定性interactionsinteraction 模式测量的字段按固定执行顺序keystrokes按场景覆盖按键数。最小的场景长这样perf/bench/scenarios/singleString.tsexport const singleString defineScenario({ name: singleString, sourceFile: perf/bench/scenarios/singleString.ts, documentType: singleString, documentId: DOCUMENT_ID, fixture: () [ {_id: drafts.${DOCUMENT_ID}, _type: singleString, stringField: }, ], interactions: [{fieldPath: stringField, kind: string}], })而 perf/bench/scenarios/recipe.ts从dev/efps/tests/recipe/recipe.ts移植演示了完整形态确定性 PRNG 生成_key、PTE 指令块、kind: ptereadbackText把 Portable Text 抽回纯文本做回读校验。会话层的回读校验细节见 perf/bench/runner/session/interaction.ts普通字符串字段要求 warmup→measured→burst 的连续输入串逐字出现在文档文本里PTE 则断言收敛每个输入的字符都出现且编辑器本地文本 mock 端抽取文本因为慢主机上的 re-render/rebased 可能重置 caret 把输入串打散到各 block。CI 集成.github/workflows/bench.yml工作流文件 .github/workflows/bench.yml 完整定义了生产环境下的运行矩阵burn-in 期间 label 门禁PR 任务仅在带trigger:perf-benchlabel 时运行全代码 PR 的 gate 已接好只差一个条件。Reference 构建merge-base 的sanity构建并打包进 HEAD 提交的perf/bench/harness即下文的历史构建配方——两侧 harness 与场景完全一致只有产品代码不同。按 merge-base sha 缓存构建失败或 merge-base 早于套件存在时回退到绝对模式报告里带警告。分片每个场景一个 job 一个 pageLoad job墙钟 构建 最慢场景。预算是上限——停止规则在 CI 收敛时提前退出。PR 评论始终原地发布/更新bench-reporttag。burn-in 期间不 gate。自测每周 cron /workflow_dispatch带self_test构建与自身对比并加--fail-on-verdict——两个完全相同构建却得出已裁决的 verdictregression 或 improvement是 harness bug不是产品变化。与bench-interaction矩阵相同的五个场景串行执行syntheticLarge被排除仅它一个就大约让 job 墙钟翻倍。Inconclusive 指标不使 job 失败§2噪声运行绝不能读作掷硬币。track-main每日 cron或workflow_dispatch带run_suite绝对模式运行固定会话数 soak INP settle定制化场景 singleString/article作为 vanilla 对照 debugLoop作为检测器每日自测settle 预期失配在运行结果存储之后才报警绝不代替存储。结果作为benchRun文档存入 Studio Radar 项目mhfozd0z/bench——RADAR_SANITY_WRITE_TOKEN是整套件中唯一的真实密钥。dispatch 路径让维护者无需等待凌晨 5 点的 cron 就能按需跑全量套件并存储。Backfillworkflow_dispatch带backfill_sha修复 harness 故障后的每日序列空洞。用历史 commit 的 packages 构建 experiment distreference 构建配方指向perf/bench/dist外加perf/bench/dist-customizations覆盖 settle跑全量套件并把文档存到该 sha 名下BENCH_GIT_SHA。Settle backfill 可以回溯到模式存在之前的 commit没有该隐患的 commit 自然静止预期红的场景静止会报失配而非失败。每个缺失日 dispatch 一次例如gh workflow run bench.yml -f backfill_sha$(git rev-list -1 --beforedayT05:00Z origin/main)。要测量过去的发布版本只需release_taggh workflow run bench.yml -f release_tagv6.12.0——工作流把 tag 解析为 commit、重放、并把该次运行存为该 release 的测量此时若再传backfill_sha必须与 tag 一致。startedAt仍是实际运行时间——读 backfill 区间时按 sha/commit 日期排序而不是墙钟。Backfill 构建失败会响亮失败无绝对模式回退被测 commit 才是关键。A/B dispatchworkflow_dispatch带ab_fromab_to完整 sha回归狩猎工具。在ab_from构建 reference、ab_to构建 experiment都走 tarball 配方、用 HEAD 的 harness跑交错对比 bootstrap-gated 裁决。verdict 表落在 run 的 summary 页没有 PR 可评论对比以mode: ab的 benchRun 文档存储git.sha experiment、mergeBaseSha reference——这是趋势仪表盘刻意不绘制的调查记录。在两个趋势点之间用 log₂(N) 次 dispatch 二分疑似回归gh workflow run bench.yml -f ab_fromsha -f ab_tosha趋势仪表盘上的Copy investigation prompt会拼好命令GitHub 没有能预填 dispatch 输入的 URL放进一份可直接交给编码 Agent 的简报。两侧构建失败都会响亮失败此路径无绝对模式回退。历史构建backfill、A/B、PR 在 merge-base 的 reference共用同一配方 perf/bench/cli/commands/buildDistAtCommit.ts历史 commit 检出到一次性 worktree--frozen-lockfile安装重放它自己的 CI 安装、构建然后把sanity连同其依赖的 workspace packagessanity/types、sanity/schema等像npm publish那样打进 tarball。HEAD 提交的perf/bench再稀疏检出到第二个一次性 worktreetarball 以 pnpmoverrides接入在那里跑sanity build——Studio 自己的 peersreact、styled-components保持 HEAD 的 lockfile 钉住版本而历史sanity的第三方依赖按当天范围内最新版重新解析。这是刻意为之序列追踪的是自家代码面的回归上游修复落地后重测旧 sha 回答的是旧的 Studio 配上修复后的依赖还会不会回归。调用方检出永不被修改所以该配方本地也能跑pnpm bench prepare-backfill --sha sha不过笔记本上的绝对数值是宿主相关的不得存入每日序列。定时失败告警cron 触发下任何失败或超时取消的 job 都会通过SLACK_WEBHOOK_URL_CI_ALERTS发到 CI 告警 Slack 频道——红色 cron 没有 PR 可以挂靠静默的 cron 会在时间序列里留下空洞。抗 flake 五规则perf/bench/README.md 明确了五条铁律没有固定 sleep——一切等待都基于条件就绪 探针按键可观测地落进文档。噪声只会加宽采样绝不翻转 verdict——预算耗尽产出 ⚪ inconclusive。处处使用种子化 PRNG——相同--seed可复现整个分析。失败会话被丢弃、分类、记录并重试连续 3 次失败硬性中止。重试次数随每个结果文档一起输出flake 是被追踪的指标。环境漂移快速失败——意外网络 → 密闭性违规意外 API 端点 → 点名该端点的会话失败。配套的密闭性守卫实现在 perf/bench/runner/browser.ts已知无害的外部主机studio-static.sanity.io、design-system-static.sanity.io、telemetry.sanity.io等被静默中止其余任何非 localhost 请求既中止又判会话失败唯一合法的真实 API 流量是硬编码到api.sanity.io/vX/intake/…的 telemetry/error 隧道。路由模式特意只匹配非 localhost URL——Playwright 路由会绕过浏览器 HTTP 缓存给每次请求加一趟拦截往返会破坏 warm-load 测量。已知注意事项Event Timing 下限快于 16ms 的交互不可观测规范的最小durationThreshold8ms 的时长粒度。因此交互会话在4× CPU 节流下运行把典型延迟抬到下限之上并相对噪声放大回归。仍不产生条目的按键记在 16ms 下限并被计数belowFloorCount。gate 阈值必须高于粒度8ms 是浏览器能报告的最小非零差所以INTERACTION_THRESHOLDS.absMs是 16ms两步——一个量化步绝不能 gate 成 verdict。早先 3ms 下限时两个完全相同的构建只要样本在任意 160ms 的指标上差一步就会 gate 成 回归自测在article/body、中位数 56ms 上抓到过。targetHalfWidthMs8ms同理受限比仪器分辨率更细的收敛目标永远达不到采样会一直跑到预算耗尽并报 ⚪ inconclusive 而不是提前停止。详见 perf/bench/stats/gate.ts——该文件还定义了裁决规则只有 CI 排除 0且点估计超过两个下限绝对毫秒数与相对参考中位数的百分比取较大者才算真实差异inconclusive与neutral刻意区分噪声运行绝不能读作通过/失败掷硬币gating 把 inconclusive 当 neutral 处理。Warm load 需要有效证书Chromium 拒绝缓存来自证书错误 origin 的响应所以 runner 通过--ignore-certificate-errors-spki-list信任自签名 bench 证书绝不用 Playwright 的ignoreHTTPSErrors。HTTP/2 是承重的h1 下浏览器每主机 6 连接的限额会饿死 Studio 的并发 SSE listenerUI 永远无法静止。mock 的 TLS 密钥生成与 SPKI 指纹逻辑见 perf/bench/mock-api/tls.ts。结语从设计哲学上讲perf/bench的三个关键词是浏览器内测量单一时钟runner 只编排、密闭性本地 mock 零密钥 端点漂移检测和诚实的统计会话级 cluster bootstrap、动态停止、四态 verdict。它把性能回归从玄学变成了一个可复现、可门禁、可在 PR 评论里直接阅读的工程事实。想深入建议从 perf/bench/stats/gate.ts裁决规则、perf/bench/runner/orchestrator.ts交错采样循环、perf/bench/runner/session/interaction.ts会话生命周期与 perf/bench/mock-api/createServer.tsmock 契约四个文件入手再对照 .github/workflows/bench.yml 看它是如何在生产 CI 中运转的。【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表