Performance
Cold and warm sync timings for esysrepo (libgit2 / hybrid / gitcmdline)
versus Google’s repo tool, from CI esysrepo-bench matrices.
The charts match the internal Grafana dashboards (durations in seconds):
- Sync duration — grouped bars for the latest samples across
j1/j4/j8: esysrepo (libgit2/hybrid/gitcmdline) vs Google repo (filters:manifest_tier,sync_mode). - Duration over time — the same four series over time (filters:
manifest_tierincluding futurelarge,-j,sync_mode).
Data is loaded from same-origin /metrics/performance/latest.json. On
production, a Pages Function serves live R2 data without a site rebuild
(ADR-0005). On staging (GitLab Pages), docs CI bakes a snapshot of that
JSON into the static tree (ADR-0006) so charts can be verified — use Grafana
for realtime trends.
Loading charts…
Clone/fetch CPU profiles (FlameGraphs)
Matrix timings show libgit2 colder sync slower than gitcmdline / Google
repo on non-trivial histories (often ~2–3× on clone/fetch-heavy work). To
attribute CPU time, CI runs erepo-bench profile (linux:bench:profile):
Linux perf samples a cold sync, then FlameGraph builds an SVG.
How to read a FlameGraph: the x-axis is the share of CPU samples (wider
= hotter). The y-axis is call-stack depth. Use the toolbar (or scroll-wheel
zoom and drag-pan) to inspect frames. System git stacks need debug symbols
(git-dbgsym); without them frames collapse to [git] / libraries only.
libgit2 backend
Cold sync with ESYSREPO_GIT_BACKEND=libgit2 (medium tier, -j1).
Loading flamegraph…
What dominates
- Collision-detecting SHA-1 (SHA1DC): frames such as
sha1_compression_states,ubc_check,sha1_process. - zlib inflate (
inflate_fast/ related) while unpacking the pack. - libgit2’s pack indexer / receive path — not esysrepo progress wrappers or logging.
Threading
- Pack indexing on clone/fetch is effectively single-threaded. libgit2
exposes thread knobs for packbuilder (push / creating packs), not for the
indexer used when receiving a pack. There is no
pack.threads-style API for this path today.
gitcmdline backend
Cold sync with ESYSREPO_GIT_BACKEND=gitcmdline (medium tier, -j1), profiled
with git-dbgsym so frames resolve into git itself.
Loading flamegraph…
What dominates
- Still pack work: almost all of
gitiscmd_index_pack(index a received pack), with the same SHA1DC + zlib diet as libgit2. - Transport appears as a separate
sshprocess slice (not pack CPU).
Threading
- Second pass is multi-threaded:
start_thread→threaded_second_pass→resolve_delta(git’spack.threads/index-packworkers). - First pass (parse/unpack as the pack streams in) remains largely serial; parallelism is concentrated in delta resolution.
What this means
| libgit2 | gitcmdline (system git) | |
|---|---|---|
| Hot work | Indexer + SHA1DC + zlib | index-pack + SHA1DC + zlib |
| Delta resolve | Single-threaded | Multi-threaded (threaded_second_pass) |
| Controllable threads | Packbuilder (push) only | pack.threads / index-pack |
- The gap is not “git avoids SHA1DC.” Both backends spend heavy CPU on SHA1DC and inflate; git spreads the heavy second pass across cores, libgit2 does not on fetch/clone.
- That validates routing clone/fetch through hybrid / gitcmdline while keeping libgit2 for operations that need its API (see product ADR-0025).
- Upstream libgit2 has long-standing clone-vs-git performance issues and an open indexer optimization PR, but no shipped multi-threaded indexer API; adding one is a large library change, not a one-line option.
How we capture: erepo-bench profile --sync-tier … --git-backend … locally,
or manual CI jobs linux:bench:profile / windows:bench:profile. SVGs on this
page are curated snapshots from those Linux runs (replace files under
website/static/img/performance/ to refresh).
How data is published
- Manual / scheduled
linux:bench:matrixpushes each cell to VictoriaMetrics (same TSDB Grafana uses) viaesys-metrics. publish:performance:sitequeries the last ≈14 days ofesys_bench_duration_secondsfrom VictoriaMetrics, writeslatest.json, and uploads it to R2 (docs/esysrepo/metrics/performance/).- A Cloudflare Pages Function serves that object at
https://esysrepo.libesys.org/metrics/performance/latest.json.
The over-time chart auto-fits the x-axis to the samples present (no empty left padding when history is shorter than 14 days).