NavMenu Performance Notes
Scope
本轮补充 NavMenu 手动遍历路径:
- accordion mode 关闭兄弟子菜单。
- submenu open 后刷新 direct child
NavMenuItem.TryUpdateCanExecute()。
Root Cause
- 旧路径在热交互分支里使用
LogicalChildren.OfType<NavMenuItem>()、GetLogicalChildren().OfType<INavMenuItem>()和ItemsView.OfType<NavMenuItem>()。 - 这些集合都有直接 Count/indexer 或控件自有
LogicalChildren可用;手动遍历能避免 LINQOfTypeiterator,同时保持原来的类型过滤语义。
Benefit
这是交互路径结构性收益:NavMenu 打开/关闭子菜单时少创建 LINQ 类型过滤 iterator;不声明页面加载耗时提升。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| NavMenu accordion sibling close LINQ operators / state change | 1 | 0 | (1 - 0) / 1 |
100.00% | 有效;改为 LogicalChildren.Count/indexer |
| NavMenu submenu opened sibling close LINQ operators / open | 1 | 0 | (1 - 0) / 1 |
100.00% | 有效;改为 LogicalChildren.Count/indexer |
| NavMenuItem child execute refresh LINQ operators / submenu toggle | 2 | 0 | (2 - 0) / 2 |
100.00% | 有效;改为 ItemsView.Count/indexer |
| Page-load timing claim | none | none | n/a | n/a | 本轮没有有效前后 timing,不声明页面级速度收益 |
Additional Structural Pass
内部关闭/互斥子菜单路径原先通过 INavMenuElement.SubItems 属性取子项;第一步已把内部热路径改为直接遍历 LogicalChildren.Count/indexer 并做同样的 is INavMenuItem 类型过滤。本轮继续把公共接口实现本身从 LogicalChildren.OfType<INavMenuItem>() 改为显式类型过滤枚举,公共 API 形态不变。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
Internal SubItems enumerable calls / submenu close-open paths |
7 | 0 | (7 - 0) / 7 |
100.00% | 有效;内部路径不再触发 OfType iterator |
NavMenu root public SubItems LINQ operators / enumeration |
1 | 0 | (1 - 0) / 1 |
100.00% | 有效;显式类型过滤替代 OfType |
NavMenuItem public SubItems LINQ operators / enumeration |
1 | 0 | (1 - 0) / 1 |
100.00% | 有效;显式类型过滤替代 OfType |
| Default path root-node LINQ calls / default path apply | 1 | 0 | (1 - 0) / 1 |
100.00% | 有效;Count 保护后直接读 pathNodes[0] |
Public INavMenuElement.SubItems compatibility |
preserved | preserved | n/a | 0.00% | 公共接口保持不变 |
| Selected path item list growth / item select | dynamic growth | exact parent depth | structural | 结构收益 | logical parent 链深度已知时预分配 |
| Default path node list growth / default path apply | dynamic growth | exact node depth | structural | 结构收益 | nav node 父链深度已知时预分配 |
说明:覆盖 top-level close、pointer-enter sibling close、submenu-open sibling close、recursive close / clear 和 item close-submenus。--verify-navmenu-states 当前存在历史失败,本轮不声明页面 timing 或完整状态 suite 收益。
Select Path Set Cleanup
DefaultNavMenuInteractionHandler / InlineNavMenuInteractionHandler 选中 leaf 时,旧路径用 ToHashSet() 构造新旧 selected path set,再用 Except() 得到要清理的旧路径。本轮改为复用 NavMenu.BuildSelectPathSet(),按路径列表 Count 预分配 HashSet,并在遍历旧 set 时直接 Contains() 判断是否需要清理。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
NavMenu selected path ToHashSet() LINQ calls / leaf select |
2 | 0 | (2 - 0) / 2 |
100.00% | 结构收益;新旧路径 set 按列表容量手写构造 |
NavMenu selected path Except() LINQ operators / leaf select |
1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;旧路径遍历时直接 Contains 判断 |
InlineNavMenu selected path ToHashSet() LINQ calls / leaf select |
1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;new path set 按列表容量手写构造 |
InlineNavMenu selected path Except() LINQ operators / leaf select |
1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;旧路径遍历时直接 Contains 判断 |
| DefaultNavMenu first leaf select empty old-path HashSet allocations / first select | 1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;无旧选中项时跳过旧路径集合 |
| InlineNavMenu first leaf select empty old-path HashSet allocations / first select | 1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;无旧选中项时跳过旧路径集合 |
| selected-path semantics | unchanged | unchanged | n/a | 0.00% | 行为保持 |
说明:这是 NavMenu 选中交互路径 structural-only 收益;没有新增页面导航 timing 对比,不声明页面加载速度提升。
Path Reverse Elimination
NavMenu.CollectSelectPathItems() 和 CollectPathNodes() 原先都按 leaf-to-root 追加路径,然后调用 Reverse()。本轮继续保留已知深度的 List 预分配,但先填充占位,再从后往前按 index 写入,直接得到 root-to-leaf 顺序;选中路径和默认路径语义不变。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| NavMenu selected path reverse passes / leaf select | 1 | 0 | (1 - 0) / 1 |
100.00% | structural-only;按父链深度倒序填充 |
| NavMenu default path reverse passes / default path apply | 1 | 0 | (1 - 0) / 1 |
100.00% | structural-only;按 node depth 倒序填充 |
| Path list capacity | exact parent/node depth | exact parent/node depth | n/a | 0.00% | 已有容量收益保持 |
| Selected/default path semantics | unchanged | unchanged | n/a | 0.00% | 行为保持 |
| Page-load timing claim | none | none | n/a | n/a | 本轮没有有效前后 timing,不声明页面级速度收益 |
Verification
dotnet build -c Release -f net10.0 --no-restore tools/performances/AtomUI.Performance/AtomUI.Performance.csprojpassed.dotnet build tools/performances/AtomUI.Performance/AtomUI.Performance.csproj --framework net10.0 --no-restorepassed with 0 warning / 0 error.--verify-navmenu-statescurrently fails on both this change and clean HEADd35b21a1awith the same default-path / lazy-subscription / binding-release expectations, so it is not introduced by this optimization.