HWu0101 commited on
Commit
b768fa7
·
verified ·
1 Parent(s): f426512

Sync from HWu0101/Local-LLM-Hardware-Explorer via hub-sync

Browse files
Files changed (1) hide show
  1. index.html +69 -44
index.html CHANGED
@@ -1122,7 +1122,18 @@
1122
  const fast = ks.slice().sort((x, y) => GPUC[y].bw - GPUC[x].bw)[0];
1123
  // Prefill compute is set by the FASTEST card, not the harmonic mean:
1124
  // a GEMM runs on one device at a time under layer split.
 
 
 
 
1125
  const pfKey = fast;
 
 
 
 
 
 
 
1126
  // A build counts as server-class if ANY card in it is. Those are passive-cooled
1127
  // parts that need chassis airflow, so they are kept out of the machine cards
1128
  // (reachable through the custom builder) but still plotted on the staircase.
@@ -1141,7 +1152,7 @@
1141
  return {
1142
  k: ks.map(k => k + (c[k] > 1 ? 'x' + c[k] : '')).join('_'), n: nm, s: nm, mine: mine, c: c,
1143
  vram: vram, bw: bw, slots: slots, p: price,
1144
- pfKey: pfKey, srv: srv,
1145
  alt: ks.length > 1 ? { vram: GPUC[fast].vram, bwFast: GPUC[fast].bw } : null,
1146
  tp: (ks.length === 1 && c[ks[0]] >= 2) ? TPF[c[ks[0]]] : null,
1147
  tpSet: (ks.length > 1 && tpSet) ? tpSet : null
@@ -1177,6 +1188,36 @@
1177
  };
1178
  const myCpu = () => CPUP[RIG.cpu] || CPUP.arrow;
1179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1180
  // PCIe effective bandwidth, GB/s, by generation at x16.
1181
  const PCIE = { 3: 11, 4: 24, 5: 55 };
1182
 
@@ -1450,7 +1491,8 @@
1450
  vram: g.vram, ram: r.ram, bwFast: g.bw,
1451
  bwSlow: pl.cpuKey ? numaBW(CPUP[pl.cpuKey], 'distribute').bw : r.bw,
1452
  res: pl.res, gpus: g.slots, plat: pl.k, cpuKey: pl.cpuKey,
1453
- pfKey: g.pfKey, srv: g.srv, pcieGBs: pcieFor(pl.k, g.slots, g.pfKey),
 
1454
  alt: g.alt, tp: g.tp, tpSet: g.tpSet, mine: (pl.k === 'd' && g.mine && r.k === 'cur'),
1455
  price: pl.base + g.p + r.p, ovh: .0012
1456
  });
@@ -1459,31 +1501,31 @@
1459
  HW.push(
1460
  {
1461
  id: 'halo', name: 'Framework Desktop', meta: 'Strix Halo \u00b7 128GB unified \u00b7 215 GB/s',
1462
- vram: 128, ram: 0, bwFast: 165, bwSlow: 165, res: 14, price: 6069, unified: 1, ovh: .0012
1463
  },
1464
  {
1465
  id: 'spark', name: 'NVIDIA DGX Spark', meta: 'GB10 \u00b7 128GB unified \u00b7 273 GB/s',
1466
- vram: 128, ram: 0, bwFast: 175, bwSlow: 175, res: 14, price: 7999, unified: 1, ovh: .0012
1467
  },
1468
  {
1469
  id: 'mac', name: 'Mac Studio M3 Ultra', meta: '96GB unified \u00b7 819 GB/s \u00b7 96GB is the max sold',
1470
- vram: 96, ram: 0, bwFast: 214, bwSlow: 214, res: 14, price: 9099, unified: 1, ovh: .0012
1471
  },
1472
  {
1473
  id: 'm5max', name: 'MacBook Pro M5 Max', meta: '128GB unified \u00b7 614 GB/s \u00b7 40-core \u00b7 buyable new',
1474
- vram: 128, ram: 0, bwFast: 262, bwSlow: 262, res: 16, price: 9999, unified: 1, ovh: .0012
1475
  },
1476
  {
1477
  id: 'mac256', name: 'Mac Studio 256GB (grey)', meta: 'M3 Ultra 256GB \u00b7 pulled May 2026 \u00b7 AU resale',
1478
- vram: 256, ram: 0, bwFast: 214, bwSlow: 214, res: 14, price: 29999, unified: 1, ovh: .0012
1479
  },
1480
  {
1481
  id: 'mac512', name: 'Mac Studio 512GB (grey)', meta: 'M3 Ultra 512GB \u00b7 pulled Mar 2026 \u00b7 US resale',
1482
- vram: 512, ram: 0, bwFast: 214, bwSlow: 214, res: 14, price: 36800, unified: 1, ovh: .0012
1483
  },
1484
  {
1485
  id: 'gb300', name: 'DGX Station GB300', meta: '252GB HBM3e + 496GB LPDDR5X \u00b7 Grace Blackwell Ultra',
1486
- vram: 252, ram: 496, bwFast: 2400, bwSlow: 350, res: 20, price: 134600, ovh: .0012,
1487
  gpus: 1, plat: 'g', link: 'NVLink-C2C at 900 GB/s, not PCIe'
1488
  });
1489
 
@@ -1514,7 +1556,8 @@
1514
  id: 'custom', name: 'Custom build',
1515
  meta: g.n + ' \u00b7 ' + ramGB + 'GB \u00b7 ' + plName + (g.slots > 1 ? ' \u00b7 layer-split' : ''),
1516
  vram: g.vram, ram: ramGB, bwFast: g.bw, bwSlow: bw, res: res, gpus: g.slots, plat: CUSTOM.plat,
1517
- pfKey: g.pfKey, pcieGBs: pcieFor(CUSTOM.plat, g.slots, g.pfKey),
 
1518
  alt: g.alt, tp: g.tp, tpSet: g.tpSet, custom: 1,
1519
  price: pl.base + g.p + ramP, ovh: .0012
1520
  };
@@ -1600,35 +1643,6 @@
1600
  // only 33.8%, with the gap being per-token dispatch structure, not matmul
1601
  // speed. Discrete GPUs suffer most; unified memory sees a milder version;
1602
  // CPU-resident experts do not pay it at all (no kernel launches).
1603
- // ---- prefill ------------------------------------------------------------
1604
- // Prefill is a different physical regime from decode. Decode is a batch-1 GEMV:
1605
- // it reads every active byte to emit one token, so it is memory-bound. Prefill
1606
- // is a GEMM over the whole prompt: weights are read once and reused across the
1607
- // batch, so it is compute-bound while resident, and TRANSFER-bound when the
1608
- // weights live in system RAM.
1609
- //
1610
- // pf = effective prefill TFLOPS, i.e. what the runtime actually sustains, not
1611
- // the marketing figure. Only two clean anchors exist:
1612
- // Qwen3.5-35B-A3B Q4, RTX 5090, fully resident: pp about 6900 t/s
1613
- // -> 6900 x 2 x 3e9 = 41.4 TFLOPS (SRC.prefill)
1614
- // V4-Flash, RTX PRO 6000 + dual EPYC, saturated: pp 3144 t/s
1615
- // -> 3144 x 2 x 13e9 = 81.7 TFLOPS, but ~47% of weights were off the card,
1616
- // so this is a LOWER bound on resident compute (SRC.userbench)
1617
- // Everything else is scaled by class off those two and is an ESTIMATE. The
1618
- // implied PRO 6000 / 5090 ratio is 1.97x where the dies are within ~1.1x, so
1619
- // the spread is runtime and model, not silicon -- which is exactly why these
1620
- // are not presented as measurements.
1621
- const PFTF = {
1622
- '4060ti': 8, '4060ti16': 8, '5060ti': 12, '5070': 18, '4070tis': 20,
1623
- '5070ti': 26, '4080s': 24, '5080': 28, '3090': 16, '4090': 32,
1624
- '5090': 41.4, 'pro6000': 81.7, 'pro4000': 20, 'pro4500': 30,
1625
- 'pro5000': 55, '6000ada': 30, 'a6000': 18,
1626
- // Server parts. Scaled by class off the 5090 anchor; datacentre silicon has
1627
- // far more peak FP16 than llama.cpp extracts, so these are deliberately well
1628
- // below the marketing figure and remain estimates.
1629
- 'v100': 18, 'a30': 26, 'l4': 14, 'a40': 20, 'l40s': 42,
1630
- 'a100': 55, 'a100x': 60, 'h100': 110, 'h100n': 125, 'h200': 130
1631
- };
1632
  // llama.cpp streams CPU-resident experts to the GPU for any batch over this
