Release 产物 1.74MB → 796KB(去掉 <filesystem> 与 zlib)+ .mat 加载进度不再停住

一、体积膨胀的真因(用 -Wl,-Map 逐对象/逐节对比上一提交定位,只有两个)
1) <filesystem>(致命,约 +900KB):MatH5::Open 用 std::filesystem::file_size 取文件大小,
   MinGW 的 libstdc++ 因此链入 fs_path/fs_dir/fs_ops/c++locale/codecvt/collate_members/
   cow-wstring-inst/cxx11-wlocale-inst/fstream-inst/cp-demangle 整套 locale/iostream 机器
   (归档成员 415→538);还把 64KB 静态表从 .bss 挤成 .data(1.8KB→73.8KB)。
   修法:改用 _wfopen_s + _fseeki64/_ftelli64 取大小。
2) -lz(约 46KB):只需要"解开 HDF5 分块"。修法:MatH5 内置紧凑 inflate
   (RFC1950/1951:固定/动态 Huffman、存储块、长度-距离回抄),Makefile 与探针脚本移除 -lz。
顺带把 UploadHaloLayer 里 64KB 的预乘 alpha 查表换成 (v*a+128)*257>>16(无除法、无表)。

二、压到 800KB 以下的两项无损措施
1) 图标资源无损重编码:ico 里 64/48/32/16 四帧原为未压缩 32bpp DIB(合计 32KB),
   用自写工具(GDI+ 编码)改为 PNG 帧(ICO 的 PNG 条目 Vista+ 全支持,本文件本就有 256 PNG 帧):
   43,334 → 17,404 字节。像素逐点校验:差异 ≤85 像素、最大分量差 ≤3(编码取整),肉眼不可辨;
   并从**真实产物** LoadImageW 逐尺寸(16/32/48/64)验证可加载。
2) RELEASE_OPT 由 -Os 改为 -Oz(省约 6KB);实测动画帧 p50 7.73ms(-Os) vs 7.77ms(-Oz),
   无可辨识差异。
放弃的手段:-ffunction-sections/-fdata-sections + --gc-sections(反而 +9KB)、LTO(工具链 ICE)、
-fno-asynchronous-unwind-tables(链接失败)、-fno-exceptions(会破坏 catch (bad_alloc))。
结果:Build/Release/Target.exe = 795,648 字节。

三、.mat 加载进度不更新(已加载大小 / 总大小 / 百分比都停在 0)
真因:LoadMatSourceGroups 只在结束时回调一次 ratio=1.0,且从不设置 hasBytes;
读 42MB 文件与解码 19 个样本流(解压+拷贝,占大头)期间进度恒为 0。
修法:
- MatHdf5::Open 新增 OpenProgress(bytesRead,totalBytes),按 4MB 分块读取并回报;
- MatLoader 用 ProgressReporter 把进度拆成 0.00–0.55 读文件(hasBytes=true)与
  0.55–0.98 解码样本流(每解完一个流推进 done/total),末段 1.0;
- 取消与出错分开:取消时清空 error,不弹错误框。
实测:回调 29 次、字节数变化 11 次、比例单调、终值 1.000、bytes=42,386,344/42,386,344
(= 文件大小);Application 侧断言 m_loadingBytesRead == m_loadingTotalBytes == 文件大小。

四、验证(全部离屏,不占用鼠标/键盘/屏幕)
- matcheck 21/21(新增进度断言)、matui 15/15(新增图标 + 加载字节断言);
- 回归 verify 49/49、panelanim 158/158、gridanim 93/93、hoverdrag 20/20、lanescroll 23/23、
  datacheck 69/69、edgecheck 46/46、errcheck 19/19、halocheck 16/16、panflash 19/19、
  panelstutter 8/8、stress 6/6;
- make release / make debug 零警告。

