Repository navigation
Performance Baseline
基准在 src-tauri/benches/,通过 src-tauri/src/bench_support.rs 调生产代码——基准不复制实现,否则量到的是基准文件里那份副本。
cd src-tauri && cargo bench门禁只编译不运行(--all-targets 会编译它们)。完整数字与已知坑见仓库内 docs/bench-baseline.md。
| 基准 | 中位数 | 说明 |
|---|---|---|
encode_png_1080p |
77.1 ms | 1080p 渐变帧,CompressionType::Fast
|
png_dimensions_1080p |
505 ns | 只解 PNG 头 |
validate_png_1080p |
20.0 ms | 完整解码,只在信任边界上用 |
validate_png_1440p |
36.3 ms | 2560×1440,截图提交的常见最坏尺寸 |
decode_png_base64_1080p |
1.27 ms | 前端回传的 data URL 解码 |
compute_hash_1mib |
1.56 ms | 1 MiB 文本的 SHA-256 去重哈希 |
rgba_fingerprint_1080p |
1.47 ms | 轮询之间「还是上一张图吗」 |
strip_html_tags_1mib |
1.41 ms | 逐字符扫描 1 MiB HTML |
is_sensitive_text_prefix_hit |
4.2 ns | 命中前缀提前返回 |
is_sensitive_text_worst_case_100kib |
31.2 µs | 不命中,走整段转小写 |
get_clips_first_page |
23.2 µs | 2000 行内存库,无查询,limit 50 |
get_clips_fts_prefix |
298 µs | ≥3 字符走 FTS5 前缀查询 |
get_clips_like_fallback_short_query |
38.1 µs | <3 字符走 LIKE 回退 |
get_clips_no_match |
259 µs | 空结果 |
只解 PNG 头:png_dimensions 从完整解码的 19.6 ms 降到 505 ns(约 39000 倍)。凡是只需要宽高的地方(缩略图缓存判断、尺寸校验、贴图 origin 查找)都不该整张解码。
指纹替代重编码:轮询之间判断图片是否变化,用 rgba_fingerprint(1.47 ms)替掉一次完整 PNG 编码(77 ms)。
裁剪基准故意不做:裁剪是 memcpy,被同路径的 encode_png 掩盖两个数量级。给它单独做基准量到的是噪声。
已落地的措施:
| 措施 | 说明 |
|---|---|
| glibc malloc 调优 |
MALLOC_ARENA_MAX / MMAP_THRESHOLD / TRIM_THRESHOLD
|
| WebKit 精简 | 禁用 GPU 加速与不需要的功能 |
| 缓存模型 |
CacheModel / clear_cache
|
| 延迟导入 | 大库按需加载 |
| 缩略图 FIFO | 有界缓存 |
| 模糊释放 | 用完即释放 |
| Cargo release profile |
strip / lto = "fat" / codegen-units = 1 / opt-level = "s" / panic = "abort"
|
这些是量出来的,不是猜的。记录归因比记录数字更重要——数字会随硬件变,归因不会。
4K 屏一次取帧 1704 ms 中,1607 ms(94%)是合成器 deflate PNG,绘制加读回只有约 100 ms。
因此「少拍像素」无效(逐屏两次 1830 ms vs 整屏一次 1900 ms)。真正的修法是换数据源——改从视频帧接口取,实测 1900 ms → 280 ms,同时还拿到原生分辨率。详见截图与冻结帧。
分数缩放桌面上,图片先被平滑放大再被合成器缩回,第一趟就丢了细节。反投影预补偿把屏上 PSNR 从 30.28 dB 提到 43.02 dB。详见显示缩放。
列表行取原图会让每次开面板都把十几 MB 送进 webview 并做十几次全尺寸 PNG 解码,全落在 webview 那一个线程上。改走后端缩略图(最长边 128 px,按 id 缓存)后消失。
原图读取、base64 编码与 128 px 缩略图解码/缩放都放到 blocking pool。冷缓存同时出现多张截图时不再占满 Tauri async worker,并以两路并发限制约束全尺寸解码的 CPU / RSS 峰值。缓存命中路径保持直接返回。
贴图显示图走按窗口 label / revision 隔离的原生协议,响应直接接管 PNG 字节,不再经过 Rust base64 → JSON → JS 解码 → Blob URL。复制、保存和画布仍从后端权威原图读取。
原实现为每个 Moved 事件创建睡眠线程,持续拖动时线程与栈内存持续增长。改为全局单个位置保存 debounce worker,仍在停止移动 300 ms 后保存最终坐标。
打开可编辑 PNG 后,运行时只保留唯一一份原图字节和不含 base64 的轻量工程元数据。每张工程不再常驻约为原图 PNG 1.33 倍的重复字符串;再次保存时才临时重建完整自包含容器。
清理 7 天前的过期记录,并只保留最近 512 条,避免数据库随历史链接无限增长。
GNOME Wayland / WebKitGTK 隔离环境,1 万条混合历史(含 2000 张图片):
- 全过程只挂载 10–15 行
- 完整向下、向上、再次向下遍历均无滚动失败、崩溃或 OOM
- 第二次遍历后 PSS 相对首次遍历末端只增加约 9 MiB
- 显式隐藏后列表对象从 1 万条归零,15 秒后 cgroup PSS 回落约 8 MiB
另有连续五次贴图关闭的观察:service cgroup PSS 在首次建立 WebKit / 图像分配高水位后稳定,没有随关闭次数线性增长。
- 带调试器、带大量诊断输出或带断点的运行不是性能基准
- 单次测量不构成基准,criterion 的中位数才是
- 一张合成图的识别耗时不能外推到所有真实图片
- 「进程还活着」「函数返回成功」都不能替代实际耗时与内存的测量