1633
  // many tokens (GGML_OP_OFFLOAD_MIN_BATCH). Below it the experts are computed on
1634
  // the CPU instead. This threshold is why offloaded prefill and offloaded decode
@@ -1833,8 +1847,13 @@
1833
  const r = calc(m, hw);
1834
  if (r.fit === 'none') return { fit: 'none' };
1835
  const act = m.active; // billions, active per token
1836
- const pfKey = hw.pfKey; // fastest card in the build
1837
- const tf = pfKey ? (PFTF[pfKey] || 20) : null;
 
 
 
 
 
1838
 
1839
  // Unified-memory machines have no link to cross: weights are already where
1840
  // the compute is, so prefill is purely compute-bound.
@@ -1844,8 +1863,7 @@
1844
 
1845
  // Resident compute. On a CPU-only or unified box, fall back to a bandwidth
1846
  // proxy rather than asserting a FLOPS figure we do not have.
1847
- const Rcomp = tf ? (tf * 1e12) / (2 * act * 1e9)
1848
- : Math.max(60, hw.bwFast / Math.max(0.5, act) * 4);
1849
 
1850
  const lanes = hw.pcieGBs || PCIE[5];
1851
  const passes = Math.max(1, Math.ceil(N / UBATCH));
@@ -2670,7 +2688,14 @@
2670
  : pp.bound === 'attention'
2671
  ? 'Attention is dominating at this prompt length; it grows with the square of the prompt, ' +
2672
  'so doubling the prompt roughly quadruples this share.'
2673
- : 'Compute-bound, which is the healthy case: the weights are on the card and the GPU is busy.',
 
 
 
 
 
 
 
2674
  0.6);
2675
  }
2676
 
 
1122
  const fast = ks.slice().sort((x, y) => GPUC[y].bw - GPUC[x].bw)[0];
1123
  // Prefill compute is set by the FASTEST card, not the harmonic mean:
1124
  // a GEMM runs on one device at a time under layer split.
1125
+ // pfKey stays the highest-BANDWIDTH card because that is what decides PCIe
1126
+ // generation. Prefill compute is a different question and must not reuse it:
1127
+ // a 3090 has more bandwidth than a 5080 but less compute, so picking by
1128
+ // bandwidth made adding one LOWER prefill.
1129
  const pfKey = fast;
1130
+ // Layer split runs layers in sequence, each on the card that holds it, so the
1131
+ // build's prefill rate is the capacity-weighted harmonic mean of its cards --
1132
+ // the same structure already used for memory bandwidth.
1133
+ const pfTF = vram / ks.reduce((t, k) => t + GPUC[k].vram * c[k] / (PFTF[k] || 20), 0);
1134
+ // If the model fits on the best-compute card alone you would simply run it
1135
+ // there, so carry that card's rate and its VRAM as the alternative.
1136
+ const pfCard = ks.slice().sort((x, y) => (PFTF[y] || 20) - (PFTF[x] || 20))[0];
1137
  // A build counts as server-class if ANY card in it is. Those are passive-cooled
1138
  // parts that need chassis airflow, so they are kept out of the machine cards
1139
  // (reachable through the custom builder) but still plotted on the staircase.
 
1152
  return {
1153
  k: ks.map(k => k + (c[k] > 1 ? 'x' + c[k] : '')).join('_'), n: nm, s: nm, mine: mine, c: c,
1154
  vram: vram, bw: bw, slots: slots, p: price,
1155
+ pfKey: pfKey, pfTF: pfTF, pfBest: (PFTF[pfCard] || 20), pfBestVram: GPUC[pfCard].vram, srv: srv,
1156
  alt: ks.length > 1 ? { vram: GPUC[fast].vram, bwFast: GPUC[fast].bw } : null,
1157
  tp: (ks.length === 1 && c[ks[0]] >= 2) ? TPF[c[ks[0]]] : null,
1158
  tpSet: (ks.length > 1 && tpSet) ? tpSet : null
 
1188
  };
1189
  const myCpu = () => CPUP[RIG.cpu] || CPUP.arrow;
1190
 
1191
+ // ---- prefill ------------------------------------------------------------
1192
+ // Prefill is a different physical regime from decode. Decode is a batch-1 GEMV:
1193
+ // it reads every active byte to emit one token, so it is memory-bound. Prefill
1194
+ // is a GEMM over the whole prompt: weights are read once and reused across the
1195
+ // batch, so it is compute-bound while resident, and TRANSFER-bound when the
1196
+ // weights live in system RAM.
1197
+ //
1198
+ // pf = effective prefill TFLOPS, i.e. what the runtime actually sustains, not
1199
+ // the marketing figure. Only two clean anchors exist:
1200
+ // Qwen3.5-35B-A3B Q4, RTX 5090, fully resident: pp about 6900 t/s
1201
+ // -> 6900 x 2 x 3e9 = 41.4 TFLOPS (SRC.prefill)
1202
+ // V4-Flash, RTX PRO 6000 + dual EPYC, saturated: pp 3144 t/s
1203
+ // -> 3144 x 2 x 13e9 = 81.7 TFLOPS, but ~47% of weights were off the card,
1204
+ // so this is a LOWER bound on resident compute (SRC.userbench)
1205
+ // Everything else is scaled by class off those two and is an ESTIMATE. The
1206
+ // implied PRO 6000 / 5090 ratio is 1.97x where the dies are within ~1.1x, so
1207
+ // the spread is runtime and model, not silicon -- which is exactly why these
1208
+ // are not presented as measurements.
1209
+ const PFTF = {
1210
+ '4060ti': 8, '4060ti16': 8, '5060ti': 12, '5070': 18, '4070tis': 20,
1211
+ '5070ti': 26, '4080s': 24, '5080': 28, '3090': 16, '4090': 32,
1212
+ '5090': 41.4, 'pro6000': 81.7, 'pro4000': 20, 'pro4500': 30,
1213
+ 'pro5000': 55, '6000ada': 30, 'a6000': 18,
1214
+ // Server parts. Scaled by class off the 5090 anchor; datacentre silicon has
1215
+ // far more peak FP16 than llama.cpp extracts, so these are deliberately well
1216
+ // below the marketing figure and remain estimates.
1217
+ 'v100': 18, 'a30': 26, 'l4': 14, 'a40': 20, 'l40s': 42,
1218
+ 'a100': 55, 'a100x': 60, 'h100': 110, 'h100n': 125, 'h200': 130
1219
+ };
1220
+
1221
  // PCIe effective bandwidth, GB/s, by generation at x16.
1222
  const PCIE = { 3: 11, 4: 24, 5: 55 };
1223
 
 
1491
  vram: g.vram, ram: r.ram, bwFast: g.bw,