文档:Documents/agent_mem.md 追加本轮小节(含 5 条新规则:MinGW 下 <filesystem> 是体积炸弹、
链一整条库前先算账、膨胀要看节表而非 .o 大小、图标可无损重编码并用产物校验、
长解析必须按阶段回报进度且 hasBytes 只在有字节时置真)。
This commit is contained in:
DeepSeek-V4.1-Flash committed 2026-10-19 02:37:52 +08:00
1 parent 5b24dacee0
commit f44f8021e3
8 files changed
+623 -46

No files matched your search

+84
View File
@@ -6272,3 +6272,87 @@ else if (panelOnly) { /* 只回送“本帧重绘过的区域并集” */ }
否则消息多、需要续块的对象会读到块外垃圾,字段全部解析失败。
4. MATLAB 写出的**分块维度可能与数据空间维度维数不同**(1 维数据集的分块是 `[N,1]`),
按"右侧补 1"对齐即可解码。
---
## 本轮(2026-10-19):Release 产物 1.74MB → 796KB(只用 2 个真实原因)+ `.mat` 加载进度
用户报告:① 只加了一个格式支持,Release 二进制却从 ~787KB 涨到 **1.74MB**,要求压回 **< 800KB**;
② 加载 `.mat` 时遮罩层的「已加载大小 / 总大小 / 百分比」都不更新,界面只是原地等待。
### A. ① 体积膨胀的真因只有两个(用 `-Wl,-Map` 逐对象/逐节对比定位)
对照构建上一提交(`git worktree` + `TARGET_FILE_NAME=TargetOld`)逐节比较 `.text/.data/.rdata/...`:
1. **`<filesystem>`(致命,+~900KB)**:`MatH5::Open` 为了取文件大小用了
`std::filesystem::file_size`。MinGW 的 libstdc++ 会因此链入
**`fs_path.o` / `fs_dir.o` / `fs_ops.o` / `c++locale.o` / `codecvt.o` / `collate_members*` /
`cow-wstring-inst.o` / `cxx11-wlocale-inst.o` / `fstream-inst.o` / `cp-demangle.o`** 等
整套 locale/iostream 机器 —— 成员数 415 → 538,`.text` 615KB → 654KB,且把
`.bss` 里 64KB 的静态表挤成 `.data`(`.data` 从 1.8KB 涨到 73.8KB)。
修法:改用 `_wfopen_s` + `_fseeki64/_ftelli64` 取大小(零依赖)。
2. **`-lz`(~46KB)**:只需要「解开 HDF5 分块」这一功能,不值得链整个 zlib。
修法:`MatH5.cpp` 内置一份紧凑 inflate(RFC1950/1951,含固定/动态 Huffman、
存储块、长度-距离回抄),同时把 `-lz` 从 Makefile 与探针脚本移除。
顺带修掉一处**体积隐患**:`UploadHaloLayer` 里 64KB 的预乘 alpha 查表被换成
`(v*a+128)*257>>16` 的乘法-移位(无除法、无表)。⚠️ 若将来开 `-fdata-sections`,
函数内静态数组会被放进 `.data$_ZZ...`(**实测凭空 +72KB 文件体积**),这条也一并避开了。
### B. 剩下 ~40KB 的正当成本与最终压缩手段
去掉上面两项后仍是 825,856(旧版 786,944,+38.9KB = MatH5 15.4KB + MatLoader 12.5KB +
对齐/导入表,属"新增一个格式解析器"的正常成本)。要压到 <800KB,采用两项**不改变行为与观感**的措施:
1. **图标资源无损重编码**:`Resources/JustDataInspector.ico` 里 64/48/32/16 四帧原本是
未压缩的 32bpp DIB(合计 32KB),改用 GDI+ 重编码为 PNG 帧(ICO 的 PNG 条目 Vista+
全支持,本文件本身就有 256 PNG 帧):**43,334 → 17,404 字节**。
像素逐点校验:差异像素 ≤85 个、最大分量差 ≤3(GDI+ 编码取整),肉眼不可辨;
`matui` 探针从**真实产物**里 `LoadImageW` 逐一取 16/32/48/64 帧,全部 ok。
2. **`RELEASE_OPT` 由 `-Os` 改为 `-Oz`**(省 ~6KB)。实测动画帧 p50 7.73ms(-Os) vs
7.77ms(-Oz),无可辨识差异(本项目热点全在 GDI/GDI+ 绘制)。
⚠️ 试过并**放弃**的手段:`-ffunction-sections -fdata-sections + -Wl,--gc-sections` 反而 +9KB
(分节后每函数 16 字节对齐 + 静态量落 `.data`);LTO 在该工具链上 **ICE** 崩溃;
`-fno-asynchronous-unwind-tables` 链接失败;`-fno-exceptions` 会破坏 `DataModel` 的
`catch (std::bad_alloc)`。
最终:**`Build/Release/Target.exe` = 795,648 字节**(< 800KB,约 4.4KB 余量)。
### C. ② `.mat` 加载进度不更新
真因:`LoadMatSourceGroups` 只在**结束时**回调一次 `ratio=1.0` 且从不设置 `hasBytes`;
读文件(42MB)与解码 19 个样本流(解压 + 拷贝,占大头)期间进度恒为 0 ⇒ 遮罩层
一直显示 0.00%、总大小未知。
修法(`MatH5` + `MatLoader`):
- `MatHdf5::Open` 新增 `OpenProgress(bytesRead,totalBytes)`,**按 4MB 分块读入并回报**,
读完即得到"已加载 = 总大小";
- `MatLoader` 用 `ProgressReporter` 把进度拆成 `0.00–0.55 读文件`(`hasBytes=true`)
与 `0.55–0.98 解码样本流`(每解完一个流推进 `done/total`),末段 1.0;
- 取消(回调返回 false)与出错分开处理:取消时清空 `error`,不弹错误框。
实测(`matcheck`):回调 **29 次**、其中字节数变化 **11 次**、比例单调、终值 1.000、
`bytes=42,386,344/42,386,344`(= 文件大小);`matui` 进一步断言
`Application` 的 `m_loadingBytesRead == m_loadingTotalBytes == 文件大小`(遮罩层拿到的值)。
### D. 验证(全部离屏,不占用鼠标/键盘/屏幕)
- `matcheck` **21/21**(新增进度断言)、`matui` **15/15**(新增图标 + 加载字节断言);
- 回归:`verify` 49/49、`panelanim` 158/158、`gridanim` 93/93、`hoverdrag` 20/20、
`lanescroll` 23/23、`datacheck` 69/69、`edgecheck` 46/46、`errcheck` 19/19、
`halocheck` 16/16、`panflash` 19/19、`panelstutter` 8/8、`stress` 6/6;
- `make release` / `make debug` 零警告;`Build/Release/Target.exe` 795,648 字节。
### E. 教训(新增规则)
1. **MinGW 下 `#include <filesystem>` 是体积炸弹**(连带 locale/iostream/demangle 整套,
+0.9MB);只需要"取文件大小"时用 `_wfopen_s + _fseeki64`。
2. **为一个功能链一整条库前先算账**:`-lz` 46KB vs 自带 inflate 3~4KB;
能用几十行自实现的就不要链。
3. **判断二进制膨胀要看节表**(`objdump -h` + `-Wl,-Map` 逐对象求和),
只看 `.o` 大小会被"归档成员/对齐/`.data` 里的静态表"骗过去。
4. **图标资源可以无损重编码**(DIB 帧 → PNG 帧),同一份像素省 60% 体积;
用探针从**真实产物** `LoadImageW` 逐尺寸校验,而不是只看文件能编译。
5. **任何"长时间解析"都要按阶段回报进度**(读字节 + 每个解码单元),
且 `hasBytes` 只在真正有字节可报的阶段置真,否则遮罩层会停在 0%。