| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- /**
- * 票 33:同环境修复前后对照(jitter-report-baseline vs jitter-report-fixed)。
- *
- * 环境说明(诚实口径):本轮探针环境的白模(cad-bim v8/roofed2 glb 批次)持续流送 +
- * 无 GPU(软件渲染),~680ms/帧的**环境底噪**会污染票面判据 (a)(b) 的绝对阈值
- * (±500ms 无长任务 / p95≤33ms 在该环境物理不可达——静置对照桶同值即证)。
- * 因此用三个**病灶专属**口径对照:
- * ① 纯 snapshot 长任务:≥100ms 长任务的 ±400ms 伴随请求里含 map-snapshot 且**不含任何
- * glb/terrain 瓦片**——旧全量重同步的特征形态(白模流送 decode 必然伴随 .glb);
- * ② 一轮刷新成簇:纯 snapshot 长任务里时间间隔 <2s 的连簇长度(旧病理:8 批连续);
- * ③ 长任务总量与 tick 增量:刷新窗口 p95 − 静置窗口 p95。
- */
- import { readFileSync } from 'node:fs'
- const [baselinePath, fixedPath] = process.argv.slice(2)
- const load = (p) => JSON.parse(readFileSync(p, 'utf8'))
- function analyze(report) {
- const tiles = (url) => /\.glb|\.terrain|\/gis\/|\.png|\.jpg/.test(url)
- const snapTimes = report.requests.filter((r) => r.url.includes('map-snapshot')).map((r) => r.t)
- const longTasks = (report.longTasksAll || report.longTaskCorrelation || []).filter((t) => t.dur >= 100)
- const pure = []
- for (const task of longTasks) {
- const rest = (report.longTaskCorrelation.find((c) => Math.abs(c.start - task.start) < 1 && c.dur === task.dur) || {}).rest || []
- const hasSnap = rest.some((u) => u.includes('map-snapshot'))
- const hasTile = rest.some((u) => tiles(u))
- if (hasSnap && !hasTile) pure.push({ start: task.start, dur: task.dur })
- }
- pure.sort((a, b) => a.start - b.start)
- let maxCluster = 0
- let run = 0
- let prev = null
- for (const p of pure) {
- run = prev !== null && p.start - prev < 2000 ? run + 1 : 1
- prev = p.start
- maxCluster = Math.max(maxCluster, run)
- }
- // 帧间隔:刷新窗口 vs 静置窗口(bucket 起点在 30–40s / 60–70s 视为刷新窗口)
- const p95 = (vals) => {
- const s = [...vals].sort((a, b) => a - b)
- return s.length ? s[Math.min(s.length - 1, Math.floor(0.95 * s.length))] : null
- }
- let refreshDts = []
- let idleDts = []
- const buckets = report.frameBuckets || []
- if (buckets.length) {
- for (const b of buckets) {
- const sec = b.from / 1000
- ;(sec >= 30 && sec < 40) || (sec >= 60 && sec < 70) ? refreshDts.push(...b.dts) : idleDts.push(...b.dts)
- }
- } else {
- // 旧版探针没有 frameBuckets:按 fpsWindows 桶近似
- for (const w of report.fpsWindows || []) {
- const sec = w.from / 1000
- const isRefresh = (sec >= 30 && sec < 40) || (sec >= 60 && sec < 70)
- if (isRefresh && w.p95 != null) refreshDts = [w.p95]
- if (!isRefresh && w.p95 != null) idleDts.push(w.p95)
- }
- }
- return {
- longTaskTotal: longTasks.length,
- pureSnapshot: pure.length,
- pureList: pure,
- maxCluster,
- refreshP95: p95(refreshDts),
- idleP95: p95(idleDts),
- }
- }
- const base = analyze(load(baselinePath))
- const fixed = analyze(load(fixedPath))
- const fmt = (v) => (v === null ? 'n/a' : Number(v).toFixed(1))
- console.log('口径 基线(无修复) 带修复')
- console.log(`≥100ms 长任务总量 ${String(base.longTaskTotal).padEnd(16)} ${fixed.longTaskTotal}`)
- console.log(`纯 map-snapshot 长任务(病灶) ${String(base.pureSnapshot).padEnd(16)} ${fixed.pureSnapshot}`)
- console.log(`最长连续簇(≈批数×120ms节拍) ${String(base.maxCluster).padEnd(16)} ${fixed.maxCluster}`)
- console.log(`刷新窗口帧间隔 p95 ${fmt(base.refreshP95).padEnd(16)} ${fmt(fixed.refreshP95)} ms`)
- console.log(`静置窗口帧间隔 p95 ${fmt(base.idleP95).padEnd(16)} ${fmt(fixed.idleP95)} ms`)
- console.log(`tick 增量(刷新−静置) ${fmt(base.refreshP95 - base.idleP95).padEnd(16)} ${fmt(fixed.refreshP95 - fixed.idleP95)} ms`)
- console.log('\n基线病灶长任务时刻:', base.pureList.map((p) => `${Math.round(p.start)}ms/${p.dur}ms`).join(' ') || '(无)')
- console.log('修复后病灶长任务时刻:', fixed.pureList.map((p) => `${Math.round(p.start)}ms/${p.dur}ms`).join(' ') || '(无)')
|