Speed
10 projects from viv's compatibility corpus, 2026-09-15, from a local
mirror so no network is measured, AMD Ryzen 9 7900X3D (24 threads) on
ext4, viv 0.13.0, composer 2.10.2, riff 0.0.7, vivacity 0.6.0,
--no-plugins --no-scripts on every tool and --no-fallback on vivacity
so a run it hands to Composer can never count as its own, three runs each.
Each cell is the geometric mean of how many times faster viv is, with the
range across projects; below 1× viv is slower. Per-project numbers are in
bench/results/corpus.md, section
2026-09-15T06:54:06Z. riff's column measures the same job as viv and
Composer (checksums, platform_check.php, proxies, installed.*); the
cosmetic differences are listed in
bench/results/README.md. vivacity is the
closest comparison: it makes the same byte-identical promise. Its column
covers the 6 projects it installs; on the other 4 it exits rather than
install a lock naming a plugin outside its list, even with
--no-plugins.2
| Scenario | viv vs Composer | viv vs riff | viv vs vivacity |
|---|---|---|---|
| Cold | 5.6× (2.8 to 11.1) | 2.5× (1.2 to 101.3)1 | 2.0× (1.8 to 2.8) |
| Warm | 17.8× (6.8 to 42.8) | 6.9× (2.7 to 149.8)1 | 2.0× (1.1 to 3.2) |
| No-op | 42.0× (19.0 to 105.5) | 13.9× (2.5 to 57.9) | 6.8× (3.0 to 15.2) |
| Update-warm | 1.9× (1.3 to 4.0) | n/a | 1.5× (1.1 to 1.8) |
The table has not been re-measured since 0.13.0; each release since is
gated against the previous one instead. v0.15.0 against v0.16.0 with
make bench-ab on laravel, symfony/demo and drupal: warm and no-op flat
or faster on all three (drupal warm 296 ms to 285 ms, laravel 41 ms to
39 ms, symfony 47 ms to 47 ms; no-op within 0.2 ms). v0.16.0 against
v0.17.0's #311 (one Snapshot per run keys every install cache): warm
within ±15 ms and no-op within ±1.1 ms on the same three projects, the
sign flipping between runs; a 60-run laravel no-op hyperfine gave 5.2 ms
to both binaries, the remaining spread coming from load on the machine,
not the change. v0.17.0 against v0.18.0's compatibility fixes (time
format, mixed-case install paths, the classmap tie-break, the alias
self.version listing): warm and no-op flat within noise on laravel,
drupal and symfony/demo. v0.19.0's changes were each gated the same way
as they landed: the plugin adapters as data (#340) measured laravel warm
37.5 ms to 36.8 ms, symfony/demo 48.1 ms to 44.7 ms, drupal 298.7 ms to
294.7 ms, no-op within 0.4 ms; the scaffold and patch bindings (#341)
and the root-version guess in the solve (#312) flat within noise.
v0.20.0's viv.lock-only install path (#344): no-op within 0.4 ms on the
same three projects. v0.20.0 against the v0.21.0 candidate (median of each
make bench-ab scenario, same machine, 2026-10-06): laravel warm 36.6 ms to
37.1 ms and no-op 6.5 ms to 6.9 ms, drupal/recommended-project warm 286.8 ms to
289.1 ms and no-op 6.3 ms to 5.9 ms, symfony/demo warm 45.5 ms to 47.9 ms and
no-op 9.3 ms to 9.3 ms. All six are within the 15% tolerance, so v0.21.0
adds no regression.
A warm update still revalidates every package's metadata with the registry,
one conditional request each, even when nothing changed. --metadata-ttl
<seconds> on update, add and rm (or VIV_METADATA_TTL; the flag wins)
skips that revalidation for a package whose cached metadata is younger than
the window, so a second update run shortly after the first makes no metadata
requests at all. It's off by default (0, always revalidate, matching
Composer), so turn it on only where a slightly stale registry view for a few
minutes is an acceptable trade for the extra speed. --offline always wins
over a configured window.
Speed
Composer, riff, vivacity and viv on the same corpus and the same compatibility sweep. Every number below comes from a results file in the repository, not a guess — see the sources under each table.
| Tool | Cold | Warm | No-op | Update-warm |
|---|---|---|---|---|
| Composer | 1.52s | 1.11s | 0.20s | 0.35s |
| riff | 0.56s (9/10) | 0.47s (9/10) | 0.07s (9/10) | n/a |
| viv | 0.26s | 0.06s | 0.01s | 0.21s |
| vivacity | 0.41s (6/10) | 0.12s (6/10) | 0.04s (6/10) | 0.22s (6/10) |
viv 0.12.0, composer 2.10.2, riff 0.0.7, vivacity 0.6.0, flags --no-plugins --no-scripts, 3 runs each, from a local mirror.
source: bench/results/corpus.md, section 2026-09-15T06:54:06Z (2026-09-15)
Skips recorded for this run:
- laravel/laravel/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- symfony/demo/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- drupal/recommended-project/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- drupal/recommended-project/vivacity: run.sh: failed: vivacity
- roots/bedrock/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- roots/bedrock/vivacity: run.sh: failed: vivacity
- composer/composer/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- phpunit/phpunit/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- slimphp/Slim-Skeleton/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- yiisoft/yii2-app-basic/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- yiisoft/yii2-app-basic/vivacity: run.sh: failed: vivacity
- statamic/statamic/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- craftcms/craft/riff: skipped, known failure at riff 0.0.7 (bench/skips.txt)
- craftcms/craft/vivacity: run.sh: failed: vivacity
Capability
| Composer | riff | vivacity | viv | |
|---|---|---|---|---|
Byte-identical vendor/ |
is the reference | no — 4 differences (autoloader-suffix, provide order, NULL vs null, a newer InstalledVersions.php) (JOURNAL.md, 2026-09-06) |
not measured here | yes, 10/10 pinned projects (compat/results/v0.21.0.md) |
| Corpus projects installed (of 10) | 10 | 9 | 6 | 10 |
| Plugins handled natively | all (runs PHP) | not measured here | its known list (JOURNAL.md, 2026-09-15) | 17 adapters (docs/plugin-strategy.md) |
| Needs PHP to install | yes | no | no | no |
Drop-in composer shim |
— | — | — | yes |
| Publishes its failures | — | — | NOTICE, changelog | compat/results/, bench/results/corpus.md footnotes, JOURNAL.md |
n/a and "not measured here" cells are viv's own gaps too, not just a
missing measurement for someone else: see docs/stability.md
for what viv's output contract does and doesn't cover.
-
riff's phpunit/phpunit cold and warm times (7.4 s and 7.3 s) are an outlier against its other rows in this corpus; kept in the range, not dropped; see
bench/results/corpus.md. ↩↩ -
roots/bedrock, drupal/recommended-project, yiisoft/yii2-app-basic and craftcms/craft, recorded in
bench/skips.txtagainst vivacity 0.6.0 so a newer release is retried. A refusal is ann/acell, never a slow one. ↩