perf(build): 加 fast 构建档、依赖并行 codegen、验收脚本一次 --bins
三条慢因里两条落在构建配置上,发布档(release)的体积取向不动: 1. `[profile.fast]`(inherits = release):opt-level 2 / lto thin / codegen-units 16, 日常迭代与验收用它,发布仍走 release。`panic = "unwind"` 是**必须显式写**的一行—— release 是 panic = "abort",inherits 会照抄,漏了它 `cargo test --profile fast` 起不来 (test harness 依赖 unwind)。 2. `[profile.release.package."*"]`:依赖的 codegen-units 放到 16(并行 codegen), **opt-level 保持 "z"**。最初按「优化等级高 = 编译快」把依赖抬到 opt-level 3, 同机冷编译实测反了:opt-3 是 22m45s / GUI 19.46 MiB,opt-"z" 是 16m01s / 15.72 MiB—— 又慢 29.6% 又大 19~24%,收益全部来自 codegen-units 而不是 opt-level。 只调优化强度,不碰 panic / strip(那两项改的是整个二进制的运行时行为)。 3. `[profile.fast.package."*"]` 显式写一遍:package 覆盖按 profile 分别生效,不赌 inherits 会不会连覆盖一起继承(实测 594 个编译单元全部 opt-level=2 + cu16,含依赖)。 实测写进 packaging/README §6/§7.1(同机、删空 target 的冷编译、无并发负载): release 16m01s(GUI 15.72 / iced 13.27 / ull 5.97 MiB),fast 24m27s(36.41 / 32.02 / 15.23 MiB)。 **fast 档在本机冷编译没有收益甚至更慢**,瓶颈是两个大 UI crate + 链接 + 机械盘 IO, 它的定位是 SSD 机器与增量重编——这条照实写进文档,不为了贴近预期挑数字。 DevDocs/ops/macos-acceptance.sh 的构建段改动(DevDocs 被 gitignore,无法入库)见完成报告。
This commit is contained in:
1 parent
b16932a312
commit
b9eae2bb73
2 files changed
+79
-37
No files matched your search
+40
@@ -108,3 +108,43 @@ lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = "symbols"
|
||||
|
||||
# P6(M96):日常迭代 / 本机验收用的「快」档,发布仍走上面的 release。
|
||||
# `inherits = "release"` 让其余各项(strip / debug / overflow-checks …)照抄 release,
|
||||
# 只覆盖拖慢构建的四处:
|
||||
# - lto = "thin":跨 crate 内联仍有,但可以并行;fat LTO 在 codegen-units = 1 下等于
|
||||
# 全程序串行链接(375 个 crate 排队),一次性吃掉十几分钟。
|
||||
# - codegen-units = 16:给 codegen 留并行度,不再把整个 crate 挤成一个 CGU。
|
||||
# - opt-level = 2:介于 "z" 与 3 之间,"z" 会为了体积关掉一批循环/内联优化,代价是运行期。
|
||||
# - panic = "unwind":**必须显式写**。release 是 panic = "abort",inherits 会照抄下来,
|
||||
# 而 test harness 依赖 unwind —— 漏了这行 `cargo test --profile fast` 直接起不来。
|
||||
#
|
||||
# ⚠️ 定位(实测,别误读):本机(12 核 + 机械盘源码盘 + -j 4)冷编译 fast = 24m27s,
|
||||
# **比 release 的 16m01s 还慢**,体积是 2.3 倍(36.41 vs 15.72 MiB GUI)。瓶颈在两个大
|
||||
# UI crate 的编译 + 链接 + 磁盘 IO,LTO/CGU 这几个旋钮被掩盖了。它的价值在 **SSD 机器**和
|
||||
# **增量重编**(thin 不必每次全程序 LTO);「加了 fast 档」≠「冷编译会变快」。
|
||||
[profile.fast]
|
||||
inherits = "release"
|
||||
opt-level = 2
|
||||
lto = "thin"
|
||||
codegen-units = 16
|
||||
panic = "unwind"
|
||||
|
||||
# P6(M96):依赖不需要为体积做极致优化,但**实测结论与直觉相反**——把依赖的 opt-level 从
|
||||
# release 的 "z" 抬到 3(本档最初就是这么定的)会让冷编译**更慢**、体积还更大:
|
||||
# 同机冷编译 opt-3 = 22m45s / 19.46 MiB(GUI),opt-"z" = 16m01s / 15.72 MiB。
|
||||
# 真正的收益全在 `codegen-units = 16`(并行 codegen),与 opt-level 无关;抬等级是在拿编译时间
|
||||
# 和体积换运行期。所以这里**只放宽并行度,opt-level 保持 "z"**,workspace 自己的 crate 仍吃
|
||||
# opt-level = "z" / 单 CGU。
|
||||
# **只调优化强度**:panic 与 strip 绝不写进这里。那两项改变的是整个二进制的运行时行为
|
||||
# (panic 策略决定能否 unwind 出栈、strip 决定符号表留不留),不是「给依赖放宽」的范畴。
|
||||
[profile.release.package."*"]
|
||||
opt-level = "z"
|
||||
codegen-units = 16
|
||||
|
||||
# 快档的依赖覆盖单独写一遍:package 覆盖是按 profile 分别生效的(`[profile.release.package."*"]`
|
||||
# 只作用在 release 档),显式写出来就不必赌 `inherits` 会不会连 package 覆盖一起继承。
|
||||
# 值与 fast 档自身一致——依赖编得快是快档的目的,不是再抬一档优化强度。
|
||||
[profile.fast.package."*"]
|
||||
opt-level = 2
|
||||
codegen-units = 16
|
||||
+39
-37
@@ -51,7 +51,8 @@
|
||||
### 本地(当前平台)
|
||||
|
||||
```bash
|
||||
cargo build --release --locked # 产出 target/release/{unlucky-launcher,ull}
|
||||
cargo build --release --locked # 产出 target/release/{unlucky-launcher,ull,unlucky-launcher-iced}
|
||||
cargo build --profile fast --bins # 只是想快点看到画面/跑测试:快档(§6),产物在 target/fast/
|
||||
python packaging/make_artifacts.py --out dist # 产物落在 dist/,含 SHA256SUMS.txt
|
||||
python packaging/make_artifacts.py --version v0.1.0 # 顺带校验版本一致性
|
||||
python packaging/make_artifacts.py --no-smoke # 跳过 ull --version 冒烟
|
||||
@@ -66,7 +67,7 @@ macOS x64),其它组合直接报错退出——不在矩阵里的平台不
|
||||
```bash
|
||||
rustup target add aarch64-pc-windows-msvc
|
||||
cargo build --release --locked --target aarch64-pc-windows-msvc \
|
||||
--bin unlucky-launcher --bin ull
|
||||
--bin unlucky-launcher --bin ull # 不用 --bins:发布矩阵只发 GUI + ull,iced 不进产物
|
||||
python packaging/make_artifacts.py --slot windows-arm64 \
|
||||
--build-dir target/aarch64-pc-windows-msvc/release --out dist
|
||||
```
|
||||
@@ -193,49 +194,50 @@ SLINT_BACKEND=winit-software unlucky-launcher # 环境变量(Slint
|
||||
它同样能在软渲染下工作,是 CI 与远程机唯一可靠的"真出图"证据。
|
||||
- 发布冒烟(本机实跑,见 §7)就是用 `--software-renderer --screenshot` 组合证明"无 GPU 也能起来"。
|
||||
|
||||
## 6. Cargo 体积优化(release profile)
|
||||
## 6. Cargo 构建档位(release 体积档 / fast 速度档)
|
||||
|
||||
`Cargo.toml`:
|
||||
| 档位 | 用在哪 | opt-level | lto | codegen-units | panic |
|
||||
|------|--------|-----------|-----|---------------|-------|
|
||||
| `release` | 发布 / 验收产物 | `"z"`(workspace 自身 crate) | `fat` | 1 | `abort` |
|
||||
| `release.package."*"` | 同上,**只作用于依赖** | `"z"` | 不可覆盖 | 16 | 继承 |
|
||||
| `fast` | 日常迭代、本机跑测试 | 2 | `thin` | 16 | `unwind` |
|
||||
|
||||
```toml
|
||||
[profile.release]
|
||||
opt-level = "z" # 体积优先(启动器不是算力密集;UI 走 Slint 渲染,热路径不在 Rust 侧)
|
||||
lto = "fat" # 全量 LTO:跨 crate 内联+死代码消除,比 thin 再小一截(实测见 §7)
|
||||
codegen-units = 1 # 单 CGU,配合 LTO 拿最大优化面
|
||||
panic = "abort" # 去掉 unwind 表
|
||||
strip = "symbols" # 剥符号表(GNU/MSVC 路径都生效)
|
||||
```
|
||||
|
||||
取舍:`lto = "fat"` + `codegen-units = 1` 让 release 链接明显变慢(本机全量 release 构建 10 分钟量级,
|
||||
见 §7.1),换来的是体积与启动时间。变慢的根源在 `codegen-units = 1`:整个 crate 只切出一个 CGU,
|
||||
LTO 也就只有一个线程可跑(`fat` 本身只管"跨 crate 全量参与",不等于单线程)。
|
||||
本地开发迭代走 `cargo build`(dev profile)不受影响,但 **`ci.yml` 每次 push/PR 都跑
|
||||
`cargo build --workspace --release`**,会实打实吃这份 fat LTO 的链接时间——CI 慢的一部分是 release
|
||||
档构建,不是测试本身。
|
||||
`panic = "abort"` 意味着 panic 不带 backtrace 栈展开——UI 层已用 `Result` 承接错误,
|
||||
`unwrap` 只出现在测试里,这是可接受的取舍。
|
||||
- **`release` 是体积档**:`fat` + 单 CGU 换来实测 GUI −10.8% / CLI −18.7%(§7.1)。变慢的根源在
|
||||
`codegen-units = 1`(整个 crate 只切一个 CGU,LTO 也只有一个线程可跑;`fat` 只管"跨 crate 全量参与",
|
||||
不等于单线程),375 个 crate 排队链接就是十几分钟的来源。
|
||||
- **`release.package."*"` 放宽的只有并行度,不是 opt-level**:实测把依赖 opt-level 抬到 3 反而**又慢 29.6%
|
||||
又大 19~24%**(§7.1),所以保持 `"z"`、只把 `codegen-units` 放到 16。package 覆盖**只允许调优化强度**:
|
||||
`panic` / `lto` / `rpath` 写进去 cargo 直接报错,`strip` 会改变整个二进制的运行时行为——都不许出现。
|
||||
- **`fast` 是速度档,不是发布物**:`thin` LTO + 16 CGU 可并行,`opt-level = 2` 介于 `z` 与 3 之间。
|
||||
必须显式 `panic = "unwind"` ——`release` 是 `abort`,`inherits` 会照抄,漏了这一行
|
||||
`cargo test --profile fast` 连编译都过不去(test harness 依赖 unwind)。**但它在机械盘机器上冷编译
|
||||
并无收益**(比 release 还慢、体积 2.3 倍,§7.1),价值在 SSD 机器与增量重编。
|
||||
- 本地开发迭代走 `cargo build`(dev profile)不受影响,但 **`ci.yml` 每次 push/PR 都跑
|
||||
`cargo build --workspace --release`**,会实打实吃这份 fat LTO 的链接时间——CI 慢的一部分是 release
|
||||
档构建,不是测试本身。`panic = "abort"` 意味着 panic 不带 backtrace 栈展开——UI 层已用 `Result` 承接错误,可接受。
|
||||
|
||||
## 7. 实测(本机,不是估算)
|
||||
|
||||
构建环境:rustc 1.99.1 stable(host `x86_64-pc-windows-gnu`,MinGW 链接器),
|
||||
`cargo build --release --locked --bin unlucky-launcher --bin ull`,12 核机器(同一时刻另有别的 worker
|
||||
在做全量 debug 构建,属于偏悲观的真实负载)。产物:`target/release/unlucky-launcher.exe`(GUI,交付物)
|
||||
与 `target/release/ull.exe`(CLI)。
|
||||
构建环境:rustc 1.99.1 stable(host `x86_64-pc-windows-gnu`,MinGW 链接器)。§7.2/§7.3 的早数字是在
|
||||
同一刻另有 worker 做全量 debug 构建(偏悲观的真实负载)时测的,§7.1 的 M96 两行是空载冷编译。
|
||||
|
||||
### 7.1 体积(实测字节数,不是估算)
|
||||
### 7.1 体积与冷构建耗时(实测字节数,不是估算)
|
||||
|
||||
| release profile | `unlucky-launcher.exe` | `ull.exe` | 冷构建耗时(本机 12 核) |
|
||||
|-----------------|-----------------------|-----------|-----------|
|
||||
| `lto = "thin"`(改前) | 16,366,080 B = 15.61 MiB | 7,572,992 B = 7.22 MiB | 11m52s |
|
||||
| `lto = "fat"`(改后,本仓库采用) | **14,595,584 B = 13.92 MiB** | **6,157,824 B = 5.87 MiB** | 9m27s(增量,依赖已编好) |
|
||||
| 档位 | `unlucky-launcher.exe` | `unlucky-launcher-iced.exe` | `ull.exe` | 冷构建耗时 |
|
||||
|------|-----------------------|----------------------------|-----------|-----------|
|
||||
| `release`,`thin`(M46 前) | 16,366,080 B = 15.61 MiB | 未测(当时无 iced 版) | 7,572,992 B = 7.22 MiB | 11m52s |
|
||||
| `release`,`fat`,依赖全 `"z"`(M46,改前) | **14,595,584 B = 13.92 MiB** | 未测 | **6,157,824 B = 5.87 MiB** | 9m27s(增量) |
|
||||
| `release`,`fat` + 依赖 opt-3 / cu16(M96 试过的那版,**已否**) | 20,407,296 B = 19.46 MiB | 18,371,584 B = 17.52 MiB | 7,743,488 B = 7.38 MiB | 22m45s(冷) |
|
||||
| `release`,`fat` + 依赖 `"z"` / cu16(**M96 起,发布档**) | **16,479,232 B = 15.72 MiB** | **13,910,528 B = 13.27 MiB** | **6,256,128 B = 5.97 MiB** | **16m01s(冷)** |
|
||||
| `fast`,`thin` + cu16(M96 起,日常档) | 38,182,400 B = 36.41 MiB | 33,570,304 B = 32.02 MiB | 15,967,744 B = 15.23 MiB | 24m27s(冷) |
|
||||
|
||||
`fat` 相对 `thin` 的实测差:GUI **−1,770,496 B(−10.8%)**,CLI **−1,415,168 B(−18.7%)**。
|
||||
一次全量 LTO 把跨 crate 的死代码与重复实例化一起消掉,收益远超常见印象里的"1~2%";
|
||||
代价是 release 链接更慢——慢在 `codegen-units = 1`(单 CGU → LTO 单线程),不是 `fat` 本身,
|
||||
且只影响发布/验收构建,日常 `cargo build` 走 dev profile 不受影响。
|
||||
|
||||
体积构成(按依赖树判断):Slint 运行时(femtovg + software 双渲染器 + winit/accesskit/托盘)+ rustls/reqwest
|
||||
+ rig/rmcp/axum 是主要来源;`ull` 明显更小是 D18 纪律的直接结果(CLI 零 UI 框架依赖)。
|
||||
测量条件(M96 三行):12 核 / `x86_64-pc-windows-gnu`,`-j 4`,源码在机械盘、`CARGO_TARGET_DIR` 在 SSD,均为**删空 target
|
||||
后的冷编译**、同机同晚无并发负载;M46 两行是更早提交上的数字(依赖已热 / 另有并发),**跨行比耗时没意义**,体积差
|
||||
也不能全归给 profile(其间多了 iced / presenter / i18n 等 crate)。
|
||||
两条结论都反直觉但都是实测:① **依赖 opt-level 从 3 退回 `"z"` 又快 29.6% 又小 19~24%**——收益全在 `codegen-units = 16` 的
|
||||
并行 codegen,抬 opt-level 是拿编译时间和体积换运行期;② `fast` **冷编译比 `release` 还慢**(24m27s vs 16m01s)、体积 2.3 倍,
|
||||
瓶颈在两个大 UI crate + 链接 + 机械盘 IO——它的定位是 **SSD 机器与增量重编**(thin 不必每次全程序 LTO),不是冷编译加速。
|
||||
体积构成(按依赖树判断):Slint 运行时(femtovg + software 双渲染器 + winit/accesskit/托盘)+ rustls/reqwest + rig/rmcp/axum 是主要来源;`ull` 更小是 D18 纪律(CLI 零 UI 依赖)。
|
||||
|
||||
### 7.2 冒烟(真跑,2026-10-31 夜间 + 11-01 早,本机 Win11 26100)
|
||||
|
||||
|
||||
Reference in new issue
Block a user