98 lines
4.3 KiB
Markdown
98 lines
4.3 KiB
Markdown
# Starlight Launcher — 实例启动性能优化记录
|
||
|
||
> 内部文档,非上线说明。本分支聚焦「实例启动慢」问题的定位与修复。
|
||
|
||
## 背景
|
||
|
||
用户反馈:点击实例启动后,要等很久才看到游戏窗口。
|
||
|
||
经实测定位,问题由多个独立环节叠加造成,本分支逐项修复。
|
||
|
||
## 改动清单
|
||
|
||
### 1. 依赖校验戳缓存(`packages/app-lib/src/launcher/direct_ensure.rs`)
|
||
|
||
**问题**:PCL/HMCL 直连实例每次启动都要对全部库文件与资产对象重算 SHA1。
|
||
实测某整合包:118 个库 + 3911 个资产对象,合计约 670 MB,机械硬盘顺序读仅
|
||
27.8 MB/s,串行校验耗时约 **17.7 秒**,占整个启动准备阶段的 97%。
|
||
|
||
**方案**:新增持久化「已验证」戳缓存,key 为文件绝对路径,value 为
|
||
`(size, mtime)`。`file_is_current` 先做 stat,命中戳则跳过 SHA1 读取;
|
||
文件被替换或修改(size/mtime 变化)时自动回退到完整 SHA1 校验,**不弱化
|
||
损坏检测**。
|
||
|
||
- 缓存文件:`<caches>/linked-verify-stamps.json`(存 Axolotl 自己的缓存目录,
|
||
不污染直连安装目录)
|
||
- 首次启动建立缓存仍走完整校验;后续启动(含跨进程重启)直接命中
|
||
|
||
**效果**:`asset_scan` 15788ms → 62ms,`ensure_deps` 整体 18.6s → 0.1s。
|
||
|
||
### 2. 库/资产校验并发化(`packages/app-lib/src/launcher/direct_ensure.rs`)
|
||
|
||
在戳缓存基础上,把库扫描与资产扫描从串行 `for` 循环改为
|
||
`try_for_each_concurrent`(并发度与下载器一致,`task_concurrency_limit * 2`)。
|
||
首次建缓存时也能吃到并发收益。
|
||
|
||
> 注:并发对机械硬盘的随机小文件读取提升有限(IOPS 瓶颈),真正的
|
||
> 数量级提升来自上面的戳缓存。
|
||
|
||
### 3. 启动阶段计时埋点(`packages/app-lib/src/api/instance/run.rs`、`packages/app-lib/src/launcher/mod.rs`)
|
||
|
||
在启动链路插入 `[launch-timing]` 前缀的计时日志,覆盖:
|
||
`hosted_prepare_launch` / `hosted_java_arguments` / `resolve_version_info` /
|
||
`resolve_java` / `resolve_gc` / `assemble_client` / `ensure_deps`(细分
|
||
`dep_lib_scan` / `asset_scan` / `dep_assets` / `dep_log_config`)/
|
||
`remove_old_natives` / `extract_linked_natives` / `process_spawn`。
|
||
|
||
用于定位瓶颈,grep `[launch-timing]` 即可。
|
||
|
||
### 4. 启动过渡动画优化(`apps/app/src/mc_transition.rs`)
|
||
|
||
**a. 动画时长**:400ms → 200ms,帧数 50 → 25。缓解卡顿。
|
||
|
||
**b. 窗口对齐修复**:全屏放大与缩回游戏窗口时,左侧留缝、整体偏右。
|
||
根因是无边框窗口的不可见 resize border(实测窗口比目标大 16×9px,
|
||
可见内容从 (8, 4.5) 才开始)。改用 `set_client_rect`,通过
|
||
`GetWindowRect` / `GetClientRect` 计算边框并补偿,让**可见客户区**精确
|
||
落在目标矩形。
|
||
|
||
**c. 单次 SetWindowPos**:每帧从两次调用(`set_position` + `set_size`)
|
||
改为单次 `SetWindowPos`,减少重绘。
|
||
|
||
**d. 恢复可见**:动画前先 `show()` + `unminimize()`,避免轻量模式遗留
|
||
导致遮罩建了却不可见。
|
||
|
||
### 5. 遮罩即时显示(`apps/app/src/lightweight_mode.rs`)
|
||
|
||
**问题**:点击启动后遮罩要等约 10 秒才出现。
|
||
|
||
**根因**:收到 `launched` 事件后,代码先 `await maximize_minecraft_window`
|
||
(最多轮询 5 秒等游戏窗口),跑完才启动遮罩动画。
|
||
|
||
**方案**:把最大化游戏窗口丢到独立任务,遮罩动画立即启动,两者并行。
|
||
|
||
### 6. debug 构建写文件日志(`packages/app-lib/src/logger.rs`)
|
||
|
||
**问题**:debug 构建的 `start_logger` 只输出到控制台、不落盘,GUI 进程
|
||
关闭后无法回溯日志,导致性能计时无法采集。
|
||
|
||
**方案**:debug 版 logger 增加一层文件输出,写入与 release 相同的
|
||
`launcher_logs` 目录,文件名带 `session_debug_` 前缀。
|
||
|
||
## 结论与边界
|
||
|
||
- **启动器侧**:准备阶段已从 18.6s 优化到约 0.1s,拉起进程约 0.6s,已到极限。
|
||
- **游戏侧**:窗口出现前约 11s、完整加载约 45.8s(230 mod 的 NeoForge 整合包),
|
||
属整合包固有成本,任何启动器无法缩短。
|
||
- 曾尝试开启 NeoForge 早期窗口(`fml.toml` 的 `earlyWindowControl`),
|
||
该整合包下与 mod 冲突导致更慢(11s → 30s),**已回滚**。
|
||
|
||
## 构建
|
||
|
||
```bash
|
||
# debug(含计时埋点、文件日志)
|
||
cargo build -p theseus_gui
|
||
|
||
# release
|
||
cargo build --release -p theseus_gui
|
||
``` |