1492
  bwSlow: pl.cpuKey ? numaBW(CPUP[pl.cpuKey], 'distribute').bw : r.bw,
1493
  res: pl.res, gpus: g.slots, plat: pl.k, cpuKey: pl.cpuKey,
1494
+ pfKey: g.pfKey, pfTF: g.pfTF, pfBest: g.pfBest, pfBestVram: g.pfBestVram,
1495
+ srv: g.srv, pcieGBs: pcieFor(pl.k, g.slots, g.pfKey),
1496
  alt: g.alt, tp: g.tp, tpSet: g.tpSet, mine: (pl.k === 'd' && g.mine && r.k === 'cur'),
1497
  price: pl.base + g.p + r.p, ovh: .0012
1498
  });
 
1501
  HW.push(
1502
  {
1503
  id: 'halo', name: 'Framework Desktop', meta: 'Strix Halo \u00b7 128GB unified \u00b7 215 GB/s',
1504
+ vram: 128, ram: 0, bwFast: 165, bwSlow: 165, res: 14, pfTF: 6.6, pfBestVram: 9999, pfBest: 6.6, price: 6069, unified: 1, ovh: .0012
1505
  },
1506
  {
1507
  id: 'spark', name: 'NVIDIA DGX Spark', meta: 'GB10 \u00b7 128GB unified \u00b7 273 GB/s',
1508
+ vram: 128, ram: 0, bwFast: 175, bwSlow: 175, res: 14, pfTF: 18.4, pfBestVram: 9999, pfBest: 18.4, price: 7999, unified: 1, ovh: .0012
1509
  },
1510
  {
1511
  id: 'mac', name: 'Mac Studio M3 Ultra', meta: '96GB unified \u00b7 819 GB/s \u00b7 96GB is the max sold',
1512
+ vram: 96, ram: 0, bwFast: 214, bwSlow: 214, res: 14, pfTF: 6.6, pfBestVram: 9999, pfBest: 6.6, price: 9099, unified: 1, ovh: .0012
1513
  },
1514
  {
1515
  id: 'm5max', name: 'MacBook Pro M5 Max', meta: '128GB unified \u00b7 614 GB/s \u00b7 40-core \u00b7 buyable new',
1516
+ vram: 128, ram: 0, bwFast: 262, bwSlow: 262, res: 16, pfTF: 8.7, pfBestVram: 9999, pfBest: 8.7, price: 9999, unified: 1, ovh: .0012
1517
  },
1518
  {
1519
  id: 'mac256', name: 'Mac Studio 256GB (grey)', meta: 'M3 Ultra 256GB \u00b7 pulled May 2026 \u00b7 AU resale',
1520
+ vram: 256, ram: 0, bwFast: 214, bwSlow: 214, res: 14, pfTF: 6.6, pfBestVram: 9999, pfBest: 6.6, price: 29999, unified: 1, ovh: .0012
1521
  },
1522
  {
1523
  id: 'mac512', name: 'Mac Studio 512GB (grey)', meta: 'M3 Ultra 512GB \u00b7 pulled Mar 2026 \u00b7 US resale',
1524
+ vram: 512, ram: 0, bwFast: 214, bwSlow: 214, res: 14, pfTF: 6.6, pfBestVram: 9999, pfBest: 6.6, price: 36800, unified: 1, ovh: .0012
1525
  },
1526
  {
1527
  id: 'gb300', name: 'DGX Station GB300', meta: '252GB HBM3e + 496GB LPDDR5X \u00b7 Grace Blackwell Ultra',
1528
+ vram: 252, ram: 496, bwFast: 2400, bwSlow: 350, res: 20, pfTF: 60, pfBestVram: 9999, pfBest: 60, price: 134600, ovh: .0012,
1529
  gpus: 1, plat: 'g', link: 'NVLink-C2C at 900 GB/s, not PCIe'
1530
  });
