news 2026/9/7 14:11:49

opencode 手动性能测试套件:基于 Playwright 与 Chrome Trace 的 App 性能诊断实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
opencode 手动性能测试套件:基于 Playwright 与 Chrome Trace 的 App 性能诊断实践

opencode 手动性能测试套件:基于 Playwright 与 Chrome Trace 的 App 性能诊断实践

【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencode

本文围绕 packages/app/e2e/performance 目录下的手动性能测试套件(manual performance suite)展开:它是 opencode 桌面应用(packages/app,Solid + Vite 构建的渲染进程)用来量化高负载交互性能的诊断体系,被刻意排除在常规本地与 CI 的 Playwright 发现流程之外。读完后你将掌握:如何构建并运行生产包下的基准场景、如何理解benchmarkfixture 的指标上报契约(BENCHMARK/BENCHMARK_PAGEJSON 行)、如何用 Chrome DevTools Trace 做渲染进程级剖析,以及套件遵循的"只断言场景完成与指标采集、不设定机器相关性能预算"的设计原则。

一、套件定位与运行机制

应用的高负载性能诊断集中在packages/app/e2e/performance,与常规回归测试物理隔离。它被排除在正常本地和 CI 的 Playwright 发现之外,原因是基准测试运行时间长、且依赖生产构建,不适合混入快速反馈回路。

