Skip to content

Instantly share code, notes, and snippets.

@TomAugspurger
Created November 7, 2025 20:05
Show Gist options
  • Select an option

  • Save TomAugspurger/c32becfcba4d2cbcd7678a2d6579e371 to your computer and use it in GitHub Desktop.

Select an option

Save TomAugspurger/c32becfcba4d2cbcd7678a2d6579e371 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Benchmark Results Explorer</title>
<link href="https://unpkg.com/[email protected]/dist/css/tabulator.min.css" rel="stylesheet">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
overflow: hidden;
}
.container {
max-width: 100%;
margin: 0 auto;
display: flex;
gap: 0;
height: calc(100vh - 40px);
}
.table-container {
background: white;
padding: 20px;
border-radius: 8px 0 0 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
flex: 1;
min-width: 200px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.resize-handle {
width: 8px;
background: #e0e0e0;
cursor: col-resize;
position: relative;
flex-shrink: 0;
transition: background 0.2s;
}
.resize-handle:hover {
background: #bdbdbd;
}
.resize-handle:active {
background: #9e9e9e;
}
.resize-handle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 40px;
background: #999;
border-radius: 1px;
}
body.resizing {
cursor: col-resize !important;
user-select: none !important;
}
body.resizing * {
cursor: col-resize !important;
}
.details-panel {
background: white;
padding: 20px;
border-radius: 0 8px 8px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
width: 500px;
min-width: 300px;
max-width: 1600px;
overflow-y: auto;
flex-shrink: 0;
}
.details-panel h2 {
margin-top: 0;
font-size: 1.2em;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.details-panel h3 {
font-size: 1em;
margin-top: 20px;
color: #666;
}
.details-panel pre {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
font-size: 0.85em;
}
.details-panel .empty-state {
color: #999;
font-style: italic;
text-align: center;
padding: 40px;
}
.info-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 8px;
font-size: 0.9em;
}
.info-grid dt {
font-weight: 600;
color: #666;
}
.info-grid dd {
margin: 0;
}
#file-input {
margin-bottom: 20px;
}
.tabulator-row.tabulator-selected {
background-color: #e3f2fd !important;
}
#table:focus {
outline: 2px solid #007bff;
outline-offset: -2px;
}
kbd {
background-color: #f4f4f4;
border: 1px solid #ccc;
border-radius: 3px;
padding: 2px 6px;
font-family: monospace;
font-size: 0.9em;
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
}
</style>
</head>
<body>
<div class="container">
<div class="table-container">
<h1>Benchmark Results Explorer</h1>
<div style="margin-bottom: 10px; color: #666; font-size: 0.9em;">
Use <kbd>j</kbd>/<kbd>k</kbd> or <kbd>↑</kbd>/<kbd>↓</kbd> to navigate rows
</div>
<input type="file" id="file-input" accept=".jsonl,.json" multiple />
<div id="file-status" style="margin-top: 8px; margin-bottom: 8px; color: #28a745; font-size: 0.9em;"></div>
<div id="table" tabindex="0" style="flex: 1; overflow: auto;"></div>
</div>
<div class="resize-handle" id="resize-handle"></div>
<div class="details-panel" id="details-panel">
<div class="empty-state">Select a row to view details</div>
</div>
</div>
<script src="https://unpkg.com/[email protected]/dist/js/tabulator.min.js"></script>
<script>
let table;
let runsData = [];
// Parse JSONL file
function parseJSONL(text) {
return text.trim().split('\n')
.filter(line => line.trim())
.map(line => JSON.parse(line));
}
// Flatten run data into table rows
function flattenRunData(runs) {
const rows = [];
runs.forEach((run, runIndex) => {
for (const [queryId, records] of Object.entries(run.records)) {
records.forEach(record => {
rows.push({
runIndex: runIndex,
run: `Run ${runIndex + 1}`,
queryId: parseInt(queryId),
iteration: record.iteration,
duration: record.duration,
executor: run.executor,
cluster: run.cluster,
scale_factor: run.scale_factor,
timestamp: run.timestamp,
_runData: run,
_recordData: record
});
});
}
});
return rows;
}
// Show details for selected row
function showDetails(row) {
const data = row.getData();
const run = data._runData;
const record = data._recordData;
const panel = document.getElementById('details-panel');
let html = `
<h2>Query ${data.queryId} - Iteration ${data.iteration}</h2>
`;
// Show query plans first if available
const plan = run.plans?.find(p => p.query === data.queryId);
if (plan) {
if (plan.physical_plan) {
html += `<h3>Physical Plan</h3><pre>${plan.physical_plan}</pre>`;
}
if (plan.logical_plan) {
html += `<h3>Logical Plan</h3><pre>${plan.logical_plan}</pre>`;
}
}
html += `
<h3>Execution</h3>
<dl class="info-grid">
<dt>Duration:</dt><dd>${data.duration.toFixed(4)}s</dd>
<dt>Query ID:</dt><dd>${data.queryId}</dd>
<dt>Iteration:</dt><dd>${data.iteration}</dd>
</dl>
<h3>Run Configuration</h3>
<dl class="info-grid">
<dt>Executor:</dt><dd>${run.executor}</dd>
<dt>Runtime:</dt><dd>${run.runtime || 'N/A'}</dd>
<dt>Cluster:</dt><dd>${run.cluster || 'N/A'}</dd>
<dt>Scale Factor:</dt><dd>${run.scale_factor}</dd>
<dt>Timestamp:</dt><dd>${new Date(run.timestamp).toLocaleString()}</dd>
<dt>Workers:</dt><dd>${run.n_workers}</dd>
<dt>Threads:</dt><dd>${run.threads}</dd>
</dl>
<h3>Software Versions</h3>
<dl class="info-grid">
<dt>Polars:</dt><dd>${run.versions.polars}</dd>
<dt>Python:</dt><dd>${run.versions.python}</dd>
<dt>cudf_polars:</dt><dd>${typeof run.versions.cudf_polars === 'object' ? run.versions.cudf_polars.version : run.versions.cudf_polars}</dd>
</dl>
<h3>Hardware</h3>
<dl class="info-grid">
<dt>GPUs:</dt><dd>${run.hardware.gpus.length}</dd>
${run.hardware.gpus[0] ? `<dt>GPU Type:</dt><dd>${run.hardware.gpus[0].name}</dd>` : ''}
</dl>
`;
// Show shuffle stats if available
if (record.shuffle_stats) {
html += `<h3>Shuffle Statistics</h3><pre>${JSON.stringify(record.shuffle_stats, null, 2)}</pre>`;
}
panel.innerHTML = html;
}
// Initialize table
function initTable(data) {
table = new Tabulator("#table", {
data: data,
layout: "fitData",
selectable: 1,
height: "100%",
columns: [
{title: "Run", field: "run", width: 100, sorter: "number"},
{title: "Query ID", field: "queryId", width: 100, sorter: "number"},
{title: "Iteration", field: "iteration", width: 100, sorter: "number"},
{title: "Duration (s)", field: "duration", width: 120, sorter: "number",
formatter: (cell) => cell.getValue().toFixed(4)},
{title: "Executor", field: "executor", width: 120},
{title: "Cluster", field: "cluster", width: 120},
{title: "Scale Factor", field: "scale_factor", width: 120},
{title: "Timestamp", field: "timestamp", width: 200,
formatter: (cell) => new Date(cell.getValue()).toLocaleString()}
],
initialSort: [
{column: "runIndex", dir: "asc"},
{column: "queryId", dir: "asc"},
{column: "iteration", dir: "asc"}
]
});
// Handle row selection
table.on("rowClick", function(e, row){
showDetails(row);
});
// Keyboard navigation
table.on("rowSelected", function(row){
showDetails(row);
});
// Select first row after table is built
table.on("tableBuilt", function(){
const rows = table.getRows();
if (rows.length > 0) {
table.selectRow(rows[0]);
}
});
}
// File input handler - supports multiple files
document.getElementById('file-input').addEventListener('change', function(e) {
const files = Array.from(e.target.files);
if (files.length === 0) return;
// Read all files and concatenate them
const readPromises = files.map(file => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = reject;
reader.readAsText(file);
});
});
Promise.all(readPromises)
.then(contents => {
try {
// Concatenate all file contents
const combinedContent = contents.join('\n');
runsData = parseJSONL(combinedContent);
const flatData = flattenRunData(runsData);
// Update status message
const statusEl = document.getElementById('file-status');
const fileWord = files.length === 1 ? 'file' : 'files';
const runWord = runsData.length === 1 ? 'run' : 'runs';
statusEl.textContent = `✓ Loaded ${files.length} ${fileWord} (${runsData.length} ${runWord}, ${flatData.length} records)`;
if (table) {
table.replaceData(flatData);
// Re-select first row after data reload
setTimeout(() => {
const rows = table.getRows();
if (rows.length > 0) {
table.selectRow(rows[0]);
}
}, 0);
} else {
initTable(flatData);
}
} catch (error) {
document.getElementById('file-status').textContent = '';
alert('Error parsing file(s): ' + error.message);
}
})
.catch(error => {
document.getElementById('file-status').textContent = '';
alert('Error reading file(s): ' + error.message);
});
});
// Vim-style keyboard navigation (j/k keys)
document.addEventListener('keydown', function(e) {
// Don't interfere with typing in input fields
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (!table) return;
const selectedRows = table.getSelectedRows();
if (selectedRows.length === 0) {
// If no row is selected, select the first one
const rows = table.getRows();
if (rows.length > 0) {
table.selectRow(rows[0]);
}
return;
}
const currentRow = selectedRows[0];
let targetRow = null;
if (e.key === 'j' || e.key === 'J') {
// j = down
e.preventDefault();
targetRow = table.getNextRow(currentRow);
} else if (e.key === 'k' || e.key === 'K') {
// k = up
e.preventDefault();
targetRow = table.getPrevRow(currentRow);
} else if (e.key === 'ArrowDown') {
// Also support arrow keys if they work
e.preventDefault();
targetRow = table.getNextRow(currentRow);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
targetRow = table.getPrevRow(currentRow);
}
if (targetRow) {
table.deselectRow();
table.selectRow(targetRow);
// Scroll to the row to keep it visible
targetRow.scrollTo();
}
});
// Resizable panels
(function() {
const resizeHandle = document.getElementById('resize-handle');
const detailsPanel = document.getElementById('details-panel');
let isResizing = false;
resizeHandle.addEventListener('mousedown', function(e) {
isResizing = true;
document.body.classList.add('resizing');
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isResizing) return;
const containerWidth = document.querySelector('.container').offsetWidth;
const newWidth = containerWidth - e.clientX + 20; // 20px for padding
// Enforce min and max width constraints
if (newWidth >= 300 && newWidth <= 1600) {
detailsPanel.style.width = newWidth + 'px';
}
});
document.addEventListener('mouseup', function() {
if (isResizing) {
isResizing = false;
document.body.classList.remove('resizing');
}
});
// Also handle mouse leaving the window
document.addEventListener('mouseleave', function() {
if (isResizing) {
isResizing = false;
document.body.classList.remove('resizing');
}
});
})();
// Optional: Auto-load a default file if hosted with the data
// fetch('pdsh_results.jsonl')
// .then(r => r.text())
// .then(text => {
// runsData = parseJSONL(text);
// initTable(flattenRunData(runsData));
// });
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment