Skip to content

release: 2.7.1 - #199

Merged
orieg merged 3 commits into
mainfrom
release/2.7.1
Aug 23, 2026
Merged

release: 2.7.1#199
orieg merged 3 commits into
mainfrom
release/2.7.1

Conversation

@orieg

@orieg orieg commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Release 2.7.1

  • SECURITY FIX: Guard against destructor re-entrancy and heap Use-After-Free (UAF) in Judy::STRING_TO_ENTRY during ArrayAccess overwrites, Judy::set(), and Judy::pruneExpired() sweeps (fix(security): guard against destructor reentrancy and UAF on STRING_TO_ENTRY #198).
  • Commits new slot payload prior to invoking zval_ptr_dtor(), and unlinks keys from the JudySL trie before freeing entry structs.
  • Bump PHP_JUDY_VERSION and package.xml to 2.7.1.

orieg added 3 commits August 22, 2026 21:48
…TO_ENTRY

- Copy old zval descriptor into temporary stack variable and commit new entry
  state before calling zval_ptr_dtor() in judy_object_write_dimension_helper()
  and Judy::set().
- Delete entry key from JudySL prior to calling zval_ptr_dtor() in
  Judy::pruneExpired() to prevent double-free or UAF if a destructor re-enters.
- Add regression test regression_string_to_entry_reentrancy_001.phpt.
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

Test Results

PHP Platform Arch TS Tests Pass Fail Skip Duration
8.1 Linux x64 - 232 232 0 0 8.3s
8.2 Linux x64 - 232 232 0 0 7.2s
8.3 Linux x64 - 232 232 0 0 8.3s
8.4 Linux x64 - 232 232 0 0 7.2s
8.5 Linux x64 - 232 232 0 0 11.1s
✅ 🧪 8.6 Linux x64 - 232 232 0 0 13.2s
8.1 Linux (Expanse) x64 - 232 232 0 0 9.4s
8.2 Linux (Expanse) x64 - 232 232 0 0 7.6s
8.3 Linux (Expanse) x64 - 232 232 0 0 9.0s
8.4 Linux (Expanse) x64 - 232 232 0 0 11.5s
8.5 Linux (Expanse) x64 - 232 232 0 0 10.9s
✅ 🧪 8.6 Linux (Expanse) x64 - 232 232 0 0 13.6s
8.1 Windows x64 nts 232 232 0 0 13.8s
8.3 Windows x64 nts 232 232 0 0 13.2s
8.4 Windows x64 nts 232 232 0 0 13.7s
8.5 Windows x64 nts 232 232 0 0 13.1s
✅ 🧪 8.6 Windows x64 nts 232 232 0 0 13.4s
8.2 Windows (Expanse) x64 nts 232 232 0 0 13.5s
8.3 Windows (Expanse) x64 nts 232 232 0 0 10.9s
8.4 Windows (Expanse) x64 nts 232 232 0 0 13.2s
8.5 Windows (Expanse) x64 nts 232 232 0 0 13.8s
✅ 🧪 8.6 Windows (Expanse) x64 nts 232 232 0 0 13.3s
Total 5104 5104 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 < 504.0 KB 8.2 MB > 17x smaller
Int to Int 4.4 MB 8.2 MB 1.9x smaller
Int to Mixed 40.4 MB 38.2 MB 1.06x larger
Int to Packed 16.4 MB 38.2 MB 2.3x smaller
String to Int 10.3 MB 38.2 MB 3.7x smaller
String to Mixed 46.4 MB 68.1 MB 1.5x smaller
String to Int (Hash) 31.5 MB 38.1 MB 1.2x smaller
String to Mixed (Hash) 67.5 MB 68.1 MB about the same
String to Int (Adaptive) 31.6 MB 38.2 MB 1.2x smaller
String to Mixed (Adaptive) 67.6 MB 68.2 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 504.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() 19.6 ms 29.4 ms 1.5x
fromArray() 19.6 ms 29.4 ms 1.5x
toArray() 26.2 ms 40.6 ms 1.6x
getAll() 1.1 ms 2.0 ms 1.9x
keys() 12.0 ms 26.2 ms 2.2x
values() 13.2 ms 25.8 ms 2.0x
sumValues() 8.2 ms 20.8 ms 2.5x
populationCount() 0.00 ms 22.7 ms O(1) vs O(n)
deleteRange() 39.2 ms 56.7 ms 1.4x
equals() 11.7 ms 50.0 ms 4.3x
union() Bitset 30.7 ms 5.1 ms 0.2x
intersect() Bitset 13.4 ms 5.6 ms 0.4x
diff() Bitset 13.3 ms 3.3 ms 0.3x
xor() Bitset 26.4 ms 11.3 ms 0.4x
mergeWith() Int→Int 52.0 ms 67.5 ms 1.3x
mergeWith() Str→Int 242.7 ms 258.9 ms 1.1x
forEach() Int→Int 29.1 ms 24.4 ms 0.8x
filter() Int→Int 44.5 ms 39.9 ms 0.9x
map() Int→Int 48.7 ms 47.8 ms ~1.0x
forEach() Str→Int 64.9 ms 60.1 ms 0.9x
filter() Str→Int 123.4 ms 115.4 ms 0.9x
map() Str→Int 165.5 ms 161.7 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 17.9 ms 7.0 ms 0.4x
Bitset Read 11.6 ms 4.8 ms 0.4x
Bitset Iter 19.8 ms 3.1 ms 0.2x
Bitset Free 0.01 ms 0.33 ms 47.8x
Int to Int Write 24.1 ms 7.5 ms 0.3x
Int to Int Read 11.4 ms 3.9 ms 0.3x
Int to Int Iter 21.5 ms 3.4 ms 0.2x
Int to Int Free 0.20 ms 0.34 ms 1.7x
Int to Mixed Write 35.1 ms 11.1 ms 0.3x
Int to Mixed Read 16.7 ms 5.9 ms 0.4x
Int to Mixed Iter 22.6 ms 4.2 ms 0.2x
Int to Mixed Free 14.9 ms 4.1 ms 0.3x
String to Int Write 93.5 ms 16.1 ms 0.2x
String to Int Read 36.8 ms 7.3 ms 0.2x
String to Int Iter 56.8 ms 4.9 ms 0.1x
String to Int Free 18.9 ms 1.3 ms 0.1x
String to Int (Adaptive) Write 166.2 ms 16.5 ms 0.1x
String to Int (Adaptive) Read 24.5 ms 7.4 ms 0.3x
String to Int (Adaptive) Iter 67.7 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 41.7 ms 1.1 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 17.9 ms 7.0 ms 0.4x
Bitset Read 11.6 ms 4.8 ms 0.4x
Bitset Iter 19.8 ms 3.1 ms 0.2x
Bitset Free 0.01 ms 0.33 ms 47.8x
Int to Int Write 24.1 ms 7.5 ms 0.3x
Int to Int Read 11.4 ms 3.9 ms 0.3x
Int to Int Iter 21.5 ms 3.4 ms 0.2x
Int to Int Free 0.20 ms 0.34 ms 1.7x
Int to Mixed Write 35.1 ms 11.1 ms 0.3x
Int to Mixed Read 16.7 ms 5.9 ms 0.4x
Int to Mixed Iter 22.6 ms 4.2 ms 0.2x
Int to Mixed Free 14.9 ms 4.1 ms 0.3x
Int to Packed Write 47.7 ms 10.5 ms 0.2x
Int to Packed Read 29.9 ms 6.1 ms 0.2x
Int to Packed Iter 40.3 ms 4.2 ms 0.1x
Int to Packed Free 10.1 ms 4.3 ms 0.4x
String to Int Write 93.5 ms 16.1 ms 0.2x
String to Int Read 36.8 ms 7.3 ms 0.2x
String to Int Iter 56.8 ms 4.9 ms 0.1x
String to Int Free 18.9 ms 1.3 ms 0.1x
String to Mixed Write 117.2 ms 19.5 ms 0.2x
String to Mixed Read 40.8 ms 10.0 ms 0.2x
String to Mixed Iter 67.2 ms 6.3 ms 0.1x
String to Mixed Free 64.1 ms 7.1 ms 0.1x
String to Int (Hash) Write 168.9 ms 16.5 ms 0.1x
String to Int (Hash) Read 24.9 ms 7.6 ms 0.3x
String to Int (Hash) Iter 68.5 ms 4.8 ms 0.1x
String to Int (Hash) Free 41.7 ms 1.1 ms 0.0x
String to Mixed (Hash) Write 233.8 ms 19.8 ms 0.1x
String to Mixed (Hash) Read 28.1 ms 10.6 ms 0.4x
String to Mixed (Hash) Iter 92.4 ms 6.8 ms 0.1x
String to Mixed (Hash) Free 123.9 ms 7.0 ms 0.1x
String to Int (Adaptive) Write 166.2 ms 16.5 ms 0.1x
String to Int (Adaptive) Read 24.5 ms 7.4 ms 0.3x
String to Int (Adaptive) Iter 67.7 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 41.7 ms 1.1 ms 0.0x
String to Mixed (Adaptive) Write 239.7 ms 19.6 ms 0.1x
String to Mixed (Adaptive) Read 27.2 ms 10.2 ms 0.4x
String to Mixed (Adaptive) Iter 91.4 ms 6.5 ms 0.1x
String to Mixed (Adaptive) Free 118.2 ms 6.8 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.4x) 12.4 ms (0.4x) 14.7 ms (0.4x) 10.0 ms (0.5x) 19.6 ms (0.4x) 17.9 ms (0.4x)
Bitset Read 11.0 ms (0.3x) 8.3 ms (0.3x) 8.2 ms (0.4x) 7.2 ms (0.4x) 11.7 ms (0.4x) 11.6 ms (0.4x)
Int to Int Write 24.2 ms (0.3x) 17.8 ms (0.3x) 19.3 ms (0.3x) 15.6 ms (0.3x) 24.7 ms (0.3x) 24.1 ms (0.3x)
Int to Int Read 11.0 ms (0.3x) 9.0 ms (0.3x) 8.7 ms (0.4x) 8.1 ms (0.4x) 12.2 ms (0.3x) 11.4 ms (0.3x)
Int to Mixed Write 34.3 ms (0.3x) 25.6 ms (0.3x) 26.3 ms (0.3x) 21.4 ms (0.3x) 32.7 ms (0.4x) 35.1 ms (0.3x)
Int to Mixed Read 13.9 ms (0.4x) 9.6 ms (0.4x) 10.9 ms (0.4x) 8.1 ms (0.5x) 14.2 ms (0.5x) 16.7 ms (0.4x)
Int to Packed Write 48.1 ms (0.2x) 33.8 ms (0.2x) 36.9 ms (0.3x) 28.7 ms (0.3x) 45.0 ms (0.3x) 47.7 ms (0.2x)
Int to Packed Read 30.1 ms (0.2x) 23.5 ms (0.2x) 21.9 ms (0.2x) 19.9 ms (0.2x) 29.9 ms (0.2x) 29.9 ms (0.2x)
String to Int Write 91.2 ms (0.2x) 75.4 ms (0.2x) 71.2 ms (0.2x) 69.6 ms (0.2x) 93.7 ms (0.2x) 93.5 ms (0.2x)
String to Int Read 36.7 ms (0.2x) 30.6 ms (0.2x) 31.3 ms (0.2x) 27.9 ms (0.2x) 40.2 ms (0.2x) 36.8 ms (0.2x)
String to Mixed Write 116.0 ms (0.2x) 90.8 ms (0.2x) 93.1 ms (0.2x) 84.4 ms (0.2x) 118.0 ms (0.2x) 117.2 ms (0.2x)
String to Mixed Read 39.9 ms (0.2x) 32.3 ms (0.2x) 34.2 ms (0.2x) 29.5 ms (0.2x) 44.7 ms (0.2x) 40.8 ms (0.2x)
String to Int (Hash) Write 159.8 ms (0.1x) 128.0 ms (0.1x) 131.3 ms (0.1x) 120.1 ms (0.1x) 167.5 ms (0.1x) 168.9 ms (0.1x)
String to Int (Hash) Read 25.5 ms (0.3x) 17.4 ms (0.3x) 20.4 ms (0.3x) 16.5 ms (0.3x) 27.8 ms (0.3x) 24.9 ms (0.3x)
String to Mixed (Hash) Write 227.4 ms (0.1x) 171.0 ms (0.1x) 191.9 ms (0.1x) 184.3 ms (0.1x) 245.9 ms (0.1x) 233.8 ms (0.1x)
String to Mixed (Hash) Read 27.7 ms (0.3x) 17.9 ms (0.3x) 23.5 ms (0.4x) 16.7 ms (0.3x) 31.5 ms (0.4x) 28.1 ms (0.4x)
String to Int (Adaptive) Write 157.7 ms (0.1x) 126.4 ms (0.1x) 124.7 ms (0.1x) 119.8 ms (0.1x) 167.7 ms (0.1x) 166.2 ms (0.1x)
String to Int (Adaptive) Read 24.9 ms (0.3x) 16.7 ms (0.3x) 19.8 ms (0.3x) 16.0 ms (0.3x) 27.2 ms (0.3x) 24.5 ms (0.3x)
String to Mixed (Adaptive) Write 235.3 ms (0.1x) 171.4 ms (0.1x) 192.4 ms (0.1x) 155.8 ms (0.1x) 261.5 ms (0.1x) 239.7 ms (0.1x)
String to Mixed (Adaptive) Read 27.1 ms (0.3x) 17.2 ms (0.3x) 22.7 ms (0.4x) 15.6 ms (0.4x) 30.7 ms (0.4x) 27.2 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) 15.7 ms (1.4x) 16.1 ms (1.4x) 13.7 ms (1.4x) 19.6 ms (1.4x) 19.6 ms (1.5x)
fromArray() 20.1 ms (1.5x) 15.7 ms (1.4x) 16.5 ms (1.4x) 13.5 ms (1.4x) 19.8 ms (1.4x) 19.6 ms (1.5x)
toArray() 22.3 ms (1.6x) 18.4 ms (1.5x) 17.2 ms (1.6x) 22.4 ms (1.4x) 22.3 ms (1.6x) 26.2 ms (1.6x)
getAll() 1.0 ms (2.0x) 0.91 ms (1.7x) 0.83 ms (2.0x) 0.95 ms (1.6x) 1.2 ms (1.9x) 1.1 ms (1.9x)
keys() 11.8 ms (2.2x) 8.7 ms (2.0x) 8.5 ms (2.2x) 8.5 ms (1.7x) 10.5 ms (2.3x) 12.0 ms (2.2x)
values() 12.7 ms (2.0x) 9.3 ms (1.9x) 8.6 ms (2.2x) 9.1 ms (1.7x) 11.8 ms (2.0x) 13.2 ms (2.0x)
sumValues() 7.1 ms (2.9x) 6.1 ms (2.5x) 5.6 ms (2.9x) 5.6 ms (2.1x) 6.9 ms (3.1x) 8.2 ms (2.5x)
populationCount() 0.00 ms (224625x) 0.00 ms (154721x) 0.00 ms (178830x) 0.00 ms (115992x) 0.00 ms (245595x) 0.00 ms (226677x)
deleteRange() 38.0 ms (1.5x) 28.4 ms (1.5x) 29.0 ms (1.5x) 25.0 ms (1.4x) 38.5 ms (1.4x) 39.2 ms (1.4x)
equals() 10.5 ms (4.5x) 8.9 ms (4.1x) 7.6 ms (4.7x) 8.2 ms (3.5x) 10.3 ms (4.8x) 11.7 ms (4.3x)
union() Bitset 30.2 ms (0.2x) 23.9 ms (0.2x) 23.9 ms (0.1x) 22.2 ms (0.2x) 32.1 ms (0.1x) 30.7 ms (0.2x)
intersect() Bitset 13.1 ms (0.4x) 11.5 ms (0.5x) 10.1 ms (0.4x) 10.3 ms (0.4x) 13.9 ms (0.4x) 13.4 ms (0.4x)
diff() Bitset 13.1 ms (0.3x) 11.6 ms (0.2x) 9.8 ms (0.3x) 10.0 ms (0.2x) 14.2 ms (0.2x) 13.3 ms (0.3x)
xor() Bitset 25.6 ms (0.6x) 23.4 ms (0.4x) 19.8 ms (0.4x) 20.3 ms (0.5x) 27.3 ms (0.4x) 26.4 ms (0.4x)
mergeWith() Int→Int 50.3 ms (1.3x) 37.8 ms (1.3x) 39.0 ms (1.3x) 32.7 ms (1.3x) 49.1 ms (1.3x) 52.0 ms (1.3x)
mergeWith() Str→Int 231.4 ms (1.1x) 175.7 ms (1.1x) 180.4 ms (1.1x) 170.3 ms (1.1x) 233.7 ms (1.1x) 242.7 ms (1.1x)
forEach() Int→Int 30.5 ms (0.8x) 23.3 ms (0.7x) 20.3 ms (0.9x) 20.2 ms (0.7x) 25.3 ms (0.9x) 29.1 ms (0.8x)
filter() Int→Int 43.7 ms (0.9x) 33.5 ms (0.8x) 31.6 ms (0.9x) 29.3 ms (0.8x) 40.6 ms (0.9x) 44.5 ms (0.9x)
map() Int→Int 46.9 ms (~1.0x) 35.9 ms (0.9x) 35.4 ms (~1.0x) 33.3 ms (0.9x) 45.0 ms (~1.0x) 48.7 ms (~1.0x)
forEach() Str→Int 66.4 ms (0.9x) 52.2 ms (0.9x) 49.5 ms (~1.0x) 49.7 ms (0.9x) 63.8 ms (~1.0x) 64.9 ms (0.9x)
filter() Str→Int 121.6 ms (0.9x) 97.9 ms (~1.0x) 91.5 ms (~1.0x) 92.1 ms (~1.0x) 119.2 ms (~1.0x) 123.4 ms (0.9x)
map() Str→Int 159.0 ms (~1.0x) 133.3 ms (~1.0x) 121.2 ms (~1.0x) 127.5 ms (~1.0x) 156.0 ms (~1.0x) 165.5 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.3 PHP 8.4 PHP 8.5 PHP 8.6
Bitset Write 20.9 ms (0.7x) 17.3 ms (0.6x) 22.8 ms (0.5x) 23.2 ms (0.6x) 23.0 ms (0.7x)
Bitset Read 16.3 ms (0.5x) 14.2 ms (0.5x) 17.7 ms (0.5x) 14.0 ms (0.5x) 19.5 ms (0.5x)
Int to Int Write 34.0 ms (0.4x) 27.6 ms (0.4x) 36.3 ms (0.3x) 25.8 ms (0.5x) 37.2 ms (0.4x)
Int to Int Read 18.2 ms (0.4x) 14.0 ms (0.5x) 18.8 ms (0.4x) 13.5 ms (0.4x) 20.5 ms (0.5x)
Int to Mixed Write 48.1 ms (0.4x) 38.3 ms (0.4x) 53.6 ms (0.3x) 47.3 ms (0.3x) 52.1 ms (0.4x)
Int to Mixed Read 21.3 ms (0.5x) 16.6 ms (0.5x) 20.4 ms (0.5x) 15.9 ms (0.4x) 18.8 ms (0.6x)
Int to Packed Write 66.5 ms (0.3x) 50.5 ms (0.3x) 68.9 ms (0.2x) 47.3 ms (0.3x) 71.9 ms (0.3x)
Int to Packed Read 48.4 ms (0.2x) 38.2 ms (0.2x) 49.0 ms (0.2x) 32.3 ms (0.2x) 47.7 ms (0.2x)
String to Int Write 143.4 ms (0.2x) 116.7 ms (0.2x) 141.5 ms (0.2x) 106.5 ms (0.3x) 154.2 ms (0.2x)
String to Int Read 61.3 ms (0.2x) 56.5 ms (0.2x) 61.2 ms (0.2x) 46.1 ms (0.2x) 67.1 ms (0.2x)
String to Mixed Write 182.2 ms (0.1x) 159.7 ms (0.2x) 190.4 ms (0.2x) 131.3 ms (0.2x) 198.4 ms (0.2x)
String to Mixed Read 62.8 ms (0.2x) 57.8 ms (0.2x) 62.5 ms (0.2x) 44.8 ms (0.2x) 68.8 ms (0.2x)
String to Int (Hash) Write 251.7 ms (0.1x) 214.9 ms (0.1x) 251.1 ms (0.1x) 188.6 ms (0.1x) 274.1 ms (0.1x)
String to Int (Hash) Read 43.8 ms (0.3x) 40.5 ms (0.3x) 44.2 ms (0.3x) 29.8 ms (0.3x) 47.8 ms (0.3x)
String to Mixed (Hash) Write 343.1 ms (0.1x) 320.7 ms (0.1x) 353.9 ms (0.1x) 283.3 ms (0.1x) 423.4 ms (0.1x)
String to Mixed (Hash) Read 47.0 ms (0.3x) 43.9 ms (0.3x) 47.0 ms (0.3x) 38.5 ms (0.2x) 50.0 ms (0.3x)
String to Int (Adaptive) Write 245.9 ms (0.1x) 208.8 ms (0.1x) 245.0 ms (0.1x) 180.5 ms (0.2x) 267.2 ms (0.1x)
String to Int (Adaptive) Read 43.4 ms (0.3x) 39.6 ms (0.3x) 43.4 ms (0.3x) 29.1 ms (0.3x) 46.8 ms (0.3x)
String to Mixed (Adaptive) Write 358.5 ms (0.1x) 356.3 ms (0.1x) 363.7 ms (0.1x) 299.8 ms (0.1x) 442.5 ms (0.1x)
String to Mixed (Adaptive) Read 45.9 ms (0.3x) 43.9 ms (0.3x) 45.7 ms (0.3x) 31.9 ms (0.2x) 48.9 ms (0.3x)

API — Windows

Operation PHP 8.1 PHP 8.3 PHP 8.4 PHP 8.5 PHP 8.6
putAll() 31.3 ms (1.5x) 25.7 ms (1.4x) 30.9 ms (1.5x) 23.8 ms (1.4x) 33.2 ms (1.4x)
fromArray() 31.2 ms (1.5x) 25.5 ms (1.4x) 30.3 ms (1.6x) 23.1 ms (1.4x) 32.6 ms (1.4x)
toArray() 28.1 ms (1.7x) 24.9 ms (1.6x) 27.7 ms (1.7x) 31.0 ms (1.4x) 32.8 ms (1.6x)
getAll() 1.4 ms (2.3x) 1.2 ms (2.2x) 1.7 ms (1.9x) 2.2 ms (1.3x) 2.1 ms (1.7x)
keys() 16.3 ms (2.3x) 11.3 ms (2.3x) 12.9 ms (2.4x) 12.0 ms (1.7x) 14.6 ms (2.3x)
values() 16.9 ms (2.1x) 11.7 ms (2.3x) 13.1 ms (2.4x) 11.2 ms (1.8x) 15.1 ms (2.3x)
sumValues() 7.7 ms (3.4x) 6.5 ms (3.3x) 6.8 ms (3.8x) 4.8 ms (3.1x) 7.0 ms (3.5x)
populationCount() 0.00 ms (259943x) 0.00 ms (211381x) 0.00 ms (253699x) 0.00 ms (158499x) 0.00 ms (263210x)
deleteRange() 53.9 ms (1.5x) 41.0 ms (1.5x) 55.2 ms (1.5x) 36.7 ms (1.4x) 57.3 ms (1.5x)
equals() 12.4 ms (4.9x) 10.1 ms (4.8x) 10.5 ms (6.3x) 7.5 ms (5.4x) 10.8 ms (5.6x)
union() Bitset 34.5 ms (0.4x) 27.7 ms (0.3x) 37.7 ms (0.2x) 23.0 ms (0.5x) 35.2 ms (0.3x)
intersect() Bitset 16.2 ms (0.5x) 12.2 ms (0.5x) 17.7 ms (0.4x) 10.1 ms (1.2x) 16.1 ms (0.6x)
diff() Bitset 15.6 ms (0.4x) 11.8 ms (0.3x) 17.8 ms (0.2x) 10.0 ms (0.4x) 16.5 ms (0.3x)
xor() Bitset 31.9 ms (0.8x) 24.0 ms (0.6x) 34.8 ms (0.5x) 21.3 ms (1.1x) 33.0 ms (0.7x)
mergeWith() Int→Int 63.9 ms (1.3x) 49.3 ms (1.4x) 64.9 ms (1.4x) 43.3 ms (1.3x) 66.9 ms (1.3x)
mergeWith() Str→Int 320.5 ms (1.1x) 260.7 ms (1.1x) 336.3 ms (1.1x) 251.6 ms (~1.0x) 353.0 ms (1.1x)
forEach() Int→Int 30.3 ms (~1.0x) 23.6 ms (~1.0x) 45.8 ms (0.7x) 20.4 ms (0.8x) 32.6 ms (0.9x)
filter() Int→Int 47.8 ms (~1.0x) 35.0 ms (1.1x) 49.4 ms (~1.0x) 39.2 ms (0.9x) 48.3 ms (1.1x)
map() Int→Int 55.0 ms (1.1x) 39.7 ms (1.2x) 56.5 ms (1.1x) 35.8 ms (1.1x) 55.1 ms (~1.0x)
forEach() Str→Int 77.2 ms (~1.0x) 62.7 ms (~1.0x) 84.4 ms (~1.0x) 69.7 ms (0.9x) 91.2 ms (~1.0x)
filter() Str→Int 154.5 ms (~1.0x) 122.2 ms (1.1x) 161.3 ms (~1.0x) 126.4 ms (0.9x) 175.5 ms (0.9x)
map() Str→Int 219.5 ms (~1.0x) 168.5 ms (~1.0x) 227.6 ms (1.1x) 186.7 ms (0.9x) 245.2 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 < 240.0 KB 9.2 MB > 39x smaller
Int to Int 4.4 MB 9.3 MB 2.1x smaller
Int to Mixed 42.6 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 38.0 MB 3.6x smaller
String to Mixed 48.7 MB 69.0 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 37.9 MB 1.2x smaller
String to Mixed (Adaptive) 70.5 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 240.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.2 ms 46.4 ms 1.4x
fromArray() 32.6 ms 46.4 ms 1.4x
toArray() 32.8 ms 54.0 ms 1.6x
getAll() 2.1 ms 3.6 ms 1.7x
keys() 14.6 ms 33.0 ms 2.3x
values() 15.1 ms 34.3 ms 2.3x
sumValues() 7.0 ms 24.4 ms 3.5x
populationCount() 0.00 ms 26.3 ms O(1) vs O(n)
deleteRange() 57.3 ms 85.4 ms 1.5x
equals() 10.8 ms 60.7 ms 5.6x
union() Bitset 35.2 ms 11.5 ms 0.3x
intersect() Bitset 16.1 ms 10.4 ms 0.6x
diff() Bitset 16.5 ms 4.8 ms 0.3x
xor() Bitset 33.0 ms 23.9 ms 0.7x
mergeWith() Int→Int 66.9 ms 86.7 ms 1.3x
mergeWith() Str→Int 353.0 ms 379.6 ms 1.1x
forEach() Int→Int 32.6 ms 28.7 ms 0.9x
filter() Int→Int 48.3 ms 53.4 ms 1.1x
map() Int→Int 55.1 ms 57.5 ms ~1.0x
forEach() Str→Int 91.2 ms 88.8 ms ~1.0x
filter() Str→Int 175.5 ms 163.5 ms 0.9x
map() Str→Int 245.2 ms 235.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 23.0 ms 16.0 ms 0.7x
Bitset Read 19.5 ms 10.2 ms 0.5x
Bitset Iter 22.8 ms 8.3 ms 0.4x
Bitset Free 0.01 ms 0.80 ms 122x
Int to Int Write 37.2 ms 15.8 ms 0.4x
Int to Int Read 20.5 ms 10.2 ms 0.5x
Int to Int Iter 23.5 ms 7.2 ms 0.3x
Int to Int Free 1.0 ms 0.74 ms 0.7x
Int to Mixed Write 52.1 ms 18.5 ms 0.4x
Int to Mixed Read 18.8 ms 10.7 ms 0.6x
Int to Mixed Iter 23.8 ms 7.9 ms 0.3x
Int to Mixed Free 20.3 ms 3.7 ms 0.2x
String to Int Write 154.2 ms 36.0 ms 0.2x
String to Int Read 67.1 ms 12.6 ms 0.2x
String to Int Iter 79.1 ms 8.3 ms 0.1x
String to Int Free 34.9 ms 3.5 ms 0.1x
String to Int (Adaptive) Write 267.2 ms 33.1 ms 0.1x
String to Int (Adaptive) Read 46.8 ms 12.6 ms 0.3x
String to Int (Adaptive) Iter 113.2 ms 8.6 ms 0.1x
String to Int (Adaptive) Free 67.1 ms 2.7 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 23.0 ms 16.0 ms 0.7x
Bitset Read 19.5 ms 10.2 ms 0.5x
Bitset Iter 22.8 ms 8.3 ms 0.4x
Bitset Free 0.01 ms 0.80 ms 122x
Int to Int Write 37.2 ms 15.8 ms 0.4x
Int to Int Read 20.5 ms 10.2 ms 0.5x
Int to Int Iter 23.5 ms 7.2 ms 0.3x
Int to Int Free 1.0 ms 0.74 ms 0.7x
Int to Mixed Write 52.1 ms 18.5 ms 0.4x
Int to Mixed Read 18.8 ms 10.7 ms 0.6x
Int to Mixed Iter 23.8 ms 7.9 ms 0.3x
Int to Mixed Free 20.3 ms 3.7 ms 0.2x
Int to Packed Write 71.9 ms 19.9 ms 0.3x
Int to Packed Read 47.7 ms 10.8 ms 0.2x
Int to Packed Iter 51.6 ms 7.9 ms 0.2x
Int to Packed Free 15.0 ms 3.8 ms 0.3x
String to Int Write 154.2 ms 36.0 ms 0.2x
String to Int Read 67.1 ms 12.6 ms 0.2x
String to Int Iter 79.1 ms 8.3 ms 0.1x
String to Int Free 34.9 ms 3.5 ms 0.1x
String to Mixed Write 198.4 ms 35.1 ms 0.2x
String to Mixed Read 68.8 ms 13.3 ms 0.2x
String to Mixed Iter 99.0 ms 8.8 ms 0.1x
String to Mixed Free 107.6 ms 7.0 ms 0.1x
String to Int (Hash) Write 274.1 ms 33.0 ms 0.1x
String to Int (Hash) Read 47.8 ms 12.8 ms 0.3x
String to Int (Hash) Iter 116.7 ms 8.4 ms 0.1x
String to Int (Hash) Free 66.9 ms 3.6 ms 0.1x
String to Mixed (Hash) Write 423.4 ms 35.3 ms 0.1x
String to Mixed (Hash) Read 50.0 ms 13.2 ms 0.3x
String to Mixed (Hash) Iter 175.0 ms 8.4 ms 0.0x
String to Mixed (Hash) Free 215.6 ms 8.6 ms 0.0x
String to Int (Adaptive) Write 267.2 ms 33.1 ms 0.1x
String to Int (Adaptive) Read 46.8 ms 12.6 ms 0.3x
String to Int (Adaptive) Iter 113.2 ms 8.6 ms 0.1x
String to Int (Adaptive) Free 67.1 ms 2.7 ms 0.0x
String to Mixed (Adaptive) Write 442.5 ms 34.5 ms 0.1x
String to Mixed (Adaptive) Read 48.9 ms 13.0 ms 0.3x
String to Mixed (Adaptive) Iter 183.8 ms 8.5 ms 0.0x
String to Mixed (Adaptive) Free 235.4 ms 9.0 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.99% (PHP-array control +0.66%); runner common-mode +0.66% divided out of every row.

