Ripple 基准测试基线报告:16 个操作级对比套件、统计方法与回归护栏解读
2026/9/16 11:27:26 网站建设 项目流程

Ripple 基准测试基线报告:16 个操作级对比套件、统计方法与回归护栏解读

【免费下载链接】ripplethe elegant TypeScript UI framework项目地址: https://gitcode.com/GitHub_Trending/ripple25/ripple

benchmarks/results/baseline-report.md是 Ripple 在性能优化工作开始之前,基于 3 次独立 normal 运行生成的操作级基线报告:它按「套件 / 操作」粒度记录 Ripple 相对于 Octane TSRX、Octane JSX、Solid、Vue Vapor 等参照框架的配对比值,并列出值得后续优化的最大时间差距。读完本文,你能掌握该报告中 score、p95、Max RME、Δ 记号等每列统计含义的来源,以及如何用 report.mjs 自行重新生成、用 compare.mjs 做变更前后离线对比的完整方法。

报告定位:优化前的观测基线,不是"谁更快"的结论

报告开头的元信息明确了它的来源与定位(见 baseline-report.md):

  • 数据来自3 次独立的 normal(非--quick)运行,硬件为 Intel Core i9-9980HK @ 2.40GHz,平台 darwin/x64,Node v24.18.0;
  • 附带两个指纹:Workload SHA-25657f55895502acec9d5c48a9b6defe353a316bd6023feec24a5e1d3d62329d394与 Lockfile SHA-256d5ccf4ee34c36a86ca23851dbf4fb8c938a4919fdac9c61ad7a0faf4d5ed4a7d
  • 原文明确声明:"These are baseline observations before Ripple optimization"(这是 Ripple 优化之前的基线观测),比值低于 1 才表示该操作上有利于 Ripple,且这些数字"不是计时优势的证据"。

三次运行的完整结果分别保存在 baseline-1、baseline-2、baseline-3 三个目录中,每个目录包含各套件的<suite>.jsonsummary.json;机器可读的合并数据则写在 baseline-report.json 中,其directories字段指向上述三个目录。

已验证的环境版本

报告内嵌了一份"Verified environment"清单,即生成基线时锁定的全部框架与工具链版本。这份清单本身也来自元数据中的packageVersions字段(可对照 baseline-report.json 的 metadata):

{ "ripple": "0.3.126", "@ripple-ts/vite-plugin": "0.3.126", "@tsrx/ripple": "0.1.63", "octane": "0.2.4", "solid-js": "2.0.0-rc.3", "@solidjs/web": "2.0.0-rc.3", "vite-plugin-solid": "3.0.0-next.5", "vue": "3.6.0-rc.1", "@vue/runtime-vapor": "3.6.0-rc.1", "react": "19.2.7", "react-dom": "19.2.7", "preact": "10.29.8", "svelte": "5.56.7", "inferno": "9.1.0", "vite": "8.1.5", "babel-plugin-react-compiler": "1.0.0" }

几个值得注意的前提:

  • 浏览器测量使用 Playwright 1.63.0 固定的 headless Chromium 153.0.8010.12(Chrome for Testing),浏览器二进制由 Playwright 依赖钉死;SSR 计时在 Node 中执行,bundle-size 套件额外在 Chromium 中运行构建产物做验证(见 benchmarks/README.md);
  • React 一侧同时固定了babel-plugin-react-compiler1.0.0,即 React 对照是带 React Compiler 的构建;
  • 该基线录制于Octane 0.2.4版本之上。benchmarks/README.md 说明此后 fixture 依赖已更新到 Octane 0.2.6,但原始基线录制与护栏被保留为 0.2.4 的历史参考,未重新录制;因此本报告中的 Octane TSRX/JSX 列对应的是 0.2.4 工作负载。

统计口径:score、p95、Max RME、Samples 各是什么意思

报告表格的列定义为:

含义
Ripple score [run range]3 次运行中每次运行"headline score"的中位数;方括号内是 3 次运行的最小/最大值,即运行间波动
p953 次运行各自 p95 值的中位数
Max RME3 次运行中最大的相对误差边界(per-run 诊断量)
Samples3 次运行的样本数之和
Octane TSRX / Octane JSX / Solid / Vue VaporRipple 分数除以该参照框架分数的配对比值,同一进程内同机测量
Best matching competitor同一操作下所有竞争方(含 preact、svelte、inferno、react、octane-jsx)中分数最低者及其分数

