Skip to content

Instantly share code, notes, and snippets.

@BenHamm
Last active June 30, 2026 17:48
Show Gist options
  • Select an option

  • Save BenHamm/aff0b721d648f2c228edf8faa3e93ea4 to your computer and use it in GitHub Desktop.

Select an option

Save BenHamm/aff0b721d648f2c228edf8faa3e93ea4 to your computer and use it in GitHub Desktop.
Scraping SemiAnalysis InferenceX (NVIDIA vs AMD inference benchmarks) programmatically via its public JSON API — stdlib-only Python client + notes

Scraping SemiAnalysis InferenceX results programmatically

InferenceX is SemiAnalysis's public dashboard of NVIDIA-vs-AMD LLM inference benchmarks (throughput, accuracy evals, GPU reliability). The front end is a JavaScript single-page app, so it looks like you'd need a headless browser — but the whole dashboard is backed by three plain, unauthenticated JSON endpoints. No API key, no browser, no scraping of rendered HTML. Just HTTP GET.

This gist documents those endpoints and ships a dependency-free Python client (scrape_inferencex.py, stdlib only). Part 2 below covers a companion source — Artificial Analysis — for the capability / use-case dimension InferenceX doesn't measure (scrape_artificial_analysis.py).


Use case: a hardware-first view ("I have B200s — what can I run?")

The InferenceX site is model-first: you pick a model, then see GPUs. A very common customer question is the reverse — "Given the hardware I already own (B200 / GB200 / GB300), which models run efficiently, and for which kind of workload?" The API makes that pivot trivial: pull each model's rows, filter by hardware, and group by workload shape.

Workload shape ≈ use case. InferenceX measures three input/output-length scenarios (isl = input tokens, osl = output tokens). They're a good proxy for the shape of a use case:

ISL / OSL Shape Typical use cases
1024 / 1024 balanced Chat, assistants
8192 / 1024 long in, short out RAG, summarization, document Q&A
1024 / 8192 short in, long out Reasoning, agents, long-form / code generation

Coverage today (2026-06-30): B200 has 9 models; GB200 / GB300 / B300 have 7 each — across the whole InferenceX model set (DeepSeek-R1 / V4, GLM-5, Kimi-K2.5, Llama-3.3-70B, MiniMax-M2.5 / M3, Qwen-3.5, gpt-oss-120b).

# Hardware-first pivot: for one GPU, rank models by throughput within each scenario.
import scrape_inferencex as ix
from collections import defaultdict

GPU = "b200"                      # b200 | b300 | gb200 | gb300 | h100 | h200
SCENARIOS = {(1024,1024):"chat", (8192,1024):"RAG/summarize", (1024,8192):"reason/codegen"}

best = defaultdict(dict)          # scenario -> {model: peak tok/s/gpu}
for db, display in ix.discover_display_names().items():
    for r in ix.get_benchmarks(display):
        if r["hardware"] != GPU:
            continue
        scen = SCENARIOS.get((r.get("isl"), r.get("osl")))
        t = (r.get("metrics") or {}).get("tput_per_gpu")
        if scen and t:
            best[scen][display] = max(best[scen].get(display, 0), t)

for scen, models in best.items():
    print(f"\n{GPU}{scen}:")
    for m, t in sorted(models.items(), key=lambda kv: -kv[1]):
        print(f"  {m:24s} {t:8.0f} tok/s/gpu")

Two honest boundaries to keep in mind:

  • InferenceX is text LLM serving only — no image/video/audio generation. The "use cases" it speaks to are text workload shapes, not modalities.
  • It measures performance, not capability. The only quality eval is gsm8k (math). So InferenceX answers "which models run efficiently on my hardware," not "which model is best at coding/agents." For the capability/use-case-fit half of the question, join against a model-quality source such as Artificial Analysis (per-model coding / math / agentic indices). The peak-throughput numbers above also ignore the latency/interactivity trade-off — for a fair "efficient" ranking, filter to a target per-user speed first (e.g. metrics.mean_intvty >= 10 tok/s/user).

