joelniklaus HF Staff commited on
Commit
1dd6b75
Β·
1 Parent(s): 4a65a6b

made throughput visualization nicer

Browse files
app/src/content/embeds/d3-optimization-sweep.html CHANGED
@@ -98,7 +98,6 @@
98
  }
99
  .d3-optimization-sweep .d3-tooltip .tip-label { color: var(--muted-color); }
100
  .d3-optimization-sweep .d3-tooltip .tip-val { font-weight: 600; }
101
- .d3-optimization-sweep .d3-tooltip .tip-best { color: var(--primary-color); font-weight: 700; }
102
  .d3-optimization-sweep .d3-tooltip .tip-regression { color: #e05252; }
103
  .d3-optimization-sweep .y-label-text {
104
  font-size: 11px;
@@ -177,17 +176,18 @@
177
 
178
  const familyPalette = getFamilyColors();
179
  const familyColor = (family) => familyPalette[FAMILIES.indexOf(family)] || 'var(--primary-color)';
180
- const BEST_COLOR = '#ee8a2a';
181
 
182
  // D3 symbol generators for each tier
183
- const SHAPE_SIZE = 48;
184
- const SHAPE_SIZE_BEST = 80;
185
  const shapeGenerators = {
186
  'Baseline': d3.symbol().type(d3.symbolCircle),
187
- 'Tier 0': d3.symbol().type(d3.symbolDiamond),
188
  'Tier 1': d3.symbol().type(d3.symbolTriangle),
189
  };
190
 
 
 
 
191
  // ── Tooltip ──
192
  let tip = container.querySelector('.d3-tooltip');
193
  let tipInner;
@@ -235,12 +235,12 @@
235
  }
236
 
237
  // ── Layout ──
238
- const margin = { top: 10, right: 62, bottom: 42, left: 148 };
239
  let width = 800, height = 500;
240
 
241
  function updateSize() {
242
  width = container.clientWidth || 800;
243
- height = Math.max(400, DATA.length * 28 + margin.top + margin.bottom);
244
  svg.attr('width', width).attr('height', height);
245
  gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
246
  return { iw: width - margin.left - margin.right, ih: height - margin.top - margin.bottom };
@@ -289,6 +289,17 @@
289
  g.selectAll('text').attr('fill', 'var(--tick-color)').style('font-size', '11px');
290
  });
291
 
 
 
 
 
 
 
 
 
 
 
 
292
  // ── X axis label ──
293
  const xLabel = state.metric === 'throughput' ? 'Tokens per second per GPU' : 'Speedup vs baseline';
294
  gRoot.selectAll('.x-label').data([0]).join('text').attr('class', 'x-label')
@@ -332,36 +343,41 @@
332
  // Remove speedup bars
333
  gRoot.selectAll('.speedup-bar').remove();
334
 
335
- // Connecting lines (baseline to rightmost point)
336
- const lineData = data.map(d => ({
337
- ...d,
338
- x1: xScale(Math.min(d.baseTps, d.t0Tps, d.t1Tps)),
339
- x2: xScale(Math.max(d.baseTps, d.t0Tps, d.t1Tps)),
340
- cy: yScale(d.model) + bandH / 2,
341
- }));
342
- gRoot.selectAll('.conn-line').data(lineData, d => d.model).join('line')
 
 
 
 
 
343
  .attr('class', 'conn-line')
344
- .attr('x1', d => d.x1).attr('x2', d => d.x2)
345
- .attr('y1', d => d.cy).attr('y2', d => d.cy)
346
  .attr('stroke', d => familyColor(d.family))
347
- .attr('stroke-width', 2).attr('opacity', 0.4);
348
 
349
- // Dots: 3 per model (baseline, tier0, tier1)
350
  const dots = [];
351
  data.forEach(d => {
352
- const cy = yScale(d.model) + bandH / 2;
353
- dots.push({ ...d, tier: 'Baseline', val: d.baseTps, cx: xScale(d.baseTps), cy, isBest: d.bestTps === d.baseTps });
354
- dots.push({ ...d, tier: 'Tier 0', val: d.t0Tps, cx: xScale(d.t0Tps), cy, isBest: d.bestTps === d.t0Tps && d.t0Tps !== d.baseTps });
355
- dots.push({ ...d, tier: 'Tier 1', val: d.t1Tps, cx: xScale(d.t1Tps), cy, isBest: d.bestTps === d.t1Tps && d.t1Tps !== d.t0Tps && d.t1Tps !== d.baseTps });
 
356
  });
357
 
358
  gRoot.selectAll('.dot').data(dots, d => d.model + '-' + d.tier).join('path')
359
  .attr('class', 'dot')
360
- .attr('d', d => shapeGenerators[d.tier].size(d.isBest ? SHAPE_SIZE_BEST : SHAPE_SIZE)())
361
  .attr('transform', d => `translate(${d.cx},${d.cy})`)
362
- .attr('fill', d => d.isBest ? BEST_COLOR : familyColor(d.family))
363
- .attr('stroke', d => d.isBest ? 'var(--text-color)' : 'none')
364
- .attr('stroke-width', d => d.isBest ? 1.5 : 0)
365
  .attr('opacity', 0.9)
366
  .attr('cursor', 'pointer')
367
  .on('mouseenter', function (event, d) {
@@ -384,8 +400,7 @@
384
  .attr('x', iw + 6)
385
  .attr('y', d => yScale(d.model) + bandH / 2)
386
  .attr('dy', '0.35em')
387
- .attr('fill', d => d.bestSpeedup >= 1.3 ? BEST_COLOR : 'var(--muted-color)')
388
- .attr('font-weight', d => d.bestSpeedup >= 1.3 ? 700 : 400)
389
  .text(d => d.bestSpeedup.toFixed(2) + 'x');
390
  }
391
 
@@ -436,23 +451,20 @@
436
  .attr('x', iw + 6)
437
  .attr('y', d => yScale(d.model) + bandH / 2)
438
  .attr('dy', '0.35em')
439
- .attr('fill', d => d.bestSpeedup >= 1.3 ? BEST_COLOR : 'var(--muted-color)')
440
- .attr('font-weight', d => d.bestSpeedup >= 1.3 ? 700 : 400)
441
  .text(d => d.bestSpeedup.toFixed(2) + 'x');
442
  }
443
 
444
  function buildTooltip(d) {
445
  const fmt = (v) => v.toLocaleString();
446
  const spd = (v) => v.toFixed(2) + 'x';
447
- const cls = (v) => v < 1.0 ? 'tip-regression' : (v >= 1.3 ? 'tip-best' : 'tip-val');
448
  return `<div style="margin-bottom:4px"><strong>${d.model}</strong> <span class="tip-label">(${d.family})</span></div>`
449
  + `<div><span class="tip-label">Baseline:</span> <span class="tip-val">${fmt(d.baseTps)}</span> tps/gpu <span class="tip-label">(tp=${d.baseTp})</span></div>`
450
  + `<div><span class="tip-label">Tier 0:</span> <span class="${cls(d.t0Speedup)}">${fmt(d.t0Tps)}</span> tps/gpu <span class="${cls(d.t0Speedup)}">${spd(d.t0Speedup)}</span></div>`
451
  + `<div style="font-size:10px;color:var(--muted-color);margin-left:8px">${d.t0Params}</div>`
452
  + `<div><span class="tip-label">Tier 1:</span> <span class="${cls(d.t1Speedup)}">${fmt(d.t1Tps)}</span> tps/gpu <span class="${cls(d.t1Speedup)}">${spd(d.t1Speedup)}</span></div>`
453
- + `<div style="font-size:10px;color:var(--muted-color);margin-left:8px">${d.t1Params}</div>`
454
- + `<div style="margin-top:4px;border-top:1px solid var(--border-color);padding-top:4px"><span class="tip-label">Best:</span> <span class="tip-best">${fmt(d.bestTps)} tps/gpu (${spd(d.bestSpeedup)})</span></div>`
455
- + `<div style="font-size:10px;color:var(--muted-color);margin-left:8px">${d.bestParams}</div>`;
456
  }
457
 
458
  // ── Controls ──
@@ -462,7 +474,7 @@
462
  const metricGroup = document.createElement('div'); metricGroup.className = 'control-group';
463
  const metricLabel = document.createElement('label'); metricLabel.textContent = 'Metric'; metricLabel.setAttribute('for', 'metric-sel-optsweep');
464
  const metricSel = document.createElement('select'); metricSel.id = 'metric-sel-optsweep';
465
- [['throughput', 'Throughput (tps/gpu)'], ['speedup', 'Speedup']].forEach(([v, t]) => {
466
  const o = document.createElement('option'); o.value = v; o.textContent = t; metricSel.appendChild(o);
467
  });
468
  metricSel.value = state.metric;
@@ -506,12 +518,6 @@
506
  const txt = document.createElement('span'); txt.textContent = tier;
507
  item.appendChild(swWrap); item.appendChild(txt); tierSection.appendChild(item);
