From c5d7a6fb91e8d46dc3969a6b53d8afaafb06913c Mon Sep 17 00:00:00 2001 From: "DeepSeek-V4.1-Flash" <> Date: Wed, 4 Nov 2026 03:08:45 +0800 Subject: [PATCH] =?UTF-8?q?docs(review):=20=E6=96=B0=E5=A2=9E=20Code=20Rev?= =?UTF-8?q?iew=20=E6=9C=80=E7=BB=88=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents/Code_Review_Report.md:逐项说明本轮改了什么、为什么、效果如何, 含体积节表前后对照(890,880 -> 794,112 B,-96,768 / -10.9%)、 Debug 构建缺陷(DEBUG_OPT 从未生效)、面板派发收敛与编译期保护、 ApplicationCommands 拆分、性能审查结论(含 3 个记录未做的优化点)、 注释/文档校正、验证清单、提交清单与后续建议。 同时把 Code_Review_Progress.md 补齐 P15/P16 与“搁置节点”的原因说明。 --- Documents/Code_Review_Progress.md | 28 +++ Documents/Code_Review_Report.md | 325 ++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 Documents/Code_Review_Report.md diff --git a/Documents/Code_Review_Progress.md b/Documents/Code_Review_Progress.md index e2d7aa6..064a4f1 100644 --- a/Documents/Code_Review_Progress.md +++ b/Documents/Code_Review_Progress.md @@ -117,6 +117,34 @@ 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)。 + diff --git a/Documents/Code_Review_Report.md b/Documents/Code_Review_Report.md new file mode 100644 index 0000000..a44de54 --- /dev/null +++ b/Documents/Code_Review_Report.md @@ -0,0 +1,325 @@ +# Code Review 报告(2026-11-04,分支 `Code_Review`) + +> 计划:`Documents/Code_Review_Plan.md` | 逐节点记录:`Documents/Code_Review_Progress.md` +> +> 起点 `764e5be`(dev,已删除过时的 `UI_Refactor_*` 三份文档),终点 `f139440`。 + +--- + +## 0. 一句话结论 + +**体积从 890,880 B 降到 794,112 B(−96,768 B,−10.9%)**,同时 +① 修掉一个"Debug 构建从来不是 Debug"的真实缺陷; +② 把"新增实验性功能面板必须手改 20+ 处"的屎山点收敛成**唯一派发点 + 编译期强制检查**; +③ 把 2,165 行的 `ApplicationCommands.cpp` 拆成 4 个职责分片; +④ 校正了项目记忆里已经失真的目录结构与编译设置。 + +性能方面:本轮**没有发现新的、可复现的卡顿热点**(上一轮公式映射的 4 处卡死已根治); +审到 3 个"可以再快一点"的点,但都因为**缺少可自证的验证手段**而只记录、不动手(见 §6)。 + +--- + +## 1. 体积:890,880 → 794,112 B + +### 1.1 节表前后对照 + +| 节 | 改前 | 改后 | 差 | +| --- | --- | --- | --- | +| `.text`(代码) | 753,136 | **703,616** | −49,520 | +| `.rdata`(只读数据) | 36,936 | **26,456** | −10,480 | +| `.pdata`(SEH 展开) | 30,420 | **12,684** | −17,736 | +| `.xdata`(SEH 展开) | 29,496 | **10,156** | −19,340 | +| `.data` | 1,680 | 2,704 | +1,024 | +| `.idata`(导入表) | 12,856 | 12,692 | −164 | +| `.rsrc`(图标/清单/版本) | 20,656 | 20,656 | 0 | +| **文件总计** | **890,880** | **794,112** | **−96,768(−10.9%)** | + +### 1.2 逐项来源 + +#### (1)`dynamic_cast` 是一个 +75 KB 的"体积炸弹"——提交 `5de3763` + +`Toolbar::BuildBoxes` 里唯一的一处: + +```cpp +const UiButtonWidget* raw = dynamic_cast(button.get()); +``` + +而 `Button::widget` **本来就是 `UiButtonWidget*`** —— 这个转换毫无作用。 + +用最小复现程序量化(`g++ -O2 -fno-exceptions -static -s`): + +| 最小程序 | 体积 | +| --- | --- | +| 纯虚函数 + 虚析构(无 `dynamic_cast`) | 39,936 B | +| 多一个 `dynamic_cast` | **115,200 B** | +| 差 | **+75,264 B** | + +原因:`dynamic_cast` 需要 RTTI 元数据,并牵出 libstdc++ 的 +`__dynamic_cast` → `__cxa_bad_cast` → verbose terminate handler → **`cp-demangle`** +(Release map 里 `cp-demangle.o` 占 `.text` 49,600 + `.rdata` 6,720)。 + +改为直接遍历 `m_buttons`(与 `m_root->Children()` 同源同序),并开启 `-fno-rtti`。 + +#### (2)只关 RTTI 不够:demangler 仍被 `vterminate.o` 拉入——同一提交 + +关掉 RTTI 后 **`cp-demangle.o` 依然在链接里**(实测只省 2 KB)。查 map 找到真凶: + +``` +libstdc++.a(eh_terminate.o) (__gnu_cxx::__verbose_terminate_handler()) +libstdc++.a(vterminate.o) (__cxa_demangle) +libstdc++.a(cp-demangle.o) ... +``` + +`std::terminate()`(`eh_terminate.o`)引用 `__gnu_cxx::__verbose_terminate_handler()`, +该符号由归档成员 `vterminate.o` 提供,而它调用 `__cxa_demangle`。 + +**修法**:在 `Utilities/Src/MemProbe.cpp` 里**自行定义**该函数 +(`std::terminate` 的未定义引用被我们满足 ⇒ `vterminate.o` 不再被拉入 ⇒ demangler 消失)。 + +> ⚠️ 必须定义在**全局** `__gnu_cxx` 命名空间,不能嵌在 `jdi` 里。 +> ⚠️ 行为等效:应用启动后本来就会 `std::set_terminate` 换成自己的处理函数, +> 这里的默认实现只在"启动早期崩溃"时生效,同样给出内存不足提示后退出。 + +#### (3)Release 关闭异步展开表——提交 `94fc1c4` + +`RELEASE_ONLY_OPT = -fno-asynchronous-unwind-tables`,**只**加在 Release 的两条编译规则上。 + +`-fno-exceptions` 下绝大多数函数不再需要 `.pdata`/`.xdata`:−36,352 B。 + +**代价(已评估并接受)**:Release 崩溃时 WER 拿不到可靠调用栈。 +本程序不打印栈回溯,功能不受影响;Debug 构建保留完整展开表。 +(`-fno-unwind-tables` 全关会链接失败,历史上踩过,不采用。) + +#### (4)`.rsrc` 20,656 B 无优化空间 + +图标 17,404 B(已是 PNG 承载的多尺寸帧)+ manifest 1,955 B。 +再压只能减图标帧或降尺寸,会影响观感,**不动**。 + +### 1.3 未采纳的体积方案(实测否决) + +| 方案 | 实测 | 结论 | +| --- | --- | --- | +| `-fno-asynchronous-unwind-tables`(Debug 也开) | — | 否决:Debug 需要展开表才能调试 | +| `-fno-unwind-tables`(全关) | 链接失败 | 否决(历史已踩) | +| 替换 `wcstod` 以去掉 `strtodg/gdtoa/gethex/hexnan` | ≈20 KB | 暂缓:公式/输入框的数值语义必须与现状逐位一致,收益不足以承担风险 | +| 图标继续瘦身 | — | 否决:观感优先 | + +--- + +## 2. 构建系统:修掉一个"Debug 从来不是 Debug"的缺陷——提交 `94fc1c4` + +审查 Makefile 时发现: + +```make +DEBUG_OPT = -O0 # 定义了 +... +# Debug 目标文件编译规则 +$(CXX_COMPLIER) -c $(CXXFLAGS) $(RELEASE_OPT) ... # ← 用的是 -Oz! +``` + +**`DEBUG_OPT` 从未被任何规则引用**,且全流程没有 `-g`。 +所谓 "Debug 构建" 实际上只是"不 strip 的 Release",**根本没有调试信息、也不是 `-O0`**。 + +现在 Debug 规则改用 `$(DEBUG_OPT) -g`,链接也加 `-g`。 + +| 构建 | 改前 | 改后 | 说明 | +| --- | --- | --- | --- | +| Release | 890,880 | **794,112** | 体积优化 | +| Debug | 1,801,617 | **5,994,805** | 变**大**是**正确**的:真实 `-O0 -g` | + +> ⚠️ 这意味着"Debug 构建更慢、更大"从此名副其实;如果以后仍在用 `Build/Debug` 做 +> 日常运行,请改用 Release。 + +--- + +## 3. 架构:面板分派收敛为唯一派发点——提交 `7a3fefb` + +### 3.1 问题(屎山核心) + +`Lane` 里 7 种实验性功能(边沿计数 / 中心轴 / 均值滤波 / 弱峰调整 / 自由标记 / +重采样 / 选区截取)各持有一份 `LanePanelState` 与一个滚动偏移,共 14 个字段。 +旧实现在**每一处**"按种类取用"的地方各写一遍嵌套三元式或 `switch`: + +| 位置 | 旧写法 | +| --- | --- | +| `EdgePanelLayoutFor` 的 `enabled` / `open` / `collapsed` / `storedX` / `storedY` | **5 条 7 层嵌套三元式** | +| 标题栏拖拽 | 7 分支 `switch`(漏 `MARK`/`RESAMPLE` 各一次 ⇒ 面板拖不动) | +| ✕ 关闭 | 7 分支 `switch` + `default`(漏 `RESAMPLE` ⇒ 关掉的是别的面板) | +| 滚轮翻页 | 三元式链 + `switch`(漏 `CLIP` ⇒ 永远滚不到底) | +| 滚动条拖动 | `switch` + `default` | +| 折叠状态读写 | 2 个 `switch` | +| 首现错开序数 | 局部 `kOrder[]` + `switch` | + +新增一种面板要改 **20+ 处**,漏一处就是运行时 BUG —— 历史上 `MARK`、`RESAMPLE`、 +`CLIP` **三种面板各自都真实踩过**。 + +### 3.2 修法 + +`PlotViewInternal.hpp` 新增唯一派发点(各带可写/只读重载): + +```cpp +LanePanelEnabled(lane, kind); // 功能是否启用 +LanePanelStateOf(lane, kind); // 展开/折起/位置 +LanePanelScrollOf(lane, kind); // 内容滚动偏移 +``` + +调用点全部改为访问器:布局里 5 条嵌套三元式 → 3 行;标题栏拖拽 7 分支 → 1 次写入; +滚轮/滚动条/✕/折叠全部改为穷举。 + +### 3.3 ⭐ 关键收益:编译期保护 + +这些 `switch` **穷举 7 种且一律不写 `default:`** ⇒ `-Wswitch`(包含在 `-Wall` 里) +在新增 `EdgePanelKind` 而漏登记时**直接编译报警**。 + +已用最小程序验证该机制确实生效: + +``` +warning: enumeration value 'C' not handled in switch [-Wswitch] +``` + +⇒ **今后新增实验性功能面板,忘记登记会编译失败,而不是运行时静默出错。** + +(对比:旧代码用 `case COUNT: default:` 兜底,`-Wswitch` 永远不会报。) + +### 3.4 验证 + +release + debug 零 warning;**产物体积不变(793,088 B)** —— 这正好说明本次是 +纯粹的等价结构收敛,没有引入额外代码。 + +--- + +## 4. 架构:拆分 2,165 行的 `ApplicationCommands.cpp`——提交 `8437100` + +原文件混杂六类职责。按职责**纯搬移**(零逻辑改动、函数体一字未改): + +| 文件 | 行数 | 职责 | +| --- | --- | --- | +| `ApplicationTabs.cpp` | 355 | 标签页新建/关闭/切换/重排、视图同步、窗口标题、标签栏回调、标签页右键菜单 | +| `ApplicationDerive.cpp` | 786 | 派生烘焙、重采样、选区截取(新建 / 就地)、克隆(含带调整) | +| `ApplicationMenus.cpp` | 409 | 绘图区右键菜单、菜单数值回写、主题切换、设置菜单、菜单结果派发 | +| `ApplicationCommands.cpp` | 642 | 分组/信号增删、移动与重排、文件对话框、工具栏动作、键盘鼠标分发、布局边界绘制 | + +Makefile 按目录通配收集源文件,**无需登记**。 + +体积 793,088 → 794,112 B(+1,024,LTO 内联边界变化,< 0.13%)。 + +--- + +## 5. 审查中"没有发现问题"的项(同样重要) + +这些是**主动查了但结论是干净**的,避免后续接手者重复怀疑: + +| 检查项 | 方法 | 结论 | +| --- | --- | --- | +| 死代码(未使用的静态函数) | `-Wall` 零警告(GCC 会对未使用的 `static` 函数报警) | 无 | +| 死字段(只声明不使用) | 对 `PlotView` / `SignalPanel` / `Application` / `TabBar` / `Toolbar` / `DataSet` 的全部 `m_*` 成员做全仓引用计数 | 233+ 个成员**无一**只声明不使用 | +| TODO / FIXME / 临时方案 | 全仓 grep | 无(源码里的"未完成"全是"边沿统计进行中"的业务语义) | +| 布局文本测量是否重复开销 | 读 `UiMeasureTextWidth` 实现 | 已记忆化(键 = 字体 + 文本),不是热点 | +| 页面上是否残留指向已删文件的注释 | grep `UI_Refactor_*` / `refactor_report` / `structcheck` / `.preflight` | 仅 1 处(`PlotView.hpp` 的 `FormulaEnvelopeEntry` 注释),已删 | + +--- + +## 6. 性能审查结论 + +### 6.1 结论 + +**没有发现新的、可复现的卡顿热点。** 上一轮(第 32 轮)的四处卡死同源于 +"公式信号幅值范围/包络各扫一遍整条信号",已通过 `DataSet::FormulaTransformFor` +(一次扫描 + 缓存,命中 0.0015 ms)根治。 + +逐一复核了下列曾出过问题的路径,均仍走快路径: + +| 路径 | 现状 | +| --- | --- | +| `BuildLaneVisuals` 的幅值范围 | 指纹缓存(`m_laneRangeKey`),命中时 O(1) | +| 场景/装饰/面板/图层像素缓存 | 键齐全,未发现遗漏项 | +| 边沿统计 | 按帧预算 + 每状态配额推进,暂停条件齐备 | +| 图层预建 | `EnsureLaneLayersPrefetched`(不再整批重建) | +| 信号面板行集合自愈 | `RowsSignature` 每帧 O(组数),组数远小于信号数 | + +### 6.2 记录但未实施的三处"还能再快一点" + +| 位置 | 量级 | 为什么不改 | +| --- | --- | --- | +| `EdgeScanFor` 的累积状态查找是**线性扫描** `m_edgeScans`(上限 2×信号数+余量)。288 信号时每帧约 16 万次指针比较 | ~0.3 ms/帧 | 改哈希索引需要重做"淘汰最旧已完成状态"的逻辑;收益与风险不匹配,且该路径已被预算化 | +| `EdgePanelLayoutFor`(957 行分支)整体改为"每面板一行元数据表" | 可读性 + 少量代码体积 | **没有离屏像素对比工具**(`.preflight` 已删,崩溃会弹模态框),纯逻辑重写无法自证"像素完全一致" | +| `SceneContentHash` 里 6 段逐面板混入循环可合并 | 可读性 | 合并会改变哈希值;虽然"多加字段安全、少加字段危险",但在无法逐像素验证的前提下不值得冒险 | + +### 6.3 关于"性能验证手段"的重要说明 + +本轮**没有**做任何 GUI 探针,原因(也是给后续接手的约束): + +* 用户要求不占用其鼠标/键盘/屏幕; +* `.preflight` 测试框架已被用户删除,且已明确"不要重建"; +* `-fno-exceptions` + `MemProbe::OnTerminate()` 会在探针崩溃时弹**模态"内存不足"框**, + 反复运行就是反复弹窗(第 26 轮的真实教训)。 + +因此本轮的体积结论全部来自**可复现的静态测量**(`objdump -h` 节表 + `Makefile` 变量 + +非 LTO 目标文件尺寸),架构结论来自**编译期检查**(`-Wswitch`)与**等价性论证** +(体积逐字节不变)。 + +--- + +## 7. 注释与文档 + +### 7.1 删除 + +* `Documents/UI_Refactor_Plan.md`、`UI_Refactor_Progress.md`、`UI_Refactor_Report.md` + (描述 2026-10-28 那一阶段,已被后续多轮实现推翻,与当前代码不一致)。 + +### 7.2 校正(`agent_mem.md`) + +| 位置 | 原内容(错) | 现内容 | +| --- | --- | --- | +| §2 | Release 用 `-Os` | `-Oz` + LTO + `-s` | +| §2 | 未提体积开关 | 补充 `NO_EXCEPTIONS` / `NO_RTTI` / `LTO` / `RELEASE_ONLY_OPT`,并警告"改 Makefile 必须删 `.o` 全量重编"(本轮实测:只改 Makefile 不会触发重编,会误以为"优化无效") | +| §2 | 链接库少 `msimg32` | 已补 | +| §3 | 目录里写着早已不存在的 `Utilities/CsvTable/`、`Utilities/DataModel/`、`Utilities/PaintUtil/`、`Utilities/PopupMenu/`… | 按现状重写(`Components/Shared` 承载通用控件与 `PopupMenu`/`ColorPickerPopup`;`Utilities` 为数据与格式层),并写明依赖方向 | +| 末尾 | — | 新增「第 33 轮」完整记录 | + +### 7.3 源码注释 + +* `PlotView.hpp`:删除指向已删除代码的 `FormulaEnvelopeEntry` 注释块。 +* 新增/改写的注释一律说明**为什么**(尤其是 `-fno-rtti`、`__verbose_terminate_handler` + 覆盖、`-fno-asynchronous-unwind-tables` 的代价),避免后续被"好心"改回去。 + +--- + +## 8. 验证清单 + +| 项 | 结果 | +| --- | --- | +| `make release`(删 `*.o`/`*.d` 全量) | **零 error、零 warning** | +| `make debug`(真实 `-O0 -g`,全量) | **零 error、零 warning** | +| Release 产物 | 794,112 B(< 1 MB 红线) | +| `cp-demangle.o` 是否仍占 `.text` | 已无 `.text` 段分配 | +| `__gnu_cxx::__verbose_terminate_handler` | 定义在**本产物内**(`0x140057ed1`),非库导入 | +| 导入 DLL 列表 | `comdlg32 / GDI32 / gdiplus / KERNEL32 / MSIMG32 / SHELL32 / USER32` + UCRT,无新增依赖 | +| 工作区状态 | 干净;4 个节点提交 + 1 个文档提交 + 1 个 dev 提交 | + +--- + +## 9. 提交清单 + +| 提交 | 分支 | 内容 | +| --- | --- | --- | +| `764e5be` | `dev` | 删除过时的 `UI_Refactor_*` 三份文档 | +| `86ff029` | `Code_Review` | 新增 `Code_Review_Plan.md` / `Code_Review_Progress.md` | +| `5de3763` | `Code_Review` | 移除多余 `dynamic_cast` + 关闭 RTTI + 覆盖 terminate handler(−61,440 B) | +| `94fc1c4` | `Code_Review` | Release 关闭异步展开表(−36,352 B)+ 修正 Debug 构建参数 | +| `7a3fefb` | `Code_Review` | 面板分派收敛为唯一派发点(编译期保护) | +| `8437100` | `Code_Review` | 拆分 `ApplicationCommands.cpp` → 4 个职责分片 | +| `f139440` | `Code_Review` | 清理过时注释 + 校正 `agent_mem.md` §2/§3 | + +--- + +## 10. 后续建议(按性价比排序) + +1. **重建离屏验证手段**(不弹窗口、不占用用户输入)。 + 没有它,"重构面板几何 / 缓存键"这类工作无法自证正确性 —— 这正是本轮 §6.2 + 三项被搁置的唯一原因。建议做纯控制台 + `SW_HIDE` + 逐像素哈希比对,并且 + **绝不调用 `Application::Initialize()`**(会 `LoadCommandLineFiles` → 失败 → 模态框)。 +2. **`EdgeScanFor` 状态查找哈希化**(0.3 ms/帧 @288 信号)。 +3. **`EdgePanelLayoutFor` 表驱动化**(需第 1 项就绪)。 +4. 若要继续压体积:`wcstod` 自写替身(≈20 KB,需逐位一致性验证)、 + 图标帧裁剪(观感优先,不建议)。