{"id":51468428,"url":"https://github.com/rivet-dev/linux-package-manager-bench","last_synced_at":"2026-07-06T14:01:09.900Z","repository":{"id":369613272,"uuid":"1290585639","full_name":"rivet-dev/linux-package-manager-bench","owner":"rivet-dev","description":"Reproducible benchmark: how long each Linux package manager takes to install git (download vs offline install), with per-phase timing","archived":false,"fork":false,"pushed_at":"2026-07-06T06:14:52.000Z","size":79,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-06T06:17:51.000Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rivet-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-06T04:24:06.000Z","updated_at":"2026-07-06T06:14:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rivet-dev/linux-package-manager-bench","commit_stats":null,"previous_names":["rivet-dev/linux-package-manager-bench"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/rivet-dev/linux-package-manager-bench","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rivet-dev%2Flinux-package-manager-bench","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rivet-dev%2Flinux-package-manager-bench/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rivet-dev%2Flinux-package-manager-bench/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rivet-dev%2Flinux-package-manager-bench/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rivet-dev","download_url":"https://codeload.github.com/rivet-dev/linux-package-manager-bench/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rivet-dev%2Flinux-package-manager-bench/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35193679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-06T02:00:07.184Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-06T14:01:08.886Z","updated_at":"2026-07-06T14:01:09.878Z","avatar_url":"https://github.com/rivet-dev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# linux-package-manager-bench\n\nHow long does it take to **install `git`** with each major Linux package manager —\nand where does that time actually go? This benchmarks six managers (**apk, pacman,\ndnf, apt, nix, brew**) in throwaway Docker containers.\n\nTo compare the **same unit of work**, each manager's **dependencies are pre-installed\nduring untimed setup**, so the timed phases handle only the **`git` package itself** (1\npackage) — not a different-sized dependency tree per manager. Each run splits into two\ntimed phases:\n\n1. **Download** — fetch the `git` package into the local cache (its deps are already\n   installed).\n2. **Install (offline)** — the warm-cache container is frozen to an image, then `git` is\n   installed in a fresh **`--network none`** container, repeated **N times** (default 5).\n   Timing is measured *inside* the container (so docker-exec startup is excluded) and\n   reported as the **median** with min–max. This isolates real install work from network\n   speed and shows run-to-run variance.\n\nEvery line of both phases is timestamped with the elapsed time since the previous line,\nso the install can be broken down step by step. Set `REPS=\u003cn\u003e` to change the repeat count.\n\n\u003e **agentos** appears in the results as a **hardcoded reference point** — it is *not* run by\n\u003e this Docker harness. Instead of a traditional package manager, agentOS loads a precomputed\n\u003e `.aospkg` into its VM; loading the `git` package takes **~0.017 ms** (median; min 0.013,\n\u003e max 0.020), with no download step. That number comes from the agentOS package-load\n\u003e benchmark, not from this repo:\n\u003e \u003chttps://github.com/rivet-dev/agentos/blob/f66918af5708776823b57fafc6b8d0e318e08764/crates/native-sidecar/tests/projection_bench.rs#L606\u003e\n\n## Latest results\n\nAuto-generated by `synthesize.js` on the last run — the chart and matrix below, and the\n**host hardware** they came from, are rewritten between the markers on every run.\n\n\u003c!-- RESULTS:START --\u003e\n![git install time by phase](chart.png)\n\n### Host\n\n- **CPU:** 12th Gen Intel(R) Core(TM) i7-12700KF (20 logical cores)\n- **Memory:** 67.2 GB   **Arch:** x64   **Kernel:** 6.1.0-41-amd64\n- **OS:** Debian GNU/Linux 12 (bookworm)   **Docker:** 28.3.1\n- **Disk (/):** /dev/nvme0n1p2 ext4  937G  261G\n- **Generated:** 7/6/2026, 3:51:01 AM PDT\n\n### Phase correlation matrix\n\n_`install MEDIAN` is the median of 30 offline runs (`install min–max` shown); sub-phases are a heuristic bucketing of log lines (see README). `download*` is a single, network-dependent sample._\n\n| phase (s) | agentos | apk | pacman | dnf | apt | nix | brew |\n|---|---:|---:|---:|---:|---:|---:|---:|\n| **download\\*** | 0.00 | 0.63 | 1.02 | 1.89 | 1.06 | 6.83 | 3.02 |\n| install: startup | 0.00 | 0.00 | 0.05 | 0.02 | 0.00 | 0.00 | 0.00 |\n| install: resolve | 0.00 | 0.00 | 0.10 | 0.04 | 0.09 | 0.00 | 0.00 |\n| install: verify | 0.00 | 0.00 | 0.02 | 0.23 | 0.00 | 0.00 | 0.00 |\n| install: unpack | 0.00 | 0.00 | 0.00 | 0.02 | 0.05 | 0.00 | 0.00 |\n| install: configure | 0.00 | 0.00 | 0.06 | 0.00 | 0.26 | 0.00 | 1.09 |\n| install: link | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |\n| install: unknown | 0.00 | 0.04 | 0.03 | 0.03 | 0.09 | 0.00 | 0.40 |\n| **install MEDIAN** | 0.00 | 0.25 | 0.48 | 0.27 | 0.87 | 0.59 | 2.11 |\n| install min–max | 0.00–0.00 | 0.23–0.28 | 0.43–0.53 | 0.25–0.38 | 0.85–0.92 | 0.29–0.60 | 2.07–2.14 |\n| git version | wasm | 2.54.0 | 2.55.0 | 2.55.0 | 2.39.5 | 2.54.0 | 2.51.2 |\n| deps ~ | 1 | 1 | 1 | 1 | 1 | 84 | 1 |\n| download size | — | — | 7.11 MiB | 5 MiB | 7264 kB | 127.7 MiB | — |\n\n_Each manager installs only the `git` package with deps pre-installed, so `deps ~` = 1 (nix shows its store closure and is not counted). The `install: *` split is from a single (last) rep, not the median. **nix** caveat: its install is a profile symlink flip — unpack happens during the download phase (store realization), so its install time is **not** comparable to the others' unpack._\n\n### Top install sub-steps (by measured delta)\n\n- **agentos** (agentos VM (V8/wasm)): \n- **apk** (alpine:latest): `0.037s Executing busybox-1.37.0-r31.trigger` · `0.003s OK: 20.4 MiB in 28 packages` · `0.000s (1/1) Installing git (2.54.0-r0)`\n- **pacman** (archlinux:latest): `0.097s Optional dependencies for git` · `0.059s :: Running post-transaction hooks...` · `0.033s (2/3) Reloading system manager configuration..`\n- **dnf** (fedora:latest): `0.235s [1/4] Verify package files              100% |` · `0.041s [2/4] Prepare transaction               100% |` · `0.030s [4/4] Removing git-core-0:2.55.0-1.fc44 100% |`\n- **apt** (debian:12-slim): `0.197s Setting up git (1:2.39.5-0+deb12u3) ...` · `0.090s Building dependency tree...` · `0.085s Suggested packages:`\n- **nix** (nixos/nix): `0.001s warning: you don't have Internet access; disab` · `0.000s warning: 'install' is a deprecated alias for '`\n- **brew** (homebrew/brew): `1.088s ==\u003e Caveats` · `0.400s ==\u003e Fetching git` · `0.000s ==\u003e Fetching downloads for: git`\n\u003c!-- RESULTS:END --\u003e\n\n## Run it\n\n```bash\nnode run.js                          # all managers (fast -\u003e slow), then synthesize + chart\nnode run.js apk pacman               # just a subset\nnode synthesize.js \u0026\u0026 python3 chart.py   # re-render tables + chart from existing logs\n```\n\n**Prerequisites:** Docker (daemon running) and Node.js 18+ for the benchmark; Python 3 +\nmatplotlib (`pip install -r requirements.txt`) for the chart. The `ts` timestamper is a\ntiny bundled script (`lib/ts.js`) — no `moreutils` needed.\n\nOutput lands in `results/` (git-ignored): per-manager `*-download.log`, `*-install.log`,\n`*.json`, `results/RESULTS.md`, and `results/data.json`. The committed **`chart.png`**\n(embedded above) is regenerated on every run.\n\n## How it works (three stages)\n\n- **`run.js`** (stage 1) — for each manager, calls `benches/\u003cmgr\u003e.sh`, which uses the\n  shared harness `benches/_lib.sh` to: start a container, do untimed **setup** (pull repo\n  metadata and **pre-install git's dependencies**, so only git itself is left to install),\n  time the **download** of the git package, then `docker commit` the warm-cache\n  container to an image and run the **offline install** `REPS` times, each in a fresh\n  `--network none` container. Timing is host wall-clock minus a measured `docker exec`\n  baseline; every line is piped through `lib/ts.js`; a failed install (or missing\n  `git --version`) fails the manager loudly. Writes the median + min–max to `\u003cmgr\u003e.json`.\n- **`synthesize.js`** (stage 2) — parses the ts-stamped logs with regex, classifies each\n  install line into a canonical phase, and emits the **phase × manager matrix**,\n  `results/RESULTS.md`, and `results/data.json`.\n- **`chart.py`** (stage 3) — renders `results/data.json` into the **stacked-bar\n  `chart.png`** with matplotlib (broken x-axis so one slow manager can't crush the rest).\n\n## Reading the matrix\n\nRows are phases (seconds), columns are managers. `install MEDIAN` (median of N runs, with\n`install min–max`) is ground truth; `download*` is a single network-dependent sample. The\n`install: *` sub-rows (and the chart's colored segments) bucket each timestamped install\nline into canonical phases:\n\n| bucket | what lands here |\n|---|---|\n| `startup` | package-manager start, reading state/db |\n| `resolve` | dependency resolution, transaction prepare/check, conflict/key checks |\n| `verify` | package integrity / signature / GPG verification |\n| `unpack` | unpacking / pouring / extracting / installing file payloads |\n| `configure` | scriptlets, triggers, post-install hooks, sysusers, cert stores |\n| `link` | symlinks, ldconfig, profile generation |\n| `unknown` | lines that matched no rule (visible, never silently folded into another phase) |\n| `overhead` | install wall-time not attributed to any log line (time before first / after last line) |\n\nThese sub-buckets are a **heuristic** classification of human-readable log lines and vary\nwith tool versions — treat them as directional. `ts` charges each line's delta to the line\nthat *ends* the interval, so a step's cost can land on the following line (often an\nunmatched one → `unknown`); and the split comes from a single (last) rep, not the median\nrun. The `install MEDIAN` / min–max totals are the exact, repeated measurement.\n\nThe **`verify` row is not cross-comparable**: managers verify at *different phases* (apt\nverifies during download, so its install `verify` = 0 despite real work earlier), and some\nskip signature checks at install — **apk** (`--allow-untrusted`) skips all verification, and\n**dnf** installs a local `@commandline` rpm so it **skips the OpenPGP signature check** (it\nstill checksums the payload). A 0 or a large `verify` cell means different things per manager.\n\n## Caveats\n\n- **Same unit of work — the git package only.** Every manager's dependencies are\n  pre-installed in untimed setup, so the timed install handles exactly **1 package (git)**,\n  not a different-sized dependency tree. What still differs is git itself: version\n  (2.39–2.55) and packaging — on Fedora `git` is a thin metapackage, so we install/measure\n  **`git-core`** (the real binary payload); other distros ship git's binary in the `git`\n  package directly.\n- **nix's install is not comparable to the others'.** For apt/dnf/pacman/apk/brew the git\n  unpack happens during the timed install; for nix it happens during *download* (store\n  realization), so nix's install is just a profile **symlink flip**. Its low install\n  number reflects a different operation, not a faster one — the chart hatches it.\n- **Install mechanics still differ slightly.** apt/pacman/apk install git fresh (unpack +\n  configure), dnf **reinstalls** `git-core` (which also *removes* the old copy — a step the\n  fresh installs don't do, working slightly against dnf), and brew pours git's bottle and\n  then re-verifies its SHA + runs `brew cleanup` + prints caveats (bookkeeping the others\n  skip). On verification: **apk** (`--allow-untrusted`) skips all checks and **dnf** (local\n  `@commandline` rpm) skips the OpenPGP signature check — so their low `verify` is partly a\n  skipped step, not speed.\n- **Reproducibility.** Install is the median of N runs (min–max shown) measured in-container\n  and run under `--network none`, but it is still CPU/IO-sensitive: on a contended host the\n  numbers inflate. Run on an otherwise-idle machine; treat sub-second gaps between the\n  middle managers as within noise. `download*` is a single sample and mixes in mirror\n  speed — not a manager comparison.\n- **Warm page cache.** Install reads come from RAM (files written seconds earlier), so\n  they reflect warm reads, not a cold first-install; this discounts I/O-heavy managers\n  (brew, apt) more than nix. The recorded `image_digest` per manager pins provenance, but\n  base images are `:latest` and drift over time.\n- **brew** setup clones the full `homebrew-core` tap (slow — minutes); untimed, and pins\n  installs to the local tap for determinism.\n- Re-running is destructive to same-named containers (`\u003cmgr\u003e-bench`, `\u003cmgr\u003e-rep*`), which\n  are force-removed at start and teardown.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frivet-dev%2Flinux-package-manager-bench","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frivet-dev%2Flinux-package-manager-bench","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frivet-dev%2Flinux-package-manager-bench/lists"}