diff --git a/Documents/Code_Review_Plan.md b/Documents/Code_Review_Plan.md new file mode 100644 index 0000000..af82755 --- /dev/null +++ b/Documents/Code_Review_Plan.md @@ -0,0 +1,176 @@ +# Code Review 计划(分支 `Code_Review`) + +> 目标:在不引入性能回归、不改变用户可见行为的前提下,对全项目做一次**结构 + 性能 + 体积** +> 的查漏补缺,并把「改一个功能要动 40 处」的屎山点收敛成表驱动/单一实现。 +> +> 基线(本计划开始时): +> - 分支 `Code_Review`,起点 `764e5be`(dev 已删除过时的 `UI_Refactor_*` 三份文档)。 +> - `make release` 产物 **890,880 B**(`.text` 753,136 B、`.rdata` 36,936 B、`.rsrc` 20,656 B、 +> `.pdata` 30,420 B、`.xdata` 29,496 B、`.idata` 12,856 B)。 +> - 源码约 **5.6 万行**;最大单文件 `PlotViewPaint.cpp` 6,561 行、`PlotView.hpp` 4,654 行。 +> +> 约束: +> - 每完成一个节点**编译验证(release + debug 零警告)**并**提交**,同时更新 +> `Documents/Code_Review_Progress.md`。 +> - 性能相关节点必须给出**同一次运行内的相对量**(历史教训:绝对毫秒阈值不可靠)。 +> - 不占用用户的鼠标/键盘/屏幕(只用纯控制台探针或源码级核对)。 + +--- + +## 一、已确认的问题(本计划的事实基础) + +### A. 体积 + +| 项 | 实测 | 说明 | +| --- | --- | --- | +| `dynamic_cast`(`Toolbar.cpp:370`) | **+75,264 B** | 最小复现程序:39,936 B → 115,200 B。它会链入 RTTI 元数据 + `cp-demangle.o`(map 里 **56,320 B**)。而该处 `Button::widget` **本来就是 `UiButtonWidget*`** ⇒ 转换完全多余。 | +| `-fno-rtti` | 未启用 | 移除上一条后即可开启,进一步去掉 `type_info` 表。 | +| `.pdata` + `.xdata` | 60 KB | `-fno-exceptions` 已开;SEH 展开表仍存在。可尝试 `-fno-asynchronous-unwind-tables`(历史上链接失败,需复核)。 | +| `libmingwex` 的 `strtodg/gdtoa/mingw_wpformat` | 27.8 KB | 来自 `wcstod` / `wprintf` 类调用;可评估用自写解析替代 `wcstod` 的收益(公式/输入框解析)。 | +| `.rsrc` | 20.7 KB | 图标(PNG 帧)+ manifest + 版本信息,已优化过。 | +| 重复常量/重复实现 | 多处 | `kFormulaEnvelopeBuckets` 已清理;仍有 `ClampDouble` 两份实现等。 | + +### B. 性能(结构性热点) + +1. **面板开关/参数处理是 6 个 `EdgePanelKind` × ~20–26 处 switch 分支**(实测每个 kind 出现 + 21–26 次,全部由手写 `if/switch` 维护)。这是**新增功能必然漏改 2–3 处**的根本原因 + (历史已发生 4 次:`MARK` 漏拖动、`RESAMPLE` 漏拖动+关闭、`CLIP` 漏滚轮+缓存键)。 +2. **`SceneContentHash` 里 6 段几乎相同的逐面板混入循环**(`PlotViewCacheKey.cpp`)。 +3. **`EdgePanelLayoutFor` 是巨型分支**,`EdgePanelLayout` 结构体带 20+ 个按面板类型区分的 + 矩形数组;高度算式与几何算式分开写(历史上多减一个 pad 造成空白空白 BUG)。 +4. **`PlotView` 有 30+ 个缓存**,每个都手写「键 + DC + 位图 + 贴图 + 失效」五件套; + 失效路径散落在 8 个文件里,`m_sceneValid=false` 出现 6 处。 +5. `ApplicationCommands.cpp` **2,165 行**混杂:标签页、数据派生、菜单、工具栏、键盘、鼠标。 +6. `PlotView.hpp` 4,654 行(声明 + 内联实现),单文件认知负担极大。 + +### C. 过时/错误注释与命名(抽样已确认) + +- `agent_mem.md` §3「目录结构」仍写着已不存在的 `Utilities/CsvTable/`、`Utilities/DataModel/` + 等目录;§2 写 `-Os` 而实际是 `-Oz`。 +- 源码中留有历史轮次的「本轮」「上一轮」叙述(对当前读者无意义),以及被推翻方案的注释。 +- `PlotViewInternal.hpp` 里 `kFormulaEnvelope*` 注释已随实现迁移,需同步。 + +--- + +## 二、节点计划 + +> 优先级:**S**(安全、收益明确、风险低)→ **A**(结构调整,中风险,需逐步验证)→ +> **B**(深度重构,必须有可验证的前后一致性)。 + +### 阶段 0:建立基线(节点 P0) + +- P0.1 记录基线:release/debug 体积、`.text/.rdata` 等节表、关键路径的可复跑测量程序 + (纯控制台,链接 `Build/Release/*.o`,不入库)。**已完成基线的部分见下表**。 +- P0.2 建立 `Documents/Code_Review_Progress.md`,每节点完成后追加一行结论 + 数字。 + +### 阶段 1:体积(节点 P1–P5,安全优先) + +- **P1 移除 `dynamic_cast` + 开启 `-fno-rtti`** + - 把 `Toolbar::BuildBoxes` 的 `dynamic_cast` 改为直接用 + `Button::widget`(已是该类型)或给 `UiWidget` 加虚函数 `ContentBounds()`。 + - Makefile `CXXFLAGS += -fno-rtti`;全仓确认无 `typeid`/`dynamic_cast`/`type_info`。 + - 预期 **−70~80 KB**。验收:产物下降 ≥60 KB;功能逐项核对(工具栏按钮、布局边界调试)。 +- **P2 体积审计第二轮** + - 复核 `-fno-asynchronous-unwind-tables`(历史上链接失败,试 `-fno-unwind-tables` 组合)。 + - 评估 `wcstod` 替身(自写定点/浮点解析,仅在公式与输入框使用)去掉 + `strtodg/gdtoa/gethex/hexnan`(约 20 KB)。 + - 评估 `--gc-sections` 在 LTO 下是否已生效(读 map 确认无死节)。 + - 验收:逐项给出「测得的字节数 + 是否采纳」的结论,**不接受「感觉变小」**。 +- **P3 重复实现归并** + - `ClampDouble` 两份(`PlotViewInternal.hpp` / `UiLayout.cpp`)→ 统一到 `Shared`。 + - 扫描全部 `static`/匿名命名空间重复工具函数,合并为单一实现。 + - 重复字符串/常量字面量、重复的三元式开关表。 +- **P4 死代码与未使用成员清理** + - 用非 LTO 对象 + `nm` 反查「定义但从未被引用」的函数;删除确认无引用的实现。 + - 清理 `PlotView.hpp` 中已无人读取的成员/字段。 +- **P5 资源与启动数据** + - `.rsrc` 复核(图标帧数、manifest、版本字符串);`.idata` 导入表复核。 + +### 阶段 2:性能(节点 P6–P10) + +- **P6 建立可复跑的渲染性能探针(纯控制台)** + - 目标:能在**不弹窗口**的情况下测量 `BuildLaneVisuals` / `SceneContentHash` / + `EdgePanelLayoutFor` / 面板绘制 / 信号面板绘制的耗时。 + - 方式:链接 `Build/Release/*.o` + 手工构造 `DataSet`/`TabConfig`,只调用纯函数与 + `PlotView` 的非窗口方法(避免 `Application::Initialize` 触发模态框,见历史教训)。 + - 所有断言用**同一进程内的相对量**。 +- **P7 面板元数据表驱动(核心)** + - 新增 `EdgePanelSpec`(每 kind 一行:命令 id、默认尺寸、是否列表型、footer 类型、 + 滚动字段偏移、面板状态字段偏移、绘制函数指针、命中函数指针)。 + - 把 6×~20 处 `switch (kind)` 收敛为「查表 + 3 个通用实现(列表型 / 表格型 / 固定行型)」。 + - 验收:**逐像素**对比改造前后面板(离屏渲染哈希);新增一个假 kind 只需加一行表项即可 + 被绘制/命中/拖动/关闭/滚动全覆盖(用编译期断言或探针验证)。 +- **P8 `SceneContentHash` 混入收敛** + - 把 6 段重复循环改为「按面板表遍历 + 每面板一个 `MixContent()`」。 + - 验收:改造前后哈希值**逐位相同**(同一状态)。 +- **P9 `EdgePanelLayoutFor` 表驱动 + 高度/几何同源** + - 高度算式与内容几何共用同一份 `PanelMetrics` 计算(消除重复减 pad 类 BUG)。 + - 验收:探针打印「内容区下边界 vs 按钮上边界」的像素差,与改造前一致。 +- **P10 缓存五件套抽象(低风险版)** + - 抽出 `OffscreenCache`(DC/位图/键/贴图/失效),**只替换三处结构完全相同的** + (derived / decor / panel),保留 scene 与 lane layer 不动(热路径最复杂)。 + - 验收:逐像素一致 + 帧时间不回退。 + +### 阶段 3:架构(节点 P11–P14) + +- **P11 `ApplicationCommands.cpp` 拆分** + - → `ApplicationTabs.cpp`(标签页)/ `ApplicationDerive.cpp`(派生/重采样/截取/克隆)/ + `ApplicationCommands.cpp`(命令分发)/ `ApplicationMenus.cpp`(上下文菜单与设置菜单)。 + - 纯搬移,零行为变更;Makefile 无需改动(按目录通配收集)。 +- **P12 `PlotView.hpp` 拆分** + - 把 `EdgePanelLayout`、派生曲线缓存条目、图层结构体等**纯数据结构**搬到 + `PlotViewTypes.hpp`;把 `PlotView` 的内联小函数按关注点分块放到 `PlotViewInline.hpp`。 + - 目标:`PlotView.hpp` ≤ 2,500 行。 +- **P13 UI 基础层职责核对** + - 核对 `UiWidget` / `UiComponent` / `UiLayout` / `UiBox` / `UiSurface` / `UiButtonWidget` / + `UiControls` 的边界是否重叠;合并确认冗余的(只保留一套矩形/命中/布局原语)。 + - 明确依赖方向:`Components/*` → `Components/Shared` → `Utilities`(校验无反向)。 +- **P14 绘制/功能组件边界** + - `PlotViewPaint` / `Raster` / `Panels` / `Features` / `Interaction` 的职责再核对; + 把「面板几何」与「面板绘制」的交叉依赖理顺(几何 → 共享 `PanelMetrics`)。 + - 把散落在 `PlotView` 里的「颜色/线宽/胶囊样式」常量收敛到 `Theme`/`PaintUtil`。 + +### 阶段 4:注释与命名清理(节点 P15) + +- **P15 注释/命名审计** + - 删除「本轮/上一轮」叙述、被推翻方案的描述、与实现不符的注释。 + - 统一命名:`*Dc`/`*DC`、`Lane*` vs `Grid*`、`Edge*` vs `Panel*` 等不一致处。 + - 确保所有注释与当前实现一致(重点:`PlotViewInternal.hpp`、`PlotViewCacheKey.cpp`)。 + - 同步更新 `agent_mem.md` 的 §2/§3(已经不准确的目录结构与 `-Os`)。 + +### 阶段 5:验证与报告(节点 P16) + +- **P16 最终验证与报告** + - release + debug 零警告;产物体积对比;关键路径探针全部通过。 + - 生成 `Documents/Code_Review_Report.md`:逐节点写「改了什么 / 为什么 / 效果 / + 未做的事与原因」,并给出体积与性能的前后对照表。 + +--- + +## 三、验收标准(硬性) + +1. `make release` 与 `make debug` **零 error、零 warning**。 +2. 产物体积**必须下降**(目标:从 890,880 B 降到 ≤ 780 KB;若某项无法达成,在报告里 + 说明实测字节数与原因)。 +3. 不改变任何用户可见行为(面板几何、绘制像素、交互语义一致)。 +4. 每条性能结论都必须有**同进程相对量**或**逐像素/逐位一致性**证据。 +5. 每个节点单独提交,提交信息用中文、说明改动与验证方式。 + +--- + +## 四、风险与对策 + +| 风险 | 对策 | +| --- | --- | +| 面板表驱动改造引入像素差异 | 改造前后用离屏渲染哈希对比;先改 `CLIP`/`MARK` 两个最简单面板做样板,再推广 | +| `-fno-rtti` 破坏第三方/库代码 | 全仓 grep 确认;先单独提交,失败可独立回退 | +| 缓存抽象拖慢热路径 | 只抽象非热路径(derived/decor/panel),scene/lane 不动;每次改完测帧时间 | +| 大文件拆分时误改逻辑 | 纯搬移(不重写函数体),逐文件 diff 核对;先拆 `ApplicationCommands` | +| 探针触发模态「内存不足」框 | 绝不在探针里调 `Application::Initialize()`(历史教训);只构造数据层/纯函数 | + +--- + +## 五、进度索引 + +进度与每节点的实测数字见 `Documents/Code_Review_Progress.md`。 +最终结论见 `Documents/Code_Review_Report.md`。 diff --git a/Documents/Code_Review_Progress.md b/Documents/Code_Review_Progress.md new file mode 100644 index 0000000..2d14c97 --- /dev/null +++ b/Documents/Code_Review_Progress.md @@ -0,0 +1,30 @@ +# 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 处手写分支)。 + +--- + +## 节点记录 + +(下面按完成顺序追加)