Skip to content

Commit 656e9ed

Browse files
rdhyeeclaude
andauthored
Add opt-in ?perf=1 timing panel to Interactive Explorer (#118)
First step of the "progressive globe as reference design" strategy: ground performance work in numbers before optimizing. Instruments three natural hook points with performance.mark(): - duckdb_init (DuckDBClient.of() call) - viewer_init (Cesium.Viewer construction) - first-globe-frame (first scene.postRender callback) A new perfPanel cell reads these marks plus the browser's paint entries and the existing p1/r{res} measures, and renders a small fixed panel in the bottom-right. Gated on ?perf=1 — off by default in production. Also dumps to console.table for CI capture. Baseline (local, headless Chrome, warm R2): - first-paint 370 ms - duckdb_init 1.47 s - nav → viewer ready 2.59 s - nav → phase 1 complete 6.57 s Next optimizations to drive against these numbers: preload critical parquets, DuckDB init off main thread earlier, R2 cache header audit. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 8e8e50c commit 656e9ed

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

tutorials/progressive_globe.qmd

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,10 @@ function updateSamples(samples) {
430430
431431
// === DuckDB ===
432432
db = {
433+
performance.mark('duckdb-init-start');
433434
const instance = await DuckDBClient.of();
435+
performance.mark('duckdb-init-end');
436+
performance.measure('duckdb_init', 'duckdb-init-start', 'duckdb-init-end');
434437
return instance;
435438
}
436439
```
@@ -441,6 +444,7 @@ db = {
441444
442445
// === Cesium Viewer (created once, never re-created) ===
443446
viewer = {
447+
performance.mark('viewer-init-start');
444448
const v = new Cesium.Viewer("cesiumContainer", {
445449
timeline: false,
446450
animation: false,
@@ -589,6 +593,17 @@ viewer = {
589593
}
590594
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
591595
596+
// Timing: viewer ready (mount complete, pre-first-render)
597+
performance.mark('viewer-init-end');
598+
performance.measure('viewer_init', 'viewer-init-start', 'viewer-init-end');
599+
600+
// Timing: first Cesium frame painted (globe visible, may be pre-cluster)
601+
const firstGlobeFrame = () => {
602+
performance.mark('first-globe-frame');
603+
v.scene.postRender.removeEventListener(firstGlobeFrame);
604+
};
605+
v.scene.postRender.addEventListener(firstGlobeFrame);
606+
592607
return v;
593608
}
594609
```
@@ -1217,6 +1232,83 @@ zoomWatcher = {
12171232
}
12181233
```
12191234

1235+
```{ojs}
1236+
//| echo: false
1237+
//| output: false
1238+
1239+
// === Performance timing panel (opt-in: append ?perf=1 to URL) ===
1240+
// v0: reads performance.mark/measure entries and renders a small fixed panel.
1241+
// Reports navigation→duckdb_init, navigation→viewer_init, phase 1 res4 load,
1242+
// and navigation→first paint. Also dumps to console.table for CI / Playwright.
1243+
perfPanel = {
1244+
if (!phase1) return; // wait for phase 1 to have run
1245+
1246+
const params = new URLSearchParams(location.search);
1247+
const isOn = params.get('perf') === '1';
1248+
if (!isOn) return;
1249+
1250+
// Give first-paint a tick to fire, then collect
1251+
await new Promise(r => setTimeout(r, 100));
1252+
1253+
const origin = performance.timeOrigin;
1254+
const mark = (name) => {
1255+
const e = performance.getEntriesByName(name, 'mark').pop();
1256+
return e ? e.startTime : null;
1257+
};
1258+
const measure = (name) => {
1259+
const e = performance.getEntriesByName(name, 'measure').pop();
1260+
return e ? e.duration : null;
1261+
};
1262+
1263+
// Paint timings from the browser (free, no instrumentation needed)
1264+
const paintEntries = performance.getEntriesByType('paint');
1265+
const fcp = paintEntries.find(e => e.name === 'first-contentful-paint')?.startTime;
1266+
const fp = paintEntries.find(e => e.name === 'first-paint')?.startTime;
1267+
1268+
const rows = [
1269+
['first-paint (browser)', fp],
1270+
['first-contentful-paint', fcp],
1271+
['duckdb init', measure('duckdb_init')],
1272+
['viewer init', measure('viewer_init')],
1273+
['nav → viewer ready', mark('viewer-init-end')],
1274+
['phase 1 res4 (duration)', measure('p1')],
1275+
['nav → phase 1 complete', mark('p1-end')],
1276+
['nav → first globe frame', mark('first-globe-frame')],
1277+
].filter(([, v]) => v != null);
1278+
1279+
// Console table for CI / offline capture
1280+
console.table(Object.fromEntries(rows.map(([k, v]) => [k, `${v.toFixed(0)} ms`])));
1281+
1282+
// Render a small floating panel
1283+
const fmt = (ms) => ms == null ? '—' : ms >= 1000 ? `${(ms/1000).toFixed(2)} s` : `${ms.toFixed(0)} ms`;
1284+
const panel = document.createElement('div');
1285+
panel.id = 'perfPanel';
1286+
panel.style.cssText = `
1287+
position: fixed; bottom: 12px; right: 12px; z-index: 9999;
1288+
background: rgba(0,0,0,0.82); color: #e8f5e9; padding: 10px 12px;
1289+
border-radius: 6px; font: 11px/1.4 ui-monospace, SFMono-Regular, monospace;
1290+
max-width: 320px; box-shadow: 0 2px 12px rgba(0,0,0,0.3);
1291+
`;
1292+
panel.innerHTML = `
1293+
<div style="font-weight:600;color:#fff;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center;">
1294+
<span>⏱ Perf timings</span>
1295+
<button id="perfClose" style="background:none;border:none;color:#aaa;cursor:pointer;font-size:14px;padding:0 4px;">×</button>
1296+
</div>
1297+
<table style="border-collapse:collapse;width:100%;">
1298+
${rows.map(([label, v]) => `
1299+
<tr><td style="padding:1px 8px 1px 0;color:#bbb;">${label}</td>
1300+
<td style="padding:1px 0;text-align:right;color:#a5d6a7;font-variant-numeric:tabular-nums;">${fmt(v)}</td></tr>
1301+
`).join('')}
1302+
</table>
1303+
<div style="margin-top:6px;color:#777;font-size:10px;">timeOrigin: ${new Date(origin).toISOString().split('T')[1].slice(0,12)}</div>
1304+
`;
1305+
document.body.appendChild(panel);
1306+
panel.querySelector('#perfClose').onclick = () => panel.remove();
1307+
1308+
return "shown";
1309+
}
1310+
```
1311+
12201312
## How This Demo Works
12211313

12221314
Pre-aggregated H3 hexagonal indices achieve near-instant globe rendering, with seamless drill-down to individual samples:

0 commit comments

Comments
 (0)