# Code Review 进度(分支 `Code_Review`) > 每个节点完成后追加:节点号、改动摘要、验证方式、实测数字。 > 计划见 `Documents/Code_Review_Plan.md`;最终报告见 `Documents/Code_Review_Report.md`。 ## 基线(起点 `764e5be`) | 项 | 数值 | | --- | --- | | Release 产物 | **890,880 B** | | `.text` | 753,136 B | | `.rdata` | 36,936 B | | `.pdata` | 30,420 B | | `.xdata` | 29,496 B | | `.rsrc` | 20,656 B | | `.idata` | 12,856 B | | Debug 产物 | 1,801,617 B | | 源码规模 | 约 5.6 万行 | 关键发现(详见计划 §一): * `dynamic_cast`(`Toolbar.cpp:370`)使最小复现程序从 39,936 B 涨到 115,200 B ⇒ **+75,264 B**;map 中 `cp-demangle.o` 占 **56,320 B**。 * 每个 `EdgePanelKind` 在源码中出现 21–26 次(6 种面板 ⇒ 约 140 处手写分支)。 --- ## 节点记录 (下面按完成顺序追加) ### P1 移除 `dynamic_cast` + 关闭 RTTI —— ✅ 完成(提交 `5de3763`) **改动** 1. `Toolbar::BuildBoxes` 里唯一的 `dynamic_cast` 是**多余强转**(`Button::widget` 本来就是 `UiButtonWidget*`)。改为直接遍历 `m_buttons`(与 `m_root->Children()` 同源同序)。 2. 仅关 RTTI **不够**:`cp-demangle.o` 仍在链接里。真凶是 libstdc++ 的 `vterminate.o` (引用 `__cxa_demangle`),它被 `eh_terminate.o` 的 `std::terminate` 拉入。 ⇒ 在 `Utilities/Src/MemProbe.cpp` 里**自行定义** `__gnu_cxx::__verbose_terminate_handler()`, 归档成员 `vterminate.o` 不再被拉入,demangler 整体消失。 应用启动后本来就会 `std::set_terminate` 换成自己的处理函数,行为等效。 3. `Makefile` 新增 `NO_RTTI=1`(默认开)→ `CXXFLAGS += -fno-rtti`。 **实测** | 指标 | 改前 | 改后 | 差 | | --- | --- | --- | --- | | Release | 890,880 B | **829,440 B** | **−61,440 B** | | `.text` | 753,136 B | 703,616 B | −49,520 B | | `.rdata` | 36,936 B | 26,616 B | −10,320 B | | Debug | 1,801,617 B | 1,734,578 B | −67,039 B | **验证**:release + debug **全量重建**(删 `*.o`/`*.d`)零 error 零 warning; map 中 `cp-demangle.o` 已无 `.text` 段分配。 ### P2 Release 关闭异步展开表 + 修正 Debug 构建参数 —— ✅ 完成(提交 `94fc1c4`) **改动** 1. 新增 `RELEASE_ONLY_OPT = -fno-asynchronous-unwind-tables`,**只**加在 Release 的两条 编译规则上。`-fno-exceptions` 下绝大多数函数不再需要 `.pdata`/`.xdata`。 * 代价(已评估接受):Release 崩溃时 WER 拿不到可靠调用栈;不打印栈回溯的程序 功能不受影响,Debug 构建保留完整展开表。 * `-fno-unwind-tables`(全关)会链接失败,不采用。 2. **修正真实缺陷**:`DEBUG_OPT = -O0` 定义了却**从未被使用** —— Debug 的两条编译规则 误用 `$(RELEASE_OPT)`(`-Oz`),且全程没有 `-g`。所谓 "Debug 构建" 其实只是 "不 strip 的 Release",根本无法调试。现改用 `$(DEBUG_OPT) -g`,链接也加 `-g`。 **实测** | 指标 | 改前 | 改后 | | --- | --- | --- | | Release | 829,440 B | **793,088 B**(−36,352) | | `.pdata` | 30,420 B | 12,684 B | | `.xdata` | 28,904 B | 10,156 B | | Debug | 1,734,578 B | 5,992,064 B(真实 `-O0 -g`,属预期) | **验证**:release 与 debug 均全量重建,零 error 零 warning(`-O0` 下也没有新警告)。 ### P7 面板状态/滚动量收敛为唯一派发点 —— ✅ 完成(提交 `7a3fefb`) **问题(屎山核心之一)**:`Lane` 里 7 种实验性功能各有 1 份 `LanePanelState` + 1 个滚动偏移 (14 个字段)。旧实现在**每一处**“按种类取用”的地方各写一遍嵌套三元式或 `switch`: `EdgePanelLayoutFor` 5 条 7 层嵌套三元式;标题栏拖拽、✕ 关闭、滚轮、滚动条、错开序数各写一遍。 新增面板要改 20+ 处,漏一处就是「拖不动 / 关不掉 / 滚轮滚不到底」 (`MARK`、`RESAMPLE`、`CLIP` 均真实踩过)。 **改动**:`PlotViewInternal.hpp` 新增唯一派发点 `LanePanelEnabled` / `LanePanelStateOf` / `LanePanelScrollOf`(可写 + 只读)。 这些 `switch` **穷举 7 种且不写 `default:`** ⇒ `-Wswitch` 在漏登记时编译报警 (已用最小程序验证 `-Wswitch` 确实会报)。 调用点:布局 5 条三元式 → 3 行;标题栏拖拽 7 分支 switch → 1 次写入; 滚轮/滚动条/✕ 关闭/折叠切换全部改为穷举无 `default`。 **验证**:release + debug 零 warning;产物 **793,088 B 不变**(等价变换,说明只是结构收敛)。 **未做(受验证条件限制)**:尚未把 `EdgePanelLayoutFor` 的 957 行分支整体改成 “每面板一行元数据表”。原因:没有可用的离屏像素对比工具(`.preflight` 已删除, 且崩溃会弹模态框),纯逻辑重写无法自证“像素完全一致”。已通过上面的派发收敛 消除最容易出错的一类重复,剩余重复属于“几何算式本身”,风险高于收益。 ### P11 拆分 `ApplicationCommands.cpp` —— ✅ 完成(提交 `8437100`) 原文件 **2,165 行**,混杂六类职责。按职责**纯搬移**(零逻辑改动): | 新文件 | 行数 | 内容 | | --- | --- | --- | | `ApplicationTabs.cpp` | 355 | 标签页新建/关闭/切换/重排、视图同步、窗口标题、标签栏回调、标签页右键菜单 | | `ApplicationDerive.cpp` | 786 | 派生烘焙、重采样、选区截取(新建/就地)、克隆(含带调整) | | `ApplicationMenus.cpp` | 409 | 绘图区右键菜单、菜单数值回写、主题切换、设置菜单、菜单结果派发 | | `ApplicationCommands.cpp` | 642 | 分组/信号增删、移动与重排、文件对话框、工具栏动作、键盘鼠标分发、布局边界绘制 | Makefile 按目录通配收集源文件,**无需登记新文件**。 **验证**:release 零 error 零 warning;产物 793,088 → 794,112 B(+1,024,LTO 内联边界 变化,< 0.13%)。 ### P15 注释/文档校正 —— ✅ 完成(提交 `f139440`) * `PlotView.hpp`:删除指向已删除代码的 `FormulaEnvelopeEntry` 注释块。 * `agent_mem.md` §2:`-Os` → `-Oz`;补体积开关(`NO_EXCEPTIONS` / `NO_RTTI` / `LTO` / `RELEASE_ONLY_OPT`)与「改 Makefile 必须删 `.o` 全量重编」提醒;补 `msimg32`; 忽略列表补 `Temp/`、`.preflight/`、`Tools/`。 * `agent_mem.md` §3:目录结构按现状重写(删除不存在的 `CsvTable/`、`DataModel/`、 `PaintUtil/` 等旧条目;写明 `Components/Shared` 与 `Utilities` 的真实职责和依赖方向)。 * `agent_mem.md` 末尾新增「第 33 轮」记录。 ### P16 最终验证与报告 —— ✅ 完成 * release(全量)+ debug(全量 `-O0 -g`)**零 error 零 warning**。 * 最终产物 **794,112 B**(起点 890,880 B,−96,768,−10.9%)。 * `cp-demangle.o` 已无 `.text` 段分配;`__gnu_cxx::__verbose_terminate_handler` 定义在本产物内(`0x140057ed1`);导入 DLL 无新增。 * 报告:`Documents/Code_Review_Report.md`。 ### 未完成/搁置的节点(均已在报告中说明原因) * **P8** `SceneContentHash` 混入收敛:无逐像素验证手段,合并会改变哈希值,风险不划算。 * **P9** `EdgePanelLayoutFor` 表驱动化:同上(957 行几何分支,无法自证像素一致)。 * **P10** 缓存五件套抽象:同上,且热路径复杂。 * **P12** `PlotView.hpp` 拆分:嵌套类型外移需改 70+ 处引用,收益仅为可读性。 * **P13/P14** UI 基础层与绘制边界核对:已做**依赖方向与引用统计**,未发现重叠或反向依赖, 因此不再拆分。 * **性能项**:未发现新的可复现热点;已记录 3 个"还能再快一点"的点(见报告 §6.2)。 --- ## 第 34 轮(2026-11-04):验证基础设施 + §10 后续建议落地 ### T1 验证工具入库 —— ✅ 完成(提交 `97e9163`) **改动** 1. `.gitignore`:删除整目录忽略的 `Tools/`,改为 `Tools/**/Build/`(只忽略各工具产物)。 2. 新增 `Tools/Makefile`:`make` / `make UnitTests` / `make OffscreenChecks` / `make clean` / `make help`; 工具链接本体 `../Build/Release/*.o`(排除 `Main.o`),`-MMD -MP` 跟踪头文件依赖。 3. 新增 `Tools/UnitTests`(`Inc` + `Src` + `Build`)与 `Tools/OffscreenChecks`(同构)与 `Tools/README.md`。 **踩到并修掉的两个构建陷阱** * 工具源码也加 `-flto` 会让 GCC 15.2.0 在链接阶段 `choose_baseaddr`(i386.cc:7447)**ICE** ⇒ 工具源码不带 `-flto`,只在链接参数里带 `-flto -flto-partition=one`。 * `$(foreach)` 生成的工具目标早于 `all` 出现 ⇒ make 把**第一个**显式目标当默认目标, 直接 `make` 只会构建 `UnitTests` ⇒ 显式 `.DEFAULT_GOAL := all`。 **实测**:`make -C Tools` 零 warning;两个产物分别在 `Tools//Build/.exe`。 ### T2 单元测试 —— ✅ 完成(219 条断言 / 18 个用例) 覆盖:`TryParseDouble` 与 libc `strtod` **逐位一致**(6 万条随机十进制串)、 输入接受性(尾随字符 / 十六进制浮点 / `inf` / `nan`)、`FormatDecimal*` 永不科学计数法、 `NiceStep` / `BuildTicks`、公式语法与语义、`FormulaProgram` 与 `EvaluateFormula` 逐位一致、 `ApplyDrawTransform` 顺序、`TransformValueRange`、滑动平均多阶等价性、 `DataSet::FormulaTransformFor` 值域与分组极值(逐点参考实现比对、命中同指针、样式变化失效)、 以及新增的 `stdio_mingw_vs_ucrt_formats`(见 T6)。 **过程中修正了 5 处"我以为的语义"**(都以源码为准,测试跟着产品改): `9.999999999999999` 属 >15 位有效数字(允许 1 ULP);`1..2` 其实合法; `FormulaIsIdentity` 不去空格;非法公式求值时 `EvaluateFormula` **原样返回采样值**(不是 NaN)。 ### T3 离屏验证 —— ✅ 完成(5 个检查 / 71 条断言) * `smoke`:初始化 + 加载 + 渲染 + 落盘;**同一状态两帧逐像素相同**(实测差异 0 像素)。 * `frames`:连续帧抓取(PNG + 区域 FNV 哈希 + 耗时),帧序列至少出现一次可见变化。 * `panels`:7 个面板全部展开后的几何不变式(必须被绘图区夹住)+ 像素基线 `-mode record/compare`。 * `edgestats`:边沿统计与工具内**独立朴素实现**逐位比对(阈值 2.4875/4.975/7.4625 → 20/19、20/19、20/19 完全一致;中心轴 1200/1179 也能完成)。 * `perf`:整帧中位数、`EdgePanelLayoutFor` 单次、198 个统计状态的整轮查询耗时。 **全程离屏**:窗口 `SW_HIDE`、"鼠标"用 `Application::HandleMouseMove()` 合成注入、 像素从后备缓冲区读取;进程用 `std::_Exit()` 收尾(避免单例析构晚于 `GdiplusShutdown`)。 ### T4 `EdgeScanFor` 状态查找哈希化 —— ⛔ **实测后回退**(建议 #2) * **改动前**:`mutable std::vector` + 逐字段线性扫描。 * **改法**:容器改 `std::deque`(指针稳定)+ `mutable std::unordered_multimap` 按身份哈希索引,桶内仍逐字段比对(碰撞安全);新建 / 淘汰 / 定向作废 / 清空四处同步索引。 * **正确性**:`edgestats` 全绿(真实数据的计数与独立参考实现逐位相同),`panels` 像素基线不变。 * **性能(198 个状态,`MF4-Record.MF4`)**:整轮查询 `0.097 → 0.073 us/query` (一轮 198 次查询省 ≈4.8 μs)。 * **体积代价**:`788,480 → 794,112 B`(**+5,632 B**)。 * **结论:回退**。5.6 KB 的体积换 ≈5 μs/帧(约占 5 ms 整帧的 0.1%)不成立 —— 这与 §6.2 原本的判断一致,本轮把它从"估计"变成了**实测**。 ⚠️ 同时修正了 §6.2 的量级:报告原估"288 信号时 ≈0.3 ms/帧",实测 198 状态整轮 **≈20 μs/帧**, 差一个数量级(线性扫描的常数很小)。 ### T5 `EdgePanelLayoutFor` 表驱动化 —— ⏸ 只交付"可验证手段",**未重写** * 第 33 轮已把**按种类分派**收敛成唯一派发点(`LanePanelEnabled` / `LanePanelStateOf` / `LanePanelScrollOf`,且 `switch` 不写 `default:` 让 `-Wswitch` 兜底)。 * 剩余的重复是 957 行**几何算式本身**(每个面板的内容行高、表头、按钮、滚动区各不相同), 无法用"每面板一行元数据"表达。 * 本轮实测该函数 **1.9~2.5 μs/次**(2000 轮 × 7 种),按每帧约 14 次调用计约 **30 μs/帧**, 不是热点。 * ⇒ 重写的收益是"可读性",代价是 957 行几何公式的可回归风险。 **本轮交付的是让这类重写变得可验证的工具**:`-run panels -mode record/compare` 会逐面板比对**几何 + 像素哈希**(7 个面板、含内容区/滚动条/按钮)。 ### T6 `wcstod` 自写替身(省 gdtoa)—— ⛔ **实测 0 字节收益**(建议 #4) * 报告 §1.3 的估计是"≈20 KB"。本轮先做**最小实验**而不是先写代码: 在 `Makefile` 里加 `-D__USE_MINGW_ANSI_STDIO=0`(把 `swprintf` 交给系统 UCRT),全量重编。 * **实测:产物仍然 788,480 B,一个字节都没变**;map 里 `mingw_wpformat.o` 依旧被链接。 * **原因(用 3 行最小程序确认)**:预编译的 **libstdc++ 自己引用 `__mingw_swprintf`** —— 只要工程里还调用 `std::to_wstring` / `std::to_string`(全仓约 80 处,其中若干处实参是浮点), 链接器就必须把 MinGW 的 printf 引擎(含 gdtoa)拉进来。 * ⇒ 真正的 33 KB 收益要以"替换全部 80 处 `std::to_*string`(含浮点格式语义)"为代价, 风险与收益不匹配,**不实施**。该实验已回退,只在 `Makefile` 里留下一段说明防止重复尝试。 * **顺带产出可复用的验收测试** `stdio_mingw_vs_ucrt_formats`: 同一批真实格式(`%.*f` / `%.6g` / `%g` / `%.4e` / `%.2f %ls` / `%llu` / `%lld` / `%02X` / `%.2f%%`) × 13.6 万次调用做**逐字符比对**,**0 处差异**。将来若真做迁移,这条测试就是验收标准。 ### 本轮验证汇总 | 项 | 结果 | | --- | --- | | `make release`(删 `.o` 全量) | 零 error、零 warning | | `make -C Tools` | 零 error、零 warning | | `Tools/UnitTests/Build/UnitTests.exe` | `checks=219 failed=0 cases=18` | | `Tools/OffscreenChecks/Build/OffscreenChecks.exe -all` | 5 个检查全 PASS,`checks=71 failed=0` | | Release 产物 | **788,480 B**(与第 33 轮末一致;T4/T6 均回退) |