Skip to content

ci: adopt CI/CD engineering standards, single rollup gate, and upstream CDN 504 resilience - #201

Merged
orieg merged 1 commit into
mainfrom
ci/standards-upgrade
Aug 23, 2026
Merged

ci: adopt CI/CD engineering standards, single rollup gate, and upstream CDN 504 resilience#201
orieg merged 1 commit into
mainfrom
ci/standards-upgrade

Conversation

@orieg

@orieg orieg commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Closes #200

…am CDN 504 resilience

- Implement canonical CI/CD guide in docs/CI_CD_GUIDE.md detailing CI commandments,
  multi-project standards, upstream CDN resilience, and Miri/Valgrind tiers.
- Add upstream CDN 504 resilience to ci.yml and release.yml:
  * Resilient curl retry loop with exponential backoff for downloads.php.net / windows.php.net
  * Matrix runner startup jitter across Linux, Alpine, and Windows runners
  * Wrap fragile steps (apt-get, PIE install, baseline fetch) in nick-fields/retry@v3
  * Cap matrix parallel concurrency (max-parallel: 6)
- Add two-tiered path filtering (dorny/paths-filter@v3) to skip heavy builds on doc-only PRs.
- Implement single rollup gate job (ci-gate) evaluating statuses across all CI matrix jobs.
- Standardize job names with functional categories: Core / ..., Compat / ..., Safety / ..., CI Gate / ...
- Implement nightly.yml workflow with scheduled Valgrind memory leak checks, PHP-dev compatibility,
  and differential fuzz soak, with automated GitHub issue triage.
- Update action versions to @v4 / @v7 across all workflows and validate with actionlint.

Closes #200
@github-actions

Copy link
Copy Markdown

Test Results

PHP Platform Arch TS Tests Pass Fail Skip Duration
8.1 Linux x64 - 232 232 0 0 8.8s
8.2 Linux x64 - 232 232 0 0 10.0s
8.3 Linux x64 - 232 232 0 0 10.2s
8.4 Linux x64 - 232 232 0 0 9.5s
8.5 Linux x64 - 232 232 0 0 8.4s
✅ 🧪 8.6 Linux x64 - 232 232 0 0 14.1s
8.1 Linux (Expanse) x64 - 232 232 0 0 9.4s
8.2 Linux (Expanse) x64 - 232 232 0 0 11.3s
8.3 Linux (Expanse) x64 - 232 232 0 0 10.0s
8.4 Linux (Expanse) x64 - 232 232 0 0 10.3s
8.5 Linux (Expanse) x64 - 232 232 0 0 8.1s
✅ 🧪 8.6 Linux (Expanse) x64 - 232 232 0 0 13.7s
8.1 Windows x64 nts 232 232 0 0 13.3s
8.2 Windows x64 nts 232 232 0 0 14.0s
8.3 Windows x64 nts 232 232 0 0 14.2s
8.4 Windows x64 nts 232 232 0 0 14.1s
8.5 Windows x64 nts 232 232 0 0 15.2s
✅ 🧪 8.6 Windows x64 nts 232 232 0 0 13.2s
8.1 Windows (Expanse) x64 nts 232 232 0 0 14.3s
8.2 Windows (Expanse) x64 nts 232 232 0 0 13.5s
8.3 Windows (Expanse) x64 nts 232 232 0 0 13.6s
8.4 Windows (Expanse) x64 nts 232 232 0 0 14.3s
8.5 Windows (Expanse) x64 nts 232 232 0 0 13.9s
✅ 🧪 8.6 Windows (Expanse) x64 nts 232 232 0 0 13.3s
Total 5568 5568 0 0

Benchmark Results

500,000 elements, 7 iterations (median), Judy 2.7.1

Run Status

Platform PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
Linux
Windows

Memory (Linux, PHP 8.6)

Peak RSS of a child process building one structure, n = 500,000, minus an empty-process floor. Judy's memory advantage is type- and scale-dependent — see BENCHMARK.md for the curve across sizes on a dedicated host.

Type Judy PHP array Judy vs PHP array
Bitset < 400.0 KB 8.0 MB > 20x smaller
Int to Int 4.3 MB 8.1 MB 1.9x smaller
Int to Mixed 40.2 MB 37.9 MB 1.06x larger
Int to Packed 16.3 MB 38.0 MB 2.3x smaller
String to Int 10.2 MB 37.9 MB 3.7x smaller
String to Mixed 46.3 MB 68.0 MB 1.5x smaller
String to Int (Hash) 31.4 MB 38.1 MB 1.2x smaller
String to Mixed (Hash) 67.4 MB 68.1 MB about the same
String to Int (Adaptive) 31.4 MB 37.9 MB 1.2x smaller
String to Mixed (Adaptive) 67.4 MB 68.0 MB about the same

< marks a structure smaller than this instrument can resolve at n = 500,000: run-to-run movement in the empty-process floor puts the resolution at 400.0 KB. The bound is real, the exact figure is not available at this size — BENCHMARK.md measures the same types up to n = 8M, where they are well clear of the floor.

API Speedups (Linux, PHP 8.6)

Speedup = PHP time / Judy time. Bold = Judy faster.

Operation Judy PHP Speedup
putAll() 20.9 ms 30.0 ms 1.4x
fromArray() 20.9 ms 30.0 ms 1.4x
toArray() 26.4 ms 41.0 ms 1.6x
getAll() 1.1 ms 2.1 ms 1.9x
keys() 12.4 ms 27.6 ms 2.2x
values() 14.2 ms 27.7 ms 1.9x
sumValues() 8.8 ms 22.1 ms 2.5x
populationCount() 0.00 ms 25.0 ms O(1) vs O(n)
deleteRange() 38.1 ms 56.6 ms 1.5x
equals() 11.7 ms 48.7 ms 4.2x
union() Bitset 32.1 ms 5.1 ms 0.2x
intersect() Bitset 13.3 ms 6.0 ms 0.5x
diff() Bitset 13.4 ms 3.4 ms 0.3x
xor() Bitset 25.0 ms 11.7 ms 0.5x
mergeWith() Int→Int 52.0 ms 68.0 ms 1.3x
mergeWith() Str→Int 232.7 ms 247.4 ms 1.1x
forEach() Int→Int 28.7 ms 25.6 ms 0.9x
filter() Int→Int 42.9 ms 40.6 ms 0.9x
map() Int→Int 45.7 ms 46.8 ms ~1.0x
forEach() Str→Int 64.5 ms 60.7 ms 0.9x
filter() Str→Int 118.8 ms 115.4 ms ~1.0x
map() Str→Int 158.4 ms 158.0 ms ~1.0x

O(1) vs O(n) marks a row whose ratio is decided by the complexity classes rather than measured: populationCount() reads libJudy's cached population, the PHP arm counts every element. It scales with n and says nothing about this change. judy-bench.php declines the same comparison for size($lo, $hi) for the same reason.

Core Types (Linux, PHP 8.6)

Speedup = PHP time / Judy time. Bold = Judy faster.

Type Op Judy PHP array Speedup
Bitset Write 20.8 ms 8.1 ms 0.4x
Bitset Read 13.1 ms 5.1 ms 0.4x
Bitset Iter 20.8 ms 3.3 ms 0.2x
Bitset Free 0.01 ms 0.38 ms 42.3x
Int to Int Write 26.1 ms 7.8 ms 0.3x
Int to Int Read 11.4 ms 4.4 ms 0.4x
Int to Int Iter 22.9 ms 3.7 ms 0.2x
Int to Int Free 0.61 ms 0.38 ms 0.6x
Int to Mixed Write 37.2 ms 12.5 ms 0.3x
Int to Mixed Read 14.1 ms 6.7 ms 0.5x
Int to Mixed Iter 22.9 ms 4.3 ms 0.2x
Int to Mixed Free 17.6 ms 5.9 ms 0.3x
String to Int Write 92.9 ms 16.8 ms 0.2x
String to Int Read 40.4 ms 7.6 ms 0.2x
String to Int Iter 57.3 ms 4.9 ms 0.1x
String to Int Free 20.8 ms 1.2 ms 0.1x
String to Int (Adaptive) Write 169.1 ms 16.7 ms 0.1x
String to Int (Adaptive) Read 27.0 ms 7.7 ms 0.3x
String to Int (Adaptive) Iter 71.0 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 49.7 ms 1.2 ms 0.0x
All types detail

Core Types (Linux, PHP 8.6) — All

Speedup = PHP time / Judy time. Bold = Judy faster.

