@@ -11,6 +11,53 @@ format:
1111 <link rel="preconnect" href="https://data.isamples.org" crossorigin>
1212 <link rel="preload" as="fetch" crossorigin="anonymous" href="https://data.isamples.org/isamples_202601_facet_summaries.parquet">
1313 <link rel="preload" as="fetch" crossorigin="anonymous" href="https://data.isamples.org/isamples_202601_facet_cross_filter.parquet">
14+ <script>
15+ // Opt-in perf panel (?perf=1). Plain script — independent of the
16+ // OJS reactive graph, which was unreliable at firing this cell.
17+ (function() {
18+ if (new URLSearchParams(location.search).get('perf') !== '1') return;
19+ const mark = (n) => { const e = performance.getEntriesByName(n, 'mark').pop(); return e ? e.startTime : null; };
20+ const meas = (n) => { const e = performance.getEntriesByName(n, 'measure').pop(); return e ? e.duration : null; };
21+ const fmt = (ms) => ms == null ? '—' : ms >= 1000 ? (ms/1000).toFixed(2)+' s' : Math.round(ms)+' ms';
22+ function render() {
23+ const paints = performance.getEntriesByType('paint');
24+ const fp = paints.find(e => e.name === 'first-paint')?.startTime;
25+ const fcp = paints.find(e => e.name === 'first-contentful-paint')?.startTime;
26+ const rows = [
27+ ['first-paint (browser)', fp],
28+ ['first-contentful-paint', fcp],
29+ ['DuckDB init + views', meas('explorer_db')],
30+ ['facet summaries query', meas('explorer_facets')],
31+ ['count query', meas('explorer_count')],
32+ ['sample data query', meas('explorer_samples')],
33+ ['nav → DuckDB ready', mark('explorer-db-end')],
34+ ['nav → facets ready', mark('explorer-facets-end')],
35+ ['nav → count ready', mark('explorer-count-end')],
36+ ['nav → samples ready', mark('explorer-samples-end')],
37+ ].filter(([,v]) => v != null);
38+ const prior = document.getElementById('perfPanel');
39+ if (prior) prior.remove();
40+ const version = new URLSearchParams(location.search).get('v') === '2' ? 'v2' : 'v1';
41+ const panel = document.createElement('div');
42+ panel.id = 'perfPanel';
43+ panel.style.cssText = 'position:fixed;bottom:12px;right:12px;z-index:9999;background:rgba(0,0,0,0.82);color:#e8f5e9;padding:10px 12px;border-radius:6px;font:11px/1.4 ui-monospace,SFMono-Regular,monospace;max-width:340px;box-shadow:0 2px 12px rgba(0,0,0,0.3);';
44+ panel.innerHTML = '<div style="font-weight:600;color:#fff;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center;"><span>⏱ Explorer perf (' + version + ')</span><button id="perfClose" style="background:none;border:none;color:#aaa;cursor:pointer;font-size:14px;padding:0 4px;">×</button></div><table style="border-collapse:collapse;width:100%;">' + rows.map(([l,v])=>'<tr><td style="padding:1px 8px 1px 0;color:#bbb;">'+l+'</td><td style="padding:1px 0;text-align:right;color:#a5d6a7;font-variant-numeric:tabular-nums;">'+fmt(v)+'</td></tr>').join('') + '</table>';
45+ document.body.appendChild(panel);
46+ panel.querySelector('#perfClose').onclick = () => panel.remove();
47+ console.table(Object.fromEntries(rows.map(([k,v])=>[k, fmt(v)])));
48+ }
49+ // Poll for the last-expected mark; re-render as each new mark arrives.
50+ let done = false;
51+ const id = setInterval(() => {
52+ const haveAll = mark('explorer-samples-end') != null && mark('explorer-count-end') != null;
53+ const haveSome = mark('explorer-db-end') != null;
54+ if (haveSome) render();
55+ if (haveAll && !done) { done = true; setTimeout(() => clearInterval(id), 500); }
56+ }, 500);
57+ // Safety: stop polling after 2 minutes regardless
58+ setTimeout(() => clearInterval(id), 120000);
59+ })();
60+ </script>
1461---
1562
1663Search and explore ** 6.7 million physical samples** from scientific collections worldwide.
@@ -919,83 +966,10 @@ Loaded: ${sampleData.length.toLocaleString()}
919966
920967---
921968
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- ```
969+ <!-- Perf timing panel is implemented as a plain <script> in the header
970+ (see include-in-header in the YAML frontmatter above). Using a plain
971+ script rather than an OJS cell because OJS was inconsistently evaluating
972+ the cell on this page. -->
999973
1000974---
1001975
0 commit comments