Design rationale for the CollectiveX tab's data pipeline. CollectiveX keeps its own Neon
database, filled by uploading the benchmark results that collectivex/run.sh writes on a
cluster: a results/ directory holding cases.json (the requested cases, with the contract
version) plus one <sku>/<case_id>.json per measured case. The results are not stored in
SemiAnalysisAI/InferenceX or any other git repository; the database is their only store.
The API routes only read that database.
Each upload is one new stored run: the latest stored run of the same version with the
uploaded files laid over it, keyed by (sku, case_id).
- An uploaded file replaces that case's stored document; every other stored document is kept.
- The upload's
cases.jsonis the new run's matrix. A stored document that is not runnable in it, or whoseversiondiffers, is dropped, and the plan names each droppedsku/case_id. A case the newcases.jsonstill requests but nobody has measured yet stayspending. - The merged set is validated as a whole before anything is written (below), so a kept stored document that no longer passes the checks stops the upload.
- When nothing is stored yet, the run is just the upload.
- A run with nothing measured is refused, never stored as an empty run, and the read queries hide any row whose summary measures nothing.
The stored row:
run_idis the upload time in epoch seconds, raised to one past the highestrun_idstored in any version when it would not exceed it (a clock behind the newest run, or two uploads in one second), so "latest" is stillORDER BY run_id DESC. Ids stay below 10^10, which migration005_remove_lazy_ingest_runs.sqlenforces with a CHECK after deleting the retired lazy ingest's rows (keyed by GitHub Actions run id, so they would otherwise outrank every later run); the bound isRUN_ID_LIMITinresults-upload.ts.source_shais the onecollectivex/commit every uploaded document was measured from (provenance.source_shaon EP results,runtime.source_shaon swap_blocks results). An upload whose documents name different commits is refused with the files of each sha listed; upload them separately. Kept stored documents keep their ownsource_sha.run_attemptis 1,source_branchis null,conclusionissuccess,generated_atis the upload time (UTC, whole seconds),matrixis the uploadedcases.jsonandsummaryis the shared reader'sbuildRunSummary.cx_run_docsholds the merged documents verbatim.- One transaction takes a fixed advisory lock (
pg_advisory_xact_lock, shared by every CollectiveX write) and then one statement inserts the row and its documents, so a reader sees the old latest run or the new one, never a mix. Under the lock the write first checks that the highest storedrun_idis still the one the upload merged against; if another run landed in between, nothing is written and the CLI asks for a rerun, so two concurrent uploads cannot drop each other's cases.
The DB stores raw documents; the shared reader (packages/db/src/collectivex/reader.ts)
assembles the dataset at read time, so a reader fix applies to stored runs without
re-ingesting. A contract change is a reader change plus a bump of the numeric version that
cases.json and every EP result carry.
packages/db/src/collectivex/results-upload.ts reads the directory: cases.json and every
*.json exactly one level down (<sku>/<file>.json). Other files, dot entries such as
.shards, and deeper directories are ignored; a symlink anywhere on that path is rejected.
Files above 4 MiB, or results above 256 MiB in total, are rejected. It then checks every
file the way the benchmark repo's own test does: the path is <sku>/<case_id>.json matching
the document's identity (EP case attempts by identity.case_id and
identity.case_factors.sku; swap_blocks results by schema: collectivex-swap-blocks-v1,
case_id and runtime.sku), the case is runnable in cases.json, the result was measured
from committed code (source_dirty: false and a 40-hex source_sha), an EP result carries
exactly the cases.json version, and no string holds an IPv4 address and no key names a
machine (host, hosts, hostname, nodelist, slurm_nodelist, master_addr,
case-insensitive). Each document is then assembled alone, so every failure names its file.
- CLI:
bun run admin:db:ingest:collectivex (--dir <results-dir> --pr <n>... | --link-run <run_id> --pr <n>... | --delete-run <run_id>) [--yes] [--no-ssl].--dirreads and validates the directory, merges it over the latest stored run, links the run to the changelog entries of the named PRs (see Changelog), prints the plan (run_id, source sha, how many uploaded, replaced, kept and dropped, each dropped case, and each entry with the cases it changes) and asks for confirmation unless--yesis given. It writes throughDATABASE_COLLECTIVEX_WRITE_URLand never prints that URL.--delete-rundeletes one stored run and its documents. After a write, whenAPP_URLandVERCEL_INVALIDATE_SECRETare set, it purges the cache withPOST ${APP_URL}/api/v1/invalidate?scope=collectivex(Bearer auth, plusx-vercel-protection-bypasswhenVERCEL_PROTECTION_BYPASS_SECRETis set). A deletion addsimmediate=true. Otherwise it says the dashboard updates when its cache expires or after the workflow below runs. - Workflow:
.github/workflows/ingest-collectivex.yml(Ingest CollectiveX Results) runs only onworkflow_dispatch. With no input it applies pending migrations (migrate-collectivex.ts --yes) and purges the CollectiveX cache, for example after an upload made without the purge variables. Withdelete_runit also deletes that run before purging, and that purge isimmediate. Every run shares one concurrency group that queues rather than cancels, and it alerts Slack on failure, likeingest-operatorx. It reads no GitHub repository.
There is no backfill and no prune: the results exist only in the database, so nothing can rebuild a deleted run.
Every PR that changes what CollectiveX measures appends an entry to InferenceX
collectivex/perf-changelog.yaml: the PR link, the date, the pools it affects, optional suite,
backend, mode, phase, EP-size and precision selectors, and a description. An upload must name the
merged PRs it publishes with --pr. The CLI reads the file from InferenceX main (with Bun's YAML
parser) and refuses the upload unless every PR has an entry, the entries select every uploaded case,
and the results' source_sha is one of each PR's commits or contains its merge commit (GitHub API,
GITHUB_TOKEN when set). Each entry is stored in cx_changelog_entries against the run, with
changes: the previous and new status, kernel generation, library version and pair-period p50 of
every selected case the run changed. --link-run <run_id> --pr <n> links a stored run, comparing
it with the run before it, and only warns on a provenance gap. GET /api/v1/collectivex/changelog
serves the entries, and the CollectiveX page lists them under the support matrix.
Upload results from a cluster checkout after collectivex/run.sh finishes:
- Apply migrations once per schema change:
bun run admin:db:migrate:collectivex(or run the workflow with no input). bun run admin:db:ingest:collectivex --dir <path/to/collectivex/results> --pr <n>, naming every merged PR the results publish, read the plan, and confirm. Upload onesource_shaat a time.- If the CLI could not purge the cache, run the
Ingest CollectiveX Resultsworkflow with no input.
Retract a bad run at once with --delete-run <run_id> (or the workflow's delete_run).
The previous run becomes the latest again, and the next upload merges over it. To fix one
case instead, upload its corrected result: the new run replaces that case and keeps the rest.
Archive before destructive maintenance, because the database is the only copy:
pg_dump --table=cx_runs --table=cx_run_docs "$DATABASE_COLLECTIVEX_WRITE_URL" > cx-archive.sql
- Reads use the primary: every route reads through
DATABASE_COLLECTIVEX_WRITE_URL, so a response cached right after an upload's purge never comes from a lagging replica.DATABASE_COLLECTIVEX_READONLY_URLis not read by the app routes. - Caching: responses use the default API cache headers (
max-age=60, a one-days-maxageandstale-while-revalidate, plus an ETag) under thecollectivexCDN tag. The ingest CLI and workflow purge only that tag throughPOST /api/v1/invalidate?scope=collectivex. An upload marks it stale, so the CDN serves the previous response once while it refreshes. A deletion passesimmediate=trueso the retracted run is never served again. The main dashboard's blob cache is untouched by CollectiveX operations. - Partial results: a run may hold results for only some pools (for example a first
upload covering only
mi300xandmi325x). Every runnable case incases.jsonwithout a result stays incoveragewith outcomepending(requested, not run), counts towardrequested_casesbut notmeasured_cases, and adds no series, so the SKU controls list only measured pools. A later upload of other pools is a new run with a laterrun_idthat also keeps the earlier pools' results, so it becomes the latest run (a superset) while the earlier one stays listed in the API. - One run on the page:
/colxshows only the newest stored run with at least one measured case; there is no run table or multi-run overlay. The header's Suites control picks the one section shown:EP(the default) shows the EP chart controls and chart,KVthe kv-transfer section, andswap_blocksthe swap_blocks section. KV and swap_blocks are offered only when the shown run carries them, and a run without EP series opens on its first suite. - Run list:
/api/v1/collectivex/runsalways reportsdiscovery_complete: true; the field stays for existing clients. - No deletion or ingest in the app: the app exposes no CollectiveX mutation. Runs are
added by the ingest CLI and removed with
--delete-runor the workflow'sdelete_run(see the runbook). - Covered pools: a run summary's
covered_skuslists only pools with at least one measured result (EP, KV or swap_blocks); pools that are requested but have no result yet are not listed. - Vendor scope is explicit: the shared reader accepts only
amdandnvidiaresults (case-insensitive). Other vendor values are omitted from chart series, coverage, and SKU summaries; runs with no supported-vendor cases stay hidden. - Headline is the pair period: the reader maps each result's
components.pair_period(the chained steady-state period the methodology ranks on) intoseries[].points[].componentsbesidedispatch,stage,combineandroundtrip, with the round trip's payload bytes. The EP chart and the views API default topair_periodat p50. A run whose results carry no pair period shows the fresh-entry round trip instead. - Withheld tails: an EP result may publish p90/p95/p99 as
nullwhen too many of a series' samples were misaligned or host-stalled (p50 is always present). The reader keeps the derived ratesnullthere, the alpha-beta fit skips them, and the tooltip shows them as unavailable. This is an API response-shape change: inseries[].points[],components.*.latency_us,components.*.activation_data_rate_gbps_at_latency_percentile,components.*.payload_data_rate_gbps_at_latency_percentileandroundtrip_token_rate_at_latency_percentilemay now carrynullforp90,p95andp99; the API documentation says so. - Links: a run links to the
collectivex/code and the methodology at itssource_sha; the results themselves are not in the repository.
The bottom of the page carries the curated known-support matrix: every SKU × library pairing for
throughput (normal) and low-latency kernels at EP8 and EP16, independent of the shown run
and suite. Green degrees are known to work on the fleet; red degrees are known NOT to work, each
carrying a numbered note with the investigated reason (upstream issue references included, e.g.
ROCm/mori#610); gray degrees do not exist for that pairing (vendor-mismatched library, a kernel
with no such mode, or a pool the library was never brought up on). The table is maintained by hand
in known-support.ts, mirroring the InferenceX repo's platform_config.json registry plus its
wall investigations — when a registry row flips or a wall falls upstream, the cell and its note
change here.
CollectiveX routes return the assembled dataset (reader over stored matrix + docs)
instead of raw rows. The reader is shared between the app and the CLI through the db
package (@semianalysisai/inferencex-db/collectivex/*), so ingest-time validation and
read-time assembly can never drift; shipping raw docs to the client would only move the
same shared transform across the wire.
swap_blocks cases are requested in cases.json with suite: "swap-blocks" and answered by
collectivex-swap-blocks-v1 documents at <sku>/<case_id>.json. Each result must answer
exactly one requested swap_blocks case of its own GPU pool; requested swap_blocks counts come
from cases.json, and those cases never enter the EP ladder parser. The shared reader
validates correctness, payload arithmetic, finite positive ordered latency percentiles and
sample counts. EP/KV documents keep their existing reader path. No database migration is
needed: raw JSON documents remain the source of truth.
The additive swap_blocks dataset field holds runtime provenance and measured points;
swap_cases labels their run summaries separately from EP/KV. One verified artifact counts
as one case; each measured (direction, layout, block bytes, block count, seed) is one point.
Budget-excluded combinations are reported separately and are never plotted as measurements.
The swap_blocks section appears when that suite is chosen in the Suites control, and the
methodology link then points at swap-blocks.md. The chart selects direction, layout and latency percentile,
plots block bytes in B/KiB/MiB/GiB, and keeps bandwidth linear with a zero baseline.
Latency is logarithmic. Bandwidth is copied payload divided by host-observed latency,
including submission and CUDA synchronization, counting payload once even for d2d.
Both /colx and /zh/colx expose the controls and tooltips.
Bandwidth charts draw dashed nominal hardware rooflines for visible GPUs. Host transfers use 64 GB/s one-way PCIe 5.0 x16 on the registered x86 pools (including HGX B300's CPU uplink). GB200/GB300 use 225 GB/s per GPU per direction: the 900 GB/s bidirectional Grace C2C link is split across two GPUs. Same-GPU copies use half the GPU Specs HBM bandwidth because each payload byte is read and written. These are hardware references, not measured limits: warm-cache copies may exceed the HBM reference; host memory, placement and protocol overhead reduce host rates. Unknown SKUs get no assumed roofline. Rooflines disappear in latency mode or when all matching series are hidden; bandwidth stays linear and includes the reference in its domain.
References: H200 specifications, AMD MI355X specifications, Grace C2C ports per GPU, GB200 bidirectional C2C, HGX CPU uplink topology.
Every swap_blocks result carries runtime.sku, which must match its requested case.
Measurement identity includes SKU so equivalent grid points from different GPU pools remain
independent.