Type Op Judy PHP array Speedup
Bitset Write 20.8 ms 8.1 ms 0.4x
Bitset Read 13.1 ms 5.1 ms 0.4x
Bitset Iter 20.8 ms 3.3 ms 0.2x
Bitset Free 0.01 ms 0.38 ms 42.3x
Int to Int Write 26.1 ms 7.8 ms 0.3x
Int to Int Read 11.4 ms 4.4 ms 0.4x
Int to Int Iter 22.9 ms 3.7 ms 0.2x
Int to Int Free 0.61 ms 0.38 ms 0.6x
Int to Mixed Write 37.2 ms 12.5 ms 0.3x
Int to Mixed Read 14.1 ms 6.7 ms 0.5x
Int to Mixed Iter 22.9 ms 4.3 ms 0.2x
Int to Mixed Free 17.6 ms 5.9 ms 0.3x
Int to Packed Write 48.8 ms 11.7 ms 0.2x
Int to Packed Read 29.6 ms 6.6 ms 0.2x
Int to Packed Iter 38.5 ms 4.3 ms 0.1x
Int to Packed Free 10.7 ms 6.1 ms 0.6x
String to Int Write 92.9 ms 16.8 ms 0.2x
String to Int Read 40.4 ms 7.6 ms 0.2x
String to Int Iter 57.3 ms 4.9 ms 0.1x
String to Int Free 20.8 ms 1.2 ms 0.1x
String to Mixed Write 121.9 ms 21.1 ms 0.2x
String to Mixed Read 44.1 ms 10.6 ms 0.2x
String to Mixed Iter 72.4 ms 7.0 ms 0.1x
String to Mixed Free 74.5 ms 8.4 ms 0.1x
String to Int (Hash) Write 167.7 ms 16.6 ms 0.1x
String to Int (Hash) Read 27.5 ms 7.7 ms 0.3x
String to Int (Hash) Iter 72.0 ms 4.9 ms 0.1x
String to Int (Hash) Free 47.9 ms 1.2 ms 0.0x
String to Mixed (Hash) Write 256.4 ms 21.4 ms 0.1x
String to Mixed (Hash) Read 31.2 ms 10.8 ms 0.3x
String to Mixed (Hash) Iter 100.4 ms 7.0 ms 0.1x
String to Mixed (Hash) Free 140.7 ms 9.3 ms 0.1x
String to Int (Adaptive) Write 169.1 ms 16.7 ms 0.1x
String to Int (Adaptive) Read 27.0 ms 7.7 ms 0.3x
String to Int (Adaptive) Iter 71.0 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 49.7 ms 1.2 ms 0.0x
String to Mixed (Adaptive) Write 260.8 ms 21.2 ms 0.1x
String to Mixed (Adaptive) Read 30.3 ms 11.0 ms 0.4x
String to Mixed (Adaptive) Iter 112.6 ms 7.0 ms 0.1x
String to Mixed (Adaptive) Free 142.3 ms 8.7 ms 0.1x
Cross-version detail (Linux)

Core Types — Linux

Each cell: Judy ms (speedup vs PHP array).

Type Op PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
Bitset Write 17.4 ms (0.5x) 19.7 ms (0.3x) 17.2 ms (0.4x) 16.2 ms (0.4x) 14.5 ms (0.4x) 20.8 ms (0.4x)
Bitset Read 11.1 ms (0.3x) 12.0 ms (0.4x) 10.4 ms (0.4x) 11.0 ms (0.4x) 10.0 ms (0.3x) 13.1 ms (0.4x)
Int to Int Write 25.0 ms (0.3x) 24.9 ms (0.3x) 24.6 ms (0.3x) 22.2 ms (0.3x) 19.9 ms (0.3x) 26.1 ms (0.3x)
Int to Int Read 11.3 ms (0.3x) 12.1 ms (0.4x) 11.5 ms (0.4x) 11.0 ms (0.3x) 10.8 ms (0.3x) 11.4 ms (0.4x)
Int to Mixed Write 34.1 ms (0.4x) 34.3 ms (0.3x) 35.1 ms (0.3x) 32.8 ms (0.3x) 28.3 ms (0.3x) 37.2 ms (0.3x)
Int to Mixed Read 14.2 ms (0.4x) 14.0 ms (0.4x) 14.2 ms (0.4x) 13.3 ms (0.4x) 11.9 ms (0.4x) 14.1 ms (0.5x)
Int to Packed Write 49.5 ms (0.3x) 45.0 ms (0.2x) 47.7 ms (0.2x) 44.5 ms (0.2x) 38.5 ms (0.2x) 48.8 ms (0.2x)
Int to Packed Read 29.8 ms (0.2x) 28.6 ms (0.2x) 29.3 ms (0.2x) 28.3 ms (0.2x) 28.1 ms (0.2x) 29.6 ms (0.2x)
String to Int Write 92.9 ms (0.2x) 92.5 ms (0.2x) 91.3 ms (0.2x) 88.8 ms (0.2x) 89.8 ms (0.2x) 92.9 ms (0.2x)
String to Int Read 37.6 ms (0.2x) 39.9 ms (0.2x) 36.9 ms (0.2x) 34.9 ms (0.2x) 35.8 ms (0.2x) 40.4 ms (0.2x)
String to Mixed Write 116.7 ms (0.2x) 115.6 ms (0.2x) 118.1 ms (0.2x) 108.4 ms (0.2x) 107.4 ms (0.2x) 121.9 ms (0.2x)
String to Mixed Read 40.4 ms (0.2x) 43.5 ms (0.2x) 41.2 ms (0.3x) 38.3 ms (0.3x) 37.4 ms (0.2x) 44.1 ms (0.2x)
String to Int (Hash) Write 167.9 ms (0.1x) 153.0 ms (0.1x) 169.4 ms (0.1x) 152.9 ms (0.1x) 149.4 ms (0.1x) 167.7 ms (0.1x)
String to Int (Hash) Read 25.8 ms (0.3x) 27.1 ms (0.3x) 25.1 ms (0.3x) 24.2 ms (0.3x) 20.9 ms (0.3x) 27.5 ms (0.3x)
String to Mixed (Hash) Write 235.6 ms (0.1x) 228.2 ms (0.1x) 237.7 ms (0.1x) 213.9 ms (0.1x) 214.2 ms (0.1x) 256.4 ms (0.1x)
String to Mixed (Hash) Read 28.0 ms (0.3x) 30.1 ms (0.3x) 28.0 ms (0.4x) 26.7 ms (0.4x) 21.7 ms (0.3x) 31.2 ms (0.3x)
String to Int (Adaptive) Write 165.0 ms (0.1x) 152.8 ms (0.1x) 171.0 ms (0.1x) 150.8 ms (0.1x) 146.1 ms (0.1x) 169.1 ms (0.1x)
String to Int (Adaptive) Read 24.9 ms (0.3x) 26.3 ms (0.3x) 24.6 ms (0.3x) 22.8 ms (0.3x) 19.9 ms (0.3x) 27.0 ms (0.3x)
String to Mixed (Adaptive) Write 242.9 ms (0.1x) 226.8 ms (0.1x) 237.0 ms (0.1x) 212.5 ms (0.1x) 235.1 ms (0.1x) 260.8 ms (0.1x)
String to Mixed (Adaptive) Read 27.3 ms (0.3x) 29.6 ms (0.3x) 27.1 ms (0.4x) 25.9 ms (0.4x) 21.5 ms (0.3x) 30.3 ms (0.4x)

API — Linux

Operation PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
putAll() 20.1 ms (1.5x) 21.3 ms (1.4x) 21.0 ms (1.4x) 19.0 ms (1.5x) 17.7 ms (1.4x) 20.9 ms (1.4x)
fromArray() 20.1 ms (1.5x) 21.9 ms (1.3x) 20.4 ms (1.4x) 20.2 ms (1.4x) 17.7 ms (1.4x) 20.9 ms (1.4x)
toArray() 23.7 ms (1.6x) 23.1 ms (1.5x) 23.9 ms (1.6x) 25.2 ms (1.5x) 20.9 ms (1.5x) 26.4 ms (1.6x)
getAll() 0.99 ms (2.0x) 1.0 ms (2.0x) 1.0 ms (2.0x) 1.1 ms (1.9x) 1.1 ms (1.6x) 1.1 ms (1.9x)
keys() 13.0 ms (2.1x) 11.8 ms (2.1x) 11.2 ms (2.4x) 11.2 ms (2.2x) 10.2 ms (1.9x) 12.4 ms (2.2x)
values() 14.1 ms (1.9x) 12.1 ms (2.1x) 12.0 ms (2.1x) 11.6 ms (2.1x) 10.1 ms (2.0x) 14.2 ms (1.9x)
sumValues() 7.6 ms (2.7x) 8.4 ms (2.6x) 7.7 ms (2.7x) 8.1 ms (2.4x) 6.3 ms (2.6x) 8.8 ms (2.5x)
populationCount() 0.00 ms (234365x) 0.00 ms (245340x) 0.00 ms (218146x) 0.00 ms (206951x) 0.00 ms (176177x) 0.00 ms (250486x)
deleteRange() 39.1 ms (1.7x) 40.5 ms (1.4x) 39.9 ms (1.4x) 44.7 ms (1.4x) 32.3 ms (1.5x) 38.1 ms (1.5x)
equals() 12.0 ms (4.1x) 11.9 ms (5.0x) 11.8 ms (4.1x) 12.0 ms (3.9x) 9.9 ms (4.3x) 11.7 ms (4.2x)
union() Bitset 30.4 ms (0.3x) 32.5 ms (0.1x) 30.7 ms (0.2x) 31.4 ms (0.1x) 26.4 ms (0.2x) 32.1 ms (0.2x)
intersect() Bitset 13.0 ms (0.4x) 14.2 ms (0.4x) 13.1 ms (0.4x) 13.2 ms (0.4x) 12.6 ms (0.5x) 13.3 ms (0.5x)
diff() Bitset 13.2 ms (0.3x) 14.4 ms (0.2x) 12.9 ms (0.3x) 12.9 ms (0.2x) 12.8 ms (0.2x) 13.4 ms (0.3x)
xor() Bitset 26.2 ms (0.6x) 27.2 ms (0.4x) 26.2 ms (0.5x) 25.8 ms (0.4x) 25.4 ms (0.4x) 25.0 ms (0.5x)
mergeWith() Int→Int 51.5 ms (1.3x) 52.4 ms (1.3x) 51.5 ms (1.3x) 49.1 ms (1.3x) 41.9 ms (1.3x) 52.0 ms (1.3x)
mergeWith() Str→Int 234.8 ms (1.1x) 227.6 ms (1.1x) 236.1 ms (1.1x) 223.9 ms (1.1x) 203.2 ms (1.1x) 232.7 ms (1.1x)
forEach() Int→Int 31.6 ms (0.7x) 26.7 ms (0.9x) 27.5 ms (0.9x) 27.5 ms (0.8x) 25.4 ms (0.7x) 28.7 ms (0.9x)
filter() Int→Int 44.9 ms (0.9x) 41.2 ms (~1.0x) 48.4 ms (0.8x) 42.7 ms (0.9x) 38.4 ms (0.8x) 42.9 ms (0.9x)
map() Int→Int 50.8 ms (0.9x) 46.0 ms (~1.0x) 59.4 ms (0.8x) 47.4 ms (~1.0x) 40.1 ms (0.9x) 45.7 ms (~1.0x)
forEach() Str→Int 66.6 ms (0.9x) 65.1 ms (0.9x) 64.2 ms (0.9x) 62.2 ms (0.9x) 61.6 ms (0.9x) 64.5 ms (0.9x)
filter() Str→Int 121.0 ms (0.9x) 116.4 ms (~1.0x) 123.6 ms (0.9x) 112.8 ms (~1.0x) 116.1 ms (0.9x) 118.8 ms (~1.0x)
map() Str→Int 158.1 ms (~1.0x) 155.8 ms (~1.0x) 169.4 ms (0.9x) 155.4 ms (~1.0x) 156.4 ms (~1.0x) 158.4 ms (~1.0x)
Cross-version detail (Windows)

Core Types — Windows

Each cell: Judy ms (speedup vs PHP array).

Type Op PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
Bitset Write 21.5 ms (0.7x) 23.1 ms (0.5x) 21.8 ms (0.5x) 23.2 ms (0.5x) 22.8 ms (0.6x) 27.8 ms (0.5x)
Bitset Read 16.5 ms (0.5x) 17.9 ms (0.5x) 17.0 ms (0.5x) 18.0 ms (0.5x) 17.7 ms (0.5x) 20.0 ms (0.4x)
Int to Int Write 33.9 ms (0.4x) 34.0 ms (0.4x) 37.0 ms (0.3x) 36.4 ms (0.3x) 35.1 ms (0.4x) 38.1 ms (0.3x)
Int to Int Read 18.5 ms (0.4x) 18.8 ms (0.5x) 18.8 ms (0.4x) 18.6 ms (0.5x) 18.7 ms (0.4x) 18.7 ms (0.5x)
Int to Mixed Write 45.9 ms (0.4x) 45.4 ms (0.4x) 47.4 ms (0.3x) 52.9 ms (0.3x) 49.1 ms (0.3x) 52.2 ms (0.3x)
Int to Mixed Read 20.4 ms (0.5x) 20.2 ms (0.5x) 21.3 ms (0.5x) 21.4 ms (0.5x) 19.7 ms (0.4x) 20.2 ms (0.5x)
Int to Packed Write 65.9 ms (0.3x) 60.5 ms (0.3x) 63.1 ms (0.3x) 70.4 ms (0.2x) 67.8 ms (0.2x) 72.3 ms (0.2x)
Int to Packed Read 47.3 ms (0.2x) 48.8 ms (0.2x) 46.9 ms (0.2x) 49.7 ms (0.2x) 48.3 ms (0.2x) 48.4 ms (0.2x)
String to Int Write 141.3 ms (0.2x) 144.1 ms (0.2x) 145.3 ms (0.2x) 141.5 ms (0.2x) 142.3 ms (0.2x) 147.9 ms (0.2x)
String to Int Read 59.0 ms (0.2x) 65.6 ms (0.2x) 63.1 ms (0.2x) 62.4 ms (0.2x) 55.2 ms (0.2x) 61.7 ms (0.2x)
String to Mixed Write 179.5 ms (0.1x) 191.0 ms (0.2x) 187.7 ms (0.2x) 186.1 ms (0.2x) 185.3 ms (0.2x) 196.3 ms (0.2x)
String to Mixed Read 60.9 ms (0.2x) 68.3 ms (0.2x) 65.0 ms (0.2x) 63.8 ms (0.2x) 63.0 ms (0.3x) 63.4 ms (0.2x)
String to Int (Hash) Write 244.9 ms (0.1x) 262.3 ms (0.1x) 254.5 ms (0.1x) 252.9 ms (0.1x) 286.9 ms (0.1x) 265.3 ms (0.1x)
String to Int (Hash) Read 43.5 ms (0.3x) 48.0 ms (0.3x) 44.7 ms (0.3x) 45.1 ms (0.3x) 57.7 ms (0.2x) 48.1 ms (0.2x)
String to Mixed (Hash) Write 337.2 ms (0.1x) 404.0 ms (0.1x) 352.3 ms (0.1x) 355.1 ms (0.1x) 482.6 ms (0.1x) 381.8 ms (0.1x)
String to Mixed (Hash) Read 45.6 ms (0.3x) 51.8 ms (0.3x) 47.3 ms (0.3x) 47.3 ms (0.3x) 62.6 ms (0.3x) 51.0 ms (0.3x)
String to Int (Adaptive) Write 241.0 ms (0.1x) 261.1 ms (0.1x) 248.6 ms (0.1x) 243.5 ms (0.1x) 281.2 ms (0.1x) 262.8 ms (0.1x)
String to Int (Adaptive) Read 42.6 ms (0.3x) 48.8 ms (0.3x) 45.7 ms (0.3x) 44.9 ms (0.3x) 47.8 ms (0.2x) 47.2 ms (0.3x)
String to Mixed (Adaptive) Write 341.5 ms (0.1x) 385.6 ms (0.1x) 368.7 ms (0.1x) 369.8 ms (0.1x) 382.6 ms (0.1x) 407.5 ms (0.1x)
String to Mixed (Adaptive) Read 44.6 ms (0.3x) 52.1 ms (0.3x) 47.1 ms (0.3x) 47.3 ms (0.3x) 65.2 ms (0.3x) 50.0 ms (0.3x)

API — Windows

Operation PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
putAll() 30.5 ms (1.5x) 31.3 ms (1.4x) 31.8 ms (1.5x) 30.2 ms (1.5x) 35.5 ms (1.5x) 33.7 ms (1.4x)
fromArray() 30.4 ms (1.5x) 30.6 ms (1.4x) 31.6 ms (1.5x) 30.6 ms (1.5x) 34.8 ms (1.5x) 33.2 ms (1.4x)
toArray() 28.4 ms (1.6x) 28.9 ms (1.7x) 28.7 ms (1.7x) 27.6 ms (1.8x) 40.1 ms (1.6x) 31.1 ms (1.6x)
getAll() 1.4 ms (2.3x) 1.4 ms (2.3x) 1.4 ms (2.3x) 1.6 ms (2.1x) 2.2 ms (1.9x) 1.9 ms (1.7x)
keys() 16.1 ms (2.3x) 12.6 ms (2.7x) 13.5 ms (2.4x) 12.6 ms (2.5x) 18.3 ms (2.0x) 14.1 ms (2.4x)
values() 16.6 ms (2.2x) 12.9 ms (2.5x) 14.2 ms (2.3x) 13.0 ms (2.4x) 18.2 ms (2.0x) 14.8 ms (2.2x)
sumValues() 7.7 ms (3.4x) 7.1 ms (3.6x) 7.8 ms (3.4x) 7.7 ms (3.5x) 7.4 ms (3.8x) 7.7 ms (3.4x)
populationCount() 0.00 ms (257073x) 0.00 ms (137267x) 0.00 ms (270731x) 0.00 ms (260637x) 0.00 ms (272801x) 0.00 ms (275267x)
deleteRange() 53.7 ms (1.5x) 52.9 ms (1.5x) 54.2 ms (1.5x) 55.8 ms (1.5x) 59.5 ms (1.5x) 58.9 ms (1.4x)
equals() 11.7 ms (5.2x) 10.8 ms (5.9x) 11.9 ms (5.5x) 12.3 ms (5.4x) 13.0 ms (5.5x) 11.7 ms (5.2x)
union() Bitset 34.2 ms (0.4x) 35.9 ms (0.2x) 34.8 ms (0.3x) 36.6 ms (0.2x) 37.5 ms (0.3x) 37.3 ms (0.3x)
intersect() Bitset 15.8 ms (0.4x) 16.4 ms (0.5x) 16.1 ms (0.5x) 17.2 ms (0.4x) 16.8 ms (0.7x) 21.0 ms (0.4x)
diff() Bitset 15.7 ms (0.4x) 16.2 ms (0.3x) 15.6 ms (0.3x) 17.2 ms (0.3x) 16.7 ms (0.4x) 18.9 ms (0.2x)
xor() Bitset 31.5 ms (0.8x) 33.5 ms (0.6x) 31.7 ms (0.6x) 34.7 ms (0.5x) 35.6 ms (0.7x) 37.5 ms (0.5x)
mergeWith() Int→Int 63.0 ms (1.3x) 62.5 ms (1.4x) 66.8 ms (1.4x) 64.8 ms (1.4x) 71.5 ms (1.3x) 69.1 ms (1.3x)
mergeWith() Str→Int 315.0 ms (1.1x) 313.9 ms (1.1x) 310.9 ms (1.1x) 331.6 ms (1.1x) 478.4 ms (~1.0x) 356.8 ms (1.1x)
forEach() Int→Int 30.0 ms (~1.0x) 29.4 ms (~1.0x) 33.6 ms (0.9x) 33.8 ms (0.9x) 33.4 ms (~1.0x) 32.3 ms (0.9x)
filter() Int→Int 47.0 ms (~1.0x) 44.3 ms (1.1x) 45.5 ms (1.1x) 48.9 ms (~1.0x) 52.2 ms (~1.0x) 51.2 ms (~1.0x)
map() Int→Int 54.9 ms (1.1x) 49.9 ms (1.2x) 55.7 ms (1.1x) 55.4 ms (1.1x) 58.2 ms (1.1x) 57.4 ms (1.1x)
forEach() Str→Int 79.2 ms (1.1x) 79.0 ms (~1.0x) 79.7 ms (~1.0x) 82.2 ms (~1.0x) 101.0 ms (0.9x) 82.9 ms (~1.0x)
filter() Str→Int 154.7 ms (~1.0x) 152.2 ms (~1.0x) 150.3 ms (~1.0x) 161.3 ms (~1.0x) 195.9 ms (~1.0x) 166.8 ms (~1.0x)
map() Str→Int 217.7 ms (~1.0x) 213.0 ms (~1.0x) 215.5 ms (~1.0x) 220.9 ms (~1.0x) 257.3 ms (~1.0x) 233.5 ms (~1.0x)
Windows results (PHP 8.6)

Memory (Windows, PHP 8.6)

Peak RSS of a child process building one structure, n = 500,000, minus an empty-process floor. Judy's memory advantage is type- and scale-dependent — see BENCHMARK.md for the curve across sizes on a dedicated host.

Type Judy PHP array Judy vs PHP array
Bitset < 144.0 KB 9.2 MB > 66x smaller
Int to Int 4.4 MB 9.2 MB 2.1x smaller
Int to Mixed 42.5 MB 38.2 MB 1.11x larger
Int to Packed 17.2 MB 38.2 MB 2.2x smaller
String to Int 10.5 MB 37.9 MB 3.6x smaller
String to Mixed 48.7 MB 68.9 MB 1.4x smaller
String to Int (Hash) 32.4 MB 38.0 MB 1.2x smaller
String to Mixed (Hash) 70.6 MB 69.0 MB about the same
String to Int (Adaptive) 32.3 MB 38.0 MB 1.2x smaller
String to Mixed (Adaptive) 70.4 MB 69.0 MB about the same

< marks a structure smaller than this instrument can resolve at n = 500,000: run-to-run movement in the empty-process floor puts the resolution at 144.0 KB. The bound is real, the exact figure is not available at this size — BENCHMARK.md measures the same types up to n = 8M, where they are well clear of the floor.

API Speedups (Windows, PHP 8.6)

Speedup = PHP time / Judy time. Bold = Judy faster.

Operation Judy PHP Speedup
putAll() 33.7 ms 47.0 ms 1.4x
fromArray() 33.2 ms 47.0 ms 1.4x
toArray() 31.1 ms 49.2 ms 1.6x
getAll() 1.9 ms 3.1 ms 1.7x
keys() 14.1 ms 34.1 ms 2.4x
values() 14.8 ms 33.1 ms 2.2x
sumValues() 7.7 ms 26.4 ms 3.4x
populationCount() 0.00 ms 27.5 ms O(1) vs O(n)
deleteRange() 58.9 ms 84.7 ms 1.4x
equals() 11.7 ms 61.4 ms 5.2x
union() Bitset 37.3 ms 9.5 ms 0.3x
intersect() Bitset 21.0 ms 8.2 ms 0.4x
diff() Bitset 18.9 ms 4.4 ms 0.2x
xor() Bitset 37.5 ms 18.6 ms 0.5x
mergeWith() Int→Int 69.1 ms 90.4 ms 1.3x
mergeWith() Str→Int 356.8 ms 386.8 ms 1.1x
forEach() Int→Int 32.3 ms 30.1 ms 0.9x
filter() Int→Int 51.2 ms 50.0 ms ~1.0x
map() Int→Int 57.4 ms 62.6 ms 1.1x
forEach() Str→Int 82.9 ms 80.4 ms ~1.0x
filter() Str→Int 166.8 ms 162.8 ms ~1.0x
map() Str→Int 233.5 ms 228.1 ms ~1.0x

O(1) vs O(n) marks a row whose ratio is decided by the complexity classes rather than measured: populationCount() reads libJudy's cached population, the PHP arm counts every element. It scales with n and says nothing about this change. judy-bench.php declines the same comparison for size($lo, $hi) for the same reason.

Core Types (Windows, PHP 8.6)

Speedup = PHP time / Judy time. Bold = Judy faster.

Type Op Judy PHP array Speedup
Bitset Write 27.8 ms 14.5 ms 0.5x
Bitset Read 20.0 ms 8.5 ms 0.4x
Bitset Iter 27.2 ms 7.7 ms 0.3x
Bitset Free 0.01 ms 0.56 ms 56.1x
Int to Int Write 38.1 ms 12.7 ms 0.3x
Int to Int Read 18.7 ms 8.5 ms 0.5x
Int to Int Iter 25.0 ms 7.2 ms 0.3x
Int to Int Free 0.80 ms 0.54 ms 0.7x
Int to Mixed Write 52.2 ms 16.4 ms 0.3x
Int to Mixed Read 20.2 ms 9.7 ms 0.5x
Int to Mixed Iter 27.0 ms 7.7 ms 0.3x
Int to Mixed Free 19.8 ms 5.3 ms 0.3x
String to Int Write 147.9 ms 27.1 ms 0.2x
String to Int Read 61.7 ms 12.8 ms 0.2x
String to Int Iter 74.6 ms 7.7 ms 0.1x
String to Int Free 31.3 ms 2.1 ms 0.1x
String to Int (Adaptive) Write 262.8 ms 26.0 ms 0.1x
String to Int (Adaptive) Read 47.2 ms 12.4 ms 0.3x
String to Int (Adaptive) Iter 111.8 ms 8.2 ms 0.1x
String to Int (Adaptive) Free 66.6 ms 2.3 ms 0.0x
All types detail

Core Types (Windows, PHP 8.6) — All

Speedup = PHP time / Judy time. Bold = Judy faster.

Type Op Judy PHP array Speedup
Bitset Write 27.8 ms 14.5 ms 0.5x
Bitset Read 20.0 ms 8.5 ms 0.4x
Bitset Iter 27.2 ms 7.7 ms 0.3x
Bitset Free 0.01 ms 0.56 ms 56.1x
Int to Int Write 38.1 ms 12.7 ms 0.3x
Int to Int Read 18.7 ms 8.5 ms 0.5x
Int to Int Iter 25.0 ms 7.2 ms 0.3x
Int to Int Free 0.80 ms 0.54 ms 0.7x
Int to Mixed Write 52.2 ms 16.4 ms 0.3x
Int to Mixed Read 20.2 ms 9.7 ms 0.5x
Int to Mixed Iter 27.0 ms 7.7 ms 0.3x
Int to Mixed Free 19.8 ms 5.3 ms 0.3x
Int to Packed Write 72.3 ms 16.2 ms 0.2x
Int to Packed Read 48.4 ms 9.6 ms 0.2x
Int to Packed Iter 54.6 ms 7.8 ms 0.1x
Int to Packed Free 13.9 ms 5.7 ms 0.4x
String to Int Write 147.9 ms 27.1 ms 0.2x
String to Int Read 61.7 ms 12.8 ms 0.2x
String to Int Iter 74.6 ms 7.7 ms 0.1x
String to Int Free 31.3 ms 2.1 ms 0.1x
String to Mixed Write 196.3 ms 29.8 ms 0.2x
String to Mixed Read 63.4 ms 14.4 ms 0.2x
String to Mixed Iter 90.2 ms 9.1 ms 0.1x
String to Mixed Free 100.3 ms 8.8 ms 0.1x
String to Int (Hash) Write 265.3 ms 26.4 ms 0.1x
String to Int (Hash) Read 48.1 ms 12.0 ms 0.2x
String to Int (Hash) Iter 114.7 ms 7.7 ms 0.1x
String to Int (Hash) Free 65.4 ms 2.1 ms 0.0x
String to Mixed (Hash) Write 381.8 ms 29.6 ms 0.1x
String to Mixed (Hash) Read 51.0 ms 13.9 ms 0.3x
String to Mixed (Hash) Iter 145.8 ms 9.0 ms 0.1x
String to Mixed (Hash) Free 194.7 ms 8.7 ms 0.0x
String to Int (Adaptive) Write 262.8 ms 26.0 ms 0.1x
String to Int (Adaptive) Read 47.2 ms 12.4 ms 0.3x
String to Int (Adaptive) Iter 111.8 ms 8.2 ms 0.1x
String to Int (Adaptive) Free 66.6 ms 2.3 ms 0.0x
String to Mixed (Adaptive) Write 407.5 ms 30.0 ms 0.1x
String to Mixed (Adaptive) Read 50.0 ms 13.7 ms 0.3x
String to Mixed (Adaptive) Iter 158.4 ms 9.7 ms 0.1x
String to Mixed (Adaptive) Free 210.3 ms 9.8 ms 0.0x

Release Comparison (2.6.0 → 2.7.1)

Interleaved A/B comparison against v2.6.0 (pie install) on the same CI runner: 5 benchmark groups × 5 rounds × 2 arms, 300,000 elements, median of 3 per child. Each row is the median of the paired current/baseline ratios with a 95% percentile-bootstrap CI; it is flagged only when the whole drift-adjusted CI clears ±10%. Advisory — benchmarks never gate a merge.

Run-wide median delta +0.64% (PHP-array control +0.28%); runner common-mode +0.28% divided out of every row.

Summary: ⚠️ 3 regressions, 97 unchanged