比值列有两条特殊记号规则,由 report.mjs 的 comparison 函数 实现:

  • 低于 1 有利于 Ripple(延迟、字节、计数的方向都是越小越好);
  • 当计时值低于 0.01 ms 或参照分数为零时,比值失去意义,改用绝对差值Δ表示(例如Δ -0.003);
  • N/A表示该操作没有对应的竞争方 fixture(能力缺口保持显式,而不是静默省略)。

headline score 本身的计算在 lib/stats.mjs 中:UI 工作负载属于延迟基准而非紧凑的 ops/sec 循环,因此score取的是"后段稳定窗口"的均值——先在全部样本上滑动一个窗口(默认约为总样本的 40%),找到均值最低的窗口位置附近(允许 8% 的 warm 容差),再对该窗口取均值,以此保留 JIT 预热痕迹的可见性;样本不足 5 个时回退到中位数策略。p95minmean等则基于全部分布样本计算,RME 使用 95% 置信度的 t 分布临界值(stats.mjs 的 T_CRITICAL_95 表):RME = (SEM × t / mean) × 100%。这也解释了为什么短耗时操作的 Max RME 经常很高(如recursive-context / partial_unmount的 236.5%)——分母极小时置信区间天然宽,所以报告强调 RME"只是逐次运行的诊断量"。

如何生成这份报告(以及它的硬性校验)

基线报告的生成命令在 benchmarks/README.md 中给出:

node benchmarks/report.mjs benchmarks/results/baseline-1 benchmarks/results/baseline-2 benchmarks/results/baseline-3

report.mjs 在合并数据前执行严格的前置校验,任何一项不满足直接抛错退出:

  • 至少提供 3 个结果目录;
  • 任一目录的summary.metadata.quick为 true 即拒绝(--quick冒烟结果不能建立 normal 基线);
  • 任一套件status !== 'PASS'即拒绝(失败或缺失的测量无法产出成功基线);
  • 三个目录的cpuplatformarchnodelockfileSha256workloadSha256rippleSourceSha256必须完全一致,否则报Incompatible baseline metadata

合并逻辑本身也很克制(report.mjs):Ripple 与各竞争方的 score 都取 3 次运行的中位数,"Best matching competitor"按中位数分数升序排序取第一个;缺失任一操作或竞争方数据都会抛错,而不是填 0。"Largest timing gaps" 列表的筛选条件是unit === 'ms'score - best > 0.1 ms,按绝对差距降序取前 10 条(report.mjs)。README 还建议用不同的--targets=顺序重复录制,以暴露目标顺序效应。

待调查的最大时间差距(Top 10)

报告专门单列了一节 "Largest timing gaps to investigate later",按"与最佳匹配竞争方的绝对时间差"排序,且明确说明"这只是候选排序,尚未开始性能工作"。完整 10 条如下:

套件 / 操作Ripple最佳参照差距
recursive-context / mount18.612 msinferno 6.075 ms12.537 ms
dbmon / mount23.717 msinferno 14.133 ms9.583 ms
portal-swarm / open_all10.550 msinferno 3.188 ms7.362 ms
js-framework / runlots65.600 mssolid 58.800 ms6.800 ms
portal-swarm / open_close_distinct10.625 msinferno 4.177 ms6.448 ms
effectful-list / remount42.592 msinferno 36.467 ms6.125 ms
portal-swarm / open_close_cycle9.600 msinferno 4.305 ms5.295 ms
js-framework / select_lots4.820 msvue-vapor 0.160 ms4.660 ms
uibench / tree/[2,2,2,2,2,2,2,2,2,2]/render10.556 msinferno 6.285 ms4.272 ms
portal-swarm / mount_closed8.962 msinferno 5.425 ms3.537 ms

从结构上看,差距集中在三类操作:深层递归树的首次挂载(recursive-context)、portal 的批量打开/开合(portal-swarm),以及大批量 keyed 行的插入与批量选择(runlotsselect_lots)。值得注意的是js-framework / select_lots:Ripple 4.820 ms 对照 vue-vapor 0.160 ms,比值高达 30.12×,是单操作比值最大的条目之一,说明大批量选择类交互是明确的可优化点。

代表性套件数据

报告共覆盖 16 个可比套件加 1 个 Ripple 专属诊断(reconcile-anchors),下面选取几组信息密度最高的表格。

js-framework:keyed 行的创建、更新、选择、交换、删除与清空

OperationUnitRipple score [run range]p95Max RMESamplesOctane TSRXOctane JSXSolidVue VaporBest matching competitor
runms6.940 [6.200, 8.160]7.5008.6%240.93×0.81×1.00×0.81×solid: 6.920
replacems13.320 [11.900, 15.800]13.7007.6%240.85×0.81×0.85×0.84×inferno: 14.700
addms6.780 [6.020, 7.160]7.70011.0%240.97×0.63×0.99×0.91×solid: 6.840
updatems0.600 [0.600, 0.800]152.6%240.64×0.22×0.21×0.83×vue-vapor: 0.720
selectms0.640 [0.620, 0.840]1.10019.8%242.00×0.30×0.82×2.91×vue-vapor: 0.220
swapms0.940 [0.920, 1.360]1.30059.0%241.24×0.33×0.96×1.74×vue-vapor: 0.540
removems0.660 [0.620, 0.760]1.20035.8%240.97×0.27×0.97×1.83×vue-vapor: 0.360
runlotsms65.600 [58.740, 77.140]73.50011.2%240.99×0.85×1.12×0.96×solid: 58.800
select_lotsms4.820 [4.300, 4.900]6.10019.5%2417.21×0.24×0.88×30.12×vue-vapor: 0.160
clearms65.720 [62.860, 85.480]74.20015.0%240.89×0.81×0.87×1.00×vue-vapor: 65.400

live_inserts_*fragment_commits_*等 count 类正确性计数在该基线上全部为 0,竞争方同样为 0,故略。)

可以看到:批量场景(runreplacerunlotsclear)Ripple 与 Octane/Solid/Vue Vapor 基本处于同一量级(比值 0.81×–1.12×),而单条select/select_lots的细粒度 DOM 写入明显落后于 vue-vapor,这直接进入了上文的 Top 10 差距榜。

recursive-context:深层递归树的创建、上下文更新与销毁

这是差距榜第一名所在的套件,完整 6 行如下:

OperationUnitRipple score [run range]p95Max RMESamplesOctane TSRXOctane JSXSolidVue VaporBest matching competitor
mountms18.612 [16.738, 22.975]21.1004.6%600.76×0.64×1.00×0.46×inferno: 6.075
update_rootms1.850 [1.813, 2.138]2.80019.3%600.37×0.36×0.83×1.35×inferno: 1.137
update_partialms0.100 [0.063, 0.112]0.200122.6%600.32×0.25×0.50×0.89×inferno: 0.100
partial_unmountms0.063 [0.013, 0.075]0.200236.5%600.33×0.36×0.31×0.45×inferno: 0.088
partial_remountms0.450 [0.412, 0.538]0.80014.0%601.00×0.63×0.88×0.97×inferno: 0.138
unmountms1.150 [0.975, 1.213]1.80058.3%600.67×0.63×0.72×0.39×inferno: 0.250

该套件同时保留了上游 Octane 的重复、顺序平衡方言对照组(见 benchmarks/README.md),max RME在两行短耗时操作上超过 100%,再次说明小数值只能当作方向性观察。

streaming-ssr:shell 投递与完整异步服务端流

OperationUnitRipple score [run range]p95Max RMESamplesOctane TSRXOctane JSXSolidVue VaporBest matching competitor
shell_staggeredms0.258 [0.240, 0.301]0.52417.1%900.78×N/A0.36×N/Ainferno: 0.190
total_staggeredms50.776 [50.554, 51.143]52.3630.9%900.99×N/A1.00×N/Apreact: 50.386
shell_allfastms0.168 [0.146, 0.187]0.27213.0%900.75×N/A0.37×N/Ainferno: 0.140
total_allfastms1.643 [1.550, 1.653]2.23924.9%900.96×N/A1.26×N/Asolid: 1.303
shell_cpu_10ms0.160 [0.115, 0.162]0.22410.7%900.82×N/AN/AN/Aoctane-tsrx: 0.195
total_cpu_10ms0.662 [0.493, 0.678]0.77811.2%901.12×N/AN/AN/Aoctane-tsrx: 0.593
shell_cpu_100ms0.642 [0.532, 0.649]1.19221.6%900.56×N/AN/AN/Aoctane-tsrx: 1.156
total_cpu_100ms4.593 [3.931, 4.622]6.06115.9%901.02×N/AN/AN/Aoctane-tsrx: 4.497
shell_cpu_800ms3.708 [3.697, 3.735]5.1763.7%900.43×N/AN/AN/Aoctane-tsrx: 8.530
total_cpu_800ms35.211 [34.943, 35.561]43.6793.2%900.99×N/AN/AN/Aoctane-tsrx: 35.634
shell_cpu_waves_50ms0.239 [0.239, 0.241]0.4055.0%900.45×N/AN/AN/Aoctane-tsrx: 0.535
total_cpu_waves_50ms1.868 [1.865, 1.944]2.1446.5%900.24×N/AN/AN/Aoctane-tsrx: 7.870

total_cpu_waves_50(50 个 CPU 密集波次的完整流)Ripple 1.868 ms 对 octane-tsrx 7.870 ms(0.24×),是该套件中 Ripple 相对优势最明显的操作;而 consumer-paced 的total_staggered与 preact 几乎打平(0.99×)。README 强调:选择 streaming 对照时"不把缓冲 HTML 包装成合成流",没有可用流式渲染器的框架以 N/A 显式标注。

ssr-throughput 与 bundle-size:吞吐与体积

ssr-throughput在 Node 中持续渲染新闻页:news-50/render单次 0.146 ms(对 octane-tsrx 0.081 ms,1.80×),news-500/render单次 2.511 ms(对 vue-vapor 1.264 ms,1.99×),即该基线上 Node 端渲染吞吐落后于两个参照,是明确的后续调查项。

bundle-size记录三组应用(js-framework 行、TodoMVC、chat)的 JS 字节数(raw / gzip / brotli,分别对应 js_、app_、fw_*),核心数字如下(完整 27 行表格见原报告):

操作Ripple 字节最佳参照备注
js_raw / js_gzip / js_brotli35047 / 13410 / 12006preact 25247 / 9990 / 9056整包(含框架)
app_raw / app_gzip / app_brotli7209 / 2312 / 1991svelte 5149(raw)纯应用代码
fw_raw / fw_gzip / fw_brotli27838 / 11098 / 10015preact 19940 / 7985 / 7273纯框架部分

可以分两层读:纯框架体积(fw_*)Ripple 约为 preact 的 1.4 倍(gzip 后 11098 vs 7985 字节,比值 0.41× 表示 Ripple 更大);但纯应用代码(app_*)与 svelte/solid 相当甚至更小。README 同时提醒,bundle-size 使用"归一化 esbuild 压缩 + 逐文件 gzip/Brotli",与其余套件的 production 构建是不同的构建契约,不可互换比较(见 benchmarks/README.md)。

其他值得留意的数据点

  • memo-wallone_change_A0.273 ms,对 octane-tsrx 0.023 ms(12.12×),是全报告中最大的单操作比值之一;而parent_rerender_equal_A/B(输入未变化的父级重渲染)各框架都在 0.17–0.20 ms 的 Δ 量级上打平;
  • uibenchtree/[10,10,10,10]/no_changeRipple 2.774 ms 对 inferno 6.641 ms(0.26×)、tree/[2,2,…,2]/no_change0.262 ms 对 1.325 ms(0.10×),空重渲染(no-change)上 Ripple 优势明显;但深层树的 render/removeAll 落后于 inferno(如tree/[2×10]/render0.75×);
  • reconcile-anchors是 Ripple 专属诊断(direct / wrapped / single / switch 四组锚点列表),只有 Ripple 自身数值,例如direct.mount10.640 ms、switch.shuffle5.990 ms,无竞争列(全 N/A),用于内部 reconcile 锚点机制的纵向对比;
  • todomvcadd1004.060 ms(对 vue-vapor 4.860 ms,0.60×)、edit102.180 ms(0.20×),DOM 计数nodes_100为 727(react 为 623),comments_100为 102 而 react 为 0。

基线如何接入回归护栏

这份报告不只是一次性快照,它还是整个回归护栏体系的"证据来源"。按 baselines/README.md 的政策:

  1. 初始护栏由三次独立 normal 运行播种:共311 条 guard,覆盖 16 个可比套件,录制工具链为 Node 24.18.0,全部 guard 已对三份录制数据回放通过。护栏明细提交在 baselines/ratios.json,跳过原因记录在 regression-threshold-initialization.json(例如 js-framework 的update/select/swap/remove/runlots/select_lots因"低于 0.1 ms 或波动过大"未设初始计时 guard);
  2. 计时 guard 政策:允许"三次运行中观察到的最大配对比值 + 50% 余量";两侧分数低于 0.1 ms、比值区间超过 1.5× 或 score/full-sample RME 超过 20% 的,不设初始计时 guard。字节类 guard 在观察值上加 32 字节余量;确定性计数直接用观察到的最大配对比值,零参照不参与比值;
  3. 本地绝对值回归检查--compare/ compare.mjs):计时回退需同时满足"分数增长超过 15%"且"最小增长超过 10%",低于 1 ms 的基线额外要求 0.1 ms 的绝对阈值;确定性度量使用严格比较。compare.mjs 还会校验两次运行元数据一致(cpu、platform、arch、node、quick、lockfileSha256、workloadSha256、CPU throttle),失败、缺失或不兼容一律报错;
  4. 护栏只防回退,不代表胜出:README 明确"passing them does not mean Ripple beats a competitor",且录制本地基线永远不会覆盖已提交的 ratios guard;调整 guard 必须逐条附书面理由,不允许因为某次运行慢就放松限制。

变更前后的标准离线对比工作流(不重新跑基准):

pnpm bench --results-dir=benchmarks/results/run-before js-framework # 在此做运行时/编译器变更,保持基准工作负载不变 pnpm bench --results-dir=benchmarks/results/run-after js-framework node benchmarks/compare.mjs benchmarks/results/run-before benchmarks/results/run-after js-framework

阅读与引用时的注意事项

  • 绝对计时是机器相关的:基线数值只对 i9-9980HK / darwin / Node 24.18.0 / Chromium 153 这套指纹成立;跨机器、跨 quick/normal 模式、不同 Octane 版本(0.2.4 与 0.2.6)的数值都不可直接比较,README 明确禁止此类混比;
  • 比值也受竞争方版本影响:"一个更快的竞争方会推高 Ripple 的比值,即使 Ripple 自身没有变慢";
  • 工作负载指纹与 runner 指纹分离:录制/报告工具的改动不会使工作负载指纹失效,metadataCorrections字段保留了这类仅元数据的修正记录(例如将 TodoMVC 的row_class_writes_complete25由 ms 更正标注为 count,原始测量值未改动);
  • 历史 Octane 数字不是本 checkout 的基线:报告与护栏全部基于本仓库锁定依赖的本地配对测量生成,raw timing values were not imported from Octane

相关文件索引

文件作用
benchmarks/results/baseline-report.md本文解析的基线报告(Markdown 版)
benchmarks/results/baseline-report.json报告机器可读版(metadata + 每操作完整行)
benchmarks/results/baseline-1/ 等 3 个目录三次独立 normal 运行的原始结果
benchmarks/report.mjs报告生成器:元数据校验、中位数合并、差距排序
benchmarks/compare.mjs两次保存结果之间的离线回归对比
benchmarks/lib/stats.mjs统计口径:稳定窗口 score、p95、RME
benchmarks/README.md运行命令、选项、套件矩阵与结果解释
benchmarks/baselines/README.md护栏政策、刷新规则与初始录制说明
benchmarks/baselines/ratios.json已提交的 311 条配对比值 guard
benchmarks/suites.json套件、端口、迭代次数矩阵声明

【免费下载链接】ripplethe elegant TypeScript UI framework项目地址: https://gitcode.com/GitHub_Trending/ripple25/ripple

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询