Performance and footprint
All numbers on this page come from serde 1.0.229: 339 files, 4,573 nodes, 9,427 edges, 2,443 embedded definitions. The measurements ran with greppy 0.4.1 on an Apple M5 (10 cores, 32 GB RAM, macOS 26.2). Times are wall time including process start; greppy --version alone takes 33 ms.
$ sysctl -n machdep.cpu.brand_string
Apple M5
$ greppy doctor
status: ok
…
inference: preference=auto explicit=false selected=metal device=metal:0000000100000471
…
device metal:0000000100000471 Apple M5 memory_free=26800144384 memory_total=26800603136
…
stats: files=339 nodes=4573 edges=9427
…
Index and embeddings
| Run (serde, Apple M5, Metal) | Time |
|---|---|
| First index with an empty cache: graph plus 2,443 embeddings | 59 s (median of 3 runs: 58.3 / 58.9 / 60.2 s) |
| Index of another copy (new store), embeddings from the shared cache | 2.8 s (median of 3 runs: 2.3 / 2.8 / 2.8 s) |
First graph answer on an unindexed repository (who-calls) |
2.6 s |
Graph queries work as soon as the graph is in place; the embeddings build in the background. A semantic search in this phase reports progress and an ETA on stderr and answers as soon as the embeddings are done; on serde, after 64 s.
greppy embeds what the cache does not hold yet. Hits for the same model, the same prompt and the same content count user-wide, so they also apply in another worktree or repository. greppy index shows the split:
$ greppy index .
indexed 219 files (120 unsupported, 0 unreadable, 0 oversize, 0 file-limit, 0 time-budget); 4571 nodes extracted; generation 1 (project: serde)
embedded 2443 code spans (0 local-store reused, 0 global-cache hits, 2443 inference misses, 4573 considered, 2150 non-definition skipped, 0 missing-file, 0 invalid-span, 0 oversize, 0 failed, 0 stale pruned)
$ greppy index .
indexed 219 files (120 unsupported, 0 unreadable, 0 oversize, 0 file-limit, 0 time-budget); 4571 nodes extracted; generation 1 (project: serde)
embedded 2443 code spans (0 local-store reused, 2443 global-cache hits, 0 inference misses, 4573 considered, 2150 non-definition skipped, 0 missing-file, 0 invalid-span, 0 oversize, 0 failed, 0 stale pruned)
Queries
These are warm times, with the index in place and the inference daemons running, each the median of 5 runs:
| Command (serde, Apple M5) | Median |
|---|---|
greppy who-calls visit_content_map |
60 ms |
greppy callees visit_content_map |
70 ms |
greppy read visit_content_map |
73 ms |
greppy brief visit_content_map |
108 ms |
greppy search "deserialize a map from buffered content" |
108 ms |
greppy impact visit_content_map |
109 ms |
A new search question embeds the question and generates the navigation hints for hits that do not have one yet. Five new questions to serde, each asked once, took 0.53 to 2.57 s, with a median of 2.16 s. When you ask the same question a second time, the embedding and the hints are already in the cache: 108 ms.
CUDA and Metal compared
The footprint harness measures both platforms with the same procedure and the same serde commit, recorded with a build before 0.4.1:
| First index with embeddings | brief, warm |
search, warm |
|
|---|---|---|---|
| CUDA (RTX A4500) | 14.5 s | 0.09–0.12 s | 0.20 s |
| Metal (Apple M5) | 63.2 s | 0.58–0.74 s | 0.91–1.04 s |
You find the recorded runs in these evidence files:
Footprint
These sizes come from greppy 0.4.1 on macOS arm64:
| Installed binary | 974 MB |
| Store per repository (serde) | 29 MB |
| Extracted models | 814 MB: EmbeddingGemma 272 MB, Qwen3.5 542 MB |
| Cache quota | 10 GiB, with garbage collection |
| Memory of the inference daemons (RSS) | embedding 328 MB, hints 673 MB |
The binary contains both models, their tokenizers, the tree-sitter parsers and SQLite. greppy downloads nothing at runtime. The Linux binary with CUDA measures 1.11 GB in the footprint harness (build before 0.4.1).
Resident models and daemons
| Default | Value |
|---|---|
| A loaded model stays in memory | 300 seconds idle |
| An inference daemon exits | after 1,800 seconds idle |
| Workspace cache entries expire | after 14 days (GREPPY_STORE_TTL_DAYS), plus an independent size quota (GREPPY_STORE_MAX_GIB) |
greppy cache status --json shows the age limit (ttl_secs) and the quota (quota_bytes) on your machine. Every greppy process of the same user shares the two daemons.
Linked worktrees and agents (0.4.1)
Two measurements from the 0.4.1 CHANGELOG show what the shared Base index and the shared inference cache bring:
- A linked worktree of a repository with 41,012 spans, which the main checkout had already embedded, took 1,520 s on an M-series Mac. With the shared inference cache, the same worktree takes 161 s and touches no model file.
- Navigation over Base and Delta uses indexed queries:
pathwent from more than 120 s to under 6 s.
Timing behavior you can rely on
| Situation | Behavior |
|---|---|
| First graph query on an unindexed repository | starts an index in the background and waits at most 2 s for the graph; on large repositories, the query then ends with exit code 75 and the condition for the next attempt |
| Another linked worktree holds the Base builder lock | index waits at most 5 s, then ends with exit code 75 and the lock path |
| Two minutes without progress | index status marks the job as possibly stalled |
The bash-smart child is silent for 15 s |
a sign of life with PID and elapsed time, then one per minute |
$ greppy bash-smart -- sleep 20
bash-smart: command still running — pid=19344, elapsed=15s; child output is being captured and will be summarized on exit
ok — exit 0
Measure on your repository
greppy trial compares a run without and a run with greppy on your own repository, executed with Pi, and prints a greppy.project-trial.v1 JSON record. See greppy trial --help and Benchmarks and paper.