508
  });
509
- // "Best" indicator
510
- const bestItem = document.createElement('span'); bestItem.className = 'item';
511
- const bestSw = document.createElement('span'); bestSw.className = 'swatch';
512
- bestSw.style.background = BEST_COLOR; bestSw.style.border = '1.5px solid var(--text-color)';
513
- const bestTxt = document.createElement('span'); bestTxt.textContent = 'Best';
514
- bestItem.appendChild(bestSw); bestItem.appendChild(bestTxt); tierSection.appendChild(bestItem);
515
  legend.appendChild(tierSection);
516
 
517
  // Family colors section
 
98
  }
99
  .d3-optimization-sweep .d3-tooltip .tip-label { color: var(--muted-color); }
100
  .d3-optimization-sweep .d3-tooltip .tip-val { font-weight: 600; }
 
101
  .d3-optimization-sweep .d3-tooltip .tip-regression { color: #e05252; }
102
  .d3-optimization-sweep .y-label-text {
103
  font-size: 11px;
 
176
 
177
  const familyPalette = getFamilyColors();
178
  const familyColor = (family) => familyPalette[FAMILIES.indexOf(family)] || 'var(--primary-color)';
 
179
 
180
  // D3 symbol generators for each tier
181
+ const SHAPE_SIZE = 42;
 
182
  const shapeGenerators = {
183
  'Baseline': d3.symbol().type(d3.symbolCircle),
184
+ 'Tier 0': d3.symbol().type(d3.symbolSquare),
185
  'Tier 1': d3.symbol().type(d3.symbolTriangle),
186
  };
187
 
188
+ // Vertical offsets within each band: spread tiers apart
189
+ const TIER_Y_OFFSET = { 'Baseline': -0.5, 'Tier 0': 0, 'Tier 1': 0.5 };
190
+
191
  // ── Tooltip ──
192
  let tip = container.querySelector('.d3-tooltip');
193
  let tipInner;
 
235
  }
236
 
237
  // ── Layout ──
238
+ const margin = { top: 24, right: 62, bottom: 42, left: 148 };
239
  let width = 800, height = 500;
240
 
241
  function updateSize() {
242
  width = container.clientWidth || 800;
243
+ height = Math.max(400, DATA.length * 36 + margin.top + margin.bottom);
244
  svg.attr('width', width).attr('height', height);
245
  gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
246
  return { iw: width - margin.left - margin.right, ih: height - margin.top - margin.bottom };
 
289
  g.selectAll('text').attr('fill', 'var(--tick-color)').style('font-size', '11px');
290
  });
291
 
292
+ // ── X axis top (mirrored ticks) ──
293
+ const xAxisTopGen = state.metric === 'throughput'
294
+ ? d3.axisTop(xScale).ticks(8).tickFormat(d => d >= 1000 ? (d / 1000) + 'k' : d)
295
+ : d3.axisTop(xScale).ticks(8).tickFormat(d => d.toFixed(1) + 'x');
296
+ gRoot.selectAll('.axis-x-top').data([0]).join('g').attr('class', 'axis-x-top')
297
+ .call(xAxisTopGen)
298
+ .call(g => {
299
+ g.selectAll('path, line').attr('stroke', 'var(--axis-color)');
300
+ g.selectAll('text').attr('fill', 'var(--tick-color)').style('font-size', '11px');
301
+ });
302
+
303
  // ── X axis label ──
304
  const xLabel = state.metric === 'throughput' ? 'Tokens per second per GPU' : 'Speedup vs baseline';
305
  gRoot.selectAll('.x-label').data([0]).join('text').attr('class', 'x-label')
 
343
  // Remove speedup bars
344
  gRoot.selectAll('.speedup-bar').remove();
345
 
346
+ // Connecting lines between the three staggered dots
347
+ const connData = [];
348
+ data.forEach(d => {
349
+ const cyCenter = yScale(d.model) + bandH / 2;
350
+ const points = [
351
+ { x: xScale(d.baseTps), y: cyCenter + TIER_Y_OFFSET['Baseline'] * bandH },
352
+ { x: xScale(d.t0Tps), y: cyCenter + TIER_Y_OFFSET['Tier 0'] * bandH },
353
+ { x: xScale(d.t1Tps), y: cyCenter + TIER_Y_OFFSET['Tier 1'] * bandH },
354
+ ];
355
+ connData.push({ model: d.model, family: d.family, points });
356
+ });
357
+ const lineGen = d3.line().x(p => p.x).y(p => p.y);
358
+ gRoot.selectAll('.conn-line').data(connData, d => d.model).join('path')
359
  .attr('class', 'conn-line')
