Files
Just-DataInspector/Documents/Code_Review_Plan.md
T
DeepSeek V4.1 Flash 5c48bf922e review(follow-ups): 落地上轮建议并给出实测结论(#2/#4 回退、#3 不重写)
一、Tools 完善
* OffscreenChecks `edgestats`:改为自动挑"最有起伏"的信号。旧实现固定取
  `signals[0]`,而 MF4 的第一条信号是常量(6000)⇒ 三个阈值的断言退化成
  "0 == 0" 的空转。现在扫描前 200 条挑 `max-min` 最大者,SCATTER_TEST 上
  三个阈值分别得到 (20,19)/(20,19)/(20,19),与独立参考实现逐位相同。
* UnitTests 新增 `stdio_mingw_vs_ucrt_formats`:把 MinGW ANSI stdio 与系统
  UCRT 的 printf 在**本程序实际用到的全部格式**(`%.*f` / `%.6g` / `%g` /
  `%.4e` / `%.2f %ls` / `%llu` / `%lld` / `%02X` / `%.2f%%`)上做逐字符比对,
  13.6 万次调用 **0 处差异**(覆盖 1e-40~1e40)。它是"把 swprintf 让给 UCRT"
  这条体积优化的验收标准。
* Tools/README.md:同步新增用例与断言数量(219 项 / 18 用例)。

二、第 33 轮报告 §10 后续建议的结论(详细数字见 Progress 第 34 轮)

1. **`EdgeScanFor` 状态查找哈希化:实测后回退。**
   实现方式为 `deque` + `unordered_multimap<keyHash, EdgeScanState*>`(桶内仍
   逐字段比对,碰撞安全;新建/淘汰/定向作废/清空四处同步索引),功能与像素
   验证全绿。但 198 个状态的整轮查询只从 0.097 降到 0.073 us/query(一轮省
   ≈4.8 us),而产物体积 +5,632 B。⇒ 回退。
   同时修正报告的量级:原估"288 信号 ≈0.3ms/帧",实测 ≈20us/帧。

2. **`EdgePanelLayoutFor` 表驱动化:不做。** 实测 1.9~2.5 us/次(每帧约 14 次
   ≈30us),不是热点;第 33 轮已消除"按种类分派"的重复,剩余 957 行是几何算式
   本身,无法表驱动。本轮改为交付"让这类重写可验证"的手段:
   `-run panels -mode record/compare` 逐面板比对几何 + 像素哈希。

3. **`wcstod` 自写替身:实测 0 字节收益。** 只加
   `-D__USE_MINGW_ANSI_STDIO=0` 全量重编后产物仍是 788,480 B、`mingw_wpformat.o`
   仍被链接。原因是预编译 libstdc++ 自己引用 `__mingw_swprintf`:只要还调用
   `std::to_wstring` / `std::to_string`(全仓约 80 处,若干处实参是浮点),
   gdtoa 必然被链入。⇒ 回退,并在 Makefile 留下说明防止重复尝试。

三、文档
* `Documents/Code_Review_Plan.md` 新增 §六(第 34 轮节点与验收标准)。
* `Documents/Code_Review_Progress.md` 新增第 34 轮逐节点记录(含全部实测数字)。
* `Documents/Code_Review_Report.md` 新增 §11(结论表 + 两处量级修正 + 更新的后续建议)。
* `Documents/agent_mem.md` 新增第 34 轮规则:Tools 约定、两个构建陷阱、
  两条"别再重复做"的实验结论。

验证:
* `make release`(删 .o 全量)零 error / 零 warning,产物 **788,480 B**(与改动前一致)。
* `make -C Tools` 零 error / 零 warning。
* `UnitTests.exe` → checks=219 failed=0 cases=18。
* `OffscreenChecks.exe -all` → 5/5 检查 PASS,checks=71 failed=0。
2026-11-04 11:52:10 +08:00

206 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<const UiButtonWidget*>` 改为直接用
`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`。
---
## 六、第 34 轮追加(2026-11-04):验证基础设施 + 后续建议落地
> 触发:§三/§10 之后剩下的三项建议(`EdgeScanFor` 哈希化、`EdgePanelLayoutFor` 表驱动化、
> `wcstod` 自写替身)都卡在**"没有可自证的验证手段"**上。
> 用户要求:先把验证工具做成仓库内的正式资产(`Tools/` 入库),再动被建议的代码。
### 6.1 节点与验收标准
| 节点 | 内容 | 验收标准 |
| --- | --- | --- |
| **T1** | `Tools/` 解除忽略;新增 `Tools/Makefile`;两个工具目录 + `README.md` | `make -C Tools` 构建全部、`make UnitTests` 构建单个、`make clean` 只清工具产物;改头文件/增删源文件自动重编重链 |
| **T2** | `Tools/UnitTests`:数据层语义单元测试 | `result=PASS`,断言数 ≥ 200 |
| **T3** | `Tools/OffscreenChecks`:隐藏窗口渲染 + 帧序列 + 面板像素基线 + 边沿统计参考实现 + 性能 | 零失败;全程不显示窗口、不动真实鼠标/键盘 |
| **T4** | 建议 #2:`EdgeScanFor` 状态查找哈希化 | **先量后改**;性能收益必须大于体积代价,否则回退 |
| **T5** | 建议 #3:`EdgePanelLayoutFor` 表驱动化 | 面板像素基线 A/B **逐像素一致**才允许合入 |
| **T6** | 建议 #4:`wcstod` 自写替身(省 gdtoa 链) | 体积收益可测且数值语义逐位一致 |
### 6.2 本轮新增的硬约束
1. **工具不得把产物写进 `Tools/`**:截图 / 基线 / 报告一律写 `Temp/tools/`(已被忽略);
`Tools/*/Build/` 同样不入库(其它协作者本地 `make -C Tools` 重建)。
2. **工具与本体 ABI 必须一致**:`-fno-exceptions`、`-fno-rtti`、链接时 `-flto -flto-partition=one`;
工具源码自身**不**编成 LTO 字节码(GCC 15.2.0 会在 `choose_baseaddr` 处 ICE)。
3. **"先量后改"**:任何以性能 / 体积为名的改动,必须在同一台机器、同一次会话内给出前后数字;
收益低于噪声、或换来体积上涨的,一律回退,并把数字写进 `Code_Review_Report.md`。
4. **像素基线只做本地 A/B**:哈希与 DPI/字体/数据文件绑定,**不入库、不当黄金图**。