性能基准¶
Hex 通过 strobe 作为原生应用进行基准测试。strobe 会构建两个变体(例如 origin/dev 和你的检出分支),在全新的私有主目录中分别启动它们,并以交替运行的方式进行比较。
strobe bench perf/idle.bench.ts --base origin/dev --head .
strobe bench perf/launch.bench.ts --base origin/dev --head .
strobe bench perf/idle.bench.ts --check # one short, untimed pass
strobe bench perf/idle.bench.ts --filter "window-closed*" --base origin/dev --head .
strobe report experiment <exp_…> --format pr --update-pr <n> # the results block in PR n
--filter 和 --target 接受名称、逗号分隔列表和 glob 表达式。
在拉取请求中¶
PR 的正文由你自己撰写:说明 strobe 显示了什么从而促成了这项改动(命令和记录 ID),以及这项改动做了什么。然后让 strobe 放置它的证据:
它会向 PR n 的正文添加一个生成的块,或在重新运行后替换该实验的块,并且不触碰正文的其余部分。不要在块内部进行编辑(strobe 拒绝更新手动编辑过的块);把你对数字的解读放在它的上方或下方。如果不带 --update-pr,该块会输出到 stdout,供你粘贴。
窗口用例通过 expect 命名它们所测量的屏幕(窗口标题和窗格标题)。如果出现的是另一个屏幕,例如窗格上的首次运行对话框,则会被拒绝为 WrongScreen 而不是计时,块的 On screen: 行会显示实际出现的内容。
请在安静的机器上运行计时基准测试。关闭其他应用,并保持会话处于解锁状态。
运行内容¶
strobe.json使用cargo build --release将hex构建到{cache}/cargo-target,这是 strobe 在各变体之间共享的目标文件夹。构建输出在 Windows 上是hex.exe,在 macOS 和 Linux 上是hex。- 构建的环境变量保留 release 代码,并在
hex.pdb中添加行表,因此 CPU 性能分析文件会标示 Hex 的函数名:CARGO_PROFILE_RELEASE_DEBUG=line-tables-only、CARGO_PROFILE_RELEASE_STRIP=false和CARGO_PROFILE_RELEASE_INCREMENTAL=false。它不需要在Cargo.toml中设置任何 profile 或 feature,因此每次提交都以相同方式构建:--base可以是基准测试存在之前的提交,并可通过--bisect在整个历史中进行二分。这种构建永远不会发布。 - 在 Windows 上,
build.windows设置RUSTUP_TOOLCHAIN=stable-x86_64-pc-windows-msvc,因此默认 Rust 工具链为 GNU 的机器仍会使用 MSVC 构建(GNU 会因 "dlltool not found" 而失败)。Windows 构建还需要 Vulkan SDK(docs/windows.md)。 - 每个变体都会在自己的多次启动之间保留 GPU 驱动程序已编译的着色器(strobe 的默认行为
gpuCache: "warm"),就像一台之前运行过 Hex 的机器一样。base 和 head 永远不会共享这些着色器,被丢弃的预热启动会在计时配对之前填充它们。如果没有它们,每次启动都会重新编译 Hex 的着色器:一次听写任务在每 6 秒的运行中,有 4.6 到 5.3 秒花在驱动程序的编译器上。因此cold-launch是回访用户的启动(Hex 未运行,但着色器已缓存)。安装或驱动程序更新后的首次启动,则需要一个使用gpuCache: "cold"的目标。
隔离¶
strobe.json 中的 hex 应用 profile 会使启动的副本远离你自己的 Hex:
HEX_APPLICATION_SUPPORT_DIR={home}/hex-data:设置、历史记录、日志、模型、锁和local-api.json都位于私有主目录中。- 非默认的数据根目录会声明自己的 Windows 单实例事件,因此副本绝不会向你正在运行的 Hex 发送信号或让位给它;在 Linux 上,它也绝不会把启动交给你的 systemd 单元。在重定向的主目录内,默认路径(
AppData/Roaming/hex、.local/share/hex)会跟随重定向,所以需要像hex-data那样另命名一个文件夹。 HEX_DISABLE_UPDATES=1:不检查更新,也不访问网络。USERPROFILE(Windows)和HOME用于定位~/.config/hex,因此你的hex.config.ts不会进入副本。- Hex 和 Annotate 从你的环境中推导出的每个文件夹,在 Windows 上也会跟随副本的环境(
USERPROFILE、APPDATA、LOCALAPPDATA;app_paths和scrawl_router::user_dirs),而dirs会向 shell 询问你自己的文件夹:Annotate 的采集文件夹及其保留策略、Scrawl 的路由文件夹和 Scrawl 设置导入、会议文件夹,以及 Hugging Face 缓存。Annotate 在基准测试中保持开启(与用户使用时一致),并且只在私有主目录内工作。 HF_HUB_CACHE={home}/.cache/huggingface/hub:副本绝不会在你的 Hugging Face 缓存中找到模型(在 Windows 上,该扫描否则会读取你的真实用户资料)。- 预置的
settings.json会关闭监听器(enable_on_launch: false):不安装键盘钩子、不打开麦克风、不加载模型。历史记录会永久保留,因此预置的history.json不会在运行中途过期。预置的onboarding-complete标记可跳过首次运行设置。两个scrawl-*迁移会跳过从你的主目录导入 Scrawl 设置。 - 如果你自己的
settings.json或local-api.json发生变化,或者你的 Hex 退出,guard会停止运行。每个 guard 都标明其操作系统。 - macOS:变体以裸的
hex可执行文件运行,而不是Hex.app,因此它没有 bundle ID,也不会与你已安装的 Hex 共享偏好设置、已保存状态或权限。moved告诉 strobe,副本将其数据保存在私有主目录(HEX_APPLICATION_SUPPORT_DIR)中,而不是你的~/Library/Application Support/hex中,因此你那里的 Hex 不会干扰基准测试。strobe 只会在键盘或鼠标输入静默一分钟后,才将副本的窗口带到前台。每个 profile 都会记录anomaly-hex-identity迁移:没有它,设置尚未就绪的副本(副本没有 Microphone 授权)会在窗口上重新打开首次运行引导,而每次点击都会落在引导上。
副本不加载任何模型,除了下面聆听和听写用例之外。
用例¶
| 文件 | 用例 | 主要指标 | 测量内容 | 锁定会话 |
|---|---|---|---|---|
perf/idle.bench.ts |
window-open |
cpu.work |
8 秒静止,窗口停留在 General | 等待一次解锁 |
perf/idle.bench.ts |
window-closed |
cpu.work |
8 秒空闲状态,仅托盘(app --hidden) |
运行 |
perf/idle.bench.ts |
window-closed-memory |
memory.app.private |
同一托盘的私有内存和专用 GPU 内存(gpu.vram) |
运行 |
perf/launch.bench.ts |
cold-launch |
launch.ready |
从进程启动到第一个绘制的窗口 | 等待解锁 |
perf/launch.bench.ts |
hidden-launch |
launch.ready |
从进程启动到 local API started,仅托盘 |
运行 |
perf/listening.bench.ts |
listening-at-rest |
cpu.work |
8 秒空闲监听,麦克风释放和打开 | 运行 |
perf/dictation.bench.ts |
recording |
cpu.wakes |
6 秒的一次听写,录制一段 fixture 音频,麦克风打开和释放 | 运行 |
perf/interaction.bench.ts |
switch-panes |
ui.cpu |
General → History → Output → General,每次点击都等待下一个窗格的内容出现在辅助功能树中 | 等待解锁 |
perf/interaction.bench.ts |
history-scroll |
ui.cpu |
History 的列表向下滚动 6,000 点,并以每秒 3,000 点返回 | 等待解锁 |
perf/interaction.bench.ts |
history-search |
ui.cpu |
向 History 的搜索框输入“cache key”,每 120 ms 输入一个键;每个键都搜索 10,000 条记录 | 等待解锁 |
perf/interaction.bench.ts |
model-picker |
ui.cpu |
打开语音模型选择器,列出所有模型的列表,返回到对比页,然后关闭 | 等待解锁 |
次要指标是线程唤醒次数(cpu.wakes)、GPU 使用率和私有内存。strobe query <rec> threads 会列出线程名称;Hex 会为每个长生命周期线程命名,并且窗口线程在 Windows 上名为 hex-ui。
托盘用例使用 window: false:strobe 既不等待也不检查窗口,因此它们也会在会话锁定时运行。带有窗口或呈现框架的用例会等待解锁。
要查看某个用例的时间花费在哪里,请录制一次:
监听用例¶
perf/listening.bench.ts 在开启监听功能的情况下运行 Hex:hex-listening 配置文件(空闲时释放麦克风)和 hex-listening-warm(默认配置,麦克风保持打开)。该副本会安装其键盘钩子并加载默认英语模型,perf/seeds/listening.ts 会从这台机器自己的 Hex 安装中将其复制到 perf/seeds/.generated。strobe 会在多次启动之间以只读方式共享如此大的种子文件(64 MB 及以上),而不是将其复制到每个私有 home 目录。warm 配置会打开机器的真实麦克风;两者都不会录音。在运行期间请不要使用听写快捷键。
听写用例¶
perf/dictation.bench.ts 在没有麦克风和粘贴的情况下运行完整的听写。hex-dictation 配置文件将 HEX_AUDIO_FIXTURE 设置为 speech.wav,该文件由 perf/seeds/dictation.ts 使用这台机器自己的文本转语音声音生成(一个 48 kHz 的句子,然后是 2 秒静音),放在监听种子链接的模型旁边。副本中的每次麦克风打开都会从头开始以实时速度、每 10 ms 一块的方式播放该文件,并且不会打开任何设备,因此不会录制任何内容。
该基准测试通过其本地 API 驱动副本,就像 hex dictation 一样。每次听写在句子开始时开始(麦克风打开,当循环文件再次轮到开头时),并以 dictation stop --print 结束,该命令返回处理后的文本而不是粘贴它。这些配置文件将副本的快捷键移动到 F19 和 F18,并取消绑定 Paste Last,因此在正在使用的机器上运行基准测试时按下的键不会到达任何地方。
只有包含 HEX_AUDIO_FIXTURE 的基准测试构建才受支持。较旧的构建会忽略它并打开真实麦克风;setup 随后会拒绝该测试块,但前提是麦克风已经打开过。
交互用例¶
perf/interaction.bench.ts 像真人一样使用设置窗口。strobe 从外部驱动它,通过 Windows 提供给辅助技术的接口:它在 Hex 的辅助功能树(UI Automation,由 Rune 使用 AccessKit 构建)中找到每个控件,并将鼠标和键盘输入投递到窗口。Hex 为此不需要任何构建特性、标志或桥接;发布构建对屏幕阅读器的响应方式相同。
- 选择器是元素键,辅助功能树将其作为 UI Automation 的
AutomationId暴露:nav-history、history-search、transcription-control。键路径(model0/choose-model)表示某个带键的祖先内部的键。role=<role>和text=<name>也可以使用。请保持这些用例使用的键稳定;docs/features/accessibility.md列出了它们。 - 每次点击都会等待(
until)下一个视图独有的键,因此运行会在更改出现在辅助功能树中时结束,而不是在固定等待后结束。 interaction.*仅当辅助功能树在输入后一秒内发生变化时才将输入计入,因此该变化对 strobe 和屏幕上的显示都是可见的。- 主目录保存着完整的 History:10,000 条听写,即 Hex 的上限,来自
perf/seeds/history.ts(一次性写入perf/seeds/.generated/history)。列表显示最新的 200 条,搜索结果也是如此。 - 选择模型不算一个用例:它会下载或加载模型,花费的是网络和模型,而不是窗口。
model-picker测量的是选择器自身的视图。 - 它们需要未锁定的会话:Windows 在锁定时会对每个点进行命中测试并指向锁屏界面,因此 strobe 此时会拒绝指针输入。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 hex19/hex