The endpoints

Base URL: https://inferencex.semianalysis.com

Endpoint Params Returns
GET /api/v1/benchmarks ?model=<DISPLAY_NAME> (required) Speed/throughput rows for one model, across every GPU + framework + ISL/OSL/concurrency config
GET /api/v1/evaluations none Every accuracy-eval row (model × task × GPU)
GET /api/v1/reliability none Per-day GPU reliability rows (n_success / total)

All three return a JSON array of row objects.

Quick taste

# Accuracy evals — no params, returns everything (~1800 rows)
curl -s --compressed https://inferencex.semianalysis.com/api/v1/evaluations | jq length

# Throughput for one model (note: --compressed is required, see gotchas)
curl -s --compressed 'https://inferencex.semianalysis.com/api/v1/benchmarks?model=DeepSeek-R1-0528' | jq '.[0]'

Row shapes

/benchmarks (speed) — one row per measured config:

{
  "hardware": "b300", "framework": "trtllm", "model": "dsr1",
  "precision": "fp4", "disagg": true, "is_multinode": false,
  "prefill_tp": 4, "decode_tp": 4, "num_prefill_gpu": 4, "num_decode_gpu": 4,
  "isl": 1024, "osl": 1024, "conc": 4,
  "metrics": { "tput_per_gpu": 14773.4, "mean_ttft": 0.17, "mean_tpot": 0.0096,
               "p99_ttft": 0.82, "p90_e2el": 9.84, "...": "..." }
}

The headline metric is metrics.tput_per_gpu (output tok/s per GPU). Latency percentiles (*_ttft, *_tpot, *_itl, *_e2el) are also in metrics.

/evaluations (accuracy):

{
  "hardware": "mi355x", "framework": "vllm", "model": "minimaxm3",
  "precision": "fp4", "task": "gsm8k", "conc": 512, "date": "2026-06-30",
  "metrics": { "em_strict": 0.953, "em_flexible": 0.953, "n_eff": 1319 },
  "run_url": "https://github.com/SemiAnalysisAI/InferenceX/actions/runs/..."
}

/reliability:

{ "hardware": "gb200", "date": "2026-06-30", "n_success": 0, "total": 0 }

Gotchas (these will bite you)

  1. Responses are gzip-encoded. Send Accept-Encoding: gzip and decompress, or use a client that auto-decompresses. requests does this for you; raw urllib does not — you'll get a JSONDecodeError on binary bytes. With curl you must pass --compressed.

  2. /benchmarks wants the human DISPLAY name, not the internal db name. ?model=DeepSeek-R1-0528 works; ?model=dsr1 returns {"error":"Unknown model"}. Meanwhile /evaluations and /reliability report the internal db name in their model field (dsr1, glm5, …). So you need a db→display map to go between them.

  3. There is no models endpoint. Discover the db names from /evaluations (the model field). The db→display map lives only in the site's JS bundle. scrape_inferencex.py ships a current snapshot (DISPLAY_NAMES) and a discover_display_names() helper that re-extracts it from the bundle so you're not stuck hand-maintaining it.

  4. Several db names can share one display name (e.g. glm5 and glm5.1 both → GLM-5; the kimik2.* family → Kimi-K2.5). One /benchmarks call for that display name returns rows for all of them — dedupe your model loop by display name or you'll fetch the same payload twice.

  5. GPU vendor isn't a field — infer it from hardware: NVIDIA = {b200, b300, gb200, gb300, h100, h200}, AMD = {mi300x, mi325x, mi355x}.


Usage

python3 scrape_inferencex.py        # runs a demo: discovers models, counts rows,
                                     # prints best tok/s/gpu per GPU for one config

As a library:

import scrape_inferencex as ix

ix.discover_models()                 # ['dsr1', 'dsv4', 'glm5', ...] (db names)
ix.get_benchmarks("DeepSeek-R1-0528")  # list of speed rows
ix.get_evaluations()                 # all accuracy rows
ix.get_reliability()                 # all reliability rows
ix.discover_display_names()          # live db->display map from the JS bundle

To pull everything to disk:

import json, scrape_inferencex as ix
names = ix.discover_display_names()
allbench = {}
for db, display in names.items():
    allbench.setdefault(display, ix.get_benchmarks(display))
json.dump(
    {"benchmarks": allbench,
     "evaluations": ix.get_evaluations(),
     "reliability": ix.get_reliability()},
    open("inferencex_dump.json", "w"))

If you really need the rendered charts

The JSON above is the data behind every chart, so prefer it. But if you need pixel-perfect screenshots of the Pareto curves (e.g. for a report), drive the SPA with Playwright: load https://inferencex.semianalysis.com/inference, wait for networkidle + svg.main-svg / .js-plotly-plot to render, click the Accuracy Evals / GPU Reliability tabs, scroll to trigger lazy loads, and screenshot each figure / top-level svg element. That's strictly a fallback — the API is faster, exact, and doesn't break when the layout changes.


Part 2 — Artificial Analysis: capability / use-case data

InferenceX answers "which models run efficiently on my hardware." It does not tell you which model is actually good at coding, math, or agentic work (its only quality eval is gsm8k). Artificial Analysis fills that gap: it benchmarks model capability across many tasks, which is the natural "use case per model" signal. Pull both and you can answer the full customer question — "Given my B200s, for code generation, which models are both fast (InferenceX) and capable (AA)?"

The endpoint

One JSON call returns all models (~540), so you fetch once and cache.

GET https://artificialanalysis.ai/api/v2/data/llms/models
    header:  x-api-key: <YOUR_KEY>
  • Key: free, from your account at https://artificialanalysis.ai/ (API section). Set it as AA_API_KEY. The free tier is rate-limited (a modest number of requests/day) — but this endpoint returns everything in one shot, so one cached call covers you.
  • Attribution is REQUIRED by AA's terms: credit "Artificial Analysis" and link https://artificialanalysis.ai/ anywhere you display the data.
  • Response shape: {"data": [ {model}, ... ]}.

Benchmarks → use cases

Each model carries an evaluations object. The individual benchmarks group into use-case buckets (this is the mapping scrape_artificial_analysis.py uses):

Use case AA benchmarks
Overall artificial_analysis_intelligence_index
Coding artificial_analysis_coding_index, livecodebench, scicode, terminalbench_hard, terminalbench_v2_1
Math artificial_analysis_math_index, aime, aime_25, math_500
Reasoning / knowledge gpqa, mmlu_pro, hle
Agentic / tool-use tau2, tau_banking, terminalbench_hard
Instruction-following ifbench
Long-context reasoning lcr

A model row also includes pricing (price_1m_input_tokens, price_1m_output_tokens, price_1m_blended_3_to_1), median_output_tokens_per_second, median_time_to_first_token_seconds, model_creator, slug, and release_date.

Scale note: the *_index fields are 0–100; the raw benchmarks (gpqa, livecodebench, tau2, …) are 0–1. The helper scales raw benchmarks ×100 so a bucket that mixes both stays on one scale.

Usage

export AA_API_KEY=...            # free key from artificialanalysis.ai
python3 scrape_artificial_analysis.py     # top models per use case + sample profiles
python3 scrape_artificial_analysis.py --json > aa_models.json   # full dump

As a library:

import scrape_artificial_analysis as aa

models = aa.fetch_models()                       # ~540 model dicts (cache this)
aa.rank_for_use_case(models, "coding", top=10)   # [(name, score, model), ...]
aa.use_case_profile(models[0])                   # {'coding': 52.9, 'math': ..., ...}

Gotchas

  1. One model → many AA rows. AA lists reasoning vs non-reasoning variants and effort tiers separately (e.g. gpt-oss-120b (low) / (high), Qwen3.5 397B A17B (Reasoning) / (Non-reasoning)). When joining to a single InferenceX model you must pick the variant that matches how it was served (InferenceX rows carry spec_method, precision, disagg — use those as hints), or compare against the variant the customer would actually run.
  2. Name matching to InferenceX is fuzzy. AA uses spaced display names (DeepSeek R1 0528, Kimi K2.5) vs InferenceX's (DeepSeek-R1-0528, Kimi-K2.5). Normalize (lowercase, strip spaces/dashes) before matching, and expect to hand-map a few. (Internally we reuse the matcher in llm-tracker/enrichment/aa_matcher.py.)
  3. Open-vs-proprietary isn't a field. AA returns everything (Claude, GPT, Gemini included). If you only want open-weights models for an on-prem GPU story, filter by creator/name — scrape_artificial_analysis.py has a _looks_open() heuristic you'll want to tune.
  4. Speed/price are AA's hosted measurements, not yours — use AA for capability, and InferenceX (or your own runs) for performance on the target GPU. Don't cross the streams.

The join, in one picture

InferenceX  ──▶  given GPU (B200) + workload shape  ──▶  models ranked by tok/s/gpu   (efficiency)
Artificial  ──▶  per-model coding/math/agentic index ──▶  models ranked by capability   (use-case fit)
Analysis
            └────────────────  match on normalized model name  ────────────────┘
                         = "fast AND capable on my hardware, for this use case"

InferenceX endpoints and the AA API verified live 2026-06-30. Both are third-party services that can change or gate access at any time; InferenceX's endpoints are undocumented, and AA's API requires a key + attribution. Be polite — cache results, don't hammer.

