Everything perf.julialang.org shows comes from one SQLite database (db/schema.sql is
the authoritative description of every table). There are three ways to read it, all
public and read-only. Pick by the size of the answer:
| Need | Use |
|---|---|
| What one page of the site shows, or one commit, package or task | the API, https://perf.julialang.org/api/ |
| A few rows or an aggregate | Datasette SQL, https://perf.julialang.org/db/ (2 s and 5000 rows per query) |
| Many queries, or scans of the large tables | the snapshot, https://perf.julialang.org/data/ci-timing.sqlite.gz |
Set B=https://perf.julialang.org for the examples below.
curl -s --compressed $B/api/ lists every route with its parameters. Pass since
(YYYY-MM-DD) wherever a route takes it: without it the route returns all history.
The shapes are the site's own and can change.
curl -s --compressed "$B/api/commit/95cbf209" # one commit across every source
curl -s --compressed "$B/api/timing/builds?since=2026-09-01"
curl -s --compressed "$B/api/pkgeval/package/DataFrames"Use curl -s -G with --data-urlencode so the SQL needs no escaping, and
_shape=array for a plain JSON array of rows:
curl -s -G "$B/db/ci-timing.json" --data-urlencode "_shape=array" --data-urlencode "sql=
SELECT date(b.created_at) AS day, round(avg(j.duration_s) / 60, 1) AS mean_min, count(*) AS runs
FROM jobs j JOIN builds b ON b.id = j.build_id
WHERE j.name = ':linux: test x86_64-linux-gnu' AND b.pipeline = 'julia-ci'
AND b.created_at >= date('now', '-7 days')
GROUP BY day ORDER BY day"Aggregate in SQL; do not page through raw rows, that is what the snapshot is for. Table
and column descriptions are in $B/db/-/metadata.json, and the saved queries listed
under queries in $B/db/ci-timing.json run as
$B/db/ci-timing/<name>.json?_shape=array&<parameter>=<value>.
curl -O $B/data/ci-timing.sqlite.gz && gunzip ci-timing.sqlite.gz # about 220 MB, 700 MB unpacked
sqlite3 ci-timing.sqliteIt is taken after every ingest. No row or time limits, and no load on the host.
- CI timing.
builds(one per Buildkite build: pipeline, number, commit_prefix, commit_sha, state, author, message, created_at, started_at, finished_at) andjobs(one per job attempt: build_id, name, retry, state, duration_s, runnable_at, started_at, agent_hostname). Pipelines:julia-ci(current),julia-masterandjulia-master-scheduled(stopped in July 2026).coverageholds Codecov and Coveralls percentages per commit. - Benchmarks.
bench_reports(one per Nanosoldier report;kind = 'daily'for the regular ones),bench_names(grp, name),bench_results(report, benchmark and estimate: time_ns, gctime_ns, memory_bytes, allocs),bench_verdicts(Nanosoldier's comparison with the baseline: verdict, time_ratio, memory_ratio),bench_report_groups(group geometric means). - PkgEval.
pkgeval_reports(one per run, with outcome counts),packages(names),pkgeval_results(report and package: status, reason, version, duration_s),pkgeval_reasons(counts per status and reason). - TTFX.
ttfx_jobs(one per master build's TTFX job),ttfx_results(job and task: precompile, load, run, warm, and load, run and warm with the GC off, in seconds),ttfx_failures,ttfx_samples(every repeat).ttfx_prsholds the latest TTFX comparison of each open pull request (head against master: verdict, suite geomean ratios, flagged tasks) and the state of the newest julia-pr build of its current head (ci_*), current state only. - Downloads.
dl_series(requests per day),dl_mix(by Julia version and release stage),dl_packages(per package, day and client type),dl_package_uuidsandregistry_packages(uuid to name),julia_tags. - Agents.
agents(every Buildkite agent seen),agent_snapshotsandagent_snapshot_members(who was connected at each ingest).pool_jobsholds every runnable job of julia-pr and julia-ci builds from the last 60 days with the pool it asked for (queue, os, arch) and when it became runnable, started and finished: the queue backlog behindapi/agents/backlogand the worker time shares behindapi/agents/usage. - Freshness.
source_runs: when each fetcher ran and whether it succeeded.
Slowest jobs of the latest julia-ci build:
SELECT j.name, round(j.duration_s / 60, 1) AS min, j.state, j.agent_hostname
FROM jobs j JOIN builds b ON b.id = j.build_id
WHERE b.pipeline = 'julia-ci' AND b.number = (SELECT max(number) FROM builds WHERE pipeline = 'julia-ci')
ORDER BY j.duration_s DESC LIMIT 15Queue wait per build over two weeks (runnable to started):
SELECT b.number, b.commit_prefix, count(*) AS jobs,
round(avg((julianday(j.started_at) - julianday(j.runnable_at)) * 86400)) AS mean_wait_s,
round(max((julianday(j.started_at) - julianday(j.runnable_at)) * 86400)) AS max_wait_s
FROM jobs j JOIN builds b ON b.id = j.build_id
WHERE b.pipeline = 'julia-ci' AND j.runnable_at IS NOT NULL AND b.created_at >= date('now', '-14 days')
GROUP BY b.id ORDER BY b.number DESCNanosoldier's regressions in the latest daily report:
SELECT n.grp, n.name, round(v.time_ratio, 2) AS time_ratio, round(v.memory_ratio, 2) AS memory_ratio
FROM bench_verdicts v JOIN bench_names n ON n.id = v.bench_id
WHERE v.verdict = 'regression'
AND v.report_id = (SELECT id FROM bench_reports WHERE kind = 'daily' ORDER BY date DESC LIMIT 1)
ORDER BY v.time_ratio DESC LIMIT 30One benchmark's minimum time over 60 days. Benchmark names are the strings
BaseBenchmarks writes; find one with
SELECT name FROM bench_names WHERE grp = 'array' AND name LIKE '%sumelt%'.
SELECT r.date, r.commit_sha, res.time_ns, res.memory_bytes, res.allocs
FROM bench_results res JOIN bench_names n ON n.id = res.bench_id JOIN bench_reports r ON r.id = res.report_id
WHERE n.grp = 'array' AND n.name = 'index/(''sumelt'', ''1.0:1.0:100000.0'')' AND res.stat = 'minimum'
AND r.date >= date('now', '-60 days')
ORDER BY r.dateA package's PkgEval history:
SELECT r.date, p.status, p.reason, p.version, r.julia_version
FROM pkgeval_results p JOIN packages k ON k.id = p.package_id JOIN pkgeval_reports r ON r.id = p.report_id
WHERE k.name = 'DataFrames' ORDER BY r.date DESC LIMIT 20TTFX of one task per build over 30 days:
SELECT j.build, j.commit_sha, j.build_created_at, r.precompile, r.load, r.run, r.load_gcoff, r.run_gcoff
FROM ttfx_results r JOIN ttfx_jobs j ON j.job_uuid = r.job_uuid
WHERE r.task = 'BaseDirs/Project-Path' AND j.build_created_at >= date('now', '-30 days')
ORDER BY j.build_created_atOne package's downloads per day, user and CI:
SELECT d.date, sum(CASE WHEN d.client_type = 'user' THEN d.request_count ELSE 0 END) AS user,
sum(CASE WHEN d.client_type = 'ci' THEN d.request_count ELSE 0 END) AS ci
FROM dl_packages d JOIN dl_package_uuids u ON u.id = d.package_id JOIN registry_packages g ON g.uuid = u.uuid
WHERE g.name = 'DataFrames' GROUP BY d.date ORDER BY d.date- Times are UTC ISO 8601 (
...Z). Durations are seconds, benchmark times nanoseconds, memory bytes. The API's timing run rows use the olderyyyy-mm-dd HH:MMform. - Many older builds have only
commit_prefix(8 characters);commit_shais filled for builds fetched from the Buildkite API. - Queue waits and build wall times exist only for builds since September 2026.
- Per-package PkgEval results exist for reports since 2025-09. The
packagestable also holds about 2.9k uuid-shaped names from 2019-20 reports; ignore them. dl_packages.request_addrscounts distinct addresses within one row only; never sum it.- Data is refreshed every hour;
$B/healthzsays when each source last ran. - To share a result, link the site with the view in its URL:
$B/commit?c=<sha>,$B/pkgeval?pkg=DataFrames,$B/downloads?edpkg=DataFrames,$B/builds?t=30,$B/history?bt=90&bm=memory&bv=verdicts.