加载指示器渲染性能¶
用大白话解释¶
想象一个轮子,它每完成一圈就告诉前台一次。前台也在处理你的输入。CSS 已经为我们转动轮子,但 React 仍会收到它每秒一次的完成圈通知。
我们将一天份的圈数放入一个动画中。轮子以相同速度移动,但每天只发送一次完成圈通知。绘制可见的轮子仍然有成本。这从输入线程中移除了反复的簿记工作;它并不会让渲染或 Orca 的其他部分免费。
此变更如何基于早期变更¶
| 变更 | 达成效果 | 剩余成本 |
|---|---|---|
| #9380:共享 JavaScript 时钟 | 在原始单代理测量中降低了帧流水线 CPU | 在输入线程上每秒写入每个加载指示器的样式 12 次 |
| #12359:合成器 CSS 旋转 | 移除了这些反复的 JavaScript 样式写入;修复了报告的输入性能回退 | React 仍会收到 CSS 迭代事件 |
| #13987:在 animationstart 上同步 | 避免了每次挂载时的同步样式查询 | 稳态动画开销保持不变 |
| 本次变更 | 保留了后续两项修复,并移除了几乎所有迭代边界 | 合成、其他应用工作、挂载/显示工作以及每天一次的迭代边界仍然存在 |
12359 中的历史测量报告称,41 个圆环每秒导致约 490 次样式写入,输入延迟 p90 为 363 ms,而在这些写入停止后为 19 ms。这些是历史生产环境测量值,不是来自本次基准测试的数字,也不是与当前应用的直接对比。¶
实现¶
生产环境变更完全位于 CSS 中。AgentWorkingSpinner、其调用方、标记、边框、animation-start 处理程序以及减少动态效果行为均保持不变。没有新增任何 DOM 节点、伪元素、包含边界、计时器、观察器或 JavaScript 动画循环。
该 transform 在 86,400 秒内行进 86,400 圈,共 1,036,800 步:恰好每秒一圈和 12 步。animationstart 仍像之前一样设置 startTime = 0,在挂载和动画重启后保留共享相位。步数是计时函数参数,而不是一个包含百万个条目的关键帧列表。
即使组件没有迭代处理程序,React 也会安装委托的 animationiteration 监听器。对 200 个隔离圆环进行 2.2 秒的原生跟踪显示,变更前有 400 次迭代事件和 800 次 JavaScript 调用;使用长周期后两者均为零。该跟踪没有安装任何动画事件监听器。这些是事件分发,而不是组件重新渲染或 400 次独立的 OS 唤醒。
全应用基准测试¶
可选参与的 Playwright 基准测试会为每个场景启动一个全新的隐藏 Orca 应用。它创建真实的 Git 工作区,并通过现有渲染器 fixture 注入工作状态,包括进程内子代理数据。它渲染正常的侧边栏、虚拟化器、谱系、代理行、标签页和终端。
| 场景 | Git 工作区 | 根代理 | 子代理 | 已挂载 / 可见圆环 | 布局 |
|---|---|---|---|---|---|
one-agent |
1 | 1 | 0 | 3 / 3 | 一个正在工作的代理 |
one-family |
1 | 2 | 4 | 8 / 8 | 所有家族行已展开 |
200-flat |
200 | 400 | 800 | 162 / 15 | 正常虚拟化;已挂载 23 个工作区 |
200-lineage |
200 | 400 | 800 | 1,401 / 15 | 已展开谱系;已挂载全部 200 个工作区 |
仅用于测量的样式会在相同元素上切换原始一秒周期和新的长周期。真实的 React 根节点、调用方、状态数据和应用保持不变。报告的运行交替执行 A/B 和 B/A,预热后每个变体采集四个 10 秒 CPU 样本。CPU 样本使用累积的 Electron 进程 CPU 以及 CDP 主线程任务/脚本/样式/布局指标。在这些 CPU 窗口期间,不会运行渲染器轮询、截图或基准测试迭代监听器。没有样本被丢弃。
输入使用现有的匀速终端输入探针单独测量:64 个按键,间隔 113 ms,每个变体两次,在带有状态流量的 2 秒预热之后。状态更新每 200 ms 以最多 8 个一组到达。按键经过 DOM、真实 PTY 和 xterm。一个 sidecar 为到达 PTY 的时间打时间戳,并通过有界的终端缓冲区扫描观察每个回显。输入或回显缺失会使基准测试失败。回显测量包含 10 ms 扫描间隔;它们不测量原生显示呈现。原生动画跟踪也独立于 CPU 和输入样本运行。
这些状态是确定性测试数据,而不是数百个付费模型会话。该测试验证代理状态流量下的 UI 成本,而不是模型推理、SSH 流量或数百个流式 PTY 的计算或网络成本。
结果¶
CPU 值是四个样本的中位数。“CPU ms/s” 表示一个实际秒内使用的处理器时间毫秒数:100 ms/s 约等于一个 CPU 核心的 10%。渲染器 + GPU 进程 CPU 包含它们的其他应用工作以及图形进程使用的 CPU;它不是 GPU 硬件利用率或整机 CPU。主线程处理输入,并包含在渲染器 CPU 中,而不是额外工作。回显 p90 表示 90% 的采样按键在该时间内被观察到;范围显示两次运行,而不是置信区间。没有按键或回显缺失。
| 场景 | 渲染器 + GPU CPU ms/s,旧 → 新 | 主线程 ms/s,旧 → 新 | Echo p90 ms,旧 → 新 |
|---|---|---|---|
one-agent |
37.0 → 38.2 | 5.4 → 3.5 | 19 → 18–19 |
one-family |
46.2 → 44.6 | 7.8 → 4.4 | 17–19 → 18–19 |
200-flat |
141.6 → 122.8 | 28.2 → 16.3 | 26–28 → 26–28 |
200-lineage |
324.6 → 295.6 | 140.8 → 70.0 | 159–239 → 93–160 |
一致收益是主线程工作量减少:在这四个场景中分别减少约 35%、43%、42% 和 50%。原生 2.2 秒的跟踪在之前记录了 6、16、324 和 2,802 个迭代事件,而在每个新变体中均为零,且未添加迭代监听器。这些被避免的工作也存在于 Orca 本身,独立于隔离测试夹具和 CPU 噪声。
在单工作树场景中,总 CPU 大致保持不变。在本次运行中,普通虚拟化下下降了 13%,扩展谱系下下降了 9%;八组配对的大规模场景 CPU 样本中有七组支持该变更。这些百分比并不具有普遍性:一个较短的三变体消融实验测得平铺列表 CPU 在变更前为 89.0 ms/s,长周期下为 108.0 ms/s,而主线程时间仍从 26.3 降至 17.4 ms/s。可复现的主线程减少比单一的总 CPU 百分比是更强的证据。
在小型和平铺列表场景中,打字表现相似。扩展谱系的 Echo p90 在最终运行中有所改善,但一个较短的消融实验显示变更前后的延迟相似。这些短实验并未确立通用的打字提速或统计意义上的无回归保证。
所有 CPU 样本¶
数值四舍五入到一位小数,并按轮次列出,未移除任何异常值。首批新的小型场景样本高于其配对基线;它们仍被保留。CPU 和打字分别采样。
| 场景 | 版本 | 渲染器 + GPU CPU ms/s | 主线程 ms/s |
|---|---|---|---|
one-agent |
旧 | 37.8, 36.2, 26.2, 39.6 | 6.5, 5.2, 4.8, 5.7 |
one-agent |
新 | 53.3, 35.8, 37.5, 39.0 | 6.7, 2.8, 3.0, 4.1 |
one-family |
旧 | 46.3, 46.1, 47.9, 44.6 | 7.8, 7.6, 9.8, 7.7 |
one-family |
新 | 53.8, 45.4, 42.5, 43.8 | 6.8, 4.5, 3.1, 4.3 |
200-flat |
旧 | 142.4, 140.8, 147.0, 136.5 | 28.3, 28.1, 32.2, 27.0 |
200-flat |
新 | 122.9, 97.2, 122.7, 126.7 | 19.2, 10.7, 16.6, 16.0 |
200-lineage |
旧 | 317.9, 385.2, 315.9, 331.2 | 134.4, 159.6, 133.9, 147.3 |
200-lineage |
新 | 318.6, 256.9, 296.5, 294.7 | 89.2, 60.8, 71.6, 68.3 |
复现¶
ORCA_BACKGROUND_LAUNCH=1 pnpm bench:spinners --sample-ms=5000
ORCA_BACKGROUND_LAUNCH=1 pnpm bench:spinners --verify-only --scale-factor=1
ORCA_BACKGROUND_LAUNCH=1 pnpm bench:spinners --verify-only --scale-factor=2
ORCA_BACKGROUND_LAUNCH=1 ORCA_SPINNER_BENCH=1 ORCA_SPINNER_KEYS=64 \
pnpm test:e2e spinner-workspace-perf.spec.ts --workers=1
完整应用命令会以 e2e 模式重新构建。对于已经通过 pnpm exec electron-vite build --mode e2e 生成的全新构建,SKIP_BUILD=1 会复用它。不要复用旧的启动策略构建。ORCA_SPINNER_SAMPLE_MS、ORCA_SPINNER_ROUNDS、ORCA_SPINNER_KEYS、ORCA_SPINNER_KEY_CADENCE_MS、ORCA_SPINNER_VARIANTS 和 ORCA_SPINNER_OUTPUT 控制实验。ORCA_SPINNER_CPU=0 仅重复打字;--grep one-agent 选择一个场景。报告、原生跟踪、打字 sidecar 文件和 CDP 截图会写入 .bench-fixtures/。一次只运行一个基准测试,不要并发构建或测试。
可选的 contained 变体保留了被拒绝的离屏实验,用于消融。它通过仅用于测量的样式向现有包装器添加 content-visibility:auto。它未在生产环境或默认基准比较中启用。
视觉与行为检查¶
1x 和 2x 显示密度检查各自通过了 720 次环形比较:6/8 px 环形、浅色/深色主题、支持的缩放极值、全部 12 个阶段、长时间经过时间以及每日回绕。比较会暂停每个动画并设置其 currentTime,因此长时间经过和每日回绕用例验证的是确定性样式路径,而不是正在运行的合成器动画。针对该路径,容差为一个通道级别,用于浮点抗锯齿舍入。运行数小时的动画可能在环形边缘相差几个通道——这是大累积角度下亚像素的抗锯齿差异,而不是阶段或形状变化。检查还覆盖共享阶段、减少动态效果、初始离屏显示、重复滚动离开/显示以及 display:none 恢复。
限制与被拒绝的方法¶
向现有静止包装器添加 content-visibility:auto 在大量挂载数量下节省了更多 CPU,但 1x 显示检查在最小 UI 缩放下发现了一像素偏移。该包含变更被排除。之前的伪元素版本也在虚拟化列表中使打字延迟回归。两个原型的 CPU 或打字数字都不能描述最终补丁。
初始打字运行使用 100 ms 按键节奏,这可能与 200 ms 状态突发反复对齐。后续运行使用 113 ms、更多按键,以及在状态流量下两秒预热。这减少了时序偏差;它不能为回归开脱。CPU 测量单独运行,且不依赖按键节奏。
早期隔离测试暗示进程 CPU 减少 31%,但更长的审计未能复现。更长的隔离审计测得原始 104.04 对比长周期 92.32 CPU ms/s,主线程 10.08 对比 0.24 ms/s。一个所有环形都远离屏幕且启用包含的测试夹具也可能接近空闲;这并不能代表带有可见动画的 Orca。两个结果都不足以声称“免费 spinner”或通用 CPU 百分比。虚拟化、未挂载的行已经零成本,且此补丁未添加离屏剔除。
所有本地测量均使用 Apple M4(10 核)、macOS、Electron 43.4.1 / Chromium 150.0.7871.224。原生窗口保持隐藏且未聚焦;仅限基准测试的设置会禁用后台节流,以驱动帧管线。这些不是可见窗口的功耗测量。未证明存在电池收益。Linux/Windows 需要各自的运行时测量。仅渲染器变更不会改变 SSH 执行、线上数据、状态语义、Git 操作或文件夹工作区所有权。
还探索了动画 PNG、遮罩、图层提升、CSS 精灵、单独的 rotate 以及旋转元素上的 containment。共享图像增加了光栅化工作,并使单环用例出现回退;精灵重新引入了逐帧样式工作。它们未满足外观和响应性要求。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca