5a3be3af33b76d69d1fc98ee76b9a37f96e96fc7 max Wed Aug 26 00:36:52 2026 -0700 Add opt-in per-phase request timing to the new hgBlat and hgSession pages, shown in a dialog with &measureTiming=1. refs #38157 New reusable perfTimer collector (lib/perfTimer.c) records labeled wall-clock intervals via clock1000() and emits them as JSON; hgBlat and hgSession attach a timing array to their payload when the existing measureTiming cart/CGI var is set. A shared gbShowTimingDialog helper in utils.js renders the server phases plus a client render-time row in a house-style modal, which opens automatically when measureTiming is on. Confirms the position band/locus annotation loop dominates hgSession, and run-BLAT plus locus lookups dominate hgBlat. diff --git src/hg/js/hgBlat.js src/hg/js/hgBlat.js index aae33b07a69..6b78ff212a3 100644 --- src/hg/js/hgBlat.js +++ src/hg/js/hgBlat.js @@ -1,27 +1,27 @@ // hgBlat.js - client-side rendering of the hgBlat "Table" output mode. // // hgBlat.c emits an inline object var hgBlatData = { config, hits } and an empty // <div id="blatResults">. This script builds the whole results UI from that data: // - a card with a summary strip (query / length / assembly / hit count + actions) // - a sortable, filterable DataTable whose cells are rendered here (identity bar, // query-coverage bar, linked loci, action links, comma-formatted position) // - a docked "selected hit" detail panel updated on row click // Header tooltips reuse the Genome Browser's own mechanism (title + convertTitleTagsToMouseovers). /* jshint esnext: true */ -/* global $, hgBlatData, convertTitleTagsToMouseovers, htmlEncode */ +/* global $, hgBlatData, convertTitleTagsToMouseovers, htmlEncode, commify, gbShowTimingDialog */ var blatSelectedRank = null; // rank of the row shown in the detail panel function blatFmt(n) { // 12345 -> "12,345" return Number(n).toLocaleString('en-US'); } function blatIdColor(id) { // UCSC identity semantic colors if (id >= 98) { return '#1f7a34'; } if (id >= 95) { return '#4d7c0f'; } if (id >= 90) { return '#b45309'; } return '#b1301f'; } @@ -407,30 +407,33 @@ box.style.display = 'none'; }); } function blatShowQuerySeq() { var box = document.getElementById('blatSeqBox'); if (box.style.display === 'flex') { box.style.display = 'none'; return; } // toggle off blatShowFasta(box, hgBlatData.config.querySeqs, 'blatQuery.fa'); } // ---- build --------------------------------------------------------------- function blatBuild() { var cfg = hgBlatData.config; var hits = hgBlatData.hits; + // When loaded with &measureTiming=1 the C side attaches hgBlatData.timing; time the client + // render too so the dialog shows the full server+client picture. + var tBuildStart = (hgBlatData.timing && window.performance) ? performance.now() : 0; // Pin a stable, shareable URL into the address bar (no server redirect) so refresh, bookmark and // "Share a link" all use the trash-backed reopen link instead of the transient POST/search URL. if (cfg.shareUrl) { try { history.replaceState(null, '', cfg.shareUrl); } catch (e) { /* older browsers: ignore */ } } var back = cfg.backUrl ? `<a class="gbPill" title="Return to the Genome Browser at your previous location (${htmlEncode(cfg.backPos)})" ` + `href="${htmlEncode(cfg.backUrl)}">Back to Genome Browser</a>` : ''; // The page actions live in the gold main-header bar (framework #sectTtl), next to the title - // so there is no separate toolbar (.blatHead is gone). Injected into #sectTtl below. var headActions = `${back}<a class="gbPill primary" title="Start a new BLAT search" href="${htmlEncode(cfg.newSearchUrl)}">New BLAT search</a>`; @@ -523,30 +526,53 @@ // Keep the selected-row highlight after sort/filter. Header tooltips are wired once below (the // <thead> persists across draws); we deliberately do NOT re-run convertTitleTagsToMouseovers on // every draw, as it re-scans the whole document and adds global listeners on each call. dt.on('draw', function() { if (blatSelectedRank !== null) { blatSelect(dt, blatSelectedRank); } }); // No hit is pre-selected: several hits are often tied on score/identity, so picking one for the // user is misleading. The detail panel shows a prompt until a row is clicked. document.getElementById('blatDetail').innerHTML = `<div class="blatSelectHint">Click a hit below to see its alignment details. ` + `If you are missing matches that you think should be there, ` + `<a target="_blank" href="../FAQ/FAQblat.html#blat1b">read our BLAT FAQ</a> or ` + `<a href="mailto:genome@soe.ucsc.edu">contact us</a>.</div>`; + + // Timing report (only when loaded with &measureTiming=1): a pill in the summary strip that opens + // the shared dialog with the server phases plus the client render time. + if (hgBlatData.timing) { + var clientRows = [{ label: 'build page (JS)', + ms: Math.round(performance.now() - tBuildStart) }]; + var pill = document.createElement('button'); + pill.type = 'button'; + pill.className = 'gbPill'; + pill.id = 'blatTimingBtn'; + pill.innerHTML = '⏱ Timing'; + pill.title = 'Show where this page spent its time (server and browser)'; + pill.addEventListener('click', function() { + gbShowTimingDialog(hgBlatData.timing, clientRows); + }); + var strip = document.querySelector('#blatResults .gbStripActions') || + document.querySelector('#blatResults .gbStrip'); + if (strip) { strip.appendChild(pill); } + // measureTiming=1 on the URL is an explicit request to see the numbers, so open the dialog + // right away; the pill stays for reopening it after Close. + gbShowTimingDialog(hgBlatData.timing, clientRows); + } + blatApplyTooltips(); } // ==== search form (the input page) ======================================== // hgBlat.c emits var hgBlatFormData = {...} together with a real <form name="mainForm"> that // contains an empty <div id="blatFormBox"> and the C-generated genome search bar. We build the // controls as real form fields *inside that form*, so the browser serializes them natively - // including the file input - and Submit / I'm feeling lucky / Clear stay plain submit buttons // handled by the existing C code. There is no shadow form and no copying of values on submit. // Styling comes from hgBlat.css (loaded by webIncludeResourceFile in hgBlat.c), shared with the results page. // The Genome Browser's standard info icon, copied from printInfoIconSvg() in hg/lib/hui.c so the // form's icons are pixel-identical to the C-rendered ones elsewhere in the browser. var BLAT_INFO_SVG = "<svg style='height:1.1em; vertical-align:top' viewBox='0 0 24 24' fill='none' " +