如何为 GitButler but CLI 性能测试框架编写新的 setup.sh + test.sh 性能场景

发布时间:2026/9/14 1:46:14
如何为 GitButler but CLI 性能测试框架编写新的 setup.sh + test.sh 性能场景 如何为 GitButler but CLI 性能测试框架编写新的 setup.sh test.sh 性能场景【免费下载链接】gitbutlerThe GitButler version control client, backed by Git, powered by Tauri/Rust/Svelte项目地址: https://gitcode.com/GitHub_Trending/gi/gitbutler如果你需要测量某个but子进程操作在 GitButler 规模历史下的耗时就要在crates/but/tests/performance下新增一个性能场景。这个框架的分工是Hyperfine 负责计时与统计shell 脚本负责确定性的 fixture 搭建。你的交付物是一个场景目录其中恰好包含两个可执行的 POSIX shell 脚本setup.sh负责恢复完整的操作前状态不计入计时test.sh负责执行一次被测操作完整计时含进程启动和输出生成。本文覆盖从建目录骨架、编写两个脚本到语法检查、smoke 验证和收尾登记的完整流程。所有命令默认在仓库根目录执行。框架文档入口是 crates/but/tests/performance/README.md共享助手函数定义在 lib.sh运行器逻辑在 run.sh。准备条件框架运行需要POSIX shell、Git、Hyperfine计时器Rust/Cargo仅在本地构建but时需要提供BUT_BIN已有二进制的绝对路径或PERF_CHANNEL下载 nightly/release 版则不需要curl和jq仅下载发布版或上传结果时需要。开发期间建议每次只跑单个场景而不是全套件用PERF_WARMUP0 PERF_RUNS1做快速 smoke文档明确说明这种单轮运行“不是有意义的性能测量”只用于验证行为。先理解计时边界再动笔写脚本前必须确定哪些内容在计时范围内这直接决定 setup.sh 和 test.sh 的分工对每次预热和每个采样setup.sh通过 Hyperfine 的--prepare以非计时方式执行test.sh被完整计时包括进程启动和输出生成。下载、编译、fixture 创建和选择器发现都必须留在计时之外。每个采样都会获得全新的工作区、bare 远端和隔离配置只共享来自固定 fixture 的不可变历史对象fixture commit 固定为cf9f4aad6fe7511d5aeb9fd7c83fc62e18a9e1b6定义在 lib.sh。场景脚本可以修改$PERF_RUN_ROOT下的任何东西但不得写入$PERF_FIXTURE_REPO或$PERF_SOURCE_REPO。fixture 使用git clone --shared不要修改对象 alternates 或运行对象清理维护要做对象存储变更类基准需要另外的 fixture 策略。这些是“warm-cache、fresh-process”基准不是冷盘测量比较要在同一台空闲机器、相同电源和散热条件下进行。另外两条硬性规则不要在test.sh里运行but status、but diff这类发现型命令加载小的状态文件在计时脚本内是允许的但发现型命令不允许不要在test.sh里添加场景本地的输出重定向——perf_exec_but默认把输出发到/dev/nullPERF_SHOW_OUTPUT1时保留输出框架自己管这件事。创建场景目录与两个脚本骨架新建目录结构固定为两个脚本crates/but/tests/performance/scenarios/scenario-name/ ├── setup.sh # restore complete pre-operation state; not timed └── test.sh # execute one measured operationscenario-name换成你的场景名例如status-my-new-workload。运行器会校验场景名不能包含/不能以.或-开头且两个脚本都必须有可执行位否则直接报scenario setup is not executable之类的错误退出。两个脚本都以同一段前言开始#!/bin/sh set -eu : ${PERF_ROOT:?PERF_ROOT is not set} # shellcheck disableSC1091 . $PERF_ROOT/lib.shPERF_ROOT由运行器注入指向crates/but/tests/performancelib.sh提供本文后续用到的所有助手函数。编写 setup.sh搭好完整的工作区状态setup.sh的职责是把每个采样重置到“被测操作开始之前”的完整状态。最小骨架是perf_reset_run_root perf_create_gitbutler_workspace $PERF_REPOperf_reset_run_root清空并重建$PERF_RUN_ROOT初始化隔离的 HOME、应用数据目录和确定性设置perf_create_gitbutler_workspace $PERF_REPO从 fixture 克隆出 bare 远端和工作区并执行but setup。如果你的场景要重放某段真实历史变更可以传第二个参数把工作区目标指到某个祖先提交保留 fixture 的全部历史perf_create_gitbutler_workspace $PERF_REPO $target_commit选提交的方法是选一个从固定 fixture 可达的真实提交用它自己的父提交作为目标。现有场景 diff-many-uncommitted-changes 的 setup.sh 给出的模式REAL_CHANGE_COMMIT需替换为完整 40 字符小写 OID且必须存在于 fixture 中REAL_CHANGE_COMMITc9d8e3a7ff59f2ddabed16a6fa1d66ea054f0215 REAL_CHANGE_PARENT$( $GIT_BIN --git-dir$PERF_FIXTURE_REPO rev-parse $REAL_CHANGE_COMMIT^ ) perf_reset_run_root perf_create_gitbutler_workspace $PERF_REPO $REAL_CHANGE_PARENT perf_git branch performance-diff $REAL_CHANGE_COMMIT perf_but apply performance-diff /dev/null applied_commit$(perf_git rev-parse refs/heads/performance-diff) [ $applied_commit $REAL_CHANGE_COMMIT ] || perf_die applying real change rewrote commit unexpectedly: $applied_commit perf_but uncommit $applied_commit /dev/null搭建工作负载时文档给出两条实践原则优先使用真实的 GitButler 提交和有代表性的仓库状态而不是极小的合成数据对 setup 的假设做校验预期路径数、hunk 数、图形状并且这些校验全部放在 setup.sh 这种非计时脚本里只有当 Git 表示确实可能合法变化时才放宽检查。可用的非计时助手包括perf_git args在$PERF_REPO下跑 git和perf_but args在$PERF_REPO下跑 but以及perf_die输出错误并退出。把 setup 的发现结果传给 test.shHyperfine 把 prepare 脚本和计时脚本作为两个独立进程启动导出的 shell 变量带不过去。框架提供的状态机制是setup 端把标量状态原子写入$PERF_RUN_ROOT/scenario.envtest 端加载。# setup.sh 中 perf_state_begin perf_state_set TARGET_COMMIT $target_commit perf_state_set SOURCE_ID $source_id perf_state_commit规则状态名必须匹配[A-Z_][A-Z0-9_]*值由perf_state_set做 POSIX 引号处理多行或二进制数据不要塞进状态写到$PERF_RUN_ROOT下的文件里把文件路径通过状态传过去。编写 test.sh只执行一个被测操作test.sh的内容限定为三件事加载准备好的状态、校验必需值、执行被测操作。perf_use_run_environment perf_state_load : ${TARGET_COMMIT:?missing TARGET_COMMIT} : ${SOURCE_ID:?missing SOURCE_ID} perf_exec_but squash $SOURCE_ID --target $TARGET_COMMIT --use-target-messageperf_use_run_environment把仓库路径、HOME、确定性 Git 配置关闭签名、固定提交者身份、gitbutler.testing.changeId42等注入当前进程perf_state_load从scenario.env读回 setup 写入的状态: ${NAME:?missing NAME}是缺失即失败的校验写法。最后通过perf_exec_but执行被测操作——它用exec把脚本进程替换成配置好的but二进制启用 profiling 捕获时替换为 profiling 入口的 recorder因此计时就是but进程本身。一个更完整的真实例子是 squash-10-committed-hunks 场景setup.sh 在.gitbutler-performance/ten-hunks.txt上构造 10 个已提交 hunk用perf_but diff的非计时输出一行行解析出 hunk ID 并存入HUNK_1到HUNK_10十个状态同时断言“恰好 10 个 hunk”对应的 test.sh 则逐项: ${HUNK_N:?missing HUNK_N}校验后调用perf_exec_but squash $HUNK_1 ... $HUNK_10 --target $TARGET_COMMIT --use-target-message。注意它把 ID 发现放在 setup、把 10 个 ID 展开成 10 行校验放在 test——这个结构可以照搬。验证新场景按 README 的 “Debugging and validation” 一节执行。先做 shell 语法检查与 ShellCheckfor script in crates/but/tests/performance/*.sh \ crates/but/tests/performance/scenarios/*/*.sh; do sh -n $script || exit done shellcheck crates/but/tests/performance/*.sh \ crates/but/tests/performance/scenarios/*/*.sh然后跑单场景短基准PERF_WARMUP0 PERF_RUNS1 \ ./crates/but/tests/performance/run.sh scenario-namescenario-name替换为你的场景目录名。默认路径下运行器会先 smoke-test除非PERF_SKIP_SMOKE1再进入 Hyperfine 计时。判断失败位置的方法如果输出停在Benchmarking scenario...之前说明是 smoke 或 setup 阶段的失败。此时把临时诊断如perf_but status 2、查看$PERF_RUN_ROOT/scenario.env加到setup.sh里不要加进计时的test.sh修好后删除诊断。需要观察but输出时用PERF_SHOW_OUTPUT1PERF_CHANNELnightly PERF_WARMUP0 PERF_RUNS1 \ ./crates/but/tests/performance/run.sh scenario-name注意文档的提示终端渲染会影响计时开输出后的运行结果不能与输出抑制的运行结果互相比对这个开关只用于调试。如果环境里没有 ShellCheck按 技能文档 的护栏要求明确报告“无法执行该验证”而不是跳过不提。收尾与边界场景通过验证后还有两件收尾事项确认两个脚本的可执行位已设置在 README 的 Included scenarios 列表中登记场景名和一句话工作负载摘要——文档要求“Add scenario and workload summary to Included scenarios”后续对场景的实质性修改也要同步更新该条目。最后记住几个容易踩的边界这些计时是 warm-cache 测量不要拿单轮 smoke 的耗时当回归依据场景名和脚本结构是运行器的硬校验setup.sh/test.sh之外不要放额外的脚本文件测量与统计由 Hyperfine 独占不要在场景脚本里自行实现计时逻辑。场景稳定后可以用 profile.sh 对单个场景做samply/perf/flamegraph剖析或用PERF_RESULTS_DIR导出 Hyperfine 的 JSON 统计这两项在 README 的 “Profiling one scenario” 一节有完整说明。【免费下载链接】gitbutlerThe GitButler version control client, backed by Git, powered by Tauri/Rust/Svelte项目地址: https://gitcode.com/GitHub_Trending/gi/gitbutler创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考