360
+ .attr('d', d => lineGen(d.points))
361
+ .attr('fill', 'none')
362
  .attr('stroke', d => familyColor(d.family))
363
+ .attr('stroke-width', 1.5).attr('opacity', 0.35);
364
 
365
+ // Dots: 3 per model (baseline, tier0, tier1) with vertical stagger
366
  const dots = [];
367
  data.forEach(d => {
368
+ const cyCenter = yScale(d.model) + bandH / 2;
369
+ ['Baseline', 'Tier 0', 'Tier 1'].forEach(tier => {
370
+ const val = tier === 'Baseline' ? d.baseTps : tier === 'Tier 0' ? d.t0Tps : d.t1Tps;
371
+ dots.push({ ...d, tier, val, cx: xScale(val), cy: cyCenter + TIER_Y_OFFSET[tier] * bandH });
372
+ });
373
  });
374
 
375
  gRoot.selectAll('.dot').data(dots, d => d.model + '-' + d.tier).join('path')
376
  .attr('class', 'dot')
377
+ .attr('d', d => shapeGenerators[d.tier].size(SHAPE_SIZE)())
378
  .attr('transform', d => `translate(${d.cx},${d.cy})`)
379
+ .attr('fill', d => familyColor(d.family))
380
+ .attr('stroke', 'none')
 
381
  .attr('opacity', 0.9)
382
  .attr('cursor', 'pointer')
383
  .on('mouseenter', function (event, d) {
 
400
  .attr('x', iw + 6)
401
  .attr('y', d => yScale(d.model) + bandH / 2)
402
  .attr('dy', '0.35em')
403
+ .attr('fill', 'var(--muted-color)')
 
404
  .text(d => d.bestSpeedup.toFixed(2) + 'x');
405
  }
406
 
 
451
  .attr('x', iw + 6)
452
  .attr('y', d => yScale(d.model) + bandH / 2)
453
  .attr('dy', '0.35em')
454
+ .attr('fill', 'var(--muted-color)')
 
455
  .text(d => d.bestSpeedup.toFixed(2) + 'x');
456
  }
457
 
458
  function buildTooltip(d) {
459
  const fmt = (v) => v.toLocaleString();
460
  const spd = (v) => v.toFixed(2) + 'x';
461
+ const cls = (v) => v < 1.0 ? 'tip-regression' : 'tip-val';
462
  return `<div style="margin-bottom:4px"><strong>${d.model}</strong> <span class="tip-label">(${d.family})</span></div>`
463
  + `<div><span class="tip-label">Baseline:</span> <span class="tip-val">${fmt(d.baseTps)}</span> tps/gpu <span class="tip-label">(tp=${d.baseTp})</span></div>`
464
  + `<div><span class="tip-label">Tier 0:</span> <span class="${cls(d.t0Speedup)}">${fmt(d.t0Tps)}</span> tps/gpu <span class="${cls(d.t0Speedup)}">${spd(d.t0Speedup)}</span></div>`
465
  + `<div style="font-size:10px;color:var(--muted-color);margin-left:8px">${d.t0Params}</div>`
466
  + `<div><span class="tip-label">Tier 1:</span> <span class="${cls(d.t1Speedup)}">${fmt(d.t1Tps)}</span> tps/gpu <span class="${cls(d.t1Speedup)}">${spd(d.t1Speedup)}</span></div>`
467
+ + `<div style="font-size:10px;color:var(--muted-color);margin-left:8px">${d.t1Params}</div>`;
 
 
468
  }
469
 
470
  // ── Controls ──
 
474
  const metricGroup = document.createElement('div'); metricGroup.className = 'control-group';
475
  const metricLabel = document.createElement('label'); metricLabel.textContent = 'Metric'; metricLabel.setAttribute('for', 'metric-sel-optsweep');
476
  const metricSel = document.createElement('select'); metricSel.id = 'metric-sel-optsweep';
477
+ [['throughput', 'Throughput'], ['speedup', 'Speedup']].forEach(([v, t]) => {
478
  const o = document.createElement('option'); o.value = v; o.textContent = t; metricSel.appendChild(o);
479
  });
480
  metricSel.value = state.metric;
 
518
  const txt = document.createElement('span'); txt.textContent = tier;
519
  item.appendChild(swWrap); item.appendChild(txt); tierSection.appendChild(item);
520
  });
 
 
 
 
 
 
521
  legend.appendChild(tierSection);
522
 
523
  // Family colors section