Benchmark Baseline Current Delta 95% CI Status
adv.filter.int_to_int 25.7 ms 27.1 ms +2.9% [-2.5%, +8.4%] ~same
adv.filter.string_to_int 66.0 ms 69.2 ms +4.1% [+0.3%, +7.6%] ~same
adv.forEach.int_to_int 16.3 ms 16.6 ms +5.7% [-6.6%, +8.2%] ~same
adv.forEach.string_to_int 37.7 ms 35.9 ms -4.0% [-6.8%, +2.5%] ~same
adv.map.int_to_int 28.5 ms 27.9 ms +1.3% [-5.8%, +1.9%] ~same
adv.map.string_to_int 90.6 ms 93.5 ms +1.4% [+0.9%, +5.0%] ~same
adv.optiter.foreach.string_to_int_adaptive.default 53.8 ms 53.3 ms -3.4% [-4.6%, +0.9%] ~same
adv.optiter.foreach.string_to_int_adaptive.optimized 37.1 ms 36.2 ms -1.2% [-9.9%, +6.5%] ~same
adv.optiter.foreach.string_to_int_hash.default 43.4 ms 44.0 ms +1.1% [-0.6%, +1.9%] ~same
adv.optiter.foreach.string_to_int_hash.optimized 30.8 ms 30.9 ms -0.3% [-9.4%, +4.2%] ~same
adv.optiter.increment.string_to_int_hash.default 30.3 ms 29.9 ms -1.8% [-4.4%, +1.2%] ~same
adv.optiter.increment.string_to_int_hash.optimized 35.5 ms 35.3 ms -0.8% [-9.0%, +6.7%] ~same
adv.optiter.overwrite.string_to_int_adaptive.default 45.9 ms 46.4 ms +1.0% [-8.6%, +5.9%] ~same
adv.optiter.overwrite.string_to_int_adaptive.optimized 44.1 ms 43.3 ms -0.4% [-3.9%, +3.6%] ~same
adv.optiter.overwrite.string_to_int_hash.default 31.1 ms 29.5 ms -5.5% [-6.9%, -3.4%] ~same
adv.optiter.overwrite.string_to_int_hash.optimized 35.1 ms 33.4 ms -3.4% [-6.9%, -1.3%] ~same
adv.optiter.ratio.foreach.string_to_int_adaptive 69.7 ms 70.3 ms +1.4% [-5.9%, +6.7%] ~same
adv.optiter.ratio.foreach.string_to_int_hash 71.8 ms 71.0 ms -1.3% [-10.7%, +2.8%] ~same
adv.optiter.ratio.increment.string_to_int_hash 116.8 ms 120.1 ms -0.2% [-5.1%, +5.2%] ~same
adv.optiter.ratio.overwrite.string_to_int_adaptive 94.2 ms 94.4 ms -0.3% [-6.2%, +8.7%] ~same
adv.optiter.ratio.overwrite.string_to_int_hash 113.1 ms 115.3 ms +0.7% [-3.6%, +4.2%] ~same
adv.optiter.ratio.toArray.string_to_int_adaptive 68.4 ms 68.8 ms +0.3% [-6.4%, +6.8%] ~same
adv.optiter.ratio.toArray.string_to_int_hash 62.7 ms 59.9 ms -5.2% [-10.6%, +2.3%] ~same
adv.optiter.ratio.values.string_to_int_adaptive 62.0 ms 62.0 ms -4.1% [-5.3%, +2.2%] ~same
adv.optiter.ratio.values.string_to_int_hash 59.2 ms 59.2 ms -0.7% [-7.4%, +4.8%] ~same
adv.optiter.toArray.string_to_int_adaptive.default 62.9 ms 62.6 ms +0.5% [-3.6%, +1.3%] ~same
adv.optiter.toArray.string_to_int_adaptive.optimized 42.6 ms 43.3 ms +1.6% [-5.7%, +3.2%] ~same
adv.optiter.toArray.string_to_int_hash.default 49.6 ms 51.1 ms +0.8% [-2.3%, +4.0%] ~same
adv.optiter.toArray.string_to_int_hash.optimized 31.4 ms 30.6 ms -2.3% [-6.7%, +0.2%] ~same
adv.optiter.values.string_to_int_adaptive.default 47.0 ms 47.1 ms -2.0% [-4.3%, +7.9%] ~same
adv.optiter.values.string_to_int_adaptive.optimized 29.8 ms 28.4 ms -6.9% [-8.2%, +7.9%] ~same
adv.optiter.values.string_to_int_hash.default 37.6 ms 38.6 ms +2.1% [+0.4%, +5.1%] ~same
adv.optiter.values.string_to_int_hash.optimized 22.5 ms 22.8 ms +1.6% [-6.7%, +5.6%] ~same
api.deleteRange.int_to_int 23.0 ms 26.2 ms +10.0% [+8.7%, +25.7%] ~same
api.equals.int_to_int 6.5 ms 7.3 ms +11.9% [+7.3%, +17.0%] ~same
api.fromArray.int_to_int 13.7 ms 13.1 ms -4.8% [-11.1%, +1.7%] ~same
api.getAll.int_to_int 0.52 ms 0.51 ms -2.3% [-11.8%, +8.0%] ~same
api.increment.int_to_int 13.7 ms 14.8 ms +3.3% [-3.0%, +9.8%] ~same
api.keys.int_to_int 6.1 ms 6.9 ms +14.6% [+3.1%, +27.9%] ~same
api.mergeWith.int_to_int 30.2 ms 30.6 ms +0.2% [-6.6%, +4.8%] ~same
api.mergeWith.string_to_int 129.4 ms 135.2 ms +2.5% [-0.8%, +5.6%] ~same
api.populationCount.int_to_int 0.00 ms 0.00 ms -0.3% [-0.3%, -0.3%] ~same
api.putAll.int_to_int 13.2 ms 13.0 ms -1.4% [-7.6%, +1.8%] ~same
api.range.keys.int_to_int 0.53 ms 0.63 ms +20.3% [+0.5%, +24.5%] ~same
api.range.size.string_to_int 2.2 ms 2.3 ms -0.3% [-5.7%, +9.1%] ~same
api.range.size1000.int_to_int 0.66 ms 0.62 ms -6.5% [-14.1%, +1.2%] ~same
api.setop.diff.bitset 7.3 ms 7.3 ms -1.2% [-8.6%, +5.8%] ~same
api.setop.diff.int_to_int 10.0 ms 10.4 ms +3.6% [-0.9%, +9.7%] ~same
api.setop.diff.string_to_int 58.0 ms 60.8 ms +5.3% [+0.1%, +8.4%] ~same
api.setop.intersect.bitset 7.7 ms 7.8 ms +1.3% [-2.8%, +8.6%] ~same
api.setop.intersect.int_to_int 10.1 ms 10.8 ms +5.8% [+3.0%, +14.0%] ~same
api.setop.intersect.string_to_int 66.5 ms 68.8 ms +3.2% [-0.8%, +7.3%] ~same
api.setop.union.bitset 18.5 ms 18.5 ms +0.3% [-0.9%, +8.0%] ~same
api.setop.union.int_to_int 26.1 ms 26.3 ms +0.6% [-6.0%, +5.3%] ~same
api.setop.union.string_to_int 151.2 ms 154.5 ms +0.4% [-1.1%, +5.3%] ~same
api.setop.xor.bitset 14.9 ms 15.3 ms +1.3% [-0.9%, +5.7%] ~same
api.setop.xor.int_to_int 20.2 ms 20.9 ms +1.3% [-3.6%, +4.6%] ~same
api.sumValues.int_to_int 4.1 ms 5.0 ms +15.0% [+10.1%, +33.0%] ⚠️ SLOWER
api.toArray.int_to_int 14.1 ms 16.2 ms +13.8% [+12.9%, +19.6%] ⚠️ SLOWER
api.values.int_to_int 6.4 ms 7.0 ms +11.6% [+9.2%, +32.9%] ~same
core.bitset.free 0.00 ms 0.01 ms +15.6% [+8.6%, +22.9%] ~same
core.bitset.iter 10.8 ms 11.2 ms +3.0% [+1.8%, +10.1%] ~same
core.bitset.read 6.6 ms 6.5 ms -2.1% [-5.1%, -0.4%] ~same
core.bitset.write 10.2 ms 10.3 ms +2.5% [-3.2%, +4.4%] ~same
core.int_to_int.free 0.29 ms 0.29 ms +2.3% [-8.2%, +6.0%] ~same
core.int_to_int.iter 11.7 ms 12.1 ms +3.0% [+1.1%, +6.8%] ~same
core.int_to_int.read 6.8 ms 6.9 ms +0.7% [-6.7%, +5.2%] ~same
core.int_to_int.write 14.6 ms 13.8 ms -5.4% [-6.7%, -4.4%] ~same
core.int_to_mixed.free 7.6 ms 8.3 ms +8.8% [+6.4%, +12.4%] ~same
core.int_to_mixed.iter 12.5 ms 13.0 ms +3.7% [+2.6%, +7.6%] ~same
core.int_to_mixed.read 8.3 ms 8.2 ms -0.6% [-3.0%, +10.5%] ~same
core.int_to_mixed.write 19.8 ms 19.7 ms -0.5% [-4.0%, +1.4%] ~same
core.int_to_packed.free 5.2 ms 5.8 ms +12.2% [+11.8%, +13.2%] ⚠️ SLOWER
core.int_to_packed.iter 22.9 ms 23.3 ms +1.9% [-3.0%, +2.4%] ~same
core.int_to_packed.read 18.0 ms 17.8 ms -2.4% [-5.4%, +1.1%] ~same
core.int_to_packed.write 27.0 ms 27.2 ms +0.6% [-4.6%, +1.8%] ~same
core.string_to_int.free 10.0 ms 10.0 ms -0.4% [-5.4%, +1.7%] ~same
core.string_to_int.iter 32.7 ms 32.6 ms -1.0% [-1.8%, +2.5%] ~same
core.string_to_int.read 20.9 ms 21.1 ms -0.5% [-4.8%, +2.0%] ~same
core.string_to_int.write 51.3 ms 51.5 ms -1.8% [-5.0%, +1.8%] ~same
core.string_to_int_adaptive.free 19.7 ms 20.3 ms +3.2% [+2.4%, +11.6%] ~same
core.string_to_int_adaptive.iter 39.1 ms 38.5 ms -1.7% [-5.7%, +0.8%] ~same
core.string_to_int_adaptive.read 14.3 ms 14.4 ms -0.7% [-7.7%, +3.9%] ~same
core.string_to_int_adaptive.write 85.7 ms 86.6 ms +0.2% [-2.2%, +4.4%] ~same
core.string_to_int_hash.free 19.4 ms 20.4 ms +4.4% [+0.8%, +7.5%] ~same
core.string_to_int_hash.iter 39.9 ms 38.8 ms -4.2% [-8.8%, +3.1%] ~same
core.string_to_int_hash.read 14.6 ms 15.1 ms +3.6% [+1.2%, +5.5%] ~same
core.string_to_int_hash.write 86.1 ms 85.3 ms +0.5% [-4.8%, +3.2%] ~same
core.string_to_mixed.free 32.9 ms 33.0 ms -2.0% [-4.1%, +2.2%] ~same
core.string_to_mixed.iter 33.6 ms 33.7 ms -1.4% [-3.6%, +5.3%] ~same
core.string_to_mixed.read 23.3 ms 23.4 ms -1.1% [-5.1%, +6.6%] ~same
core.string_to_mixed.write 61.8 ms 61.8 ms -0.1% [-2.2%, +1.5%] ~same
core.string_to_mixed_adaptive.free 61.2 ms 62.1 ms +1.5% [-1.3%, +3.8%] ~same
core.string_to_mixed_adaptive.iter 44.6 ms 44.7 ms -2.6% [-4.4%, +2.9%] ~same
core.string_to_mixed_adaptive.read 15.7 ms 15.9 ms 0.0% [-0.3%, +6.3%] ~same
core.string_to_mixed_adaptive.write 120.1 ms 117.7 ms -3.1% [-4.0%, +0.7%] ~same
core.string_to_mixed_hash.free 60.4 ms 61.0 ms +0.4% [-0.3%, +2.4%] ~same
core.string_to_mixed_hash.iter 47.3 ms 45.4 ms -2.2% [-9.3%, +0.9%] ~same
core.string_to_mixed_hash.read 15.6 ms 16.4 ms +3.0% [+1.1%, +4.7%] ~same
core.string_to_mixed_hash.write 121.7 ms 117.2 ms -3.4% [-5.8%, -0.8%] ~same