#!/usr/bin/env python3
"""
Scrape Artificial Analysis model intelligence — programmatically.
=================================================================
Artificial Analysis (https://artificialanalysis.ai) benchmarks LLM *capability*
(coding, math, reasoning, agentic, etc.) — the dimension InferenceX does NOT
cover. Where InferenceX tells you which models run *efficiently* on a given GPU,
AA tells you which models are *good at a given use case*. Join the two and you
can answer: "Given my B200s, for code generation, which models are both fast
AND capable?"
One JSON endpoint, one API key:
GET https://artificialanalysis.ai/api/v2/data/llms/models
header: x-api-key: <YOUR_KEY>
* Get a free key at https://artificialanalysis.ai/ (account -> API). The free
tier is rate-limited (a modest number of requests/day) — this endpoint
returns ALL models in one shot, so one call is all you need; cache it.
* Attribution is REQUIRED by AA's terms — credit "Artificial Analysis" and
link https://artificialanalysis.ai/ wherever you display the data.
* Returns {"data": [ {model}, ... ]} — ~540 models as of 2026-06.
Stdlib only. Python 3.9+. Set AA_API_KEY in the environment.
"""
import json
import os
import ssl
import sys
import urllib.request
from urllib.error import HTTPError
AA_API_URL = "https://artificialanalysis.ai/api/v2/data/llms/models"
# AA per-model `evaluations` benchmarks, grouped into use-case buckets.
# Each value is 0..100 (the *_index fields) or 0..1 (raw benchmarks).
USE_CASES = {
"overall": ["artificial_analysis_intelligence_index"],
"coding": ["artificial_analysis_coding_index", "livecodebench",
"scicode", "terminalbench_hard", "terminalbench_v2_1"],
"math": ["artificial_analysis_math_index", "aime", "aime_25", "math_500"],
"reasoning": ["gpqa", "mmlu_pro", "hle"],
"agentic": ["tau2", "tau_banking", "terminalbench_hard"],
"instruction": ["ifbench"],
"long_context": ["lcr"],
}
def fetch_models(api_key=None):
"""Fetch the full AA model list. Returns a list of model dicts."""
api_key = api_key or os.environ.get("AA_API_KEY", "")
if not api_key:
raise RuntimeError("Set AA_API_KEY (get a free key at https://artificialanalysis.ai/)")
req = urllib.request.Request(AA_API_URL, headers={
"x-api-key": api_key,
"Accept": "application/json",
"User-Agent": "aa-scraper/1.0",
})
# Some corporate networks have cert issues; fall back to an unverified context.
try:
ctx = ssl.create_default_context()
except Exception:
ctx = ssl._create_unverified_context()
try:
with urllib.request.urlopen(req, timeout=60, context=ctx) as resp:
data = json.loads(resp.read().decode("utf-8"))
except HTTPError as e:
raise RuntimeError(f"AA API HTTP {e.code} {e.reason} "
f"(401/403 = bad/missing key; 429 = rate-limited)")
return data.get("data", data if isinstance(data, list) else [])
def use_case_profile(model):
"""Return {use_case: score} for one model, averaging its benchmarks.
*_index fields are 0..100; raw benchmarks are 0..1 — we scale the latter to
0..100 so a use case mixing both stays on one scale. Missing benchmarks are
skipped; a use case with no data is omitted.
"""
evals = model.get("evaluations") or {}
profile = {}
for use_case, keys in USE_CASES.items():
vals = []
for k in keys:
v = evals.get(k)
if v is None:
continue
vals.append(v if k.endswith("_index") else v * 100.0)
if vals:
profile[use_case] = round(sum(vals) / len(vals), 1)
return profile
def rank_for_use_case(models, use_case, top=10, open_only=False):
"""Rank models by a single use case. Returns [(name, score, model), ...]."""
keys = USE_CASES[use_case]
scored = []
for m in models:
evals = m.get("evaluations") or {}
vals = [(evals[k] if k.endswith("_index") else evals[k] * 100.0)
for k in keys if evals.get(k) is not None]
if not vals:
continue
if open_only and not _looks_open(m):
continue
scored.append((m.get("name", "?"), round(sum(vals) / len(vals), 1), m))
scored.sort(key=lambda t: -t[1])
return scored[:top]
def _looks_open(model):
"""Heuristic open-weights filter — AA doesn't flag it directly. Tune as needed."""
creator = (model.get("model_creator") or {}).get("name", "").lower()
open_creators = {"deepseek", "meta", "mistral", "alibaba", "qwen", "moonshot",
"minimax", "z.ai", "zhipu", "nvidia", "openai"} # gpt-oss is open
name = model.get("name", "").lower()
if "gpt-oss" in name:
return True
if any(c in creator for c in open_creators) and "gpt-5" not in name and "gpt-4" not in name:
return True
return False
def demo():
models = fetch_models()
print(f"Fetched {len(models)} models from Artificial Analysis\n")
for uc in ("coding", "math", "agentic"):
print(f"Top 5 — {uc}:")
for name, score, _ in rank_for_use_case(models, uc, top=5):
print(f" {score:5.1f} {name}")
print()
# Full use-case profile + price/speed for a few specific models.
print("Per-model use-case profiles (intelligence is the headline index):")
for m in models:
if m.get("name", "").startswith(("gpt-oss-120b (high)", "MiniMax-M3", "Qwen3.5 397B")):
prof = use_case_profile(m)
price = (m.get("pricing") or {}).get("price_1m_blended_3_to_1")
spd = m.get("median_output_tokens_per_second")
print(f"\n {m['name']} (${price}/M blended, {spd} tok/s on AA's host)")
for uc, s in prof.items():
print(f" {uc:13s} {s}")
if __name__ == "__main__":
if "--json" in sys.argv:
json.dump(fetch_models(), sys.stdout, indent=2)
else:
demo()
#!/usr/bin/env python3
"""
Scrape SemiAnalysis InferenceX benchmark results — programmatically.
====================================================================
InferenceX (https://inferencex.semianalysis.com) publishes NVIDIA-vs-AMD GPU
inference benchmarks. The dashboard is a JS single-page app, but it is backed
by three plain public JSON endpoints. You do NOT need a browser, Playwright,
or an API key — just HTTP GET.
GET /api/v1/benchmarks?model=<DISPLAY_NAME> # speed/throughput, per model
GET /api/v1/evaluations # accuracy evals (all models)
GET /api/v1/reliability # GPU reliability (all models)
Notes that bite you:
* Responses are gzip-encoded. Send `Accept-Encoding: gzip` and decompress,
or use a client that auto-decompresses (requests does; raw urllib does not).
* /benchmarks REQUIRES the human DISPLAY name (e.g. "DeepSeek-R1-0528"),
not the internal db name ("dsr1"). Wrong/empty name -> {"error":"Unknown model"}.
* /evaluations and /reliability take no params and return everything; the
`model` field there is the internal db name.
* There is no public endpoint that lists models — discover db names from
/evaluations, then map to display names (see DISPLAY_NAMES below).
Stdlib only. Python 3.9+.
"""
import gzip
import json
import sys
import urllib.error
import urllib.parse
import urllib.request
API_BASE = "https://inferencex.semianalysis.com"
# db-name -> display-name. The /benchmarks endpoint wants the display name.
# db names are discoverable from /evaluations; display names live only in the
# site's JS bundle. This snapshot was lifted from that bundle (2026-06-30) —
# or call discover_display_names() below to pull a fresh copy. Note several
# db names can share one display name (e.g. glm5 and glm5.1 -> "GLM-5"), so
# querying that display name returns rows for all of them.
DISPLAY_NAMES = {
"dsr1": "DeepSeek-R1-0528",
"dsv4": "DeepSeek-V4-Pro",
"gptoss120b": "gpt-oss-120b",
"llama70b": "Llama-3.3-70B-Instruct-FP8",
"qwen3.5": "Qwen-3.5-397B-A17B",
"kimik2.5": "Kimi-K2.5",
"kimik2.6": "Kimi-K2.5",
"kimik2.7-code": "Kimi-K2.5",
"minimaxm2.5": "MiniMax-M2.5",
"minimaxm2.7": "MiniMax-M2.5",
"minimaxm3": "MiniMax-M3",
"glm5": "GLM-5",
"glm5.1": "GLM-5",
}
NVIDIA_HW = {"b200", "b300", "gb200", "gb300", "h100", "h200"}
AMD_HW = {"mi300x", "mi325x", "mi355x"}
def fetch_json(url):
"""GET a URL and parse JSON, handling gzip and HTTP errors."""
req = urllib.request.Request(url, headers={
"Accept": "application/json",
"Accept-Encoding": "gzip",
"User-Agent": "inferencex-scraper/1.0",
})
with urllib.request.urlopen(req, timeout=20) as resp:
data = resp.read()
if resp.headers.get("Content-Encoding") == "gzip":
data = gzip.decompress(data)
return json.loads(data)
def get_benchmarks(display_name):
"""Speed/throughput rows for one model. `display_name` must be the
human name, e.g. 'DeepSeek-R1-0528'."""
q = urllib.parse.quote(display_name)
return fetch_json(f"{API_BASE}/api/v1/benchmarks?model={q}")
def get_evaluations():
"""All accuracy-eval rows (every model/task/GPU)."""
return fetch_json(f"{API_BASE}/api/v1/evaluations")
def get_reliability():
"""All GPU reliability rows (per-day n_success / total)."""
return fetch_json(f"{API_BASE}/api/v1/reliability")
def discover_models():
"""Return the set of internal db model names currently in the dataset,
derived from the evaluations endpoint (no models endpoint exists)."""
rows = get_evaluations()
return sorted({r["model"] for r in rows if r.get("model")})
def discover_display_names():
"""Best-effort: extract the live db-name -> display-name map from the
site's JS bundle so you don't have to hand-maintain DISPLAY_NAMES.
The map is emitted in a chunk as a JS object literal like
{dsr1:"DeepSeek-R1-0528", ... ,dsv4:"DeepSeek-V4-Pro"}. We find the chunk
that mentions a known model and pull the surrounding object. Falls back to
the static DISPLAY_NAMES on any failure.
"""
import re
try:
page = fetch_text(f"{API_BASE}/inference")
chunks = sorted(set(re.findall(r"/_next/static/chunks/[\w/.-]+\.js", page)))
anchor = "dsr1" # a stable db name present in the mapping object
for path in chunks:
js = fetch_text(f"{API_BASE}{path}")
if f'{anchor}:"' not in js:
continue
# Grab the {...} object literal that contains the anchor pair.
m = re.search(r'\{[^{}]*' + anchor + r':"[^{}]*\}', js)
if not m:
continue
blob = m.group(0)
# Parse "key":"val" and bareword key:"val" pairs.
pairs = re.findall(r'"?([\w.\-]+)"?:"([^"]+)"', blob)
mapping = {k: v for k, v in pairs}
if mapping:
return mapping
except Exception:
pass
return dict(DISPLAY_NAMES)
def fetch_text(url):
"""GET a URL and return decoded text (handles gzip)."""
req = urllib.request.Request(url, headers={
"Accept-Encoding": "gzip",
"User-Agent": "inferencex-scraper/1.0",
})
with urllib.request.urlopen(req, timeout=20) as resp:
data = resp.read()
if resp.headers.get("Content-Encoding") == "gzip":
data = gzip.decompress(data)
return data.decode("utf-8", "replace")
def vendor(hw):
hw = (hw or "").lower()
if hw in NVIDIA_HW:
return "nvidia"
if hw in AMD_HW:
return "amd"
return "other"
def demo():
print("Discovering models from /evaluations ...")
db_names = discover_models()
print(f" {len(db_names)} models: {', '.join(db_names)}\n")
# Pull speed benchmarks for whatever we have a display name for.
# Multiple db names can share a display name (glm5/glm5.1 -> "GLM-5"), and
# one /benchmarks call returns rows for all of them — so dedupe by display.
grand_total = 0
seen = set()
for db in db_names:
display = DISPLAY_NAMES.get(db)
if not display:
print(f" ! no display-name mapping for '{db}' — add it to DISPLAY_NAMES, skipping")
continue
if display in seen:
continue
seen.add(display)
try:
rows = get_benchmarks(display)
except urllib.error.HTTPError as e:
print(f" ! {display}: HTTP {e.code}")
continue
grand_total += len(rows)
nv = sum(1 for r in rows if vendor(r.get("hardware")) == "nvidia")
amd = sum(1 for r in rows if vendor(r.get("hardware")) == "amd")
print(f" {display:22s} {len(rows):5d} rows (NVIDIA:{nv} AMD:{amd})")
evals = get_evaluations()
rel = get_reliability()
print(f"\nTotals: {grand_total} benchmark rows, "
f"{len(evals)} eval rows, {len(rel)} reliability rows")
# Example: best decode throughput per GPU for one model/config.
print("\nExample — DeepSeek-R1-0528, best tput_per_gpu at ISL=1024/OSL=1024:")
best = {}
for r in get_benchmarks("DeepSeek-R1-0528"):
if r.get("isl") != 1024 or r.get("osl") != 1024:
continue
hw = r.get("hardware", "")
t = (r.get("metrics") or {}).get("tput_per_gpu")
if t and (hw not in best or t > best[hw]):
best[hw] = t
for hw, t in sorted(best.items(), key=lambda kv: -kv[1]):
print(f" [{vendor(hw):6s}] {hw:8s} {t:8.1f} tok/s/gpu")
if __name__ == "__main__":
demo()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment