ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Ripple 仓库实战解析:UIbench 桌面工作负载基准套件(96 用例矩阵)的正确性门禁与计时方法

Ripple 仓库实战解析:UIbench 桌面工作负载基准套件(96 用例矩阵)的正确性门禁与计时方法 Ripple 仓库实战解析:UIbench 桌面工作负载基准套件(96 用例矩阵)的正确性门禁与计时方法【免费下载链接】ripplethe elegant TypeScript UI framework项目地址: https://gitcode.com/GitHub_Trending/ripple25/ripple本文基于仓库内 benchmarks/uibench/README.md 展开,介绍 Ripple 团队如何将公开项目 UIbench 的桌面工作负载矩阵移植进统一基准框架:96 个用例的完整构成(表格/动画/树形/重排历史最坏情况)、先过正确性门禁再计时的三道校验闸门、每类用例的采样与重复次数设计,以及 7 个框架目标各自的提交实现差异。读完本文,你可以独立复现整套基准运行流程,并理解其计时数据为什么可与其他框架横向比较。一、套件定位:一个同步 state-to-state 提交测量矩阵该套件位于 benchmarks/uibench/,是 Ripple 仓库统一基准框架(入口 benchmarks/bench.mjs,套件清单 benchmarks/suites.json)中的一个browser类子套件。它针对 96 个公开的桌面用例,各测量一次同步的 state-to-state 提交,对比对象包括:React Compiler(React 19 flushSync 仓库的生产级 React Compiler 集成);原生 Preact(原生 hooks keyed JSX,其排队到 microtask 的提交在计时窗口内被 await);Solid 2(锁定的 Solid 2 beta 生产渲染器,keyedcreateStore/reconcile加flush());Vue Vapor(Vue Vapor 3.6 生产渲染器,keyedv-for,计时窗口内 awaitnextTick());Ripple(生产.tsrx编译器、tracked snapshot、keyedfor块与flushSync);Inferno(原生 class state 公开rerender()flush)。操作名称与维度刻意保持与上游公开矩阵一致,便于熟悉 UIbench 的读者直接对照结果;数据是确定性的,且所有 key 在每次状态迁移中保持稳定。README 同时说明:该套件的定位是对 keyed reconciler 与 prev-guarded 更新路径的守卫,而不是又一张宽泛的框架排行榜——仓库中已存在 js-framework、effectful-list 与 recursive-context 等更宽范围的对照套件。二、96 个用例的完整构成用例由 benchmarks/uibench/shared/workloads.js 中的buildCases()函数集中生成,共 6 组:2.1 表格组:4 形状 × 11 种操作TABLE_SHAPES定义 4 种行列形状([100,4]、[50,4]、[100,2]、[50,2]),每种形状生成 11 个用例:table/{shape}/render(空表 → 全量)与table/{shape}/removeAll(全量 → 空表);table/{shape}/sort/0、table/{shape}/sort/1:按第 0 列、第 1 列文本localeCompare排序;源码注释说明每列使用不同的稳定置换((row * (37 column * 16) column * 29) % 101),使两种排序走不同的 keyed 顺序,且 fixture 内不引入随机性;table/{shape}/filter/{32,16,8,4}与table/{shape}/activate/{32,16,8,4}:稀疏过滤(剔除第 n 个)与稀疏 class 更新(激活第 n 个)。单元格文本形如a0x、b0y(列字母 36 进制两位值),行以r{row}为稳定 key。2.2 动画组:100 个盒子的稀疏 transform 更新anim/100/{32,16,8,4}:100 个a{i}盒子初始translateX(0px),after 端点仅把第 n 个盒子的 transform 推进到translateX(1px),测的是稀疏样式更新路径。2.3 树形组:5 种形状 × render / removeAllTREE_SHAPES为[500](500 个平铺叶节点)、[50,10]、[10,50]、[5,100](扁平/深树谱系)与[2,2,2,2,2,2,2,2,2,2](十层二叉)。节点由makeForest()按形状递归生成,key 为路径编码的n-...序列,保证跨状态迁移稳定。2.4 树变异组:4 形状 × 7 种迁移对前 4 种树形状,MUTATION_TREE_SHAPES逐一生成 7 个用例:[reverse]、[insertFirst(1)]、[insertLast(1)]、[removeFirst(1)]、[removeLast(1)]、[moveFromEndToStart(1)]、[moveFromStartToEnd(1)],覆盖反向、插入、移除与端到端/首到末移动四类 keyed 重排路径。2.5 历史最坏情况组:4 个经典 reorder 反例在 500 节点平铺树上构造:tree/[500]/[kivi_worst_case]:首尾剔除后整段反向;tree/[500]/[snabbdom_worst_case]:由snabbdomWorstCase()实现——移走首节点、再抽出倒数第二节点放到末尾;tree/[500]/[react_worst_case]、tree/[500]/[virtual_dom_worst_case]:分别为端点移动与头部双节点后移。2.6 无变更组:相同内容的两次提交tree/[10,10,10,10]/no_change与tree/[2,2,2,2,2,2,2,2,2,2]/no_change:before 与 after 内容完全相同(after 用cloneForest()深拷贝),专测 reconciler 在什么都不该变时的空操作成本。每个用例是一个{ name, before, after }三元组,before/after均为{kind: table|anim|tree, ...}的快照对象,由 shared/workloads.js 的caseByName()供各框架 fixture 取用。三、来源与许可边界:为什么没有直接搬运上游代码README 明确记载了工作负载的出处:上游localvoid/uibench与localvoid/uibench-base两个仓库的两个不可变提交(3acab5c...与efacae6...,仓库内以固定哈希钉住)。关键事实是:这两个上游仓库都没有可用的许可文本——uibench-base/package.json仅声明了含糊的BSD,聚合仓库干脆没有 license 字段。因此:本目录不 vendor上游任何源码、bundle 或资源;上游公开的操作矩阵 工作负载尺寸只被当作行为规格;数据模型、渲染器、harness 与样式全部是本仓库的原生实现,遵循本仓库自身的许可。这一处理在 shared/workloads.js 头部注释与 benchmarks/uibench/run.mjs 头部注释中都有对应声明,是引用他人基准时值得参考的合规做法:公开事实(命名与维度)可以参照,代码必须重写。四、正确性边界:计时必须通过三道闸门run.mjs的核心设计是:任何计时开始之前,每个用例必须先通过两道完整矩阵遍历(2 轮 × 96 用例)的三道闸门,且所有before/after端点会在其他用例跑完后被再次复测(第二轮 cycle)。闸门逻辑见 run.mjs 的 gateTarget():语义签名比对:浏览器端把活 DOM 序列化为 table/anim/tree 三类语义签名(表格是行 key|class|单元格文本,动画是盒子 key|transform,树是深度|key|leaf/container|label的逐行前序),与workloads.js中独立实现的确定性模型modelSignature()比对,不一致即报semantic mismatch并给出首个不一致的行号;keyed 身份保持:对每次迁移,所有存活的 keyed 行/盒子/树节点必须在 before 与 after 之间是同一个 DOM 节点——harness 用Map记录 before 端各 key 对应的元素,逐一对比 after 端,任何一个identityBroken 0都直接判负(replaced X/Y keyed survivors);跨目标一致性:所有目标在全部 96 个端点上报的签名与元素数量必须完全相同;框架自己的 marker 注释被刻意排除在 oracle 之外,不能靠注释蒙混过关。闸门或浏览器错误(通过 Playwright 的pageerror/console error事件收集)一旦发生,harness 写入带failed字段的 JSON 并以非零码退出(main()),保证错误样本不会进入结果。此外,cases(96)、elements_largest(最大端点元素数)与identity_shared(首轮身份共享总数)作为确定性操作一并写进结果 JSON,供同一次运行的比率守卫(same-run ratio guard)使用:它们在所有目标上必须逐位相同,一旦漂移即说明矩阵本身或模型出现变化,而非框架差异。五、计时方法:只测正向提交,内层重复压过定时器粒度每个样本的完整流程(timeCase())是:window.__prepare(name)同步提交该用例的before快照(这是每个样本与每次重复之间的重置,位于计时器之外);setTimeout(0)让 setup 的渲染与布局沉降;请求暴露的浏览器 GC(Chromium 以--js-flags--expose-gc启动);对repetitionsPerSample次循环,只对window.__run(name)的after快照同步提交计时(performance.now()差值累加),取每次 forward commit 的平均毫秒。对低于 0.1 ms 的小用例,这个样本内重复正向迁移、重置提交全部在外层的技巧用用例尺寸的重复次数压过了 Chromium 定时器粒度,同时避免把反向(重置)迁移混入结果。这套口径对应 UIbench 默认的 JavaScript-time 模式——不请求 style/layout/paint 计时,因此结果只反映提交本身。各用例的重复次数由 repetitionsFor() 按名称规则给出:用例类别重复次数anim/*64tree/[10,10,10,10]/no_change1其余no_change4十层[2,2,...]树2表格render/removeAll8其他render/removeAll4名称含worst_case16其余32预热轮数同理随规模调节:快速模式 1 轮,迭代数 ≥ 5 时 2 轮,否则 1 轮。跨源隔离保证亚毫秒精度七个预览页(各目标的vite.config.js,如 ripple/vite.config.js)统一注入Cross-Origin-Embedder-Policy: require-corp与Cross-Origin-Opener-Policy: same-origin响应头;harness 在开跑前检查window.crossOriginIsolated,非隔离页面直接报错拒绝。这样亚毫秒用例能保留浏览器高分辨率定时器,而不是塌缩到 0.1 ms 桶。确定性随机源为了让所有目标面对完全一致的 fixture 行为,harness 在浏览器内把Math.random替换为一个线性同余/IMUL 混合的确定性 PRNG(seedRandom()),消除环境噪声。六、各框架目标如何接入:一个共享桥接层所有框架 fixture 通过 shared/bridge.js 这个极小的模块接入:组件内部调用bindSetter(setter),harness 侧的commit(snapshot)触发该 setter 完成一次状态迁移。每个目标还暴露统一的window.__mount / __reset / __prepare / __run / __ready钩子,例如 ripple/src/main.js 中__prepare/__run都用flushSync(() commit(...))包裹,保证提交是同步且可计时的。几个代表性实现:React(react/src/App.jsx):纯useState keyedmap,配合生产 React Compiler 与flushSync;Vue Vapor(vue-vapor/src/App.vue):shallowRef存快照,模板按snapshot.kind三选一渲染,v-for全部带:key,树形拆出独立的TreeItem.vue递归组件;Solid(solid/src/App.jsx):keyedcreateStorereconcileflush(),README 特别提到两个端点的私有副本在计时器外准备,防止 reconcile 改动共享工作负载快照;Ripple(ripple/src/App.tsrx):.tsrx编译器语法,TableView/AnimView/TreeView用for ... key做 keyed 列表;值得细看的是其adapt()适配器——用rowCache/boxCache两个Map按行/盒子 id 缓存track()出的信号,同 id 的后续快照直接更新signal.value,跨状态复用同一组信号对象,使表格行激活与盒子 transform 更新走细粒度路径而不是整列表重渲染;Inferno:原生 class state 加公开rerender()flush。七个目标页面全部渲染进#main,由 harness 统一驱动,保证比较基准完全相同。七、运行方式仓库提供两种入口(完整命令继承自 README):7.1 统一 runner(推荐)node benchmarks/bench.mjs --quick uibench node benchmarks/bench.mjs uibenchbench.mjs 会按 suites.json 中uibench套件的配置(normal 10 次迭代 / quick 2 次迭代)自动执行:检查目标端口未被占用(拒绝误杀无关进程)、pnpm --filter {target} build构建各目标、启动pnpm --filter {target} preview、等待就绪后注入TARGETS/BENCH_JSON环境变量运行run.mjs,把分片结果合并写入benchmarks/results/(现有示例见 benchmarks/results/baseline-1/uibench.json,其中每个操作包含rawSamples、score(取后 5 个样本的均值)、median、p95、sd、rme、scoreRme、warmupRatio等统计字段)。runner 还支持--list、--record、--compare、--ratios标志与--targets/--timeout等选项,可用于只跑部分目标或做基线比对。7.2 独立运行先构建并启动 7 个生产预览:pnpm --filter octane-tsrx-uibench-bench build pnpm --filter react-uibench-bench build pnpm --filter solid-uibench-bench build pnpm --filter preact-uibench-bench build pnpm --filter vue-vapor-uibench-bench build pnpm --filter ripple-uibench-bench build pnpm --filter inferno-uibench-bench build pnpm --filter octane-tsrx-uibench-bench preview # :5315 pnpm --filter react-uibench-bench preview # :5316 pnpm --filter solid-uibench-bench preview # :5317 pnpm --filter preact-uibench-bench preview # :5318 pnpm --filter vue-vapor-uibench-bench preview # :5319 pnpm --filter ripple-uibench-bench preview # :5322 pnpm --filter inferno-uibench-bench preview # :5325 node benchmarks/uibench/run.mjs 10node run.mjs [iterations]的迭代数必须为正整数(默认 10);也可用环境变量TARGETS(目标列表 JSON,如[{name:ripple,url:http://localhost:5322/}])只测部分目标,用BENCH_JSON指定输出路径,用BENCH_QUICK1切换快速预热。终端输出是一张console.table:每个用例一行、每个目标一列(单位 ms,取 score 保留 3 位小数),多目标时自动附基准/对照比率列(保留 2 位)。八、小结:从该套件可以学到的工程实践正确性先于计时:三道闸门(语义签名、keyed 身份、跨目标一致)加两轮遍历,使跑得慢但结果错的数据根本无法产生;口径声明即合约:只测 JavaScript 同步提交、不测样式/布局/绘制,README 与 README 中每个目标的提交机制(如 Preact 的 microtask 提交在窗口内被 await)都写明,横向对比才有意义;确定性设计:确定性数据模型、稳定 key、注入的确定性 PRNG、确定性守卫操作(cases/elements_largest/identity_shared),让同一轮运行内部的漂移可以被精确检测;许可洁癖:上游无可用许可文本时,只把公开矩阵当规格、全部代码重写,并用固定提交哈希记录出处。这套做法使 benchmarks/uibench/ 同时充当 Ripple keyed reconciler 与 prev-guarded 更新路径的回归守卫,以及一份可复现、可审计的桌面工作负载基准。【免费下载链接】ripplethe elegant TypeScript UI framework项目地址: https://gitcode.com/GitHub_Trending/ripple25/ripple创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表