Memory Comparison

Memory: 0 regressions, 10 unchanged

Type Baseline Current Delta Status
Bitset 160 B 160 B 0.0% ~same
Int to Int 160 B 160 B 0.0% ~same
Int to Mixed 4.6 MB 4.6 MB 0.0% ~same
Int to Packed 8.0 MB 8.0 MB 0.0% ~same
String to Int 64.2 KB 64.2 KB 0.0% ~same
String to Int (Adaptive) 64.2 KB 64.2 KB 0.0% ~same
String to Int (Hash) 64.2 KB 64.2 KB 0.0% ~same
String to Mixed 4.6 MB 4.6 MB 0.0% ~same
String to Mixed (Adaptive) 4.6 MB 4.6 MB 0.0% ~same
String to Mixed (Hash) 4.6 MB 4.6 MB 0.0% ~same
Full benchmark output

Linux — PHP 8.6

══════════════════════════════════════════════════════════════════════════════════════════
  php-judy Benchmark Suite — 500,000 elements, 7 iterations (median)
  PHP 8.6.0-dev | Judy ext 2.7.1 | Linux x86_64
  Suite: all | Groups: core.int,core.str,api.batch,api.setops,adv.iter,adv.sso
  memory_limit: -1
  Memory: peak RSS of a child process, empty-process floor subtracted, median of 3
══════════════════════════════════════════════════════════════════════════════════════════

── Core: Integer-Keyed Types ────────────────────────────────────────────────

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  BITSET                           20.79         13.06         20.78          0.01    < 400.0 KB
    PHP array                       8.10          5.12          3.29          0.38        8.0 MB
  INT_TO_INT                       26.11         11.45         22.89          0.61        4.3 MB
    PHP array                       7.79          4.40          3.71          0.38        8.1 MB
  INT_TO_MIXED                     37.17         14.14         22.90         17.63       40.2 MB
    PHP array                      12.51          6.70          4.26          5.88       37.9 MB
  INT_TO_PACKED                    48.79         29.58         38.48         10.66       16.3 MB
    PHP array                      11.73          6.62          4.25          6.06       38.0 MB

── Core: String-Keyed Types ────────────────────────────────────────────────

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                    92.88         40.39         57.32         20.81       10.2 MB
    PHP array                      16.83          7.60          4.86          1.16       37.9 MB
  STRING_TO_MIXED                 121.85         44.07         72.44         74.50       46.3 MB
    PHP array                      21.10         10.63          6.99          8.43       68.0 MB
  STR_TO_INT_HASH                 167.68         27.54         71.97         47.86       31.4 MB
    PHP array                      16.61          7.70          4.85          1.19       38.1 MB
  STR_TO_MIX_HASH                 256.45         31.18        100.44        140.68       67.4 MB
    PHP array                      21.40         10.75          6.98          9.34       68.1 MB
  STR_INT_ADAPTIVE                169.05         27.00         71.04         49.66       31.4 MB
    PHP array                      16.71          7.69          4.86          1.23       37.9 MB
  STR_MIX_ADAPTIVE                260.83         30.32        112.58        142.29       67.4 MB
    PHP array                      21.18         11.04          7.04          8.65       68.0 MB

── API: Batch Operations (INT_TO_INT) ────────────────────────────────────────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               20.92         30.02        1.4x
  fromArray()                            20.92         30.02        1.4x
  toArray()                              26.41         40.98        1.6x
  getAll(50100 keys)                      1.11          2.10        1.9x
  increment(500000 ops/1000 keys)         24.99         12.19        0.5x

── API: Native Methods (keys, values, sum, popCount, deleteRange, equals) ────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      12.42         27.57        2.2x
  values() INT_TO_INT                    14.24         27.70        1.9x
  keys($lo,$hi) INT_TO_INT                1.06         28.61       26.9x
    vs slice()->keys()                    3.02             -        2.8x
  size($lo,$hi) INT x1000                 1.05             -        O(1)
  size($lo,$hi) STRING_TO_INT             4.21          5.53        1.3x
  sumValues() INT_TO_INT                  8.82         22.09        2.5x
  populationCount() INT_TO_INT            0.00         25.05   278318.0x
  deleteRange() INT_TO_INT mid50%         38.05         56.58        1.5x
  equals() INT_TO_INT                    11.71         48.66        4.2x

── API: Set Operations (BITSET, 50% overlap) ────────────────────────────────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         32.11          5.08        0.2x
  intersect() BITSET                     13.27          5.97        0.5x
  diff() BITSET                          13.37          3.38        0.3x
  xor() BITSET                           25.00         11.67        0.5x
  union() INT_TO_INT                     44.22         88.19        2.0x
  intersect() INT_TO_INT                 17.01         41.85        2.5x
  diff() INT_TO_INT                      16.80         42.79        2.5x
  xor() INT_TO_INT                       33.45         86.10        2.6x
  union() STRING_TO_INT                 280.92        312.15        1.1x
  intersect() STRING_TO_INT             122.58        142.56        1.2x
  diff() STRING_TO_INT                  117.84        136.67        1.2x
  mergeWith() INT_TO_INT                 51.96         68.03        1.3x
  mergeWith() STRING_TO_INT             232.73        247.41        1.1x

── Advanced: C-Level forEach/filter/map ──────────────────────────────────────

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   28.71         25.57        0.9x
  forEach() STRING_TO_INT                64.46         60.74        0.9x
  filter() INT_TO_INT (50% pass)         42.92         40.55        0.9x
  filter() STRING_TO_INT (50% pass)        118.83        115.43        1.0x
  map() INT_TO_INT (*2)                  45.68         46.82        1.0x
  map() STRING_TO_INT (*2)              158.42        157.96        1.0x

── Advanced: optimizeIteration trade (opt-in key-index payload mirror) ──────

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    57.28         34.48         60.2%
  values() HASH kl16                     53.93         26.47         49.1%
  toArray() HASH kl16                    71.37         34.79         48.7%
  overwrite() HASH kl16                  45.36         48.05        105.9%
  increment() HASH kl16                  46.96         47.52        101.2%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40                71.88         46.97         65.3%
  values() ADAPTIVE kl40                 65.01         37.36         57.5%
  toArray() ADAPTIVE kl40                78.23         52.06         66.5%
  overwrite() ADAPTIVE kl40              61.91         57.40         92.7%
    ADAPTIVE kl40: honoured?      yes
  skipped — increment() ADAPTIVE kl40: Judy::increment() is only supported for INT_TO_INT, STRING_TO_INT and STRING_TO_INT_HASH types

  Ratios are optimized as a percentage of default, measured inside this
  build; below 100 is the traversal win, above 100 the write price. Lower
  is better on every row, so a lost win reads as a rise.

── Advanced: Adaptive SSO — short keys (<8 bytes) via JudyL ─────────────────

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (short keys)          78.58 ms      122.53 ms      114.42 ms       1.6x
  read (random access)         21.89 ms       16.35 ms       17.00 ms       1.3x
  foreach (iterate all)        50.44 ms       75.87 ms       77.85 ms       1.5x

── Advanced: Adaptive SSO — long keys (>=8 bytes) fallback to JudyHS ────────

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (long keys)          122.17 ms      219.89 ms      219.49 ms       1.8x
  read (random access)         39.80 ms       32.50 ms       33.37 ms       1.2x
  foreach (iterate all)        95.59 ms      103.58 ms      102.77 ms       1.1x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-08-23 16:18:55
  All timings: median of 7 iterations via hrtime(true), 1 warmup run
  Memory: peak RSS of a child process building one structure of 500,000 elements,
          minus an empty-process floor. Measured with getrusage(), not
          memory_get_usage(), for the reason above. Judy's memory advantage is
          scale- and type-dependent — see BENCHMARK.md for the curve.
══════════════════════════════════════════════════════════════════════════════════════════

  JSON results written to: bench.json

Windows — PHP 8.6

