Skip to content

Commit 0fe19f2

Browse files
rdhyeeclaude
andauthored
Port ?perf=1 timing panel to Search Explorer (baseline step) (#124)
First step toward the Explorer rethink: establish measured baseline for the current slow implementation before touching architecture. Adds performance.mark() hooks at four natural boundaries: - explorer_db (DuckDB init + CREATE VIEW for samples/sample_facets) - explorer_facets (2 KB facet summaries load) - explorer_count (COUNT(*) on wide parquet through WHERE clause) - explorer_samples (ORDER BY RANDOM() LIMIT N on wide parquet — the slow one) New perfPanel cell gated on ?perf=1. Also shows ?v=2 in the header so v1 vs v2 numbers can be compared side-by-side in follow-up PR. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent eaa0751 commit 0fe19f2

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

tutorials/isamples_explorer.qmd

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ html`<div style="margin-bottom: 16px;">
296296
//| code-fold: true
297297
// Initialize DuckDB-WASM
298298
db = {
299+
performance.mark('explorer-db-start');
299300
const bundle = await duckdbModule.selectBundle(duckdbModule.getJsDelivrBundles());
300301
301302
const worker_url = URL.createObjectURL(
@@ -315,6 +316,8 @@ db = {
315316
await conn.query(`CREATE VIEW sample_facets AS SELECT * FROM read_parquet('${sample_facets_url}')`);
316317
await conn.close();
317318
319+
performance.mark('explorer-db-end');
320+
performance.measure('explorer_db', 'explorer-db-start', 'explorer-db-end');
318321
return instance;
319322
}
320323
@@ -347,8 +350,11 @@ mutable facetSummariesError = null
347350
// Tier 1: Load pre-computed facet summaries (2KB, instant)
348351
facetSummaries = {
349352
mutable facetSummariesError = null;
353+
performance.mark('explorer-facets-start');
350354
try {
351355
const rows = await runQuery(`SELECT * FROM read_parquet('${facet_summaries_url}')`);
356+
performance.mark('explorer-facets-end');
357+
performance.measure('explorer_facets', 'explorer-facets-start', 'explorer-facets-end');
352358
return rows;
353359
} catch (e) {
354360
console.error("Facet summaries load error:", e);
@@ -642,9 +648,12 @@ sourceCounts = facetsByType.source
642648
//| code-fold: true
643649
// Get total count matching current filters
644650
totalCount = {
651+
performance.mark('explorer-count-start');
645652
const query = `SELECT COUNT(*) as count FROM samples WHERE ${whereClause}`;
646653
try {
647654
const rows = await runQuery(query);
655+
performance.mark('explorer-count-end');
656+
performance.measure('explorer_count', 'explorer-count-start', 'explorer-count-end');
648657
return rows[0]?.count || 0;
649658
} catch (e) {
650659
return 0;
@@ -662,6 +671,7 @@ sampleData = {
662671
statusDiv.textContent = 'Loading samples...';
663672
}
664673
674+
performance.mark('explorer-samples-start');
665675
try {
666676
const query = `
667677
SELECT
@@ -681,6 +691,9 @@ sampleData = {
681691
682692
const data = await runQuery(query);
683693
694+
performance.mark('explorer-samples-end');
695+
performance.measure('explorer_samples', 'explorer-samples-start', 'explorer-samples-end');
696+
684697
if (statusDiv) {
685698
statusDiv.textContent = `Loaded ${data.length.toLocaleString()} samples`;
686699
setTimeout(() => { statusDiv.style.display = 'none'; }, 2000);
@@ -906,6 +919,86 @@ Loaded: ${sampleData.length.toLocaleString()}
906919

907920
---
908921

922+
```{ojs}
923+
//| echo: false
924+
//| output: false
925+
926+
// === Performance timing panel (opt-in: append ?perf=1 to URL) ===
927+
// Ported from progressive_globe.qmd. Reads performance.mark/measure entries
928+
// and renders a small fixed panel. Ship with perf=1 to measure baseline,
929+
// then v2=1 to compare.
930+
perfPanel = {
931+
// Depend on sampleData so the panel appears after initial data loads
932+
if (sampleData == null) return;
933+
934+
const params = new URLSearchParams(location.search);
935+
if (params.get('perf') !== '1') return;
936+
937+
await new Promise(r => setTimeout(r, 100));
938+
939+
const mark = (name) => {
940+
const e = performance.getEntriesByName(name, 'mark').pop();
941+
return e ? e.startTime : null;
942+
};
943+
const measure = (name) => {
944+
const e = performance.getEntriesByName(name, 'measure').pop();
945+
return e ? e.duration : null;
946+
};
947+
948+
const paintEntries = performance.getEntriesByType('paint');
949+
const fcp = paintEntries.find(e => e.name === 'first-contentful-paint')?.startTime;
950+
const fp = paintEntries.find(e => e.name === 'first-paint')?.startTime;
951+
952+
const rows = [
953+
['first-paint (browser)', fp],
954+
['first-contentful-paint', fcp],
955+
['DuckDB init + views', measure('explorer_db')],
956+
['facet summaries query', measure('explorer_facets')],
957+
['count query', measure('explorer_count')],
958+
['sample data query', measure('explorer_samples')],
959+
['nav → DuckDB ready', mark('explorer-db-end')],
960+
['nav → facets ready', mark('explorer-facets-end')],
961+
['nav → count ready', mark('explorer-count-end')],
962+
['nav → samples ready', mark('explorer-samples-end')],
963+
].filter(([, v]) => v != null);
964+
965+
console.table(Object.fromEntries(rows.map(([k, v]) => [k, `${v.toFixed(0)} ms`])));
966+
967+
const fmt = (ms) => ms == null ? '—' : ms >= 1000 ? `${(ms/1000).toFixed(2)} s` : `${ms.toFixed(0)} ms`;
968+
// Remove any prior panel (page re-renders on filter change)
969+
const prior = document.getElementById('perfPanel');
970+
if (prior) prior.remove();
971+
972+
const version = params.get('v') === '2' ? 'v2' : 'v1';
973+
const panel = document.createElement('div');
974+
panel.id = 'perfPanel';
975+
panel.style.cssText = `
976+
position: fixed; bottom: 12px; right: 12px; z-index: 9999;
977+
background: rgba(0,0,0,0.82); color: #e8f5e9; padding: 10px 12px;
978+
border-radius: 6px; font: 11px/1.4 ui-monospace, SFMono-Regular, monospace;
979+
max-width: 340px; box-shadow: 0 2px 12px rgba(0,0,0,0.3);
980+
`;
981+
panel.innerHTML = `
982+
<div style="font-weight:600;color:#fff;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center;">
983+
<span>⏱ Explorer perf (${version})</span>
984+
<button id="perfClose" style="background:none;border:none;color:#aaa;cursor:pointer;font-size:14px;padding:0 4px;">×</button>
985+
</div>
986+
<table style="border-collapse:collapse;width:100%;">
987+
${rows.map(([label, v]) => `
988+
<tr><td style="padding:1px 8px 1px 0;color:#bbb;">${label}</td>
989+
<td style="padding:1px 0;text-align:right;color:#a5d6a7;font-variant-numeric:tabular-nums;">${fmt(v)}</td></tr>
990+
`).join('')}
991+
</table>
992+
`;
993+
document.body.appendChild(panel);
994+
panel.querySelector('#perfClose').onclick = () => panel.remove();
995+
996+
return "shown";
997+
}
998+
```
999+
1000+
---
1001+
9091002
<style>
9101003
.card {
9111004
background: #fafafa;

0 commit comments

Comments
 (0)