Document after-phase0 benchmark comparison and hidden timer validation
This commit is contained in:
@@ -130,3 +130,14 @@ To verify performance gains and detect regressions in later phases:
|
||||
node perf/baseline.mjs --compare perf/results/baseline-2026-10-07.json
|
||||
```
|
||||
Prints an ASCII/Markdown delta comparison table highlighting percentage changes across wire bytes, boot duration, and update overhead.
|
||||
|
||||
---
|
||||
|
||||
## 6. Hidden Timers Verification (`perf/hidden-timers.mjs`)
|
||||
|
||||
Verifies that non-playback UI intervals (`chips`, `reanchor`, `rotateChips`, `flush`, `checkBuildTag`) suspend while the document is hidden and restore upon visibility, while essential background intervals (`maybeCheck`, 120s resume check) remain active:
|
||||
```bash
|
||||
node perf/hidden-timers.mjs
|
||||
```
|
||||
Exits with code 0 on success, or code 1 if any interval fails to pause, fails to resume, or if page errors occur.
|
||||
|
||||
|
||||
@@ -173,3 +173,77 @@ Because automated headless WebKit cannot fully replicate iOS Safari's mobile bac
|
||||
|
||||
5. **Log Findings**:
|
||||
- Record iPhone Model, iOS Version, Battery Drain (% per 30m), Dropouts Count (0 = pass), and Resumption Status (Pass/Fail).
|
||||
|
||||
---
|
||||
|
||||
## 7. After Phase 0 Benchmark (Step S5 Verification)
|
||||
|
||||
**Date**: 2026-10-07
|
||||
**Commit**: `eb6aae7`
|
||||
**Dataset**: `perf/results/after-phase0-2026-10-07.json` (compared against `perf/results/baseline-2026-10-07.json`)
|
||||
|
||||
### Headline Before vs. After Comparison Table (Median of 5 Runs)
|
||||
|
||||
| Browser / Profile | Scenario | Requests (Before → After) | Wire Bytes (Before → After) | FCP / LCP (ms) | Boot Done (ms) | SW Dur / Reload (ms) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **Chromium LTE** | Cold First Load | 164 → 166 (+1.2%) | 1,015.0 KB → 1,015.8 KB (+0.1%) | 1540/1896 → 1572/1916 | 10,163 → 10,480 (+3.1%) | 38,446 → 38,364 |
|
||||
| | Warm Reload (SW) | 4 → 4 (0.0%) | 5.2 KB → 5.2 KB (0.0%) | 180/684 → 184/680 | 675 → 692 (+2.5%) | - |
|
||||
| | Offline Reload | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 72/108 → 76/112 | 219 → 259 | 240 → 282 |
|
||||
| | Update (1-line JS) | 88 → 89 (+1.1%) | 596.6 KB → 597.0 KB (+0.1%) | - | - | 2,013 → 2,155 (+7.1%) |
|
||||
| | Update (1-line CSS) | 88 → 89 (+1.1%) | 596.7 KB → 597.1 KB (+0.1%) | - | - | 1,970 → 2,107 (+7.0%) |
|
||||
| **Chromium Lossy** | Cold First Load | 164 → 166 (+1.2%) | 1,015.0 KB → 1,015.8 KB (+0.1%) | 2344/2912 → 2372/2940 | 16,015 → 16,408 (+2.5%) | 42,518 → 42,031 |
|
||||
| | Warm Reload (SW) | 4 → 4 (0.0%) | 5.2 KB → 5.2 KB (0.0%) | 184/644 → 188/648 | 651 → 768 (+18.0%) | - |
|
||||
| | Offline Reload | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 80/92 → 84/96 | 237 → 241 | 253 → 262 |
|
||||
| | Update (1-line JS) | 88 → 89 (+1.1%) | 596.6 KB → 597.0 KB (+0.1%) | - | - | 2,031 → 2,115 (+4.1%) |
|
||||
| | Update (1-line CSS) | 88 → 89 (+1.1%) | 596.7 KB → 597.1 KB (+0.1%) | - | - | 2,004 → 2,116 (+5.6%) |
|
||||
| **WebKit LTE** | Cold First Load | 162 → 164 (+1.2%) | 1.12 MB → 1.13 MB (+0.1%) | 1088/1287 → 1100/1303 | 3,201 → 3,290 (+2.8%) | 30,508 → 31,026 |
|
||||
| | Warm Reload (SW) | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 110/110 → 112/112 | 131 → 139 (+6.1%) | - |
|
||||
| | Offline Reload | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 107/107 → 110/110 | 128 → 137 | 135 → 148 |
|
||||
| | Update (1-line JS) | 86 → 87 (+1.2%) | 664.3 KB → 664.9 KB (+0.1%) | - | - | 4,457 → 4,584 (+2.8%) |
|
||||
| | Update (1-line CSS) | 86 → 87 (+1.2%) | 664.3 KB → 664.9 KB (+0.1%) | - | - | 4,510 → 4,550 (+0.9%) |
|
||||
| **WebKit Lossy** | Cold First Load | 162 → 164 (+1.2%) | 1.12 MB → 1.13 MB (+0.1%) | 3557/3735 → 3738/3905 | 7,303 → 7,305 (+0.0%) | 35,449 → 35,101 |
|
||||
| | Warm Reload (SW) | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 111/112 → 112/114 | 131 → 128 (-2.3%) | - |
|
||||
| | Offline Reload | 0 → 0 (0.0%) | 0 B → 0 B (0.0%) | 109/119 → 111/120 | 129 → 132 | 141 → 139 |
|
||||
| | Update (1-line JS) | 86 → 87 (+1.2%) | 664.3 KB → 664.9 KB (+0.1%) | - | - | 8,135 → 10,017 (+23.1%) |
|
||||
| | Update (1-line CSS) | 87 → 87 (0.0%) | 664.4 KB → 664.9 KB (+0.1%) | - | - | 8,269 → 10,013 (+21.1%) |
|
||||
|
||||
### Hidden Timers Verification Result
|
||||
|
||||
Executed via `node perf/hidden-timers.mjs`:
|
||||
```
|
||||
=== Hidden Timers Verification ===
|
||||
Initial visible intervals (8): anon1000, anon120000, checkBuildTag, chips, flush, maybeCheck, reanchor, rotateChips
|
||||
Hidden intervals (3): anon1000, anon120000, maybeCheck
|
||||
Restored visible intervals (9): anon1000, anon120000, anon50000, checkBuildTag, chips, flush, maybeCheck, reanchor, rotateChips
|
||||
Page errors: none
|
||||
|
||||
Result: ALL ASSERTIONS PASSED
|
||||
```
|
||||
All five non-playback UI timers (`chips`, `reanchor`, `rotateChips`, `flush`, `checkBuildTag`) suspend immediately when `document.hidden` becomes true and resume immediately upon returning to visible, while essential background loops (`maybeCheck` inbox poll and `anon120000` 120s OPFS download resume check) remain continuously active.
|
||||
|
||||
### Variance & Noise Observed
|
||||
|
||||
Across the 5 iterations per scenario:
|
||||
- **Transfer Volumes & Request Counts**: Completely deterministic (0.0% variance across all runs).
|
||||
- **Boot Milestones & Paint Timings**: Stable within ±1–3% under LTE emulation on both Chromium and WebKit.
|
||||
- **Update Duration under Lossy WebKit**: Exhibited expected network noise (~8.1 s vs ~10.0 s, ~21–23% delta) due to the random 1,500 ms connection stall injection pattern in `perf/proxy.mjs`.
|
||||
- **Chromium Update Duration**: Clustered closely around ~2,100 ms with occasional ~100 ms fast runs when the browser resolves registration updates asynchronously.
|
||||
|
||||
### Findings
|
||||
|
||||
Introducing `frontend/visible-timer.js` successfully eliminates unnecessary CPU wakeups and timer execution during hidden tab states while adding virtually zero runtime transfer overhead (+1 request and +373–471 wire bytes on service worker updates; +2 requests and +843 wire bytes on initial cold visit). Background audio playback and essential network recovery handlers are preserved without regressing paint, boot, or offline capabilities.
|
||||
|
||||
### Structural Transfer Analysis: Fonts & Icons vs. Code, and Cold Double-Download
|
||||
|
||||
Detailed breakdown of network transfer patterns revealed two major inefficiencies in the current asset delivery architecture:
|
||||
|
||||
1. **Static Fonts and Icons vs. JS/CSS**:
|
||||
- **Cold First Visit**: Total wire transfer is 1,040.2 KB. Web fonts (`PlusJakartaSans`, `BricolageGrotesque`, `HankenGrotesk`, `JetBrainsMono`, and `fonts.css`) consume **397.1 KB (38.2%)** and PNG icons consume **6.3 KB (0.6%)**, totaling **403.4 KB (38.8%)** of immutable media. Application JavaScript consumes **478.1 KB (46.0%)** and CSS consumes **130.7 KB (12.6%)**, totaling **608.8 KB (58.5%)**.
|
||||
- **Update N -> N+1 Wire Cost**: On any single-line change to `app.js` or `theme-glass.css`, `sw.js` invalidates the entire shell cache and re-downloads all 89 `SHELL` items. Out of 611.3 KB transferred, **~259.5 KB (~42.5%)** is spent re-downloading static web fonts and icons that never changed, compared to **~321.9 KB (52.7%)** for JS/CSS.
|
||||
2. **Cold First-Visit Shell Double-Download**:
|
||||
- The initial visit downloads every app shell resource **twice over the wire**:
|
||||
- First, the browser's HTML parser requests `index.html`, stylesheets, scripts, and fonts to render the document (83 requests, ~520 KB).
|
||||
- Second, once `boot()` registers `/sw.js`, the service worker `install` event triggers `precacheShell()`. Line 165 of `frontend/sw.js` executes `fetch(new Request(url, { cache: 'reload' }))`.
|
||||
- The explicit `{ cache: 'reload' }` directive bypasses the browser's HTTP cache entirely, forcing a redundant full re-download of all 89 shell assets from the server.
|
||||
- As measured by the harness request log, cold first visit request count is **166 requests** (exactly double the shell count) and wire data transfer is **1,040 KB** (twice the unprimed payload).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user