══════════════════════════════════════════════════════════════════════════════════════════
  php-judy Benchmark Suite — 500,000 elements, 7 iterations (median)
  PHP 8.6.0beta1 | Judy ext 2.7.1 | WINNT AMD64
  Suite: all | Groups: core.int,core.str,api.batch,api.setops,adv.iter,adv.sso
  memory_limit: 4G
  Memory: peak RSS of a child process, empty-process floor subtracted, median of 3
══════════════════════════════════════════════════════════════════════════════════════════

── Core: Integer-Keyed Types ────────────────────────────────────────────────

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  BITSET                           27.80         19.96         27.25          0.01    < 144.0 KB
    PHP array                      14.54          8.50          7.71          0.56        9.2 MB
  INT_TO_INT                       38.07         18.70         24.97          0.80        4.4 MB
    PHP array                      12.70          8.49          7.22          0.54        9.2 MB
  INT_TO_MIXED                     52.22         20.24         27.03         19.80       42.5 MB
    PHP array                      16.44          9.75          7.74          5.34       38.2 MB
  INT_TO_PACKED                    72.33         48.41         54.58         13.90       17.2 MB
    PHP array                      16.20          9.58          7.77          5.69       38.2 MB

── Core: String-Keyed Types ────────────────────────────────────────────────

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                   147.91         61.69         74.64         31.34       10.5 MB
    PHP array                      27.08         12.75          7.67          2.13       37.9 MB
  STRING_TO_MIXED                 196.26         63.38         90.17        100.30       48.7 MB
    PHP array                      29.76         14.37          9.05          8.83       68.9 MB
  STR_TO_INT_HASH                 265.32         48.09        114.67         65.40       32.4 MB
    PHP array                      26.37         12.00          7.71          2.13       38.0 MB
  STR_TO_MIX_HASH                 381.76         50.96        145.77        194.72       70.6 MB
    PHP array                      29.60         13.86          8.96          8.74       69.0 MB
  STR_INT_ADAPTIVE                262.80         47.24        111.77         66.63       32.3 MB
    PHP array                      25.97         12.35          8.23          2.27       38.0 MB
  STR_MIX_ADAPTIVE                407.45         49.98        158.45        210.32       70.4 MB
    PHP array                      30.00         13.69          9.69          9.75       69.0 MB

── API: Batch Operations (INT_TO_INT) ────────────────────────────────────────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               33.70         46.97        1.4x
  fromArray()                            33.21         46.97        1.4x
  toArray()                              31.15         49.23        1.6x
  getAll(50100 keys)                      1.86          3.14        1.7x
  increment(500000 ops/1000 keys)         35.72         23.94        0.7x

── API: Native Methods (keys, values, sum, popCount, deleteRange, equals) ────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      14.06         34.12        2.4x
  values() INT_TO_INT                    14.80         33.13        2.2x
  keys($lo,$hi) INT_TO_INT                1.01         33.95       33.6x
    vs slice()->keys()                    3.61             -        3.6x
  size($lo,$hi) INT x1000                 0.73             -        O(1)
  size($lo,$hi) STRING_TO_INT             5.37          6.93        1.3x
  sumValues() INT_TO_INT                  7.70         26.41        3.4x
  populationCount() INT_TO_INT            0.00         27.53   275267.0x
  deleteRange() INT_TO_INT mid50%         58.91         84.66        1.4x
  equals() INT_TO_INT                    11.72         61.36        5.2x

── API: Set Operations (BITSET, 50% overlap) ────────────────────────────────

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         37.34          9.54        0.3x
  intersect() BITSET                     20.98          8.21        0.4x
  diff() BITSET                          18.90          4.36        0.2x
  xor() BITSET                           37.51         18.64        0.5x
  union() INT_TO_INT                     58.40        112.78        1.9x
  intersect() INT_TO_INT                 21.78         54.25        2.5x
  diff() INT_TO_INT                      21.64         57.35        2.7x
  xor() INT_TO_INT                       43.69        114.70        2.6x
  union() STRING_TO_INT                 404.02        465.71        1.2x
  intersect() STRING_TO_INT             186.69        220.55        1.2x
  diff() STRING_TO_INT                  168.64        204.63        1.2x
  mergeWith() INT_TO_INT                 69.13         90.42        1.3x
  mergeWith() STRING_TO_INT             356.76        386.84        1.1x

── Advanced: C-Level forEach/filter/map ──────────────────────────────────────

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   32.28         30.06        0.9x
  forEach() STRING_TO_INT                82.94         80.43        1.0x
  filter() INT_TO_INT (50% pass)         51.23         49.97        1.0x
  filter() STRING_TO_INT (50% pass)        166.82        162.82        1.0x
  map() INT_TO_INT (*2)                  57.41         62.55        1.1x
  map() STRING_TO_INT (*2)              233.50        228.09        1.0x

── Advanced: optimizeIteration trade (opt-in key-index payload mirror) ──────

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    96.61         61.86         64.0%
  values() HASH kl16                     87.36         49.29         56.4%
  toArray() HASH kl16                   100.36         62.50         62.3%
  overwrite() HASH kl16                  72.69         82.63        113.7%
  increment() HASH kl16                  76.80         82.38        107.3%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40               118.87         55.46         46.7%
  values() ADAPTIVE kl40                106.05         43.25         40.8%
  toArray() ADAPTIVE kl40               131.82         63.16         47.9%
  overwrite() ADAPTIVE kl40              90.30        105.73        117.1%
    ADAPTIVE kl40: honoured?      yes
  skipped — increment() ADAPTIVE kl40: Judy::increment() is only supported for INT_TO_INT, STRING_TO_INT and STRING_TO_INT_HASH types

  Ratios are optimized as a percentage of default, measured inside this
  build; below 100 is the traversal win, above 100 the write price. Lower
  is better on every row, so a lost win reads as a rise.

── Advanced: Adaptive SSO — short keys (<8 bytes) via JudyL ─────────────────

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (short keys)         109.37 ms      211.00 ms      195.77 ms       1.9x
  read (random access)         39.22 ms       38.00 ms       32.33 ms       1.2x
  foreach (iterate all)        66.65 ms      159.65 ms      162.10 ms       2.4x

── Advanced: Adaptive SSO — long keys (>=8 bytes) fallback to JudyHS ────────

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (long keys)          190.89 ms      333.43 ms      334.15 ms       1.7x
  read (random access)         63.32 ms       55.42 ms       56.01 ms       1.1x
  foreach (iterate all)       122.95 ms      187.07 ms      186.90 ms       1.5x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-08-23 16:22:21
  All timings: median of 7 iterations via hrtime(true), 1 warmup run
  Memory: peak RSS of a child process building one structure of 500,000 elements,
          minus an empty-process floor. Measured with getrusage(), not
          memory_get_usage(), for the reason above. Judy's memory advantage is
          scale- and type-dependent — see BENCHMARK.md for the curve.
══════════════════════════════════════════════════════════════════════════════════════════

  JSON results written to: bench.json

@github-actions

Copy link
Copy Markdown

Benchmark Gate — PASS