Summary: ⚠️ 2 regressions, 94 unchanged, 4 not evaluated (unstable)

Benchmark Baseline Current Delta 95% CI Status
adv.filter.int_to_int 16.9 ms 17.9 ms +5.2% [-3.0%, +11.7%] ~same
adv.filter.string_to_int 52.3 ms 52.4 ms +1.7% [-4.4%, +8.1%] ~same
adv.forEach.int_to_int 11.0 ms 11.6 ms +4.0% [-6.3%, +16.2%] ~same
adv.forEach.string_to_int 27.5 ms 27.1 ms -1.5% [-6.4%, +6.7%] ~same
adv.map.int_to_int 18.3 ms 18.6 ms +1.2% [-10.6%, +6.7%] ~same
adv.map.string_to_int 70.0 ms 70.9 ms -0.2% [-11.4%, +6.3%] ~same
adv.optiter.foreach.string_to_int_adaptive.default 63.7 ms 64.8 ms +0.1% [-2.2%, +1.7%] ~same
adv.optiter.foreach.string_to_int_adaptive.optimized 27.6 ms 28.8 ms +3.7% [+0.1%, +7.4%] ~same
adv.optiter.foreach.string_to_int_hash.default 49.7 ms 45.2 ms -11.0% [-25.9%, -3.8%] ~same
adv.optiter.foreach.string_to_int_hash.optimized 24.2 ms 24.6 ms +0.8% [-5.3%, +2.5%] ~same
adv.optiter.increment.string_to_int_hash.optimized 32.9 ms 32.8 ms -6.3% [-12.6%, +6.5%] ~same
adv.optiter.overwrite.string_to_int_adaptive.default 47.2 ms 45.9 ms -1.4% [-10.0%, +5.6%] ~same
adv.optiter.overwrite.string_to_int_adaptive.optimized 55.0 ms 55.3 ms +1.4% [-10.1%, +9.5%] ~same
adv.optiter.overwrite.string_to_int_hash.optimized 34.9 ms 36.1 ms -1.4% [-19.4%, +16.2%] ~same
adv.optiter.ratio.foreach.string_to_int_adaptive 42.7 ms 44.4 ms +2.9% [-0.1%, +6.4%] ~same
adv.optiter.ratio.increment.string_to_int_hash 129.2 ms 141.7 ms +9.8% [-5.9%, +41.0%] ~same
adv.optiter.ratio.overwrite.string_to_int_adaptive 117.7 ms 115.6 ms +3.9% [-10.1%, +5.0%] ~same
adv.optiter.ratio.toArray.string_to_int_adaptive 54.8 ms 55.4 ms +0.5% [-7.5%, +11.0%] ~same
adv.optiter.ratio.toArray.string_to_int_hash 47.7 ms 54.4 ms +8.7% [-0.7%, +23.4%] ~same
adv.optiter.ratio.values.string_to_int_adaptive 41.0 ms 39.7 ms -5.3% [-10.4%, +7.8%] ~same
adv.optiter.ratio.values.string_to_int_hash 45.1 ms 46.2 ms -0.4% [-7.0%, +21.2%] ~same
adv.optiter.toArray.string_to_int_adaptive.default 81.0 ms 75.0 ms -1.2% [-20.4%, +4.5%] ~same
adv.optiter.toArray.string_to_int_adaptive.optimized 44.4 ms 43.7 ms +0.6% [-18.6%, +4.2%] ~same
adv.optiter.toArray.string_to_int_hash.default 55.9 ms 52.4 ms -6.8% [-16.9%, +12.2%] ~same
adv.optiter.toArray.string_to_int_hash.optimized 26.9 ms 28.0 ms +2.2% [-6.9%, +21.9%] ~same
adv.optiter.values.string_to_int_adaptive.default 63.4 ms 64.0 ms -2.2% [-15.4%, +9.7%] ~same
adv.optiter.values.string_to_int_adaptive.optimized 26.5 ms 23.2 ms -1.0% [-19.4%, +6.3%] ~same
adv.optiter.values.string_to_int_hash.default 43.9 ms 39.4 ms -5.0% [-18.3%, +3.0%] ~same
adv.optiter.values.string_to_int_hash.optimized 18.9 ms 19.1 ms -3.6% [-4.8%, +6.6%] ~same
api.deleteRange.int_to_int 14.4 ms 15.0 ms +3.4% [+1.4%, +4.6%] ~same
api.equals.int_to_int 4.4 ms 4.7 ms +6.9% [+3.3%, +8.5%] ~same
api.fromArray.int_to_int 8.9 ms 8.7 ms -3.2% [-9.9%, -0.2%] ~same
api.getAll.int_to_int 0.37 ms 0.38 ms +0.9% [-4.2%, +3.6%] ~same
api.increment.int_to_int 9.4 ms 9.3 ms -2.2% [-3.6%, +1.1%] ~same
api.keys.int_to_int 4.5 ms 4.7 ms +5.7% [+2.7%, +7.9%] ~same
api.mergeWith.int_to_int 19.1 ms 19.1 ms -0.9% [-2.6%, +1.9%] ~same
api.mergeWith.string_to_int 91.4 ms 91.7 ms +0.3% [-1.6%, +3.7%] ~same
api.populationCount.int_to_int 0.00 ms 0.00 ms -0.7% [-0.7%, -0.7%] ~same
api.putAll.int_to_int 8.8 ms 8.6 ms -2.2% [-7.2%, +4.5%] ~same
api.range.keys.int_to_int 0.38 ms 0.41 ms +7.8% [+4.0%, +12.4%] ~same
api.range.size.string_to_int 1.9 ms 1.9 ms -1.6% [-3.3%, +1.5%] ~same
api.range.size1000.int_to_int 0.26 ms 0.25 ms -5.3% [-6.7%, -1.0%] ~same
api.setop.diff.bitset 5.4 ms 5.8 ms +9.9% [+6.4%, +11.3%] ~same
api.setop.diff.int_to_int 6.6 ms 6.9 ms +3.9% [+3.4%, +7.0%] ~same
api.setop.diff.string_to_int 46.8 ms 47.3 ms +0.6% [-1.7%, +2.8%] ~same
api.setop.intersect.bitset 5.3 ms 6.0 ms +13.3% [+10.8%, +14.0%] ⚠️ SLOWER
api.setop.intersect.int_to_int 6.7 ms 6.9 ms +3.7% [+1.5%, +9.5%] ~same
api.setop.intersect.string_to_int 52.6 ms 53.3 ms +0.2% [-2.2%, +5.5%] ~same
api.setop.union.bitset 11.3 ms 13.0 ms +16.0% [+12.7%, +17.3%] ⚠️ SLOWER
api.setop.union.int_to_int 17.1 ms 16.8 ms -0.4% [-2.8%, +3.8%] ~same
api.setop.union.string_to_int 112.6 ms 115.0 ms +0.5% [-4.6%, +4.7%] ~same
api.setop.xor.bitset 10.5 ms 12.0 ms +13.7% [+9.1%, +20.5%] ~same
api.setop.xor.int_to_int 13.3 ms 13.8 ms +3.8% [+3.2%, +7.1%] ~same
api.sumValues.int_to_int 3.0 ms 3.3 ms +9.1% [+5.2%, +14.4%] ~same
api.toArray.int_to_int 12.7 ms 14.1 ms +10.9% [+5.9%, +11.7%] ~same
api.values.int_to_int 4.6 ms 4.9 ms +7.0% [+2.7%, +7.8%] ~same
core.bitset.free 0.00 ms 0.00 ms -11.1% [-49.0%, +4.9%] ~same
core.bitset.iter 6.0 ms 6.7 ms +7.1% [-2.3%, +12.9%] ~same
core.bitset.read 4.3 ms 4.6 ms +2.6% [-3.1%, +11.2%] ~same
core.bitset.write 6.3 ms 6.2 ms -3.3% [-10.5%, +4.1%] ~same
core.int_to_int.free 0.17 ms 0.17 ms -4.9% [-11.5%, +19.9%] ~same
core.int_to_int.iter 6.8 ms 7.5 ms +10.4% [-4.0%, +17.1%] ~same
core.int_to_int.read 4.6 ms 4.9 ms +5.3% [-9.7%, +18.4%] ~same
core.int_to_int.write 9.5 ms 9.6 ms -0.2% [-8.4%, +11.4%] ~same
core.int_to_mixed.free 5.3 ms 5.5 ms +5.5% [-1.2%, +20.5%] ~same
core.int_to_mixed.iter 7.0 ms 7.2 ms +3.0% [-5.2%, +9.4%] ~same
core.int_to_mixed.read 4.9 ms 5.1 ms +3.2% [-3.1%, +9.0%] ~same
core.int_to_mixed.write 12.8 ms 13.2 ms +2.9% [-4.0%, +11.2%] ~same
core.int_to_packed.free 3.3 ms 3.5 ms +7.3% [-4.6%, +15.5%] ~same
core.int_to_packed.iter 14.8 ms 15.4 ms +3.0% [+1.8%, +7.3%] ~same
core.int_to_packed.read 12.2 ms 12.4 ms -3.6% [-12.3%, +6.7%] ~same
core.int_to_packed.write 17.9 ms 17.5 ms -1.4% [-7.4%, +1.0%] ~same
core.string_to_int.free 9.4 ms 9.4 ms -1.9% [-2.8%, -0.6%] ~same
core.string_to_int.iter 24.7 ms 24.1 ms -1.4% [-8.1%, +0.1%] ~same
core.string_to_int.read 15.5 ms 15.4 ms -0.2% [-7.6%, +0.8%] ~same
core.string_to_int.write 38.2 ms 37.5 ms -2.9% [-5.2%, +0.3%] ~same
core.string_to_int_adaptive.free 16.2 ms 16.2 ms -0.1% [-2.1%, +1.4%] ~same
core.string_to_int_adaptive.iter 30.6 ms 29.9 ms -3.2% [-5.5%, +0.1%] ~same
core.string_to_int_adaptive.read 8.8 ms 9.0 ms +2.2% [-1.6%, +10.3%] ~same
core.string_to_int_adaptive.write 63.8 ms 64.8 ms +0.9% [-1.1%, +17.7%] ~same
core.string_to_int_hash.free 16.4 ms 16.3 ms -0.2% [-9.4%, +10.4%] ~same
core.string_to_int_hash.iter 30.6 ms 30.0 ms -0.8% [-3.5%, +8.0%] ~same
core.string_to_int_hash.read 9.1 ms 9.4 ms +0.3% [-4.5%, +10.4%] ~same
core.string_to_int_hash.write 65.5 ms 66.2 ms -0.3% [-5.9%, +13.2%] ~same
core.string_to_mixed.free 29.0 ms 28.5 ms -3.8% [-4.9%, -2.4%] ~same
core.string_to_mixed.iter 25.7 ms 25.1 ms -2.2% [-11.2%, -0.4%] ~same
core.string_to_mixed.read 16.4 ms 16.6 ms +1.5% [-6.2%, +4.0%] ~same
core.string_to_mixed.write 47.6 ms 46.4 ms -0.1% [-6.2%, +0.9%] ~same
core.string_to_mixed_adaptive.free 56.9 ms 58.2 ms -1.2% [-13.5%, +11.3%] ~same
core.string_to_mixed_adaptive.iter 32.9 ms 32.0 ms -3.0% [-3.9%, +8.7%] ~same
core.string_to_mixed_adaptive.read 9.1 ms 9.4 ms +3.0% [+1.8%, +7.3%] ~same
core.string_to_mixed_adaptive.write 101.2 ms 102.2 ms -0.8% [-3.8%, +5.7%] ~same
core.string_to_mixed_hash.free 46.5 ms 47.1 ms +0.1% [-2.7%, +10.4%] ~same
core.string_to_mixed_hash.iter 33.0 ms 33.4 ms 0.0% [-3.1%, +5.9%] ~same
core.string_to_mixed_hash.read 9.6 ms 9.8 ms +0.7% [-2.7%, +6.7%] ~same
core.string_to_mixed_hash.write 94.8 ms 93.0 ms -1.6% [-8.0%, +4.5%] ~same
4 benchmarks not evaluated (within-arm scatter exceeded the effect being claimed)
Benchmark Baseline Current Delta 95% CI Status
adv.optiter.increment.string_to_int_hash.default 24.7 ms 22.1 ms -7.7% [-34.0%, +0.5%] — unstable
adv.optiter.overwrite.string_to_int_hash.default 31.0 ms 29.7 ms -11.1% [-26.1%, +6.7%] — unstable
adv.optiter.ratio.foreach.string_to_int_hash 48.6 ms 51.8 ms +11.5% [+3.5%, +37.4%] — unstable
adv.optiter.ratio.overwrite.string_to_int_hash 116.7 ms 120.7 ms +8.2% [-10.0%, +32.6%] — unstable

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                           17.86         11.55         19.79          0.01    < 504.0 KB
    PHP array                       7.03          4.84          3.06          0.33        8.2 MB
  INT_TO_INT                       24.13         11.39         21.53          0.20        4.4 MB
    PHP array                       7.45          3.91          3.43          0.34        8.2 MB
  INT_TO_MIXED                     35.07         16.70         22.58         14.85       40.4 MB
    PHP array                      11.11          5.90          4.22          4.14       38.2 MB
  INT_TO_PACKED                    47.74         29.92         40.30         10.12       16.4 MB
    PHP array                      10.52          6.06          4.18          4.34       38.2 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                    93.49         36.84         56.75         18.94       10.3 MB
    PHP array                      16.15          7.33          4.87          1.27       38.2 MB
  STRING_TO_MIXED                 117.17         40.84         67.24         64.10       46.4 MB
    PHP array                      19.51         10.02          6.35          7.06       68.1 MB
  STR_TO_INT_HASH                 168.87         24.86         68.46         41.70       31.5 MB
    PHP array                      16.48          7.60          4.85          1.12       38.1 MB
  STR_TO_MIX_HASH                 233.76         28.15         92.38        123.87       67.5 MB
    PHP array                      19.76         10.61          6.76          7.00       68.1 MB
  STR_INT_ADAPTIVE                166.24         24.50         67.66         41.70       31.6 MB
    PHP array                      16.49          7.38          4.88          1.13       38.2 MB
  STR_MIX_ADAPTIVE                239.69         27.20         91.44        118.25       67.6 MB
    PHP array                      19.58         10.20          6.47          6.83       68.2 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               19.59         29.43        1.5x
  fromArray()                            19.57         29.43        1.5x
  toArray()                              26.19         40.62        1.6x
  getAll(50100 keys)                      1.07          2.00        1.9x
  increment(500000 ops/1000 keys)         23.66         10.96        0.5x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      12.01         26.16        2.2x
  values() INT_TO_INT                    13.20         25.81        2.0x
  keys($lo,$hi) INT_TO_INT                1.07         26.87       25.2x
    vs slice()->keys()                    2.99             -        2.8x
  size($lo,$hi) INT x1000                 0.78             -        O(1)
  size($lo,$hi) STRING_TO_INT             3.81          5.17        1.4x
  sumValues() INT_TO_INT                  8.21         20.79        2.5x
  populationCount() INT_TO_INT            0.00         22.67   283346.3x
  deleteRange() INT_TO_INT mid50%         39.17         56.70        1.4x
  equals() INT_TO_INT                    11.70         50.00        4.3x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         30.75          5.06        0.2x
  intersect() BITSET                     13.37          5.56        0.4x
  diff() BITSET                          13.32          3.34        0.3x
  xor() BITSET                           26.37         11.33        0.4x
  union() INT_TO_INT                     43.27         90.13        2.1x
  intersect() INT_TO_INT                 18.02         43.14        2.4x
  diff() INT_TO_INT                      17.64         44.87        2.5x
  xor() INT_TO_INT                       35.77         90.24        2.5x
  union() STRING_TO_INT                 281.00        318.83        1.1x
  intersect() STRING_TO_INT             116.79        138.85        1.2x
  diff() STRING_TO_INT                  113.21        137.82        1.2x
  mergeWith() INT_TO_INT                 52.01         67.45        1.3x
  mergeWith() STRING_TO_INT             242.66        258.94        1.1x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   29.10         24.40        0.8x
  forEach() STRING_TO_INT                64.87         60.07        0.9x
  filter() INT_TO_INT (50% pass)         44.47         39.92        0.9x
  filter() STRING_TO_INT (50% pass)        123.37        115.36        0.9x
  map() INT_TO_INT (*2)                  48.68         47.80        1.0x
  map() STRING_TO_INT (*2)              165.47        161.68        1.0x

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

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    48.82         32.52         66.6%
  values() HASH kl16                     42.00         23.79         56.6%
  toArray() HASH kl16                    57.11         32.14         56.3%
  overwrite() HASH kl16                  37.76         40.02        106.0%
  increment() HASH kl16                  33.75         38.26        113.4%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40                60.34         41.13         68.2%
  values() ADAPTIVE kl40                 53.57         32.25         60.2%
  toArray() ADAPTIVE kl40                65.47         43.76         66.8%
  overwrite() ADAPTIVE kl40              52.27         48.54         92.9%
    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)          73.60 ms      121.66 ms      113.39 ms       1.7x
  read (random access)         19.07 ms       15.62 ms       16.31 ms       1.2x
  foreach (iterate all)        50.28 ms       82.39 ms       83.81 ms       1.6x

