docs: changelog 2026-07-11 效能第一層——掃描提速(category 記憶化 + orphan 葉塊預計算)
新增 changelog 條目+md-twin 同步。下游效能訴求一之第一層:S3 category 以 WeakMap 記憶化砍除跨 pass 重複 el.matches()(實測約降三成)、S2 orphan 以上攀預計算「非葉塊」取代逐塊 querySelector 掃 子樹之 O(Σ塊子樹)。皆無狀態、保行為、無公開 API 變更;測試 129→130 全綠。並記亲驗數據與 insidePill 之下一機會。changelog HTML 經 build.sh 重建。
This commit is contained in:
@@ -25,6 +25,19 @@
|
||||
<!-- BODY -->
|
||||
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
|
||||
|
||||
<h2>效能第一層:掃描提速(category 記憶化 + orphan 葉塊預計算) <span class="badge accent">2026-07-11</span></h2>
|
||||
<p>下游效能訴求(掃描疑 O(n²))之第一層——無狀態、保行為、無公開 API 變更之確定收益。單元測試 129→130 全綠、輸出不變。</p>
|
||||
<h3>優化</h3>
|
||||
<ul>
|
||||
<li><strong>S3 category 記憶化</strong>:<code>finder.category</code> 以 <code>WeakMap<Element, InlineCategory></code> 記憶化,砍除 <code>collectRuns</code>/<code>jiyaAdjacency</code>/<code>jinze</code>/<code>split</code> 跨 pass 對同一元素之重複 <code>el.matches()</code>(pill/isolate/avoid 選擇器)。實測 pill/isolate 密集頁面 <code>el.matches()</code> 8204→5842(約降三成,命中率 55%)。安全:單次 render 內一元素之分類恆定、且 Finder 隨 <code>makeCtx</code> 每次 render 重建 → 快取僅存活單次 render、無跨 render 失效(持久化屬另議)。</li>
|
||||
<li><strong>S2 orphan 葉塊預計算</strong>:以一趟上攀預計算「非葉塊」Set 取代逐塊 <code>block.querySelector(sel)</code> 掃整棵子樹之 O(Σ塊子樹)(深塊嵌套下 O(n²));某塊為另一塊之最近塊祖先 ⟺ 含塊級後代,與原 querySelector 同口徑、行為等價。</li>
|
||||
</ul>
|
||||
<h3>亲驗與待議</h3>
|
||||
<ul>
|
||||
<li>亲驗:暫禁 category 快取則 <code>computeCategory</code> 由 1041 增至 2322、<code>el.matches()</code> 由 5842 增至 8204——證快取真減負而非空操作(初次插桩誤打 <code>HTMLBodyElement.prototype</code> 而漏計,改補 <code>Element.prototype.matches</code> 後為準)。</li>
|
||||
<li>新發現(下一機會):剩餘 <code>el.matches()</code> 之最大來源為 <code>insidePill</code> 之「逐文本節點沿祖先鏈 <code>pillMatches</code>」(<code>eachTextNode</code> 每文本節點一趟、未記憶化)。</li>
|
||||
</ul>
|
||||
|
||||
<h2>splitTreeAfter 攀升對稱守衛(opaque 中間祖先不再被克隆肢解) <span class="badge accent">2026-07-11</span></h2>
|
||||
<p>下游 <code>.selection-bar-message</code> 病例:垂懸字(orphan)之段末實義字落於 opaque <code><button></code>(巢於可切 <code><span></code>)時,<code>splitElementAt</code> 僅驗頂層 span 可切;<code>splitTreeAfter</code> 逐層克隆「文本節點→top」祖先鏈時,中間 opaque button 從未複查即被克隆肢解(複製出 <code>splitoff</code> button、破壞互動元素)。<code>firstText</code>/<code>lastText</code> 守下潛(遇非可切回 <code>null</code>),攀升卻無對稱守衛——此即真正病根,下游 per-instance 迴避僅治標。</p>
|
||||
<h3>修復</h3>
|
||||
@@ -243,6 +256,22 @@
|
||||
<script type="text/markdown" id="md-source">
|
||||
# 變更紀錄(Changelog)— 聚珍(Juzhen)/cjk-autospace
|
||||
|
||||
## 效能第一層:掃描提速(category 記憶化 + orphan 葉塊預計算)(2026-07-11)
|
||||
|
||||
下游效能訴求(掃描疑 O(n²))之第一層——無狀態、保行為、無公開 API 變更。測試 129→130 全綠、輸出不變。
|
||||
|
||||
### 優化
|
||||
- S3 category 記憶化:finder.category 以 WeakMap<Element,InlineCategory> 記憶化,砍除各遍歷原語跨 pass
|
||||
對同一元素之重複 el.matches()。實測 pill/isolate 密集頁 el.matches() 8204→5842(約降三成、命中率 55%)。
|
||||
安全:單次 render 內分類恆定、Finder 每次 render 重建 → 快取僅存活單次 render、無跨 render 失效。
|
||||
- S2 orphan 葉塊預計算:以一趟上攀預計算「非葉塊」Set 取代逐塊 block.querySelector(sel) 掃子樹之
|
||||
O(Σ塊子樹)(深塊嵌套 O(n²));某塊為另一塊之最近塊祖先 ⟺ 含塊級後代,與原 querySelector 同口徑、等價。
|
||||
|
||||
### 亲驗與待議
|
||||
- 亲驗:暫禁 category 快取則 computeCategory 1041→2322、el.matches() 5842→8204(初次插桩誤打
|
||||
HTMLBodyElement.prototype 漏計,改補 Element.prototype.matches 後為準)。
|
||||
- 新發現(下一機會):剩餘 el.matches() 最大來源為 insidePill 之逐文本節點沿祖先鏈 pillMatches(未記憶化)。
|
||||
|
||||
## splitTreeAfter 攀升對稱守衛(opaque 中間祖先不再被克隆肢解)(2026-07-11)
|
||||
|
||||
下游 .selection-bar-message 病例:垂懸字段末實義字落於 opaque `<button>`(巢於可切 `<span>`)時,splitElementAt 僅驗頂層 span,splitTreeAfter 逐層克隆「文本節點→top」祖先鏈時中間 opaque button 從未複查即被克隆肢解(複製出 splitoff button)。firstText/lastText 守下潛,攀升卻無對稱守衛——真正病根,下游 per-instance 迴避僅治標。
|
||||
|
||||
Reference in New Issue
Block a user