Relative regression detection. Every quantity below is a ratio of two arms measured in the same interleaved rounds on the same runner, compared against the ratio stored in baselines/arm-ratios.json. Absolute times are never compared across runs: on a shared runner they mean nothing, and this project has the false-regression history to prove it (#87, #153).

Platforms measured

platform tier toolchain arms rebuild control hygiene gate
linux-glibc-x86_64 ci-relative cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 / PHP 8.4.24 B x1 C x2 S x2 yes clean PASS
linux-musl-x86_64 ci-relative cc (Alpine 15.2.0) 15.2.0 / musl / PHP 8.4.24 C x2 S x2 yes clean PASS
macos-arm64 ci-relative Apple clang version 21.0.0 (clang-2100.1.1.101) / PHP 8.4.24 B x1 C x2 S x2 yes clean PASS

Controls

The PHP-array control sees runner drift. The C-vs-C rebuild control — two independently linked builds of identical source — is the one that matters, because it shares Judy's memory-access character (pointer-chasing, DRAM-bound) and can therefore see the LLC and memory-bandwidth contention the array control is structurally blind to. Its scatter is each run's own noise floor and raises that run's threshold when it exceeds the stored one.

platform PHP-array control C-vs-C control C-vs-C scatter
linux-glibc-x86_64 +0.27% (scatter 2.14%) +0.09% 2.67%
linux-musl-x86_64 +0.13% (scatter 1.15%) +0.04% 1.66%
macos-arm64 +1.05% (scatter 11.21%) +0.41% 11.36%

Thresholds applied

platform axis applied offline floor this run's control governed by
linux-glibc-x86_64 timing.s_over_c 18.50% 18.50% 2.67% the offline derived floor
linux-glibc-x86_64 timing.a_over_c 97.50% 97.50% 2.67% the offline derived floor
linux-glibc-x86_64 memory.a_over_c 6.00% 6.00% n/a the offline derived floor
linux-musl-x86_64 timing.s_over_c 11.00% 11.00% 1.66% the offline derived floor
linux-musl-x86_64 timing.a_over_c 46.50% 46.50% 1.66% the offline derived floor
linux-musl-x86_64 memory.a_over_c 4.00% 4.00% n/a the offline derived floor
macos-arm64 timing.s_over_c 16.00% 16.00% 11.36% the offline derived floor
macos-arm64 timing.a_over_c 16.00% 16.00% 11.36% the offline derived floor
macos-arm64 memory.a_over_c 10.00% 10.00% n/a the offline derived floor

S → C — the vendored libJudy patches (below 1.00 means the patches are faster)

benchmark linux-glibc-x86_64 linux-musl-x86_64 macos-arm64
api.mergeWith.int_to_int 0.772 0.794 0.819
api.mergeWith.string_to_int 0.831 0.890 0.909
api.setop.diff.bitset 0.719 0.758 0.910
api.setop.diff.int_to_int 0.768 0.801 0.867
api.setop.diff.string_to_int 0.797 0.851 0.882
api.setop.intersect.bitset 0.731 0.738 0.903
api.setop.intersect.int_to_int 0.777 0.794 0.785
api.setop.intersect.string_to_int 0.826 0.883 0.904
api.setop.union.bitset 0.783 0.819 0.895
api.setop.union.int_to_int 0.770 0.761 0.818
api.setop.union.string_to_int 0.824 0.900 0.870
api.setop.xor.bitset 0.688 0.749 0.912
api.setop.xor.int_to_int 0.756 0.773 0.801
core.bitset.iter 0.919 0.917 0.817
core.bitset.read 0.875 0.765 0.858
core.bitset.write 0.919 0.906 0.838
core.int_to_int.free 1.010
core.int_to_int.iter 1.020 0.957 0.831
core.int_to_int.read 0.830 0.725 0.794
core.int_to_int.write 0.787 0.791 0.807
core.int_to_mixed.free 1.009 0.933 0.906
core.int_to_mixed.iter 1.006 0.954 0.921
core.int_to_mixed.read 0.783 0.768 0.877
core.int_to_mixed.write 0.829 0.828 0.857
core.int_to_packed.free 0.989 0.922 0.888
core.int_to_packed.iter 1.013 0.969 0.956
core.int_to_packed.read 0.886 0.852 0.891
core.int_to_packed.write 0.890 0.888 0.919
core.string_to_int.free 0.900 0.936 1.023
core.string_to_int.iter 0.884 0.892 1.067
core.string_to_int.read 0.863 0.859 1.036
core.string_to_int.write 0.853 0.898 0.959
core.string_to_int_adaptive.free 0.925 0.927 0.963
core.string_to_int_adaptive.iter 0.875 0.853 0.950
core.string_to_int_adaptive.read 0.872 0.865 0.969
core.string_to_int_adaptive.write 0.883 0.912 1.021
core.string_to_int_hash.free 0.905 0.928 1.008
core.string_to_int_hash.iter 0.870 0.858 0.977
core.string_to_int_hash.read 0.881 0.871 0.924
core.string_to_int_hash.write 0.881 0.910 0.919
core.string_to_mixed.free 0.897 0.879 0.939
core.string_to_mixed.iter 0.911 0.908 1.007
core.string_to_mixed.read 0.879 0.868 0.995
core.string_to_mixed.write 0.874 0.898 0.955
core.string_to_mixed_adaptive.free 0.895 0.893 0.908
core.string_to_mixed_adaptive.iter 0.901 0.886 0.990
core.string_to_mixed_adaptive.read 0.898 0.879 0.999
core.string_to_mixed_adaptive.write 0.902 0.901 0.910
core.string_to_mixed_hash.free 0.896 0.885 0.917
core.string_to_mixed_hash.iter 0.902 0.908 0.978
core.string_to_mixed_hash.read 0.905 0.879 0.956
core.string_to_mixed_hash.write 0.872 0.900 0.913

A → C — php-judy against a PHP native array (above 1.00 means the array is faster)

benchmark linux-glibc-x86_64 linux-musl-x86_64 macos-arm64
api.setop.diff.bitset 3.800 4.262 4.629
api.setop.intersect.bitset 1.983 2.082 2.065
api.setop.union.bitset 8.622 8.054 7.613
api.setop.xor.bitset 2.181 2.433 2.495
core.bitset.iter 6.053 5.996 4.246
core.bitset.read 2.697 2.506 2.696
core.bitset.write 2.813 2.932 3.232
core.int_to_int.free 4.085
core.int_to_int.iter 6.680 6.416 4.398
core.int_to_int.read 2.840 2.772 3.059
core.int_to_int.write 3.479 3.742 4.663
core.int_to_mixed.free 4.855 4.223 3.243
core.int_to_mixed.iter 5.636 5.996 4.148
core.int_to_mixed.read 2.391 1.971 2.433
core.int_to_mixed.write 3.204 4.217 3.874
core.int_to_packed.free 3.096 3.209 1.909
core.int_to_packed.iter 9.829 10.157 8.301
core.int_to_packed.read 5.012 4.247 5.233
core.int_to_packed.write 4.211 5.558 5.291
core.string_to_int.free 17.420 31.569 19.591
core.string_to_int.iter 11.493 12.313 16.816
core.string_to_int.read 5.006 5.009 7.184
core.string_to_int.write 4.780 7.177 6.363
core.string_to_int_adaptive.free 39.442 65.861 34.931
core.string_to_int_adaptive.iter 14.191 15.659 21.187
core.string_to_int_adaptive.read 3.328 3.821 3.125
core.string_to_int_adaptive.write 8.209 15.318 9.062
core.string_to_int_hash.free 39.456 65.219 31.266
core.string_to_int_hash.iter 14.392 15.767 22.031
core.string_to_int_hash.read 3.456 3.850 3.150
core.string_to_int_hash.write 8.292 15.004 8.978
core.string_to_mixed.free 9.610 10.878 10.133
core.string_to_mixed.iter 9.844 9.890 13.553
core.string_to_mixed.read 3.985 3.755 6.372
core.string_to_mixed.write 4.966 7.078 7.176
core.string_to_mixed_adaptive.free 18.083 21.283 18.936
core.string_to_mixed_adaptive.iter 13.402 13.982 18.841
core.string_to_mixed_adaptive.read 2.749 2.995 3.176
core.string_to_mixed_adaptive.write 10.109 16.165 10.567
core.string_to_mixed_hash.free 17.542 20.282 18.103
core.string_to_mixed_hash.iter 14.229 14.785 18.781
core.string_to_mixed_hash.read 2.874 3.015 3.294
core.string_to_mixed_hash.write 9.664 15.374 11.714

Memory — PHP array bytes / php-judy bytes (above 1.00 is a php-judy win)

workload linux-glibc-x86_64 linux-musl-x86_64 macos-arm64
bitset@1000000 ~22.82x ⚠ ~61.29x ⚠ ~27.39x ⚠
int_sparse@1000000 3.32x 3.33x 3.19x
int_to_int@1000000 2.36x 2.31x 2.04x
string_to_int@1000000 3.81x 3.71x 3.68x

⚠ Not resolvable by this instrument, and therefore not gated: the structure is small enough that run-to-run scatter swamps the signal. Recorded for completeness; do not quote these as measurements.
bitset@1000000 on linux-glibc-x86_64 — baseline spread 18.9%.
bitset@1000000 on linux-musl-x86_64 — baseline spread 119.9%.
bitset@1000000 on macos-arm64 — baseline spread 4.4%.

Arm S — verified unpatched on each platform

platform O1 fingerprint (arm S) O1 fingerprint (arm C) O3 (S / C)
linux-glibc-x86_64 popcnt = 0 popcnt = 90 12 / 983
linux-musl-x86_64 popcnt = 0 popcnt = 94 12 / 983
macos-arm64 cnt = 0 cnt = 88 32 / 673

The operative evidence is the source-hash manifest (arm-s-manifest.json), which compares every upstream file against the pristine import commit and works on every platform including Windows. The instruction census above is an architecture-specific cross-check: x86-64 fingerprints O1 as popcnt and O3 as bswap, arm64 as cnt and rev.

Findings

status platform axis cell baseline current drift threshold
improved linux-musl-x86_64 timing.s_over_c api.setop.diff.bitset 0.9000 0.7578 -15.80% [-16.21, -15.59] 14.00%
improved linux-musl-x86_64 timing.s_over_c api.setop.intersect.bitset 0.8964 0.7383 -17.63% [-18.79, -17.26] 16.00%
improved linux-musl-x86_64 timing.s_over_c core.bitset.read 1.0019 0.7646 -23.68% [-24.35, -20.58] 12.50%

A cell is flagged only when its whole bootstrap CI clears the threshold in the adverse direction. suppressed means the run's hygiene failed, so it measured movement but is not permitted to accuse.

Full run: https://github.com/orieg/php-judy/actions/runs/32651096035

@orieg
orieg merged commit 4dd3abe into main Aug 23, 2026
40 checks passed
@orieg
orieg deleted the ci/standards-upgrade branch August 23, 2026 16:34
orieg added a commit that referenced this pull request Aug 26, 2026
…sts under memcheck (#204)

The nightly Valgrind job (new in #201) invoked run-tests.php directly,
which never loads the freshly built modules/judy.so — 227/232 tests
silently SKIPped on extension_loaded('judy') and the job has failed on
every run since it was introduced. Switch to make test, which passes
-d extension_dir=modules -d extension=judy.so; the -m/-q/--show-diff
flags ride in TESTS, which the generated Makefile appends to the
run-tests.php invocation.

Two classes of false positives surface once the suite actually runs
under memcheck (--trace-children=yes, USE_ZEND_ALLOC=0):

- The three bench-harness tests spawn child PHP processes. Children are
  traced too: memcheck inflates their RSS by an order of magnitude
  (breaking bench_memory_measurement_001's heap-vs-RSS assertions) and
  child memory_limit fatals leave allocations unfreed by design, which
  memcheck attributes to the test as a leak (bench_memory_limit_001).
  All three now skip when USE_ZEND_ALLOC=0 is exported, the standard
  run-tests marker for a Valgrind run.

- PCRE2's sljit-generated code trips uninitialised-value reports inside
  the php binary itself (no judy.so frame), flagging every preg_*-using
  test as LEAK. -d pcre.jit=0 in the Valgrind run silences it.

Verified in a Linux container (PHP 8.4, valgrind): the fixed command
runs 229/232 under memcheck with 0 failures and 0 leaks (3 skips are
the guarded bench tests); the normal suite still passes 232/232 with
0 skips.

Fixes #202
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: adopt CI/CD engineering standards, single rollup gate, and upstream CDN 504 resilience

1 participant