── 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.07 ms      205.50 ms      205.83 ms       1.7x
  read (random access)         42.16 ms       37.21 ms       37.97 ms       1.1x
  foreach (iterate all)        86.54 ms       96.58 ms       95.79 ms       1.1x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-08-23 05:59:52
  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                           23.01         19.55         22.78          0.01    < 240.0 KB
    PHP array                      15.95         10.19          8.34          0.80        9.2 MB
  INT_TO_INT                       37.18         20.47         23.46          1.03        4.4 MB
    PHP array                      15.78         10.24          7.21          0.74        9.3 MB
  INT_TO_MIXED                     52.14         18.85         23.82         20.33       42.6 MB
    PHP array                      18.51         10.74          7.87          3.66       38.2 MB
  INT_TO_PACKED                    71.94         47.71         51.60         15.04       17.2 MB
    PHP array                      19.95         10.80          7.89          3.82       38.2 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                   154.22         67.08         79.09         34.89       10.5 MB
    PHP array                      36.00         12.65          8.33          3.46       38.0 MB
  STRING_TO_MIXED                 198.42         68.83         98.97        107.56       48.7 MB
    PHP array                      35.14         13.32          8.81          6.97       69.0 MB
  STR_TO_INT_HASH                 274.11         47.84        116.74         66.92       32.4 MB
    PHP array                      33.04         12.78          8.45          3.64       38.0 MB
  STR_TO_MIX_HASH                 423.45         50.01        174.98        215.58       70.6 MB
    PHP array                      35.29         13.23          8.41          8.60       69.0 MB
  STR_INT_ADAPTIVE                267.21         46.80        113.24         67.13       32.3 MB
    PHP array                      33.08         12.64          8.60          2.67       37.9 MB
  STR_MIX_ADAPTIVE                442.53         48.91        183.75        235.42       70.5 MB
    PHP array                      34.48         13.04          8.55          9.03       69.0 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               33.20         46.41        1.4x
  fromArray()                            32.56         46.41        1.4x
  toArray()                              32.77         54.03        1.6x
  getAll(50100 keys)                      2.11          3.57        1.7x
  increment(500000 ops/1000 keys)         36.62         26.57        0.7x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      14.61         33.02        2.3x
  values() INT_TO_INT                    15.09         34.25        2.3x
  keys($lo,$hi) INT_TO_INT                0.95         33.65       35.5x
    vs slice()->keys()                    4.24             -        4.5x
  size($lo,$hi) INT x1000                 0.56             -        O(1)
  size($lo,$hi) STRING_TO_INT             6.08          7.69        1.3x
  sumValues() INT_TO_INT                  7.01         24.38        3.5x
  populationCount() INT_TO_INT            0.00         26.32   263210.0x
  deleteRange() INT_TO_INT mid50%         57.34         85.43        1.5x
  equals() INT_TO_INT                    10.83         60.65        5.6x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         35.16         11.50        0.3x
  intersect() BITSET                     16.06         10.42        0.6x
  diff() BITSET                          16.49          4.83        0.3x
  xor() BITSET                           32.97         23.94        0.7x
  union() INT_TO_INT                     59.13        109.25        1.8x
  intersect() INT_TO_INT                 21.26         49.35        2.3x
  diff() INT_TO_INT                      20.41         52.84        2.6x
  xor() INT_TO_INT                       41.59        107.18        2.6x
  union() STRING_TO_INT                 386.00        430.54        1.1x
  intersect() STRING_TO_INT             166.21        190.09        1.1x
  diff() STRING_TO_INT                  160.15        184.76        1.2x
  mergeWith() INT_TO_INT                 66.94         86.73        1.3x
  mergeWith() STRING_TO_INT             353.03        379.59        1.1x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   32.63         28.75        0.9x
  forEach() STRING_TO_INT                91.16         88.77        1.0x
  filter() INT_TO_INT (50% pass)         48.29         53.38        1.1x
  filter() STRING_TO_INT (50% pass)        175.49        163.51        0.9x
  map() INT_TO_INT (*2)                  55.07         57.47        1.0x
  map() STRING_TO_INT (*2)              245.17        235.15        1.0x

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

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    96.84         61.77         63.8%
  values() HASH kl16                     86.66         50.62         58.4%
  toArray() HASH kl16                   104.62         65.89         63.0%
  overwrite() HASH kl16                  62.64         79.43        126.8%
  increment() HASH kl16                  64.67         82.53        127.6%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40               112.83         55.04         48.8%
  values() ADAPTIVE kl40                101.61         43.17         42.5%
  toArray() ADAPTIVE kl40               126.71         67.36         53.2%
  overwrite() ADAPTIVE kl40              82.51         96.86        117.4%
    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)         110.53 ms      206.22 ms      181.34 ms       1.9x
  read (random access)         23.91 ms       21.82 ms       20.31 ms       1.2x
  foreach (iterate all)        69.07 ms      116.35 ms      114.53 ms       1.7x

── 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)          193.70 ms      345.70 ms      344.76 ms       1.8x
  read (random access)         49.24 ms       43.52 ms       43.60 ms       1.1x
  foreach (iterate all)       119.05 ms      181.22 ms      184.05 ms       1.5x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-08-23 05:59:53
  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

@orieg
orieg merged commit c14718a into main Aug 23, 2026
58 of 64 checks passed
@orieg
orieg deleted the release/2.7.1 branch August 23, 2026 06:08
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.

1 participant