依
docs/pathology-report-template.md版型,八節全含,不刪節。實測數字唯一來源:
docs/measurements/2026-07-26-reproducibility-4x-5.json(2026-07-26 下午完整六標本掃描,66 筆,isFullSweep: true,problems: [],consoleErrors: [],measuredAt 2026-07-26T05:52:29.671Z)。 欄位路徑一律寫成records[mode=…,run=…].欄位,指的是該 JSON 內specimenId = 06-rerender-storm的那 12 筆。其他輪(同日上午-4x.json、2026-07-25)的數字 只以「另一 session」身分出現並逐一標明出處 —— 跨 session 絕對值不可比 (docs/phase2-expected-results.md:944-945,下稱phase2)。判準狀態:
phase2:1259-1338檔尾追記已廢除 #6 的兩條比值判準 (≥ 6×、renderRatio < 0.5),新判準生效於下一輪起、對本份 JSON 不回溯 (phase2:1334-1338)。所以本報告只報方向與絕對值,不寫「通過/未通過比值判準」; 文中出現的倍率一律是描述性的,並各自附警語。
病症一句話:按一次「開始推送」後靜置 5 秒,模擬資料流每 25ms 推一批 100 台裝置更新 (共 1000 台);病變版每收到一批就重建整張 1000 列清單,5 秒量測窗(60Hz,窗頂約 300 幀) 內掉幀峰值 275~281 —— 幾乎每一幀都掉,有渲染的幀距 median 約 200ms(一秒五格畫面); 治療二(細粒度)同一窗內掉幀 14~18,幀距貼回 16.7ms 的 vsync 底線。
⚠️ 這句話的絕對值只屬於本份 JSON(4x、CDP、2026-07-26 下午那一台機器的那一個小時)。 治療臂未飽和,絕對值是機器速度的函數:同日上午另一 session 的細粒度臂是 85 / 59 / 84(
phase2:1085-1090),更早一輪還出現過 4~8 (specimens/06-rerender-storm.ts:19,下稱ts)。病變臂因飽和反而對機器不敏感 (上午 281/284/277 → 本輪 281/277/275,phase2:1087)—— 於是兩臂的比值也是機器的函數(phase2:1088-1090),單獨引用這句話時必須連 session 一起引。
同一組條件之間才能比較。任何一項改動,先前所有數字作廢。
| CPU throttle | 4x —— 宣告值(sweep.cpuThrottle、cpuThrottlingRate: 4)。驅動器有實套 CDP Emulation.setCPUThrottlingRate(tools/reproducibility.mjs bootShell),但 JS 偵測不到、本份 JSON 內部無 1x 臂可檢核,這一格只能是宣告 |
| 驅動方式 | CDP 機器驅動(sweep.driver:Input.dispatchMouseEvent)。本標本整輪只有一次真實點擊,其後靜置 |
| viewport | 800 × 600(FROZEN_VIEWPORT,src/specimens.ts:411;CLS 與 LCP 都是 viewport 相對量) |
| refreshHz | 60(records[*].refreshHz,12 筆皆 60)。掉幀門檻與一幀預算由實測值推導(ts:173-179 targetFrameMs),不寫死 16.7ms |
| 操作程序 | click × 1(「開始推送」)→ 靜置,串流 5000ms 自動停止(action: 'stream'、repetitions: 1、intervalMs: null,src/specimens.ts:403-409;STREAM_DURATION_MS,ts:159)。串流長度=掉幀滾動窗(droppedFrameWindowMs: 5000,src/protocol.ts:51)⇒ 窗頂 ≈ 300 幀 |
| 負載參數(凍結) | DEVICE_COUNT 1000(ts:99)、PUSH_INTERVAL_MS 25(ts:129)、BATCH_SIZE 100(ts:132)。12 筆 records 的 custom.deviceCount / pushIntervalMs / batchSize 逐筆回報同值 |
| buildId | 0.1.0-ms191c71(records[*].buildId,12 筆同) |
| protocolVersion | 1(src/protocol.ts:8 的常數)。⚠️ 本份 JSON 的 records 沒有這個欄位(全檔 grep 零筆),此格取自程式碼,不是量測實錄 |
| dispatchSpanMs / Nominal | null × 12 —— 本標本不走絕對排程(單次點擊,無節拍器),是「不適用」不是 0 |
⚠️ 量測窗的登記值與程式從未對齊:
phase2:138登記「10 秒」,程式從頭就是 5000ms。 裁決紀錄已追記現行有效值 5000ms(phase2:1104-1114),但同段也判定:ts:156-158的註解宣稱「登記值已在該檔修正紀錄更正」在寫下當時是不存在的事實 (依規矩 4 屬缺陷等級)—— 該更正是裁決紀錄事後補上的。本報告依現行有效值 5000ms。
資料讀取註記:本標本 12 筆的
runId是 run-2~run-16、缺 run-1/5/9/13。 依驅動器流程(換標本與每輪結尾的「重跑」各會推進 run context,capture 在點「重跑」之前、 以runIdBefore對回 history ——tools/reproducibility.mjs:651-655),缺號是簿記不是丟樣本; 此為推斷,已列入待複核。
登記出處(分層,後者修正前者、原文皆未動): 原始登記
phase2:126-176(200 台 / 50ms)→ 校準phase2:361-377(1000 台)→ 重新設計phase2:741-825(25ms / 批 100、樹狀梯度、補充判定欄; 「本次所有新登記預期值都是推導值,還沒量過一次」phase2:823-825)→ 檔尾追記phase2:1285-1300, 1319-1332(判準重立,生效於下一輪,不回溯本 JSON)。 這一節不准回頭改去迎合實測。
phase2:153 寫的是 custom.droppedFrames(5 秒窗);現行主指標是
custom.droppedFramesPeak(src/specimens.ts:394)。⚠️ 這條欄位漂移與 #4 的同型漂移
(phase2:1109)是同一件事,但裁決紀錄「登記值沒跟上程式」表只記了 #4,沒記 #6 ——
兩者不是同一個量(峰值 vs 快照當下的窗值,phase2:1117)。本報告照現行欄位取數,並回報此缺漏。broken 150phase2:153)、fixed-batch 20phase2:156)、
fixed-granular < 30(phase2:157)、fixed-backpressure < 15(phase2:158)。
⚠️ 後三帶推導自 200 台 / 50ms 的舊設計與鏈狀梯度,兩次校準後從未按新參數重推。phase2:161)。無 INP 樣本(沒有使用者互動,ts:37-38)。broken / fixed-granular ≥ 6×(phase2:163)——已作廢(phase2:1285-1300,
廢因:分子飽和不動、分母隨機器漂,固定比值判的是機器不是標本);
renderRatio(fixed-backpressure) < 0.5(phase2:159)——已作廢(phase2:1319-1332)。
取而代之的兩個絕對錨(broken ≥ 240、fixed-granular ≤ 150,皆由解析式
dropped ≈ 300 − 5000/幀距 ts:94 反推)與方向性 renderRatio 判準
自下一輪生效,本報告不用它們宣稱通過與否。renderFrameGapMedianMs(只取「這一幀有渲染」的幀距)
取代已作廢的「渲染耗時」備援(phase2:782, 815);
broken 對 fixed-batch 這一對的判定改用它,因為主指標在這一對上飽和(ts:93-97)。fixed-granular 的 rendersSkipped > 0,
「背壓不接在細粒度之後」的實證前提就倒,樹狀梯度正當性整段重寫(ts:34-35)。phase2:165-175):droppedFrames 有上限(窗頂 ~300),病變類臂會飽和、彼此分不出高下;setInterval 卡頓後累積補償回呼 —— 此條方向記反(phase2:417-429:漏拍被丟棄、
不是排隊補發,推送率會反向塌陷),重設計後改為照時鐘補發(ts:319-366)。三輪,同一組凍結條件。離散度算在每輪的值上、判定不用 max
(src/protocol.ts:309「抗離群。可重現性判定用這個,不用 max」——
模板引作 :290,行號已漂)。相對離散度 = (max − min) ÷ median-of-three。
主指標 custom.droppedFramesPeak(每輪一值,records[mode=…,run=1..3].custom.droppedFramesPeak):
| mode | 三輪主指標 | 相對離散度 | 絕對全距 | 判定 |
|---|---|---|---|---|
| broken 每批重建整表 | 281 / 277 / 275 | 2.2%(6÷277) | 6 幀 | ✅ 可重現(飽和臂,貼窗頂) |
| fixed-batch 批次化 + rAF | 234 / 238 / 232 | 2.6%(6÷234) | 6 幀 | ✅ 可重現(同樣貼近窗頂) |
| fixed-granular 只改變動節點 | 14 / 18 / 18 | 22.2%(4÷18) | 4 幀 ≤ 底線 5 幀 | ✅ 可重現(兩判準皆過) |
| fixed-backpressure 背壓降頻 | 137 / 135 / 136 | 1.5%(2÷136) | 2 幀 | ✅ 可重現 |
⚠️ 同日上午另一 session 的
fixed-granular是 85 / 59 / 84、31.0% 擦線判 unstable (phase2:932)。本輪 14 / 18 / 18 通過,靠的是絕對全距落在 5 幀底線內 —— 未飽和治療臂的絕對值跨 session 漂了 4~6 倍而工作量一格沒動(phase2:1085-1088), 可重現的是「本輪之內」,不是那個數字本身。
補充判定欄 custom.renderFrameGapMedianMs(每輪 median,直接引用不重算):
| mode | 三輪(ms) | 相對離散度 | 備註 |
|---|---|---|---|
| broken | 200.1 / 208.3 / 200.0 | 4.1%(8.3÷200.1) | 一格畫面約 12 個 vsync |
| fixed-batch | 100.1 / 116.6 / 116.6 | 14.2%(16.5÷116.6) | 全距恰為一個刷新週期 —— 幀距落在 vsync 網格上,這是量化不是抖動 |
| fixed-granular | 16.7 / 16.7 / 16.7 | 0.0% | 貼死 vsync 底線 |
| fixed-backpressure | 100.0 / 100.0 / 100.0 | 0.0% | 有渲染的幀仍是整表重建的幀 |
計數器(逐輪,records[mode=…,run=1..3].custom.*):
| mode | batchesReceived | batchesRendered | rendersSkipped | renderRatio | updatesApplied | catchupClamped |
|---|---|---|---|---|---|---|
| broken | 201 / 202 / 201 | 201 / 202 / 201 | 0 / 0 / 0 | 1.0 × 3 | 19136 / 19234 / 19136 | 0 × 3 |
| fixed-batch | 206 / 201 / 203 | 42 / 41 / 42 | 0 / 0 / 0 | 0.21 × 3 | 15707 / 15374 / 16009 | 0 × 3 |
| fixed-granular | 200 / 200 / 200 | 191 / 185 / 187 | 0 / 0 / 0 | 0.96 / 0.93 / 0.94 | 18876 / 18813 / 18840 | 0 × 3 |
| fixed-backpressure | 201 / 204 / 204 | 26 / 26 / 26 | 137 / 144 / 142 | 0.13 × 3 | 13618 / 13720 / 13753 | 0 × 3 |
三件事這張表直接證明:
phase2:422-423)。
catchupClamped 12 筆全 0:無任何一輪觸發「補發被砍即作廢」條款(ts:140-147)。rendersSkipped 137 / 144 / 142(前兩輪設計下三輪恆 0、
被判死程式碼,phase2:483-493;重設計後上午 session 已量到 140 / 143 / 142,
phase2:922-924,本輪再現)。fixed-granular 的 rendersSkipped 三輪皆 0(ts:34-35 登記:
若 > 0 樹狀梯度理由重寫)—— 本輪不需要重寫。登記的兇手是 loaf(phase2:161),不是 INP 段 —— 本標本整輪只有「開始推送」那一下點擊
(totalInteractions 12 筆皆 1,stats.n 皆 1,isMaxNotP98: true),
INP 16~24ms 落在 16ms 底線與 8ms 量化網格上,四臂等價、不做任何臂間排名。
下表照模板填,INP 三段只為完整性列出:
| broken | fixed-batch | fixed-granular | fixed-backpressure | |
|---|---|---|---|---|
| inp(單次點擊,n=1) | 24 / 24 / 16 ms | 16 / 24 / 24 ms | 24 / 16 / 16 ms | 24 / 24 / 24 ms |
| inputDelay | 1.2 ~ 2.0 ms | 0.9 ~ 2.0 ms | 1.2 ~ 1.8 ms | 1.9 ~ 2.1 ms |
| processing | 2.5 ~ 3.9 ms | 1.7 ~ 9.2 ms | 3.1 ~ 9.6 ms | 1.7 ~ 3.5 ms |
| presentation | 11.2 ~ 20.2 ms | 11.7 ~ 21.4 ms | 11.0 ~ 13.2 ms | 18.5 ~ 20.2 ms |
| LoAF forcedStyleAndLayout | 無 entry(forcedSamples: []、forcedMedian: null × 3) |
同左 | 同左 | 同左 |
| loafPickedBy | specimenScriptDuration × 3 |
同左 | 同左 | 同左 |
| specimenScript(備援選出的幀) | 141.3 / 143.5 / 141.8 ms | 40.2 / 22.6 / 19.4 ms | 16.8 / 21.1 / 20.0 ms | 35.1 / 20.3 / 17.0 ms |
| sourceFunctionName(forcedFn 欄) | pushDeviceBatch × 3 |
onAnimationFrame × 3 |
空字串 × 3 | 空字串 × 3 |
⚠️ 12 筆的
loafPickedBy全是specimenScriptDuration—— 那些幀是備援路徑選出來的,forcedFn欄不構成「有強制版面幀」的證據(模板已填範例的同一條警語;forcedMedian: null是「無 entry」,不是量到 0)。這個標本本來就不該有強制版面: 它的病是重建太頻繁,不是讀寫交錯。forcedPeak一欄 records 實錄為 0。
真正的歸因鏈是掉幀與幀距,而且有解析式可對帳:標本內建
dropped ≈ 300 − 5000/幀距(ts:94)。代入本輪 broken 的幀距 median 200.1ms
⇒ 300 − 25 ≈ 275,與三輪實測峰值 275281 相符 —— 主指標與幀距兩個獨立量測互相咬合。
LoAF 側的佐證方向一致:broken 被選出的幀 141143ms、三輪皆歸因到 pushDeviceBatch
(keepNames 生效,補發迴圈裡的連續重建正是該函式做的事),
治療臂全部落在 17~40ms。
三輪兇手一致性:broken 的 forcedFn / loafPickedBy / specimenScript 三輪一致 ✅ ——
但「一致的是備援幀的歸因」,宣稱強度止於此。
登記的 loaf.blockingDuration 帶(100~400ms,phase2:155)在本份 records 無此欄位,未入帳,不評。
presentation繼承duration的 8ms 量化,會落在 8ms 網格上。 對量級對照無影響,只在替兩個都已經很快的方案排名時咬人。blockingDuration是整幀的值,含外殼,規格上無法拆到單一 script;forcedStyleAndLayoutDuration是逐 script 的,所以只有它能乾淨濾掉外殼貢獻。
梯度是樹狀不是鏈狀(phase2:784-793、src/specimens.ts:381-391):
治療二與治療二乙都接在治療一之後、互為兄弟,各自相對父臂只翻一個變因。
背壓臂不是「治療三」—— 叫治療三會讓讀者以為它疊在細粒度之上。
broken ──► 治療一 fixed-batch ──┬──► 治療二 fixed-granular (翻:每一次渲染的成本)
└──► 治療二乙 fixed-backpressure(翻:渲染的次數)
| 治療 | 父臂(翻的變因) | 主指標(峰值,三輪) | 相對父臂的方向 | 代價 |
|---|---|---|---|---|
| 治療一 · 批次化 + rAF | broken(渲染的時機) | 234 / 238 / 232 | 主指標飽和,這一對表達不了差異(275 |
更新最多晚一幀上畫,而此臂一幀是 100~117ms;仍整表重建,掉幀仍飽和 |
| 治療二 · 細粒度 | fixed-batch(每次渲染的成本) | 14 / 18 / 18 | 峰值 234custom.renderScriptMs,快照值、只含 JS)對照父臂 15.2 |
必須維護每列節點參照;整表重建過後參照全部失效,寫舊參照是「畫面不動、程式不報錯」的安靜失敗(ts:481-483, 826-829) |
| 治療二乙 · 背壓降頻 | fixed-batch(渲染的次數) | 137 / 135 / 136 | 渲染次數 41renderRatio 0.21 → 0.13(方向與下一輪生效的新判準同向,此處不宣稱通過) |
畫面在被跳過的幀顯示的不是最新值:rendersSkipped 137 / 144 / 142 逐輪上報(登記風險 #2 的實價);仍整表重建,有渲染的幀仍是 100ms |
治療臂與底線的關係:fixed-granular 的峰值全距(4 幀)落在 5 幀雜訊底線內、
但三輪值 14~18 本身在底線之上 —— 依模板規則它不屬「落進底線不報比值」的情形;
真正禁止報比值判準的理由是檔尾追記的不回溯條款。描述性倍率照 phase2:1089 的先例
給一次、連警語一起給:broken/granular = 277 ÷ 18 ≈ 15.4×,這個倍率是機器速度的函數
(同日上午同一算式得 3.3×,phase2:1088-1089),跨 session 引用它是無效的。
三個必須寫明的結構事實:
phase2:928-930,另一 session 同方向)。renderRatio 對 granular 不判別:0.93~0.96 高於父臂的 0.21,但那不是「批得比較差」——
它的幀便宜,渲染得起更頻繁。此欄只在渲染路徑相同的兩臂間可比(ts:543-544),
即 fixed-batch 對 fixed-backpressure 這一對。rendersSkipped(137~144 次渲染機會被丟棄),以及
allFrameGapMedianMs 16.7 對 renderFrameGapMedianMs 100 的落差 ——
畫面每 16.7ms 都在刷新,但大多數幀刷的不是最新資料(ts:650-653:
代價正好藏在兩組樣本的差裡)。這是取捨,不是免費的勝利。無效或反向的治療:本輪無。前兩輪的「治療一 1.04× 零效果」(phase2:497-504)
是舊參數未進作用區間,重設計後翻案 —— 見下一節。
broken 281 / 277 / 275 落在原始登記 4x 帶 150~290(phase2:153;1000 台校準後沿用,
phase2:372-373)內、貼上緣。fixed-granular 14 / 18 / 18 < 30(phase2:157 的 4x 帶)。broken 對 fixed-batch
上分不出高下(phase2:167-170、ts:93-97 皆預測),renderFrameGapMedianMs
照重設計登記(phase2:782, 815)接手了那一對的判定。rendersSkipped,phase2:171-172)。phase2:763、ts:117),
實測 batchesReceived ÷ batchesRendered = 4.83rendersSkipped 0 → 137~144;
死碼判定在 phase2:483-493,翻案首見 phase2:922-925,本輪再現)。rendersSkipped 三輪恆 0 —— 登記的可證偽判準(ts:34-35)未觸發,
樹狀梯度的實證前提本輪成立。fixed-batch 232phase2:156)—— 超上限約 2×。
該帶推導自 200 台 / 50ms 的舊設計;1000 台之下「任何仍整表重建的臂都會飽和」
是同一份登記體系裡更晚、更明確的預測(ts:93-94),且與 20~120 帶互相矛盾 ——
落空的是從未按新參數重推的舊帶,不是標本。依處置順序,不動 protocol,
判定改走登記在案的補充欄。fixed-backpressure 135~137 對登記帶 < 15(phase2:158)—— 差約 9×。
登記時它是鏈狀「治療三」、接在細粒度之後(phase2:147),< 15 的前提是承接
細粒度的便宜渲染;樹狀重排(phase2:784-793)後它接在 fixed-batch 之下、仍整表重建,
前提已不存在。方向(優於父臂、優於病變)成立。setInterval 補償方向記反(phase2:417-429),
背壓守衛不可達(phase2:483-493)。≥ 6×(phase2:163)與 renderRatio < 0.5(phase2:159)已作廢;
接替的絕對錨 240 / 150 與方向性 renderRatio(phase2:1285-1300, 1319-1332)
生效於下一輪,本報告依不回溯條款(phase2:1334-1338)不宣稱通過與否。
留給下一輪對照的本輪落點(純描述):broken 275renderRatio 0.13(backpressure)對 0.21(batch)。處置順序固定:① 查環境 ② 換更精確的驅動方式 ③ 最後才動 protocol。
本輪 sweep.problems 為空、catchupClamped 全 0、節拍欄不適用(無節拍器),①②無事可查。
setInterval 模擬(ts:40-44),刻意排除網路抖動 ——
換到的是決定性(固定種子 DATASET_SEED,ts:170, 602-607),犧牲的是真實感。for 迴圈、七次重建共用一次排版(登記在案的建模選擇,
ts:337-351、phase2:804-809):量到的差距只含白做的節點建構、不含白做的排版,
是病變的下界,偏差方向對治療保守。「同步補發 vs 逐 task 補發」的對照臂未開。ts:31-32),宣稱的只有「觀測到的負載下沒有觸發」。renderScriptMs 只含 JS,抓不到 style / layout / paint —— 上一輪反推系統性低估
真實幀成本約 7.5×(ts:28-30, 223)。本報告不拿它做任何判定,僅作快照描述。phase2:1085-1090)。loafPickedBy 全為備援路徑:LoAF 側證據是「最重 specimen script 的幀」,
不是強制版面幀(本標本本就無強制版面)。lcp(248ms,裸 p)與 cls(0.018868…)12 筆逐位元相同 —— A 類單一 document
的載入期值,不描述臂間差異,本標本主指標也不在載入期。cls.sessionCount 沿 12 筆單調爬升
1 → 23(records[*].cls.sessionCount),即串流期間持續有新的 layout-shift
session window 產生(來源未查;clsIgnoredByInput 皆 0),只是沒有任何一個窗
超過載入窗的 0.0189。不影響本標本判定,已列入回報待查。ts:161-168)、
取樣 rAF 迴圈四臂同跑(ts:369-375)—— 對稱,但不是零。FULL_REBUILD_FRAME_MS = 96 是上一輪的機器常數(ts:105),換機器要按 ts:65-69
的反推式重量,PUSH_INTERVAL_MS 的上下界推導(ts:112-129,下界是相變點不是安全邊際)
隨之重推。ts:85-87)。catchupClamped > 0 即作廢、間隔提到 32ms 重跑(ts:140-147);
機器快到整表重建塞得進一幀時,背壓臂依設計無事可做(phase2:1329-1331),
那是「不適用」不是失敗。layoutChecksum 式的硬檢核,等價性由固定種子的決定性保證,
是推論不是實測(且背壓臂刻意不等價,那正是它的代價);
逐臂 LCP 標的記錄(此處 12 筆同元素,無 #5 的跨臂問題,但欄位解析度同樣只有裸 tag)。