基准配置的核心行为定义在 playwright.config.ts:它继承主 Playwright 配置,将testDir锁定到 performance 目录本身(unit/**被排除在 Playwright 发现外),并在webServer中强制使用生产构建:

// packages/app/e2e/performance/playwright.config.ts(节选) webServer: { ...config.webServer, command: `bun run build && bun run serve -- --host 0.0.0.0 --port ${port} --strictPort`, reuseExistingServer: false, }

即:bun run build构建应用、再以vite preview托管生产包,然后串行执行场景(fullyParallel: falseworkers: 1)。这与 AGENTS.md 中"对生产构建运行基准""串行运行基准以避免跨测试竞争"的原则一一对应。

1.1 运行方式

packages/app目录显式运行:

bun run test:bench

在 packages/app/package.json 中,该脚本展开为两步:

"test:bench": "bun test ./e2e/performance/unit && playwright test --config e2e/performance/playwright.config.ts"

先跑e2e/performance/unit下的纯单元测试(指标计算、探针、trace 写出等模块的确定性验证),再跑 Playwright 浏览器基准。

Windows PowerShell 下需要显式设置单 worker:

$env:PLAYWRIGHT_WORKERS = "1" bun run test:bench

此外还有一组配套的显式入口:

# 运行指定场景(可配合 trace 目录,见下文) bunx playwright test --config e2e/performance/playwright.config.ts \ timeline/session-tab-switch-benchmark.spec.ts

1.2 场景覆盖范围

套件当前的基准场景覆盖五类高价值交互(可在各 spec 文件中逐一核对):

  • 冷/热会话标签页切换计时:session-tab-switch-benchmark.spec.ts 对 cold/hot 各跑 5 轮(可用SESSION_TAB_SWITCH_RUNS覆盖),v2 布局下再按 review 面板开/关分拆,统计firstDestinationObservedMsfirstCorrectObservedMsstableObservedMs的 min/median/max;
  • 首页会话点击计时:home-tab-navigation-benchmark.spec.ts 将"打开首页会话并绘制标题栏 tab"拆分为 content 与 titlebar-tab paint 两段测量;
  • 单会话标签关闭计时:关闭唯一会话 tab 后经由稳定的 home 恢复路径绘制首页;
  • 缓存会话重绘与变更追踪:session-tab-flash.spec.ts 采样点击后缓存会话的重绘,并验证所有已打开会话 tab 的预取行为;
  • 流式时间线吞吐诊断:session-timeline-benchmark.spec.ts 度量流式渲染的吞吐、RAF 回调间隔分布、长任务、几何稳定性与重挂载诊断,并按"新布局开关 × review 面板开/关 × 是否携带 diff"组合出多个场景。

已提交的 smoke 与 regression 测试则继续负责分页、tab 绘制、上下文调整、折叠状态、composer 间距等正确性覆盖——基准套件不重复承担正确性断言。

二、benchmark fixture:统一的上报契约

所有基准共享同一个benchmarkfixture,定义在 benchmark.ts。一个新基准应当"看起来像一个普通 Playwright 测试":

import { benchmark, expect } from "../benchmark" benchmark("measures one interaction", async ({ page, report }) => { // Only scenario-specific setup and interaction belong here. report({ durationMs: 42 }) })

fixture 的行为约束(对照 benchmark.ts 源码):

  • 强制report()report只允许调用一次,重复调用直接抛错(Benchmark reported metrics more than once);benchmarkResultauto: true的钩子,测试结束时若没有上报且测试状态与预期一致,会以Benchmark did not report metrics让该测试失败——这是"断言指标采集完成"而非断言数值的关键实现;
  • 自动命名与关闭 trace:通过覆写pagefixture,为每个页面安装observePerformancePage诊断,结束时调用reportPerformancePage停止 trace 并输出诊断行;
  • 导航历史捕获与失败附载framenavigated事件记录主框架导航 URL 序列;当testInfo.status !== testInfo.expectedStatus时,把导航历史以performance-navigations附件挂到测试报告上,便于回放失败场景的页面路径;
  • 一致的指标输出:每个基准结束时输出一行BENCHMARKJSON。

2.1 BENCHMARK / BENCHMARK_PAGE 两行诊断协议

源码 benchmark.ts#L29-L46 输出的BENCHMARK行比 README 简写版更完整:

BENCHMARK {"schemaVersion":2,"runID":"...","name":"...","status":"passed","expectedStatus":"expected","retry":0, "repeatEachIndex":0, "context":{"project":"chromium","platform":"darwin", ...场景 context}, "metrics":{...}, "error":undefined}

每个被观察的页面在最终带状态的BENCHMARK记录之前,先输出一条BENCHMARK_PAGE(benchmark.ts#L120-L138),携带同一 runID、导航历史与可选的 trace 文件路径:

BENCHMARK_PAGE {"schemaVersion":2,"runID":"...","name":"...","context":{"platform":"...","trace":".../xxx.json", "selectorTrace":false}, "navigations":["https://..."]}

runID 由 playwright.config.ts#L5 生成(ISO 时间戳 + 进程 PID 的OPENCODE_PERFORMANCE_RUN_ID),用于把一次运行中所有页面级诊断与基准结果关联起来。

2.2 隔离上下文:withBenchmarkPage

需要隔离浏览器上下文(例如冷/热切换的每轮试验都要干净的环境)时,使用withBenchmarkPage(benchmark.ts#L100-L118):它自行browser.newContext()、创建页面、安装同一套诊断生命周期,并在结束时关闭 context。会话 tab 切换基准正是用它包裹每一轮 cold/hot 试验,保证 5 轮结果互不污染:

results[mode].push( await withBenchmarkPage(browser, `session-tab-switch-${mode}-${run}`, (page) => trial(page, mode), testInfo), )

三、Chrome Trace:页面全生命周期的标准轨迹采集

设置OPENCODE_PERFORMANCE_TRACE_DIR后,每个基准页面都会自动产出标准 Chrome DevTools trace(无需在测试内写任何 trace 代码),可直接加载进 Chrome DevTools 的 Performance 面板:

OPENCODE_PERFORMANCE_TRACE_DIR=/tmp/opencode-performance-traces \ bunx playwright test --config e2e/performance/playwright.config.ts \ timeline/session-tab-switch-benchmark.spec.ts

3.1 采集实现:CDP Tracing + ReturnAsStream

chrome-trace.ts 的startChromeTrace通过 CDP 会话采集轨迹,要点(chrome-trace.ts#L21-L67):

  • 类别配置:默认排除-*前缀的类别,包含devtools.timelinev8.executelatencyInfodisabled-by-default-devtools.timeline.stack等;当OPENCODE_PERFORMANCE_SELECTOR_TRACE=1时,额外开启disabled-by-default-blink.debugdisabled-by-default-devtools.timeline.invalidationTracking——这是selector 统计(INP 类分析的前置数据)所需的增强采集
  • 传输模式Tracing.start使用ReturnAsStream,停止后经Tracing.tracingComplete拿到流句柄,再用IO.read分块落盘(chrome-trace.ts#L84-L95);
  • 完整性保证:先写${file}.partial,若 Chromium 报告dataLossOccurred则保留 partial 文件并抛错Chrome trace lost data,成功才rename为正式文件——对应 Puppeteer 官方 tracing 的默认生命周期与失败语义;
  • 文件命名${runID}-${sanitized-name}-${sha256(name)[0:8]}-${nonce}[-selectors].json,trace 路径随后出现在BENCHMARK_PAGE行中,供命令行工具消费:
bunx devtools-tracing stats <trace-path-from-BENCHMARK_PAGE>

README 明确边界:Chrome trace 是浏览器级、页面全生命周期的诊断数据;而场景指标(如首帧可见时间、稳定时间)使用的是更窄的显式命名观察窗口。二者的分工写进了 AGENTS.md:"不要跨 harness、probe 和 trace 重复测量;自定义探针只用于产品特有度量"。

INP 分析需要包含受支持的导航/交互 insight 的 trace;selector 统计则需要以OPENCODE_PERFORMANCE_SELECTOR_TRACE=1捕获。

3.2 去帧率限制的 uncapped 配置

playwright.uncapped.config.ts 在默认配置上追加启动参数--disable-frame-rate-limit--disable-gpu-vsync,用于显式的去上限诊断(例如观察 RAF 间隔分布时排除合成器节流干扰)。README 同时声明:原生产品基准应使用默认 Playwright 配置。

四、剖析开关、环境变量与指标语义

4.1 剖析开关(默认关闭)

CPU 与高负载视觉剖析默认禁用,以保持基准负载的确定性:

环境变量作用
TIMELINE_CPU_PROFILE=1同时开启 CPU profile 与视觉剖析
TIMELINE_VISUAL_PROFILE=0在已开启 CPU profile 时仅保留 CPU 剖析
OPENCODE_PERFORMANCE_TRACE_DIR开启全页面 Chrome trace 采集
OPENCODE_PERFORMANCE_SELECTOR_TRACE=1trace 中追加 selector/失效跟踪类别(INP、selector 统计所需)
OPENCODE_PERFORMANCE_RUN_ID诊断行 runID,默认由配置自动生成

这与 AGENTS.md 的原则"当详细剖析会改变负载行为时保持其 opt-in"一致——视觉剖析的截图读取会扰动计时,因此默认关闭。

4.2 流式场景的负载参数

流式时间线场景的默认值与可调项(对照 session-timeline-benchmark.spec.ts#L102-L110 的解析代码):

环境变量默认值含义
TIMELINE_DELTA_COUNT160流式增量事件数量;覆盖时会随结果 context 上报
TIMELINE_HISTORY_TURNS320会话历史轮数(DOM 规模)
TIMELINE_EVENT_BATCH1事件投递批大小;覆盖时同样进入 context
TIMELINE_CPU_THROTTLE3030x CPU 节流系数
TIMELINE_COMPLETION_TIMEOUT_MS420000流式完成等待超时(测试超时在其上再加 60s)
TIMELINE_MINIMAL=1时使用最小化负载布局
SESSION_TAB_SWITCH_RUNS5tab 切换基准每种模式轮次

4.3 指标语义的三条重要边界

README 对流式基准的指标给出了明确的解释边界,值得在引用数据时牢记:

  1. 30x CPU 节流是确定性压力画像,不是模拟真实低端设备;同样,stability 套件中的 4x CPU 压力也如此声明;
  2. 这些指标是主线程回调诊断:吞吐、RAF 回调间隔分布、帧预算等价值、长任务,都度量到"渲染器观察到的完成时间"与"最终几何稳定"为止——不是合成器呈现或掉帧测量;探针禁用的视觉/几何指标会以null呈现;
  3. 基准不断言机器相关的性能预算。断言只验证"场景完成 + 指标采集完成"。重绘状态做游程分组(run-length grouping)用于压缩,但每条原始观察时间戳与原始 mutation 批次、布局偏移都无损保留——对应 AGENTS.md 的"保留原始诊断数据或采用无损表示"。

五、与 timeline-stability 套件的分工

e2e/performance下还有第二个独立配置目录 timeline-stability,通过bun run test:stability运行(见 packages/app/package.json#L30):它对时间线布局连续性做可判定的合约测试(锚点保持、行序、披露状态在虚拟化中的保持、键盘/滚轮所有权一致等),并对失败场景保留video.webmtrace.zip、失败截图、采样 DOM/布局 trace JSON 等证据。

两者分工清晰:stability 套件负责"布局连续性合约是否被违反"的 pass/fail 判定;performance 套件负责"交互到底花了多久、主线程回调分布如何"的诊断量化。前者的判明边界(不检查每个合成器呈现像素、不覆盖特定刷新率/设备)在其 README 中有专门声明。

六、方法论依据与扩展边界

套件的方法论直接对齐浏览器与框架生态的官方建议:Electron 官方教程推荐用重复的 Chrome DevTools / Chrome Tracing 测量做性能回归;Chrome DevTools 官方文档推荐用 Performance 录制刻画运行时工作;Playwright 官方将 trace 定位为测试调试工具而非渲染进程剖析工具——因此本套件选择"Playwright 管场景编排与完成检查、Chrome trace 管通用浏览器剖析、自定义探针只做产品特有测量"的三层结构。

对于未来的打包版(Electron)基准,README 给出了明确路线:若需要主进程与多进程归因,应使用 Electron 官方contentTracingAPI,而不是往这套渲染器 harness 里加自制的进程级插桩。当前套件剖析的对象始终是 Chromium 中的共享 app 渲染进程。

参考路径汇总

  • 套件说明:packages/app/e2e/performance/README.md
  • 共享 fixture 与诊断协议:packages/app/e2e/performance/benchmark.ts
  • Chrome trace 采集实现:packages/app/e2e/performance/chrome-trace.ts
  • 基准 Playwright 配置:packages/app/e2e/performance/playwright.config.ts、uncapped 配置
  • 场景基准:会话 tab 切换、流式时间线、首页 tab 导航、首次导航、review 面板扩展
  • 设计原则:packages/app/e2e/performance/AGENTS.md
  • 布局连续性合约套件:packages/app/e2e/performance/timeline-stability/README.md

【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 14:09:49

电脑玩游戏偶尔重启?从电源到内存的系统排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 14:08:16

视频性能优化工具DROP部署指南:从环境配置到批量处理实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 14:07:05

Rhino建筑设计工作流:界面交互、互操作性与流程独立性实战指南

Rhino 建筑设计工作流的核心逻辑&#xff0c;拆开看就是界面交互、互操作性和流程独立性。很多人以为工作流等于某个参数化电池组或者某个一键出图脚本&#xff0c;但真正常被卡住的地方&#xff0c;是今天用Rhino做方案和深化时&#xff0c;人和软件之间怎么交互、文件和其它平…

作者头像 李华
网站建设 2026/9/7 14:06:19

Maya建模实战:科技培养皿布线原理与多边形建模全解析

刚接触 Maya 的同学&#xff0c;绝大多数不是卡在“不会拉模型”&#xff0c;而是卡在“模型一旦平滑就变形”“布线布到一半不敢继续改”“明明照着做&#xff0c;法线就是黑一块白一块”。这些问题其实不是你手不稳&#xff0c;而是没有建立一套适合新手理解的布线逻辑。这篇…

作者头像 李华
网站建设 2026/9/7 14:05:30

多模态学习

什么是多模态 描述一个物品的各种模式&#xff0c;如图片声音/视频等等 迁移学习和zore-shot

作者头像 李华
网站建设 2026/9/7 14:04:33

手写DICOM Viewer:从像素解码到窗宽窗位的完整实践

简介&#xff1a;Dicom_Viewer 是一份基于 C# 开发的 DICOM 医学影像查看器演示项目&#xff0c;适合医疗软件开发者、C# 后端/桌面端工程师&#xff0c;以及刚接触 DICOM 标准的学习者&#xff0c;用于快速掌握医学影像文件的解析、显示、缩放旋转与元数据处理流程。资源包共 …

作者头像 李华