Skip to content

release: 2.8.0 - #226

Merged
orieg merged 1 commit into
mainfrom
release/2.8.0
Oct 8, 2026
Merged

orieg merged 1 commit into
mainfrom
release/2.8.0

Conversation

@orieg

@orieg orieg commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Summary

Prepare the 2.8.0 release: bump the version files in lockstep and record the
notes. Follows the same shape as the 2.7.1 (#199) and 2.7.0 (#195) release
commits — only php_judy.h and package.xml change.

File Change
php_judy.h PHP_JUDY_VERSION 2.7.1 → 2.8.0
package.xml <date> → 2026-10-08; <release>/<api> → 2.8.0; new <notes>; 2.7.1 notes moved into <changelog> as the top entry

What 2.8.0 ships (unreleased since v2.7.1)

Craftsmanship hardening sprint (#217), fixing several memory-safety defects and
tightening behavior:

  • SECURITY FIX — $j[] = ... on a string-keyed array (incl. STRING_TO_ENTRY) no longer crashes; the append guard enumerates all seven string-keyed types.
  • SECURITY FIX — forEach()/filter()/map() on STRING_TO_ENTRY read values through the entry accessor instead of reinterpreting a cache-entry struct as a zval; equals() gained a matching ENTRY branch.
  • SECURITY FIX — set() rejects keys longer than the 64 KB key buffer before taking a slot (heap overflow); deleteRange() on string-keyed ENTRY arrays now frees the struct and its value reference instead of leaking both.
  • FIX — expired STRING_TO_ENTRY values are hidden from reads/traversals while count()/size() stay raw-counter O(1) until pruneExpired(); mergeWith() copies TTL/flags verbatim; get_gc() collects stored ENTRY values.
  • FIX — wrong-category range bounds now throw TypeError instead of silently coercing to a wrong-range read.
  • FIX — __unserialize() validates type, payload shape and optimizeIteration strictly before touching the object, so a malformed payload throws without destroying an already-populated array, and an out-of-range type throws instead of emitting an E_WARNING (fix(serialize): strict validation in __unserialize() rejection paths #225).

Release-ceremony checklist

  • php_judy.h version bumped (step 1)
  • package.xml version/date/notes + previous notes moved to <changelog> (step 2)
  • Version lockstep verified (php_judy.h and package.xml both 2.8.0); xmllint clean
  • BENCHMARK.md stamps reviewed (left at their Craftsmanship hardening sprint: security, resilience, coverage, CI gate, docs #217-reconciled, honest state — see PR discussion)
  • Step 4 bench-baseline pin: left untouched by design; it is the post-publish step-7 baseline move
  • Steps 5–7 (tag, GitHub release, PECL upload, post-publish pin+baseline commit) are manual after merge

Base is origin/main @ 57df19c, whose main CI run is success.

Bump PHP_JUDY_VERSION and package.xml to 2.8.0 (date 2026-10-08), move the
2.7.1 notes into <changelog>, and add 2.8.0 notes covering the craftsmanship
hardening sprint (#217) and the strict __unserialize() validation (#225).

Version lockstep: php_judy.h and package.xml both read 2.8.0.
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

discipline: passed

Base main (origin/main) (merge base 57df19c242) · 27 gate(s) passed · 2174 item(s) examined · 0 warning(s) · 0 override(s)

discipline explain <gate> prints what a gate checks and how a finding is lifted. This comment is updated on every run; the check's status is the verdict.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results

PHP Platform Arch TS Tests Pass Fail Skip Duration
✅ 8.1 Linux x64 - 258 258 0 0 7.9s
✅ 8.2 Linux x64 - 258 258 0 0 8.2s
✅ 8.3 Linux x64 - 258 258 0 0 10.3s
✅ 8.4 Linux x64 - 258 258 0 0 7.7s
✅ 8.5 Linux x64 - 258 258 0 0 10.1s
✅ 🧪 8.6 Linux x64 - 258 258 0 0 13.6s
✅ 8.1 Linux (Expanse) x64 - 258 258 0 0 9.5s
✅ 8.2 Linux (Expanse) x64 - 258 258 0 0 7.5s
✅ 8.3 Linux (Expanse) x64 - 258 258 0 0 10.3s
✅ 8.4 Linux (Expanse) x64 - 258 258 0 0 9.4s
✅ 8.5 Linux (Expanse) x64 - 258 258 0 0 7.3s
✅ 🧪 8.6 Linux (Expanse) x64 - 258 258 0 0 8.5s
✅ 8.1 Windows x64 nts 258 258 0 0 15.6s
✅ 8.2 Windows x64 nts 258 258 0 0 14.3s
✅ 8.3 Windows x64 nts 258 258 0 0 17.2s
✅ 8.4 Windows x64 nts 258 258 0 0 17.1s
✅ 8.5 Windows x64 nts 258 258 0 0 15.5s
✅ 🧪 8.6 Windows x64 nts 258 258 0 0 14.4s
✅ 8.1 Windows (Expanse) x64 nts 258 258 0 0 15.5s
✅ 8.2 Windows (Expanse) x64 nts 258 258 0 0 16.0s
✅ 8.3 Windows (Expanse) x64 nts 258 258 0 0 14.5s
✅ 8.4 Windows (Expanse) x64 nts 258 258 0 0 10.6s
✅ 8.5 Windows (Expanse) x64 nts 258 258 0 0 15.2s
✅ 🧪 8.6 Windows (Expanse) x64 nts 258 258 0 0 14.4s
✅ Total 6192 6192 0 0

Benchmark Results

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

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 < 464.0 KB 8.0 MB > 18x smaller
Int to Int 4.4 MB 8.1 MB 1.8x smaller
Int to Mixed 40.2 MB 38.1 MB 1.06x larger
Int to Packed 16.3 MB 38.1 MB 2.3x smaller
String to Int 10.3 MB 38.0 MB 3.7x smaller
String to Mixed 46.2 MB 68.0 MB 1.5x smaller
String to Int (Hash) 31.5 MB 38.0 MB 1.2x smaller
String to Mixed (Hash) 67.4 MB 68.1 MB about the same
String to Int (Adaptive) 31.4 MB 38.1 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 464.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.0 ms 29.4 ms 1.5x
fromArray() 20.0 ms 29.4 ms 1.5x
toArray() 23.2 ms 36.4 ms 1.6x
getAll() 1.2 ms 2.1 ms 1.9x
keys() 11.3 ms 25.0 ms 2.2x
values() 12.6 ms 25.2 ms 2.0x
sumValues() 8.0 ms 21.1 ms 2.6x
populationCount() 0.00 ms 22.8 ms O(1) vs O(n)
deleteRange() 39.5 ms 55.2 ms 1.4x
equals() 11.0 ms 48.8 ms 4.4x
union() Bitset 31.7 ms 5.5 ms 0.2x
intersect() Bitset 13.9 ms 6.0 ms 0.4x
diff() Bitset 13.6 ms 3.4 ms 0.2x
xor() Bitset 27.6 ms 11.7 ms 0.4x
mergeWith() Int→Int 50.1 ms 66.9 ms 1.3x
mergeWith() Str→Int 241.0 ms 259.3 ms 1.1x
forEach() Int→Int 27.1 ms 22.7 ms 0.8x
filter() Int→Int 42.8 ms 39.3 ms 0.9x
map() Int→Int 47.9 ms 47.2 ms ~1.0x
forEach() Str→Int 66.3 ms 59.0 ms 0.9x
filter() Str→Int 121.3 ms 114.0 ms 0.9x
map() Str→Int 159.4 ms 157.5 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 18.1 ms 7.6 ms 0.4x
Bitset Read 11.9 ms 4.2 ms 0.4x
Bitset Iter 19.0 ms 3.3 ms 0.2x
Bitset Free 0.01 ms 0.36 ms 59.3x
Int to Int Write 25.1 ms 7.4 ms 0.3x
Int to Int Read 12.9 ms 4.2 ms 0.3x
Int to Int Iter 20.0 ms 3.3 ms 0.2x
Int to Int Free 0.83 ms 0.38 ms 0.5x
Int to Mixed Write 34.8 ms 11.5 ms 0.3x
Int to Mixed Read 14.4 ms 6.0 ms 0.4x
Int to Mixed Iter 21.1 ms 4.6 ms 0.2x
Int to Mixed Free 14.6 ms 4.5 ms 0.3x
String to Int Write 90.9 ms 17.1 ms 0.2x
String to Int Read 37.0 ms 7.6 ms 0.2x
String to Int Iter 56.6 ms 4.9 ms 0.1x
String to Int Free 18.3 ms 1.2 ms 0.1x
String to Int (Adaptive) Write 168.8 ms 17.6 ms 0.1x
String to Int (Adaptive) Read 25.3 ms 7.3 ms 0.3x
String to Int (Adaptive) Iter 67.9 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 43.1 ms 1.4 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 18.1 ms 7.6 ms 0.4x
Bitset Read 11.9 ms 4.2 ms 0.4x
Bitset Iter 19.0 ms 3.3 ms 0.2x
Bitset Free 0.01 ms 0.36 ms 59.3x
Int to Int Write 25.1 ms 7.4 ms 0.3x
Int to Int Read 12.9 ms 4.2 ms 0.3x
Int to Int Iter 20.0 ms 3.3 ms 0.2x
Int to Int Free 0.83 ms 0.38 ms 0.5x
Int to Mixed Write 34.8 ms 11.5 ms 0.3x
Int to Mixed Read 14.4 ms 6.0 ms 0.4x
Int to Mixed Iter 21.1 ms 4.6 ms 0.2x
Int to Mixed Free 14.6 ms 4.5 ms 0.3x
Int to Packed Write 48.2 ms 11.2 ms 0.2x
Int to Packed Read 30.8 ms 6.4 ms 0.2x
Int to Packed Iter 38.6 ms 4.5 ms 0.1x
Int to Packed Free 9.1 ms 4.8 ms 0.5x
String to Int Write 90.9 ms 17.1 ms 0.2x
String to Int Read 37.0 ms 7.6 ms 0.2x
String to Int Iter 56.6 ms 4.9 ms 0.1x
String to Int Free 18.3 ms 1.2 ms 0.1x
String to Mixed Write 116.7 ms 21.4 ms 0.2x
String to Mixed Read 41.2 ms 10.0 ms 0.2x
String to Mixed Iter 67.7 ms 7.0 ms 0.1x
String to Mixed Free 66.0 ms 7.3 ms 0.1x
String to Int (Hash) Write 170.1 ms 17.5 ms 0.1x
String to Int (Hash) Read 25.3 ms 7.3 ms 0.3x
String to Int (Hash) Iter 71.2 ms 4.9 ms 0.1x
String to Int (Hash) Free 43.6 ms 1.3 ms 0.0x
String to Mixed (Hash) Write 236.2 ms 19.8 ms 0.1x
String to Mixed (Hash) Read 28.0 ms 9.9 ms 0.4x
String to Mixed (Hash) Iter 90.7 ms 6.8 ms 0.1x
String to Mixed (Hash) Free 121.2 ms 7.5 ms 0.1x
String to Int (Adaptive) Write 168.8 ms 17.6 ms 0.1x
String to Int (Adaptive) Read 25.3 ms 7.3 ms 0.3x
String to Int (Adaptive) Iter 67.9 ms 4.9 ms 0.1x
String to Int (Adaptive) Free 43.1 ms 1.4 ms 0.0x
String to Mixed (Adaptive) Write 254.6 ms 20.3 ms 0.1x
String to Mixed (Adaptive) Read 27.8 ms 9.8 ms 0.4x
String to Mixed (Adaptive) Iter 100.2 ms 7.0 ms 0.1x
String to Mixed (Adaptive) Free 128.4 ms 7.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 15.7 ms (0.4x) 13.8 ms (0.4x) 19.6 ms (0.4x) 9.5 ms (0.4x) 17.9 ms (0.4x) 18.1 ms (0.4x)
Bitset Read 9.6 ms (0.4x) 9.5 ms (0.3x) 12.1 ms (0.4x) 5.5 ms (0.4x) 11.7 ms (0.3x) 11.9 ms (0.4x)
Int to Int Write 20.0 ms (0.3x) 19.5 ms (0.3x) 24.6 ms (0.3x) 13.5 ms (0.3x) 23.9 ms (0.3x) 25.1 ms (0.3x)
Int to Int Read 9.6 ms (0.4x) 9.7 ms (0.3x) 11.3 ms (0.4x) 6.5 ms (0.3x) 11.4 ms (0.3x) 12.9 ms (0.3x)
Int to Mixed Write 29.2 ms (0.3x) 29.4 ms (0.3x) 34.1 ms (0.3x) 18.2 ms (0.4x) 34.5 ms (0.3x) 34.8 ms (0.3x)
Int to Mixed Read 11.1 ms (0.4x) 10.2 ms (0.4x) 14.2 ms (0.4x) 7.2 ms (0.4x) 13.6 ms (0.4x) 14.4 ms (0.4x)
Int to Packed Write 41.8 ms (0.2x) 38.9 ms (0.2x) 45.5 ms (0.3x) 25.6 ms (0.2x) 49.0 ms (0.2x) 48.2 ms (0.2x)
Int to Packed Read 22.7 ms (0.2x) 25.7 ms (0.2x) 28.1 ms (0.2x) 14.6 ms (0.2x) 30.0 ms (0.2x) 30.8 ms (0.2x)
String to Int Write 71.7 ms (0.2x) 85.0 ms (0.2x) 92.2 ms (0.2x) 58.7 ms (0.2x) 90.7 ms (0.2x) 90.9 ms (0.2x)
String to Int Read 31.3 ms (0.2x) 34.4 ms (0.2x) 40.1 ms (0.2x) 24.6 ms (0.2x) 36.7 ms (0.2x) 37.0 ms (0.2x)
String to Mixed Write 91.5 ms (0.2x) 110.0 ms (0.1x) 115.8 ms (0.2x) 76.4 ms (0.2x) 114.3 ms (0.2x) 116.7 ms (0.2x)
String to Mixed Read 33.9 ms (0.2x) 36.2 ms (0.2x) 43.5 ms (0.2x) 27.2 ms (0.3x) 40.3 ms (0.2x) 41.2 ms (0.2x)
String to Int (Hash) Write 139.4 ms (0.1x) 145.9 ms (0.1x) 158.0 ms (0.1x) 104.2 ms (0.1x) 163.7 ms (0.1x) 170.1 ms (0.1x)
String to Int (Hash) Read 21.3 ms (0.3x) 20.5 ms (0.3x) 26.8 ms (0.3x) 15.5 ms (0.3x) 26.2 ms (0.3x) 25.3 ms (0.3x)
String to Mixed (Hash) Write 203.5 ms (0.1x) 220.4 ms (0.1x) 235.9 ms (0.1x) 156.2 ms (0.1x) 226.3 ms (0.1x) 236.2 ms (0.1x)
String to Mixed (Hash) Read 23.7 ms (0.3x) 21.2 ms (0.3x) 30.1 ms (0.3x) 18.1 ms (0.4x) 28.4 ms (0.3x) 28.0 ms (0.4x)
String to Int (Adaptive) Write 136.9 ms (0.1x) 144.1 ms (0.1x) 156.6 ms (0.1x) 98.7 ms (0.1x) 164.2 ms (0.1x) 168.8 ms (0.1x)
String to Int (Adaptive) Read 20.4 ms (0.3x) 20.7 ms (0.3x) 26.1 ms (0.3x) 15.2 ms (0.3x) 24.0 ms (0.3x) 25.3 ms (0.3x)
String to Mixed (Adaptive) Write 208.2 ms (0.1x) 241.2 ms (0.1x) 243.5 ms (0.1x) 173.8 ms (0.1x) 227.1 ms (0.1x) 254.6 ms (0.1x)
String to Mixed (Adaptive) Read 22.5 ms (0.3x) 20.9 ms (0.3x) 29.3 ms (0.3x) 17.6 ms (0.4x) 26.8 ms (0.4x) 27.8 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() 16.6 ms (1.3x) 17.4 ms (1.4x) 21.1 ms (1.3x) 10.7 ms (1.5x) 20.1 ms (1.4x) 20.0 ms (1.5x)
fromArray() 16.8 ms (1.3x) 17.4 ms (1.4x) 21.4 ms (1.3x) 10.6 ms (1.5x) 20.2 ms (1.4x) 20.0 ms (1.5x)
toArray() 17.3 ms (1.6x) 21.0 ms (1.5x) 22.0 ms (1.6x) 12.1 ms (1.7x) 22.5 ms (1.6x) 23.2 ms (1.6x)
getAll() 0.83 ms (1.9x) 1.00 ms (1.7x) 1.1 ms (2.0x) 0.59 ms (1.9x) 1.1 ms (1.8x) 1.2 ms (1.9x)
keys() 10.0 ms (2.1x) 10.2 ms (2.0x) 10.7 ms (2.2x) 5.3 ms (2.4x) 11.7 ms (2.1x) 11.3 ms (2.2x)
values() 11.0 ms (2.0x) 10.6 ms (1.9x) 11.1 ms (2.2x) 6.2 ms (1.8x) 12.3 ms (2.0x) 12.6 ms (2.0x)
sumValues() 7.2 ms (2.4x) 6.9 ms (2.5x) 7.0 ms (2.8x) 3.6 ms (2.5x) 8.0 ms (2.6x) 8.0 ms (2.6x)
populationCount() 0.00 ms (197776x) 0.00 ms (177041x) 0.00 ms (212338x) 0.00 ms (93746x) 0.00 ms (219731x) 0.00 ms (227914x)
deleteRange() 30.8 ms (1.5x) 31.6 ms (1.5x) 37.8 ms (1.5x) 19.9 ms (1.4x) 38.9 ms (1.5x) 39.5 ms (1.4x)
equals() 9.0 ms (4.2x) 10.2 ms (4.1x) 9.8 ms (4.7x) 5.9 ms (3.9x) 12.0 ms (4.1x) 11.0 ms (4.4x)
union() Bitset 25.0 ms (0.2x) 26.8 ms (0.2x) 31.5 ms (0.1x) 15.1 ms (0.2x) 32.8 ms (0.1x) 31.7 ms (0.2x)
intersect() Bitset 11.1 ms (0.4x) 13.1 ms (0.4x) 13.0 ms (0.4x) 6.5 ms (0.5x) 14.8 ms (0.4x) 13.9 ms (0.4x)
diff() Bitset 10.9 ms (0.3x) 13.0 ms (0.2x) 12.6 ms (0.2x) 6.7 ms (0.3x) 15.0 ms (0.2x) 13.6 ms (0.2x)
xor() Bitset 22.0 ms (0.5x) 25.4 ms (0.5x) 25.6 ms (0.4x) 12.9 ms (0.5x) 29.6 ms (0.4x) 27.6 ms (0.4x)
mergeWith() Int→Int 40.2 ms (1.3x) 42.2 ms (1.3x) 52.1 ms (1.3x) 26.8 ms (1.2x) 51.3 ms (1.3x) 50.1 ms (1.3x)
mergeWith() Str→Int 180.9 ms (1.1x) 201.4 ms (1.1x) 228.5 ms (1.1x) 140.1 ms (1.1x) 232.1 ms (1.1x) 241.0 ms (1.1x)
forEach() Int→Int 21.0 ms (0.9x) 25.7 ms (0.7x) 26.8 ms (0.9x) 12.3 ms (0.8x) 27.9 ms (0.9x) 27.1 ms (0.8x)
filter() Int→Int 33.4 ms (0.9x) 37.9 ms (0.8x) 40.0 ms (0.9x) 19.9 ms (0.9x) 47.9 ms (0.9x) 42.8 ms (0.9x)
map() Int→Int 36.2 ms (1.1x) 40.7 ms (0.9x) 45.7 ms (~1.0x) 23.2 ms (1.3x) 50.3 ms (~1.0x) 47.9 ms (~1.0x)
forEach() Str→Int 49.8 ms (0.9x) 57.8 ms (0.9x) 64.5 ms (0.9x) 36.6 ms (0.9x) 65.3 ms (0.9x) 66.3 ms (0.9x)
filter() Str→Int 96.1 ms (~1.0x) 111.4 ms (0.9x) 114.8 ms (~1.0x) 69.6 ms (~1.0x) 119.3 ms (0.9x) 121.3 ms (0.9x)
map() Str→Int 133.8 ms (~1.0x) 149.3 ms (~1.0x) 154.4 ms (~1.0x) 93.1 ms (~1.0x) 160.8 ms (~1.0x) 159.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 23.0 ms (0.7x) 18.6 ms (0.5x) 17.8 ms (0.9x) 22.8 ms (0.5x) 23.1 ms (0.5x) 23.3 ms (0.6x)
Bitset Read 16.3 ms (0.5x) 13.8 ms (0.5x) 14.5 ms (0.5x) 17.7 ms (0.5x) 17.3 ms (0.5x) 16.7 ms (0.5x)
Int to Int Write 34.0 ms (0.4x) 26.1 ms (0.4x) 31.8 ms (0.5x) 37.2 ms (0.3x) 35.7 ms (0.3x) 37.7 ms (0.3x)
Int to Int Read 17.6 ms (0.4x) 13.9 ms (0.5x) 15.3 ms (0.5x) 18.5 ms (0.5x) 19.2 ms (0.5x) 17.2 ms (0.5x)
Int to Mixed Write 45.3 ms (0.5x) 36.6 ms (0.4x) 43.0 ms (0.4x) 53.4 ms (0.3x) 51.5 ms (0.3x) 52.4 ms (0.3x)
Int to Mixed Read 20.1 ms (0.5x) 16.5 ms (0.5x) 17.0 ms (0.5x) 20.6 ms (0.5x) 20.2 ms (0.5x) 18.3 ms (0.5x)
Int to Packed Write 64.0 ms (0.3x) 48.6 ms (0.3x) 59.4 ms (0.3x) 71.2 ms (0.2x) 69.3 ms (0.2x) 69.7 ms (0.2x)
Int to Packed Read 47.3 ms (0.2x) 38.7 ms (0.2x) 42.7 ms (0.2x) 49.6 ms (0.2x) 47.2 ms (0.2x) 46.8 ms (0.2x)
String to Int Write 140.2 ms (0.2x) 113.9 ms (0.2x) 149.4 ms (0.2x) 145.9 ms (0.2x) 145.9 ms (0.2x) 143.2 ms (0.2x)
String to Int Read 63.8 ms (0.2x) 51.0 ms (0.2x) 64.4 ms (0.2x) 63.3 ms (0.2x) 61.4 ms (0.2x) 59.3 ms (0.2x)
String to Mixed Write 182.2 ms (0.2x) 153.4 ms (0.2x) 206.9 ms (0.2x) 193.2 ms (0.2x) 191.9 ms (0.1x) 192.2 ms (0.1x)
String to Mixed Read 65.2 ms (0.2x) 54.4 ms (0.2x) 67.1 ms (0.2x) 65.0 ms (0.2x) 61.8 ms (0.2x) 59.7 ms (0.2x)
String to Int (Hash) Write 247.9 ms (0.1x) 207.9 ms (0.1x) 271.5 ms (0.1x) 252.1 ms (0.1x) 251.0 ms (0.1x) 264.5 ms (0.1x)
String to Int (Hash) Read 43.5 ms (0.3x) 39.4 ms (0.3x) 48.6 ms (0.2x) 45.4 ms (0.3x) 43.6 ms (0.3x) 48.0 ms (0.2x)
String to Mixed (Hash) Write 345.7 ms (0.1x) 304.5 ms (0.1x) 513.9 ms (0.1x) 365.1 ms (0.1x) 354.6 ms (0.1x) 364.6 ms (0.1x)
String to Mixed (Hash) Read 46.2 ms (0.3x) 43.1 ms (0.3x) 52.5 ms (0.2x) 47.7 ms (0.3x) 47.6 ms (0.3x) 48.4 ms (0.3x)
String to Int (Adaptive) Write 243.4 ms (0.1x) 249.4 ms (0.1x) 275.6 ms (0.1x) 256.4 ms (0.1x) 248.6 ms (0.1x) 253.4 ms (0.1x)
String to Int (Adaptive) Read 42.7 ms (0.3x) 39.3 ms (0.3x) 48.9 ms (0.2x) 45.5 ms (0.3x) 43.3 ms (0.3x) 45.0 ms (0.3x)
String to Mixed (Adaptive) Write 364.3 ms (0.1x) 327.0 ms (0.1x) 485.1 ms (0.1x) 390.0 ms (0.1x) 373.2 ms (0.1x) 375.1 ms (0.1x)
String to Mixed (Adaptive) Read 45.8 ms (0.3x) 43.4 ms (0.3x) 51.6 ms (0.2x) 47.2 ms (0.3x) 47.3 ms (0.3x) 46.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.2 ms (1.5x) 24.3 ms (1.4x) 30.2 ms (1.5x) 31.6 ms (1.5x) 30.1 ms (1.5x) 33.0 ms (1.4x)
fromArray() 30.0 ms (1.5x) 24.2 ms (1.4x) 29.9 ms (1.5x) 30.6 ms (1.6x) 30.3 ms (1.5x) 33.0 ms (1.4x)
toArray() 28.0 ms (1.6x) 23.8 ms (1.6x) 35.2 ms (1.4x) 28.1 ms (1.7x) 27.2 ms (1.8x) 27.9 ms (1.7x)
getAll() 1.4 ms (2.2x) 1.1 ms (2.3x) 1.9 ms (1.8x) 1.7 ms (2.0x) 1.6 ms (2.0x) 1.7 ms (1.8x)
keys() 15.7 ms (2.3x) 10.1 ms (2.4x) 14.2 ms (2.2x) 13.1 ms (2.5x) 13.4 ms (2.6x) 12.8 ms (2.4x)
values() 16.1 ms (2.3x) 10.5 ms (2.3x) 15.0 ms (2.0x) 13.8 ms (2.3x) 13.1 ms (2.7x) 12.9 ms (2.4x)
sumValues() 7.1 ms (3.3x) 5.4 ms (3.6x) 6.2 ms (3.5x) 7.4 ms (3.3x) 8.0 ms (3.4x) 7.1 ms (3.7x)
populationCount() 0.00 ms (244606x) 0.00 ms (196460x) 0.00 ms (222209x) 0.00 ms (129209x) 0.00 ms (279349x) 0.00 ms (274179x)
deleteRange() 53.4 ms (1.5x) 40.8 ms (1.5x) 49.8 ms (1.5x) 56.2 ms (1.5x) 58.1 ms (1.5x) 58.5 ms (1.4x)
equals() 11.2 ms (5.6x) 8.5 ms (5.8x) 10.0 ms (5.7x) 11.0 ms (6.0x) 11.4 ms (5.7x) 11.2 ms (5.5x)
union() Bitset 35.4 ms (0.4x) 29.8 ms (0.2x) 30.2 ms (0.4x) 36.7 ms (0.2x) 36.5 ms (0.2x) 38.5 ms (0.2x)
intersect() Bitset 16.1 ms (0.5x) 12.3 ms (0.6x) 14.2 ms (0.9x) 19.8 ms (0.4x) 16.9 ms (0.5x) 18.5 ms (0.4x)
diff() Bitset 15.6 ms (0.4x) 12.3 ms (0.3x) 13.9 ms (0.4x) 17.3 ms (0.3x) 16.4 ms (0.3x) 18.4 ms (0.2x)
xor() Bitset 31.5 ms (0.8x) 24.5 ms (0.6x) 28.4 ms (~1.0x) 34.4 ms (0.5x) 33.4 ms (0.5x) 33.3 ms (0.5x)
mergeWith() Int→Int 64.2 ms (1.3x) 48.3 ms (1.4x) 56.7 ms (1.3x) 65.4 ms (1.4x) 64.7 ms (1.4x) 70.3 ms (1.3x)
mergeWith() Str→Int 315.8 ms (1.1x) 255.2 ms (1.1x) 304.0 ms (1.1x) 342.1 ms (1.1x) 340.4 ms (1.1x) 343.4 ms (1.1x)
forEach() Int→Int 28.9 ms (~1.0x) 21.6 ms (~1.0x) 28.5 ms (~1.0x) 29.9 ms (~1.0x) 35.3 ms (0.9x) 31.5 ms (0.9x)
filter() Int→Int 46.5 ms (1.1x) 33.3 ms (1.1x) 42.2 ms (1.1x) 50.1 ms (~1.0x) 55.3 ms (0.9x) 49.6 ms (~1.0x)
map() Int→Int 52.0 ms (1.1x) 38.5 ms (1.1x) 46.6 ms (1.1x) 57.4 ms (~1.0x) 61.2 ms (1.1x) 57.9 ms (~1.0x)
forEach() Str→Int 79.0 ms (~1.0x) 65.0 ms (~1.0x) 80.0 ms (0.9x) 84.3 ms (~1.0x) 90.0 ms (0.9x) 85.6 ms (~1.0x)
filter() Str→Int 155.0 ms (~1.0x) 120.9 ms (~1.0x) 158.6 ms (~1.0x) 161.9 ms (~1.0x) 160.7 ms (~1.0x) 165.9 ms (0.9x)
map() Str→Int 213.4 ms (~1.0x) 168.0 ms (~1.0x) 222.6 ms (~1.0x) 221.1 ms (~1.0x) 224.3 ms (~1.0x) 232.3 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 < 104.0 KB 9.2 MB > 90x smaller
Int to Int 4.5 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.2 MB 37.9 MB 1.2x smaller
String to Mixed (Hash) 70.4 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.4 MB 68.9 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 104.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.0 ms 46.6 ms 1.4x
fromArray() 33.0 ms 46.6 ms 1.4x
toArray() 27.9 ms 46.3 ms 1.7x
getAll() 1.7 ms 3.0 ms 1.8x
keys() 12.8 ms 30.9 ms 2.4x
values() 12.9 ms 30.6 ms 2.4x
sumValues() 7.1 ms 26.5 ms 3.7x
populationCount() 0.00 ms 27.4 ms O(1) vs O(n)
deleteRange() 58.5 ms 83.3 ms 1.4x
equals() 11.2 ms 61.7 ms 5.5x
union() Bitset 38.5 ms 9.0 ms 0.2x
intersect() Bitset 18.5 ms 7.7 ms 0.4x
diff() Bitset 18.4 ms 4.5 ms 0.2x
xor() Bitset 33.3 ms 17.3 ms 0.5x
mergeWith() Int→Int 70.3 ms 93.7 ms 1.3x
mergeWith() Str→Int 343.4 ms 374.1 ms 1.1x
forEach() Int→Int 31.5 ms 29.2 ms 0.9x
filter() Int→Int 49.6 ms 49.5 ms ~1.0x
map() Int→Int 57.9 ms 59.6 ms ~1.0x
forEach() Str→Int 85.6 ms 84.5 ms ~1.0x
filter() Str→Int 165.9 ms 157.3 ms 0.9x
map() Str→Int 232.3 ms 225.4 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.3 ms 12.9 ms 0.6x
Bitset Read 16.7 ms 8.2 ms 0.5x
Bitset Iter 24.1 ms 7.2 ms 0.3x
Bitset Free 0.01 ms 0.53 ms 68.6x
Int to Int Write 37.7 ms 12.8 ms 0.3x
Int to Int Read 17.2 ms 8.3 ms 0.5x
Int to Int Iter 24.3 ms 7.2 ms 0.3x
Int to Int Free 0.85 ms 0.54 ms 0.6x
Int to Mixed Write 52.4 ms 16.4 ms 0.3x
Int to Mixed Read 18.3 ms 9.5 ms 0.5x
Int to Mixed Iter 24.6 ms 7.3 ms 0.3x
Int to Mixed Free 20.9 ms 6.3 ms 0.3x
String to Int Write 143.2 ms 25.5 ms 0.2x
String to Int Read 59.3 ms 11.9 ms 0.2x
String to Int Iter 76.7 ms 7.7 ms 0.1x
String to Int Free 30.7 ms 1.9 ms 0.1x
String to Int (Adaptive) Write 253.4 ms 24.8 ms 0.1x
String to Int (Adaptive) Read 45.0 ms 11.7 ms 0.3x
String to Int (Adaptive) Iter 111.0 ms 8.1 ms 0.1x
String to Int (Adaptive) Free 62.2 ms 2.0 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.3 ms 12.9 ms 0.6x
Bitset Read 16.7 ms 8.2 ms 0.5x
Bitset Iter 24.1 ms 7.2 ms 0.3x
Bitset Free 0.01 ms 0.53 ms 68.6x
Int to Int Write 37.7 ms 12.8 ms 0.3x
Int to Int Read 17.2 ms 8.3 ms 0.5x
Int to Int Iter 24.3 ms 7.2 ms 0.3x
Int to Int Free 0.85 ms 0.54 ms 0.6x
Int to Mixed Write 52.4 ms 16.4 ms 0.3x
Int to Mixed Read 18.3 ms 9.5 ms 0.5x
Int to Mixed Iter 24.6 ms 7.3 ms 0.3x
Int to Mixed Free 20.9 ms 6.3 ms 0.3x
Int to Packed Write 69.7 ms 16.1 ms 0.2x
Int to Packed Read 46.8 ms 9.4 ms 0.2x
Int to Packed Iter 53.0 ms 7.0 ms 0.1x
Int to Packed Free 14.2 ms 4.6 ms 0.3x
String to Int Write 143.2 ms 25.5 ms 0.2x
String to Int Read 59.3 ms 11.9 ms 0.2x
String to Int Iter 76.7 ms 7.7 ms 0.1x
String to Int Free 30.7 ms 1.9 ms 0.1x
String to Mixed Write 192.2 ms 28.3 ms 0.1x
String to Mixed Read 59.7 ms 13.7 ms 0.2x
String to Mixed Iter 88.9 ms 8.9 ms 0.1x
String to Mixed Free 98.5 ms 7.9 ms 0.1x
String to Int (Hash) Write 264.5 ms 24.9 ms 0.1x
String to Int (Hash) Read 48.0 ms 11.7 ms 0.2x
String to Int (Hash) Iter 111.9 ms 8.1 ms 0.1x
String to Int (Hash) Free 63.4 ms 1.9 ms 0.0x
String to Mixed (Hash) Write 364.6 ms 28.1 ms 0.1x
String to Mixed (Hash) Read 48.4 ms 13.3 ms 0.3x
String to Mixed (Hash) Iter 135.5 ms 8.9 ms 0.1x
String to Mixed (Hash) Free 184.8 ms 7.9 ms 0.0x
String to Int (Adaptive) Write 253.4 ms 24.8 ms 0.1x
String to Int (Adaptive) Read 45.0 ms 11.7 ms 0.3x
String to Int (Adaptive) Iter 111.0 ms 8.1 ms 0.1x
String to Int (Adaptive) Free 62.2 ms 2.0 ms 0.0x
String to Mixed (Adaptive) Write 375.1 ms 28.2 ms 0.1x
String to Mixed (Adaptive) Read 46.0 ms 13.2 ms 0.3x
String to Mixed (Adaptive) Iter 138.4 ms 9.3 ms 0.1x
String to Mixed (Adaptive) Free 196.5 ms 7.9 ms 0.0x

Release Comparison (2.6.0 → 2.8.0)

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.28% (PHP-array control -0.08%); runner common-mode -0.08% divided out of every row.

Summary: 0 regressions, 90 unchanged, 10 not evaluated (unstable)

Benchmark Baseline Current Delta 95% CI Status
adv.filter.int_to_int 13.1 ms 12.9 ms -2.0% [-4.3%, +1.8%] ~same
adv.filter.string_to_int 41.8 ms 40.9 ms -4.2% [-10.7%, +4.1%] ~same
adv.forEach.int_to_int 7.9 ms 7.9 ms +1.6% [-8.3%, +2.5%] ~same
adv.forEach.string_to_int 21.9 ms 21.5 ms -0.7% [-12.5%, +11.9%] ~same
adv.map.string_to_int 54.1 ms 54.7 ms +1.1% [-7.8%, +6.1%] ~same
adv.optiter.foreach.string_to_int_adaptive.default 42.4 ms 45.1 ms +3.4% [-1.6%, +17.0%] ~same
adv.optiter.foreach.string_to_int_adaptive.optimized 29.9 ms 29.5 ms -2.0% [-8.7%, +13.3%] ~same
adv.optiter.foreach.string_to_int_hash.optimized 23.9 ms 23.2 ms -2.9% [-9.0%, +11.2%] ~same
adv.optiter.increment.string_to_int_hash.optimized 27.6 ms 30.1 ms +9.1% [-10.2%, +20.1%] ~same
adv.optiter.overwrite.string_to_int_adaptive.optimized 39.5 ms 41.1 ms +0.9% [-0.3%, +12.4%] ~same
adv.optiter.ratio.foreach.string_to_int_adaptive 70.6 ms 68.0 ms -7.3% [-10.6%, +9.6%] ~same
adv.optiter.ratio.foreach.string_to_int_hash 68.2 ms 67.2 ms -2.3% [-15.6%, +23.7%] ~same
adv.optiter.ratio.overwrite.string_to_int_adaptive 97.9 ms 104.4 ms +9.1% [+2.9%, +19.7%] ~same
adv.optiter.ratio.toArray.string_to_int_adaptive 65.0 ms 66.1 ms -0.3% [-10.2%, +4.6%] ~same
adv.optiter.ratio.toArray.string_to_int_hash 55.8 ms 53.8 ms -2.6% [-8.0%, +2.6%] ~same
adv.optiter.ratio.values.string_to_int_adaptive 62.0 ms 63.9 ms +1.4% [-2.6%, +7.2%] ~same
adv.optiter.ratio.values.string_to_int_hash 55.6 ms 58.8 ms +5.9% [-4.1%, +16.3%] ~same
adv.optiter.toArray.string_to_int_adaptive.default 53.9 ms 53.1 ms -1.3% [-1.4%, +11.5%] ~same
adv.optiter.toArray.string_to_int_adaptive.optimized 34.9 ms 35.0 ms +0.9% [-1.6%, +4.7%] ~same
adv.optiter.toArray.string_to_int_hash.default 44.0 ms 46.2 ms +0.2% [-5.3%, +10.1%] ~same
adv.optiter.toArray.string_to_int_hash.optimized 24.6 ms 24.5 ms -1.6% [-6.0%, +7.1%] ~same
adv.optiter.values.string_to_int_adaptive.default 43.4 ms 41.0 ms -2.6% [-6.2%, +11.4%] ~same
adv.optiter.values.string_to_int_adaptive.optimized 26.6 ms 26.2 ms -0.9% [-2.5%, +9.6%] ~same
adv.optiter.values.string_to_int_hash.default 34.1 ms 31.4 ms -1.9% [-13.6%, +2.8%] ~same
adv.optiter.values.string_to_int_hash.optimized 19.0 ms 18.8 ms +1.8% [-8.5%, +6.7%] ~same
api.deleteRange.int_to_int 12.1 ms 11.9 ms -1.3% [-3.0%, +3.6%] ~same
api.equals.int_to_int 3.2 ms 3.0 ms -3.8% [-13.7%, -0.7%] ~same
api.fromArray.int_to_int 7.4 ms 7.4 ms -0.7% [-1.7%, +3.4%] ~same
api.getAll.int_to_int 0.28 ms 0.27 ms -0.4% [-4.7%, +0.1%] ~same
api.increment.int_to_int 6.9 ms 7.1 ms +0.1% [-1.3%, +3.2%] ~same
api.mergeWith.int_to_int 15.4 ms 15.2 ms -1.4% [-3.7%, -0.5%] ~same
api.mergeWith.string_to_int 80.2 ms 81.3 ms +1.5% [-2.2%, +6.4%] ~same
api.populationCount.int_to_int 0.00 ms 0.00 ms +0.1% [+0.1%, +0.1%] ~same
api.putAll.int_to_int 7.5 ms 7.4 ms -1.3% [-13.5%, +0.1%] ~same
api.range.keys.int_to_int 0.26 ms 0.26 ms -2.0% [-5.5%, +3.9%] ~same
api.range.size.string_to_int 1.4 ms 1.4 ms -0.4% [-3.9%, +2.2%] ~same
api.range.size1000.int_to_int 0.45 ms 0.42 ms -7.1% [-10.7%, +17.3%] ~same
api.setop.diff.bitset 3.7 ms 3.7 ms -1.2% [-4.7%, +0.4%] ~same
api.setop.diff.int_to_int 4.7 ms 4.7 ms -0.3% [-5.7%, +6.5%] ~same
api.setop.diff.string_to_int 38.2 ms 39.1 ms +1.6% [-4.6%, +6.2%] ~same
api.setop.intersect.bitset 3.7 ms 3.6 ms -1.3% [-5.6%, +1.7%] ~same
api.setop.intersect.int_to_int 4.7 ms 4.7 ms +0.1% [-8.9%, +5.5%] ~same
api.setop.intersect.string_to_int 44.8 ms 43.6 ms -0.8% [-4.2%, +2.8%] ~same
api.setop.union.bitset 8.8 ms 8.8 ms -0.5% [-3.5%, +2.9%] ~same
api.setop.union.int_to_int 12.6 ms 12.6 ms -1.3% [-3.1%, +8.1%] ~same
api.setop.union.string_to_int 99.6 ms 101.1 ms +2.2% [-0.7%, +3.6%] ~same
api.setop.xor.bitset 7.4 ms 7.3 ms -1.4% [-3.9%, +5.0%] ~same
api.setop.xor.int_to_int 9.6 ms 9.6 ms -1.1% [-1.9%, +4.8%] ~same
api.sumValues.int_to_int 1.9 ms 1.9 ms -0.7% [-3.2%, +3.2%] ~same
api.values.int_to_int 3.1 ms 3.1 ms 0.0% [-4.6%, +1.2%] ~same
core.bitset.free 0.00 ms 0.00 ms +0.1% [+0.1%, +40.1%] ~same
core.bitset.iter 4.9 ms 5.0 ms +0.6% [-1.4%, +4.7%] ~same
core.bitset.read 3.2 ms 3.2 ms -1.2% [-4.5%, +3.7%] ~same
core.bitset.write 5.3 ms 5.4 ms +1.7% [+0.4%, +8.1%] ~same
core.int_to_int.free 0.18 ms 0.18 ms -1.3% [-2.7%, +1.8%] ~same
core.int_to_int.iter 5.3 ms 5.3 ms -0.3% [-0.8%, +4.6%] ~same
core.int_to_int.read 3.5 ms 3.6 ms +1.7% [-5.5%, +15.9%] ~same
core.int_to_int.write 7.6 ms 7.8 ms +3.6% [-2.5%, +9.2%] ~same
core.int_to_mixed.free 4.3 ms 4.2 ms -1.9% [-4.2%, -1.1%] ~same
core.int_to_mixed.iter 5.3 ms 5.5 ms +4.5% [-0.3%, +6.9%] ~same
core.int_to_mixed.read 4.2 ms 4.1 ms -0.2% [-10.3%, +1.5%] ~same
core.int_to_mixed.write 10.4 ms 10.5 ms +1.2% [-3.7%, +6.5%] ~same
core.int_to_packed.free 2.4 ms 2.4 ms -1.0% [-2.5%, -0.1%] ~same
core.int_to_packed.iter 10.4 ms 10.4 ms +1.0% [-2.9%, +14.0%] ~same
core.int_to_packed.read 8.8 ms 9.0 ms +1.4% [-0.1%, +2.6%] ~same
core.int_to_packed.write 13.7 ms 14.0 ms +2.2% [-2.3%, +4.4%] ~same
core.string_to_int.free 6.7 ms 6.7 ms +0.1% [-1.8%, +2.9%] ~same
core.string_to_int.iter 19.3 ms 19.3 ms +0.3% [-8.6%, +5.9%] ~same
core.string_to_int.read 13.5 ms 13.5 ms -0.2% [-2.9%, +4.6%] ~same
core.string_to_int.write 29.8 ms 30.7 ms +2.8% [-1.9%, +11.5%] ~same
core.string_to_int_adaptive.free 13.6 ms 14.0 ms +4.4% [+2.1%, +9.5%] ~same
core.string_to_int_adaptive.iter 24.2 ms 24.2 ms -0.8% [-2.4%, +3.8%] ~same
core.string_to_int_adaptive.read 8.4 ms 8.8 ms +4.0% [+1.0%, +17.2%] ~same
core.string_to_int_adaptive.write 51.8 ms 52.7 ms +2.0% [-0.4%, +5.7%] ~same
core.string_to_int_hash.free 14.4 ms 14.3 ms -2.2% [-3.7%, +7.6%] ~same
core.string_to_int_hash.iter 24.6 ms 24.4 ms -0.5% [-2.4%, +1.9%] ~same
core.string_to_int_hash.read 8.8 ms 8.9 ms +0.5% [-3.8%, +2.8%] ~same
core.string_to_int_hash.write 53.3 ms 52.8 ms +0.1% [-2.5%, +3.4%] ~same
core.string_to_mixed.free 22.9 ms 22.9 ms +0.6% [-2.8%, +3.3%] ~same
core.string_to_mixed.iter 23.2 ms 22.5 ms -1.2% [-4.7%, +1.7%] ~same
core.string_to_mixed.read 15.5 ms 15.5 ms +0.2% [-1.8%, +2.2%] ~same
core.string_to_mixed.write 39.5 ms 39.5 ms +0.5% [-2.1%, +7.2%] ~same
core.string_to_mixed_adaptive.free 46.5 ms 46.6 ms +1.4% [-9.7%, +6.6%] ~same
core.string_to_mixed_adaptive.iter 32.9 ms 32.2 ms -3.7% [-5.7%, -1.2%] ~same
core.string_to_mixed_adaptive.read 10.4 ms 10.2 ms -0.8% [-19.2%, -0.3%] ~same
core.string_to_mixed_adaptive.write 83.7 ms 86.3 ms +1.5% [-0.6%, +4.8%] ~same
core.string_to_mixed_hash.free 43.3 ms 43.6 ms +0.2% [-3.1%, +2.3%] ~same
core.string_to_mixed_hash.iter 35.5 ms 35.0 ms -0.2% [-4.2%, +0.0%] ~same
core.string_to_mixed_hash.read 10.7 ms 10.5 ms -1.0% [-16.7%, +4.8%] ~same
core.string_to_mixed_hash.write 76.2 ms 77.1 ms +1.7% [-1.6%, +4.6%] ~same
10 benchmarks not evaluated (within-arm scatter exceeded the effect being claimed)
Benchmark Baseline Current Delta 95% CI Status
adv.map.int_to_int 14.0 ms 13.5 ms -4.1% [-38.2%, +3.8%] — unstable
adv.optiter.foreach.string_to_int_hash.default 35.4 ms 34.7 ms -2.4% [-20.1%, +31.8%] — unstable
adv.optiter.increment.string_to_int_hash.default 30.9 ms 28.4 ms -0.7% [-11.9%, +35.9%] — unstable
adv.optiter.overwrite.string_to_int_adaptive.default 41.2 ms 38.1 ms -6.0% [-12.3%, -1.1%] — unstable
adv.optiter.overwrite.string_to_int_hash.default 32.3 ms 29.1 ms -9.3% [-15.9%, +14.2%] — unstable
adv.optiter.overwrite.string_to_int_hash.optimized 27.3 ms 28.0 ms +3.0% [-13.5%, +4.8%] — unstable
adv.optiter.ratio.increment.string_to_int_hash 93.6 ms 110.6 ms +0.2% [-11.6%, +24.6%] — unstable
adv.optiter.ratio.overwrite.string_to_int_hash 87.7 ms 94.1 ms +11.4% [-9.7%, +24.7%] — unstable
api.keys.int_to_int 3.0 ms 2.9 ms -2.2% [-42.1%, +1.8%] — unstable
api.toArray.int_to_int 7.8 ms 7.9 ms +1.5% [-29.3%, +2.7%] — 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.8.0 | 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                           18.08         11.85         18.97          0.01    < 464.0 KB
    PHP array                       7.61          4.22          3.28          0.36        8.0 MB
  INT_TO_INT                       25.13         12.93         19.99          0.83        4.4 MB
    PHP array                       7.37          4.22          3.27          0.38        8.1 MB
  INT_TO_MIXED                     34.82         14.43         21.13         14.58       40.2 MB
    PHP array                      11.54          6.04          4.61          4.49       38.1 MB
  INT_TO_PACKED                    48.21         30.81         38.63          9.06       16.3 MB
    PHP array                      11.17          6.42          4.48          4.84       38.1 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                    90.94         37.03         56.60         18.32       10.3 MB
    PHP array                      17.05          7.55          4.92          1.20       38.0 MB
  STRING_TO_MIXED                 116.69         41.15         67.74         65.98       46.2 MB
    PHP array                      21.35          9.99          7.00          7.33       68.0 MB
  STR_TO_INT_HASH                 170.07         25.32         71.17         43.61       31.5 MB
    PHP array                      17.48          7.31          4.91          1.32       38.0 MB
  STR_TO_MIX_HASH                 236.20         27.97         90.73        121.24       67.4 MB
    PHP array                      19.83          9.95          6.78          7.54       68.1 MB
  STR_INT_ADAPTIVE                168.82         25.26         67.86         43.10       31.4 MB
    PHP array                      17.60          7.34          4.91          1.38       38.1 MB
  STR_MIX_ADAPTIVE                254.57         27.84        100.21        128.40       67.4 MB
    PHP array                      20.32          9.80          6.97          7.74       68.0 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               20.00         29.39        1.5x
  fromArray()                            20.05         29.39        1.5x
  toArray()                              23.15         36.36        1.6x
  getAll(50100 keys)                      1.16          2.14        1.9x
  increment(500000 ops/1000 keys)         22.53         10.65        0.5x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      11.27         25.04        2.2x
  values() INT_TO_INT                    12.57         25.23        2.0x
  keys($lo,$hi) INT_TO_INT                0.92         26.04       28.4x
    vs slice()->keys()                    2.71             -        3.0x
  size($lo,$hi) INT x1000                 0.77             -        O(1)
  size($lo,$hi) STRING_TO_INT             3.80          5.11        1.3x
  sumValues() INT_TO_INT                  7.98         21.14        2.6x
  populationCount() INT_TO_INT            0.00         22.79   321005.9x
  deleteRange() INT_TO_INT mid50%         39.47         55.24        1.4x
  equals() INT_TO_INT                    10.96         48.77        4.4x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         31.73          5.45        0.2x
  intersect() BITSET                     13.89          5.97        0.4x
  diff() BITSET                          13.59          3.36        0.2x
  xor() BITSET                           27.56         11.71        0.4x
  union() INT_TO_INT                     41.75         86.19        2.1x
  intersect() INT_TO_INT                 17.23         42.25        2.5x
  diff() INT_TO_INT                      17.05         44.19        2.6x
  xor() INT_TO_INT                       33.56         89.02        2.7x
  union() STRING_TO_INT                 275.96        313.53        1.1x
  intersect() STRING_TO_INT             117.56        138.58        1.2x
  diff() STRING_TO_INT                  112.85        137.26        1.2x
  mergeWith() INT_TO_INT                 50.05         66.87        1.3x
  mergeWith() STRING_TO_INT             241.04        259.27        1.1x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   27.11         22.71        0.8x
  forEach() STRING_TO_INT                66.32         59.01        0.9x
  filter() INT_TO_INT (50% pass)         42.80         39.34        0.9x
  filter() STRING_TO_INT (50% pass)        121.29        114.03        0.9x
  map() INT_TO_INT (*2)                  47.93         47.24        1.0x
  map() STRING_TO_INT (*2)              159.39        157.50        1.0x

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

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    55.63         32.82         59.0%
  values() HASH kl16                     48.65         24.60         50.6%
  toArray() HASH kl16                    60.76         32.53         53.5%
  overwrite() HASH kl16                  41.14         41.70        101.4%
  increment() HASH kl16                  38.04         45.12        118.6%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40                61.28         42.60         69.5%
  values() ADAPTIVE kl40                 55.76         34.72         62.3%
  toArray() ADAPTIVE kl40                70.66         45.15         63.9%
  overwrite() ADAPTIVE kl40              54.70         52.52         96.0%
    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.48 ms      128.65 ms      126.53 ms       1.8x
  read (random access)         22.76 ms       26.96 ms       17.32 ms       1.6x
  foreach (iterate all)        49.89 ms      105.65 ms       87.62 ms       2.1x

── 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)          127.99 ms      213.41 ms      212.16 ms       1.7x
  read (random access)         45.36 ms       54.26 ms       40.22 ms       1.3x
  foreach (iterate all)        86.51 ms       96.25 ms       96.33 ms       1.1x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-10-08 23:31:48
  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.0RC3 | Judy ext 2.8.0 | 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.26         16.67         24.10          0.01    < 104.0 KB
    PHP array                      12.90          8.17          7.21          0.53        9.2 MB
  INT_TO_INT                       37.73         17.18         24.30          0.85        4.5 MB
    PHP array                      12.84          8.30          7.21          0.54        9.2 MB
  INT_TO_MIXED                     52.37         18.32         24.65         20.95       42.5 MB
    PHP array                      16.39          9.49          7.30          6.30       38.2 MB
  INT_TO_PACKED                    69.74         46.85         53.02         14.23       17.2 MB
    PHP array                      16.14          9.44          7.03          4.56       38.2 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)        Memory
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                   143.21         59.30         76.74         30.71       10.5 MB
    PHP array                      25.54         11.93          7.68          1.93       37.9 MB
  STRING_TO_MIXED                 192.16         59.70         88.92         98.49       48.7 MB
    PHP array                      28.34         13.69          8.88          7.93       68.9 MB
  STR_TO_INT_HASH                 264.53         47.96        111.93         63.40       32.2 MB
    PHP array                      24.93         11.68          8.14          1.89       37.9 MB
  STR_TO_MIX_HASH                 364.63         48.38        135.51        184.77       70.4 MB
    PHP array                      28.09         13.26          8.92          7.86       69.0 MB
  STR_INT_ADAPTIVE                253.39         45.02        111.00         62.21       32.3 MB
    PHP array                      24.78         11.73          8.15          1.99       37.9 MB
  STR_MIX_ADAPTIVE                375.14         45.96        138.43        196.53       70.4 MB
    PHP array                      28.20         13.22          9.26          7.91       68.9 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               32.99         46.62        1.4x
  fromArray()                            32.95         46.62        1.4x
  toArray()                              27.91         46.26        1.7x
  getAll(50100 keys)                      1.65          3.02        1.8x
  increment(500000 ops/1000 keys)         33.35         23.84        0.7x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      12.82         30.85        2.4x
  values() INT_TO_INT                    12.93         30.61        2.4x
  keys($lo,$hi) INT_TO_INT                0.99         32.29       32.5x
    vs slice()->keys()                    3.58             -        3.6x
  size($lo,$hi) INT x1000                 0.68             -        O(1)
  size($lo,$hi) STRING_TO_INT             5.41          7.04        1.3x
  sumValues() INT_TO_INT                  7.10         26.49        3.7x
  populationCount() INT_TO_INT            0.00         27.42   274179.0x
  deleteRange() INT_TO_INT mid50%         58.54         83.34        1.4x
  equals() INT_TO_INT                    11.25         61.67        5.5x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         38.47          8.96        0.2x
  intersect() BITSET                     18.51          7.68        0.4x
  diff() BITSET                          18.38          4.46        0.2x
  xor() BITSET                           33.30         17.30        0.5x
  union() INT_TO_INT                     57.26        116.48        2.0x
  intersect() INT_TO_INT                 21.21         56.12        2.6x
  diff() INT_TO_INT                      20.69         58.77        2.8x
  xor() INT_TO_INT                       42.73        116.29        2.7x
  union() STRING_TO_INT                 395.73        448.75        1.1x
  intersect() STRING_TO_INT             173.24        212.49        1.2x
  diff() STRING_TO_INT                  158.35        196.31        1.2x
  mergeWith() INT_TO_INT                 70.33         93.72        1.3x
  mergeWith() STRING_TO_INT             343.44        374.11        1.1x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   31.49         29.17        0.9x
  forEach() STRING_TO_INT                85.57         84.55        1.0x
  filter() INT_TO_INT (50% pass)         49.62         49.46        1.0x
  filter() STRING_TO_INT (50% pass)        165.89        157.31        0.9x
  map() INT_TO_INT (*2)                  57.91         59.62        1.0x
  map() STRING_TO_INT (*2)              232.27        225.40        1.0x

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

  Operation                        default(ms)    optim.(ms)   optim./def.
  ──────────────────────────────  ────────────  ────────────  ────────────
  foreach() HASH kl16                    84.62         60.51         71.5%
  values() HASH kl16                     73.81         45.96         62.3%
  toArray() HASH kl16                    82.78         57.89         69.9%
  overwrite() HASH kl16                  55.82         67.04        120.1%
  increment() HASH kl16                  60.02         63.36        105.6%
    HASH kl16: honoured?          yes
  foreach() ADAPTIVE kl40                97.31         52.58         54.0%
  values() ADAPTIVE kl40                 86.52         39.96         46.2%
  toArray() ADAPTIVE kl40               109.93         60.50         55.0%
  overwrite() ADAPTIVE kl40              71.45         82.48        115.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)         106.66 ms      192.36 ms      172.10 ms       1.8x
  read (random access)         27.44 ms       26.18 ms       24.27 ms       1.1x
  foreach (iterate all)        65.75 ms      131.14 ms      126.16 ms       2.0x

── 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.56 ms      336.87 ms      331.95 ms       1.7x
  read (random access)         55.55 ms       49.33 ms       52.11 ms       1.1x
  foreach (iterate all)       123.26 ms      181.36 ms      179.85 ms       1.5x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-10-08 23:34:05
  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 f0e4dea into main Oct 8, 2026
39 checks passed
@orieg
orieg deleted the release/2.8.0 branch October 8, 2026 23:37
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