1531
 
 
1556
  id: 'custom', name: 'Custom build',
1557
  meta: g.n + ' \u00b7 ' + ramGB + 'GB \u00b7 ' + plName + (g.slots > 1 ? ' \u00b7 layer-split' : ''),
1558
  vram: g.vram, ram: ramGB, bwFast: g.bw, bwSlow: bw, res: res, gpus: g.slots, plat: CUSTOM.plat,
1559
+ pfKey: g.pfKey, pfTF: g.pfTF, pfBest: g.pfBest, pfBestVram: g.pfBestVram,
1560
+ pcieGBs: pcieFor(CUSTOM.plat, g.slots, g.pfKey),
1561
  alt: g.alt, tp: g.tp, tpSet: g.tpSet, custom: 1,
1562
  price: pl.base + g.p + ramP, ovh: .0012
1563
  };
 
1643
  // only 33.8%, with the gap being per-token dispatch structure, not matmul
1644
  // speed. Discrete GPUs suffer most; unified memory sees a milder version;
1645
  // CPU-resident experts do not pay it at all (no kernel launches).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1646
  // llama.cpp streams CPU-resident experts to the GPU for any batch over this
1647
  // many tokens (GGML_OP_OFFLOAD_MIN_BATCH). Below it the experts are computed on
1648
  // the CPU instead. This threshold is why offloaded prefill and offloaded decode
 
1847
  const r = calc(m, hw);
1848
  if (r.fit === 'none') return { fit: 'none' };
1849
  const act = m.active; // billions, active per token
1850
+ // Effective prefill TFLOPS for this machine. Every machine now has one --
1851
+ // fixed and unified boxes carry pfTF directly -- so no build falls through to
1852
+ // a different formula. Comparing a PFTF-derived rate against a bandwidth proxy
1853
+ // was what made Strix Halo read as 0.0x against a 5080.
1854
+ const disp0 = hw.unified ? 0 : (OSVRAM[RIG.os] !== undefined ? OSVRAM[RIG.os] : 3);
1855
+ const fitsBest = hw.pfBestVram && (m.gb + kvGB(m) + 1) <= (hw.pfBestVram - disp0);
1856
+ const tf = fitsBest ? hw.pfBest : (hw.pfTF || null);
1857
 
1858
  // Unified-memory machines have no link to cross: weights are already where
1859
  // the compute is, so prefill is purely compute-bound.
 
1863
 
1864
  // Resident compute. On a CPU-only or unified box, fall back to a bandwidth
1865
  // proxy rather than asserting a FLOPS figure we do not have.
1866
+ const Rcomp = (tf ? tf : 20) * 1e12 / (2 * act * 1e9);
 
1867
 
1868
  const lanes = hw.pcieGBs || PCIE[5];
1869
  const passes = Math.max(1, Math.ceil(N / UBATCH));
 
2688
  : pp.bound === 'attention'
2689
  ? 'Attention is dominating at this prompt length; it grows with the square of the prompt, ' +
2690
  'so doubling the prompt roughly quadruples this share.'
2691
+ : 'Compute-bound, which is the healthy case: the weights are on the card and the GPU is busy.' +
2692
+ ((hw.gpus || 0) > 1 && hw.pfTF && hw.pfBest && hw.pfTF < hw.pfBest * 0.9
2693
+ ? ' Note the mixed pair costs you here: layer split runs layers in SEQUENCE, so the '
2694
+ + 'slower card sets the pace in proportion to how many layers its VRAM holds. This build '
2695
+ + 'converts ' + hw.pfTF.toFixed(0) + ' effective TFLOPS against ' + hw.pfBest.toFixed(0)
2696
+ + ' for its fastest card alone. Extra VRAM buys decode residency and costs prefill compute '
2697
+ + '— a real trade, not a rounding error.'
2698
+ : ''),
2699
  0.6);
2700
  }
2701