Skip to content

chore: bump version to 2.4.0#42

Merged
orieg merged 11 commits intomainfrom
release/v2.4.0
Mar 3, 2026
Merged

chore: bump version to 2.4.0#42
orieg merged 11 commits intomainfrom
release/v2.4.0

Conversation

@orieg
Copy link
Owner

@orieg orieg commented Feb 28, 2026

Summary

  • Bumps PHP_JUDY_VERSION from 2.3.0 to 2.4.0 in php_judy.h
  • Updates package.xml version, date (2026-03-02), and release notes for 2.4.0
  • Updates version test expectation in tests/001.phpt

All features merged into main for the 2.4.0 release:

PR Category Feature
#38 Feature Fast ZPP macros + native zend_object_handlers (read/write/has/unset_dimension) + native C iterators via get_iterator
#39 Feature BITSET set operations -- union(), intersect(), diff(), xor()
#40 Feature Range slicing -- slice($start, $end) for all array types
#41 Feature JsonSerializable interface + __serialize()/__unserialize()
#44 Feature Batch operations -- fromArray(), toArray(), putAll(), getAll(), increment()
#45 Test Comprehensive test coverage improvements (16 new tests + bug fix)
#47 Docs memoryUsage() documentation and memory patterns benchmark
#48 Test memoryUsage() coverage for all Judy types
#49 Feature INT_TO_PACKED type (type 6) -- GC-free opaque value storage via php_var_serialize
#49 Fix Fix: JudySL hang on Windows x64 (issue #46) -- cJU_MASKATSTATE 0xffL truncation at States 5-8
#50 Feature STRING_TO_MIXED_HASH type (type 7) -- JudyHS-backed O(1) string→mixed map
#51 Feature STRING_TO_INT_HASH type (type 8) -- JudyHS-backed O(1) string→int map
#51 Feature INT_TO_INT set operations -- union/intersect/diff/xor with left-wins value semantics
#51 Feature .stub.php arginfo generation replacing hand-written C arginfo
#52 Perf ecalloc→emalloc on 9 zval allocs; direct judy_free_array_internal in __destruct
#53 Perf O(1) count() for all types -- per-insert/delete counter replaces O(n) J1C/JLC scans
#54 Perf foreach iterator overhaul -- valid flag, single heap key buffer, zend_string reuse
#55 Perf Tagged-union packed format for INT_TO_PACKED -- eliminates serialize/unserialize for scalar values
#56 Perf Type-specialized bulk-insert loops in fromArray()/putAll()/__unserialize()
#57 Perf JLG+JLI elimination -- ~50% fewer tree traversals in write paths for STRING types
#57 Perf Branch prediction hints (JUDY_LIKELY/JUDY_UNLIKELY) on hot lookup and validation paths
#57 Perf LTO and loop unrolling enabled in compiler flags
#57 Perf Memory layout repacked for cache-line alignment and reduced padding
#57 Perf Stack safety -- 64KB stack buffers replaced with heap-allocated key_scratch buffer (Fiber-safe)
#58 Feature keys(), values() -- native C extraction, 2-3x faster than PHP foreach
#58 Feature sumValues(), averageValues() -- C-level aggregation for integer-valued types
#58 Feature populationCount($start, $end) -- range counting via Judy's internal population cache
#58 Feature deleteRange($start, $end) -- bulk deletion in a single C pass
#58 Feature equals(Judy $other) -- short-circuit identity comparison
#59 Feature C-level forEach(), filter(), map() -- bypass PHP Iterator protocol for bulk traversal
#59 Feature mergeWith() -- in-place mutating merge
#59 Feature String set operations -- union/intersect/diff/xor for STRING_TO_INT and STRING_TO_MIXED types
#59 Feature STRING_TO_MIXED_ADAPTIVE type (type 9) -- SSO packs short strings (<8 bytes) into JudyL for ultra-fast O(1) lookup; longer strings fall back to JudyHS
#59 Feature STRING_TO_INT_ADAPTIVE type (type 10) -- same SSO strategy for string→int workloads
#61 Fix Double-traversal elimination in adaptive write paths -- removes redundant JLG/JHSG before JLI/JHSI (4 paths: SSO + non-SSO × INT + MIXED)
#61 Fix hs_array NULL-check in judy_free_array_internal -- prevents memory leak in adaptive types

Changes directly in this PR:

Commit Category Description
603eb65 Fix Adaptive type clone handler, mergeWith() wiring, lazy key_scratch allocation (NULL for integer-keyed types, saves 64KB per instance)
39522ab Fix Move judy_init_type_flags() to header for cross-translation-unit visibility
45c0d61 Bench Unified benchmark suite (judy-bench.php) with structured JSON output replacing 6 separate scripts
45c0d61 Bench Cross-version comparison tool (judy-bench-compare.php) with configurable regression threshold
287d77e Bench CI report redesign -- consistent speedup direction, absolute ms values, progressive disclosure
abbdf72 Bench Release baseline comparison against committed baselines/latest.json
d12a4fb Bench Memory comparison in release regression report with ±1% threshold
86188ab Bench mergeWith() benchmarks for INT_TO_INT and STRING_TO_INT

Test plan

  • 174/174 tests pass (CI, PHP 8.1–8.5, Linux + Windows)

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request finalizes the judy extension's version 2.4.0 release, incorporating a significant overhaul of performance and feature extensions. It updates all relevant version identifiers and documentation to reflect the new stable release, which includes advanced functionalities like optimized parameter parsing, native object handlers, and improved serialization capabilities.

Highlights

  • Version Bump: The PHP_JUDY_VERSION has been updated from 2.3.0 to 2.4.0 in php_judy.h, and the package.xml file reflects this new version.
  • Release Notes Update: The package.xml now includes detailed release notes for version 2.4.0, outlining several new features and improvements.
  • Test Expectation Update: The test tests/001.phpt has been updated to expect the new 2.4.0 version string.
  • Performance Overhaul & Feature Completion: This release marks the completion of four phases of performance enhancements and feature extensions, including Fast ZPP macros, BITSET set operations, range slicing, and JsonSerializable support.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • package.xml
    • NEW FEATURE: Fast ZPP (Zend Parameter Parsing) macros across all methods for improved performance
    • NEW FEATURE: Native zend_object_handlers (read/write/has/unset_dimension) bypass ArrayAccess overhead
    • NEW FEATURE: Native C iterators via get_iterator for zero-overhead foreach loops
    • NEW FEATURE: BITSET set operations -- union(), intersect(), diff(), xor() methods
    • NEW FEATURE: slice($start, $end) for efficient range extraction on all array types
    • NEW FEATURE: JsonSerializable interface -- json_encode() works natively on Judy arrays
    • NEW FEATURE: __serialize()/__unserialize() for native PHP serialize/unserialize support
    • IMPROVEMENT: 85 tests covering all features
Activity
  • All 85 tests passed successfully on Docker PHP 8.3.
  • CI matrix for PHP 8.1-8.5 (Linux + Windows) is pending.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Feb 28, 2026

Test Results

PHP Platform Arch TS Tests Pass Fail Skip Duration
8.1 Linux x64 - 176 176 0 0 1.4s
8.2 Linux x64 - 176 176 0 0 1.8s
8.3 Linux x64 - 176 176 0 0 1.8s
8.4 Linux x64 - 176 176 0 0 2.1s
8.5 Linux x64 - 176 176 0 0 2.0s
8.1 Windows x64 nts 176 176 0 0 8.4s
8.2 Windows x64 nts 176 176 0 0 8.4s
8.3 Windows x64 nts 176 176 0 0 8.4s
8.4 Windows x64 nts 176 176 0 0 8.4s
8.5 Windows x64 nts 176 176 0 0 8.4s
Total 1760 1760 0 0

Benchmark Results

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

Run Status

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

Memory Efficiency (Linux, PHP 8.5)

Type Judy PHP array Savings
Bitset 160 B 8.0 MB 52454.9x less
Int to Int 160 B 8.0 MB 52454.9x less
Int to Mixed 7.6 MB 8.0 MB ~1.0x
Int to Packed 13.3 MB 8.0 MB 0.6x
String to Int 64.2 KB 20.0 MB 319.2x less
String to Mixed 7.7 MB 20.0 MB 2.6x less
String to Int (Hash) 64.2 KB 20.0 MB 319.2x less
String to Mixed (Hash) 7.7 MB 20.0 MB 2.6x less
String to Int (Adaptive) 64.2 KB 20.0 MB 319.2x less
String to Mixed (Adaptive) 7.7 MB 20.0 MB 2.6x less

API Speedups (Linux, PHP 8.5)

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

Operation Judy PHP Speedup
putAll() 25.9 ms 35.2 ms 1.4x
fromArray() 25.7 ms 35.2 ms 1.4x
toArray() 24.8 ms 38.7 ms 1.6x
getAll() 1.5 ms 2.4 ms 1.7x
keys() 11.5 ms 25.0 ms 2.2x
values() 12.1 ms 25.1 ms 2.1x
sumValues() 7.5 ms 19.2 ms 2.6x
populationCount() 0.00 ms 20.0 ms 199850x
deleteRange() 52.5 ms 69.2 ms 1.3x
equals() 13.9 ms 52.2 ms 3.8x
union() Bitset 37.3 ms 4.9 ms 0.1x
intersect() Bitset 16.8 ms 5.9 ms 0.4x
diff() Bitset 16.8 ms 3.2 ms 0.2x
xor() Bitset 33.9 ms 11.4 ms 0.3x
mergeWith() Int→Int 68.1 ms 76.0 ms 1.1x
mergeWith() Str→Int 291.7 ms 282.1 ms ~1.0x
forEach() Int→Int 27.6 ms 23.2 ms 0.8x
filter() Int→Int 55.9 ms 43.0 ms 0.8x
map() Int→Int 57.8 ms 54.2 ms 0.9x
forEach() Str→Int 76.3 ms 65.9 ms 0.9x
filter() Str→Int 148.9 ms 123.2 ms 0.8x
map() Str→Int 174.1 ms 171.1 ms ~1.0x

Core Types (Linux, PHP 8.5)

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

Type Op Judy PHP array Speedup
Bitset Write 19.9 ms 7.0 ms 0.4x
Bitset Read 11.4 ms 3.8 ms 0.3x
Bitset Iter 19.2 ms 3.1 ms 0.2x
Bitset Free 0.01 ms 0.32 ms 56.1x
Int to Int Write 29.2 ms 7.3 ms 0.3x
Int to Int Read 13.4 ms 3.9 ms 0.3x
Int to Int Iter 20.5 ms 3.1 ms 0.2x
Int to Int Free 0.21 ms 0.31 ms 1.5x
Int to Mixed Write 40.0 ms 11.3 ms 0.3x
Int to Mixed Read 16.7 ms 5.8 ms 0.3x
Int to Mixed Iter 21.3 ms 3.8 ms 0.2x
Int to Mixed Free 14.5 ms 4.1 ms 0.3x
String to Int Write 102.8 ms 16.5 ms 0.2x
String to Int Read 41.6 ms 7.2 ms 0.2x
String to Int Iter 63.9 ms 4.6 ms 0.1x
String to Int Free 22.5 ms 1.2 ms 0.1x
String to Int (Adaptive) Write 163.4 ms 16.7 ms 0.1x
String to Int (Adaptive) Read 26.9 ms 7.3 ms 0.3x
String to Int (Adaptive) Iter 79.2 ms 4.6 ms 0.1x
String to Int (Adaptive) Free 46.0 ms 1.2 ms 0.0x
All types detail

Core Types (Linux, PHP 8.5) — All

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

Type Op Judy PHP array Speedup
Bitset Write 19.9 ms 7.0 ms 0.4x
Bitset Read 11.4 ms 3.8 ms 0.3x
Bitset Iter 19.2 ms 3.1 ms 0.2x
Bitset Free 0.01 ms 0.32 ms 56.1x
Int to Int Write 29.2 ms 7.3 ms 0.3x
Int to Int Read 13.4 ms 3.9 ms 0.3x
Int to Int Iter 20.5 ms 3.1 ms 0.2x
Int to Int Free 0.21 ms 0.31 ms 1.5x
Int to Mixed Write 40.0 ms 11.3 ms 0.3x
Int to Mixed Read 16.7 ms 5.8 ms 0.3x
Int to Mixed Iter 21.3 ms 3.8 ms 0.2x
Int to Mixed Free 14.5 ms 4.1 ms 0.3x
Int to Packed Write 51.5 ms 10.5 ms 0.2x
Int to Packed Read 33.9 ms 6.0 ms 0.2x
Int to Packed Iter 39.1 ms 4.1 ms 0.1x
Int to Packed Free 9.4 ms 4.2 ms 0.4x
String to Int Write 102.8 ms 16.5 ms 0.2x
String to Int Read 41.6 ms 7.2 ms 0.2x
String to Int Iter 63.9 ms 4.6 ms 0.1x
String to Int Free 22.5 ms 1.2 ms 0.1x
String to Mixed Write 127.4 ms 20.0 ms 0.2x
String to Mixed Read 45.6 ms 9.5 ms 0.2x
String to Mixed Iter 74.0 ms 6.2 ms 0.1x
String to Mixed Free 76.2 ms 6.9 ms 0.1x
String to Int (Hash) Write 180.6 ms 16.4 ms 0.1x
String to Int (Hash) Read 27.3 ms 7.2 ms 0.3x
String to Int (Hash) Iter 80.0 ms 4.6 ms 0.1x
String to Int (Hash) Free 46.2 ms 1.1 ms 0.0x
String to Mixed (Hash) Write 263.9 ms 19.7 ms 0.1x
String to Mixed (Hash) Read 30.8 ms 9.5 ms 0.3x
String to Mixed (Hash) Iter 99.2 ms 6.3 ms 0.1x
String to Mixed (Hash) Free 138.1 ms 6.6 ms 0.0x
String to Int (Adaptive) Write 163.4 ms 16.7 ms 0.1x
String to Int (Adaptive) Read 26.9 ms 7.3 ms 0.3x
String to Int (Adaptive) Iter 79.2 ms 4.6 ms 0.1x
String to Int (Adaptive) Free 46.0 ms 1.2 ms 0.0x
String to Mixed (Adaptive) Write 259.5 ms 20.4 ms 0.1x
String to Mixed (Adaptive) Read 30.7 ms 9.6 ms 0.3x
String to Mixed (Adaptive) Iter 98.0 ms 6.2 ms 0.1x
String to Mixed (Adaptive) Free 137.8 ms 6.5 ms 0.0x
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
Bitset Write 20.2 ms (0.4x) 19.9 ms (0.4x) 19.8 ms (0.4x) 19.9 ms (0.4x) 19.9 ms (0.4x)
Bitset Read 12.5 ms (0.4x) 11.9 ms (0.3x) 11.4 ms (0.3x) 12.0 ms (0.3x) 11.4 ms (0.3x)
Int to Int Write 29.8 ms (0.3x) 29.1 ms (0.2x) 29.2 ms (0.2x) 29.6 ms (0.3x) 29.2 ms (0.3x)
Int to Int Read 13.6 ms (0.3x) 13.5 ms (0.3x) 13.4 ms (0.3x) 13.5 ms (0.3x) 13.4 ms (0.3x)
Int to Mixed Write 38.6 ms (0.3x) 38.3 ms (0.3x) 41.7 ms (0.3x) 39.5 ms (0.3x) 40.0 ms (0.3x)
Int to Mixed Read 16.6 ms (0.3x) 16.5 ms (0.3x) 16.3 ms (0.3x) 18.9 ms (0.3x) 16.7 ms (0.3x)
Int to Packed Write 55.6 ms (0.2x) 52.3 ms (0.2x) 51.9 ms (0.2x) 51.6 ms (0.2x) 51.5 ms (0.2x)
Int to Packed Read 33.7 ms (0.2x) 34.4 ms (0.2x) 33.3 ms (0.2x) 33.5 ms (0.2x) 33.9 ms (0.2x)
String to Int Write 102.1 ms (0.2x) 104.3 ms (0.2x) 103.1 ms (0.2x) 105.7 ms (0.2x) 102.8 ms (0.2x)
String to Int Read 41.5 ms (0.2x) 41.1 ms (0.2x) 41.1 ms (0.2x) 41.7 ms (0.2x) 41.6 ms (0.2x)
String to Mixed Write 124.4 ms (0.1x) 125.7 ms (0.2x) 123.5 ms (0.2x) 127.8 ms (0.2x) 127.4 ms (0.2x)
String to Mixed Read 44.1 ms (0.2x) 45.2 ms (0.2x) 44.6 ms (0.2x) 45.5 ms (0.2x) 45.6 ms (0.2x)
String to Int (Hash) Write 177.7 ms (0.1x) 179.6 ms (0.1x) 171.7 ms (0.1x) 186.6 ms (0.1x) 180.6 ms (0.1x)
String to Int (Hash) Read 27.5 ms (0.3x) 27.5 ms (0.3x) 27.1 ms (0.3x) 27.8 ms (0.3x) 27.3 ms (0.3x)
String to Mixed (Hash) Write 259.4 ms (0.1x) 260.4 ms (0.1x) 251.0 ms (0.1x) 255.0 ms (0.1x) 263.9 ms (0.1x)
String to Mixed (Hash) Read 29.6 ms (0.3x) 29.8 ms (0.3x) 29.7 ms (0.3x) 29.8 ms (0.3x) 30.8 ms (0.3x)
String to Int (Adaptive) Write 160.0 ms (0.1x) 159.2 ms (0.1x) 154.5 ms (0.1x) 166.5 ms (0.1x) 163.4 ms (0.1x)
String to Int (Adaptive) Read 26.5 ms (0.3x) 26.2 ms (0.3x) 26.5 ms (0.3x) 26.9 ms (0.3x) 26.9 ms (0.3x)
String to Mixed (Adaptive) Write 246.6 ms (0.1x) 252.2 ms (0.1x) 244.7 ms (0.1x) 277.5 ms (0.1x) 259.5 ms (0.1x)
String to Mixed (Adaptive) Read 28.4 ms (0.3x) 31.3 ms (0.3x) 28.8 ms (0.3x) 29.1 ms (0.4x) 30.7 ms (0.3x)

API — Linux

Operation PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
putAll() 25.8 ms (1.3x) 25.8 ms (1.3x) 26.1 ms (1.4x) 26.3 ms (1.3x) 25.9 ms (1.4x)
fromArray() 26.0 ms (1.3x) 26.0 ms (1.3x) 26.2 ms (1.4x) 26.5 ms (1.3x) 25.7 ms (1.4x)
toArray() 25.1 ms (1.5x) 25.0 ms (1.5x) 24.5 ms (1.5x) 26.0 ms (1.5x) 24.8 ms (1.6x)
getAll() 1.4 ms (1.7x) 1.4 ms (1.6x) 1.5 ms (1.7x) 1.5 ms (1.6x) 1.5 ms (1.7x)
keys() 13.3 ms (2.0x) 12.0 ms (2.1x) 11.6 ms (2.2x) 12.0 ms (2.2x) 11.5 ms (2.2x)
values() 13.9 ms (1.9x) 11.7 ms (2.1x) 11.4 ms (2.2x) 12.9 ms (2.0x) 12.1 ms (2.1x)
sumValues() 7.7 ms (2.5x) 7.4 ms (2.6x) 7.3 ms (3.0x) 8.4 ms (2.5x) 7.5 ms (2.6x)
populationCount() 0.00 ms (209204x) 0.00 ms (205181x) 0.00 ms (213061x) 0.00 ms (220606x) 0.00 ms (199850x)
deleteRange() 51.8 ms (1.3x) 51.6 ms (1.3x) 51.3 ms (1.3x) 52.0 ms (1.3x) 52.5 ms (1.3x)
equals() 13.9 ms (3.6x) 14.2 ms (3.5x) 13.8 ms (3.8x) 14.7 ms (3.4x) 13.9 ms (3.8x)
union() Bitset 37.5 ms (0.2x) 38.0 ms (0.1x) 37.5 ms (0.1x) 37.6 ms (0.1x) 37.3 ms (0.1x)
intersect() Bitset 16.8 ms (0.3x) 16.9 ms (0.4x) 16.8 ms (0.3x) 16.8 ms (0.4x) 16.8 ms (0.4x)
diff() Bitset 16.8 ms (0.2x) 16.8 ms (0.2x) 16.9 ms (0.2x) 16.9 ms (0.2x) 16.8 ms (0.2x)
xor() Bitset 34.1 ms (0.4x) 34.0 ms (0.4x) 34.1 ms (0.4x) 34.1 ms (0.3x) 33.9 ms (0.3x)
mergeWith() Int→Int 67.9 ms (1.1x) 69.9 ms (1.1x) 68.1 ms (1.1x) 68.9 ms (1.1x) 68.1 ms (1.1x)
mergeWith() Str→Int 285.8 ms (~1.0x) 285.2 ms (~1.0x) 282.7 ms (~1.0x) 289.9 ms (~1.0x) 291.7 ms (~1.0x)
forEach() Int→Int 28.6 ms (0.8x) 27.6 ms (0.8x) 28.5 ms (0.8x) 28.7 ms (0.8x) 27.6 ms (0.8x)
filter() Int→Int 53.3 ms (0.8x) 52.4 ms (0.8x) 54.6 ms (0.8x) 52.0 ms (0.8x) 55.9 ms (0.8x)
map() Int→Int 56.3 ms (0.9x) 56.2 ms (~1.0x) 55.9 ms (~1.0x) 51.3 ms (~1.0x) 57.8 ms (0.9x)
forEach() Str→Int 71.3 ms (0.9x) 71.7 ms (0.9x) 70.9 ms (0.9x) 70.5 ms (0.9x) 76.3 ms (0.9x)
filter() Str→Int 147.3 ms (0.8x) 145.7 ms (0.8x) 146.2 ms (0.8x) 147.3 ms (0.8x) 148.9 ms (0.8x)
map() Str→Int 171.3 ms (~1.0x) 174.5 ms (~1.0x) 173.0 ms (~1.0x) 172.0 ms (~1.0x) 174.1 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
Bitset Write 103.0 ms (0.1x) 52.7 ms (0.2x) 97.9 ms (0.1x) 103.8 ms (0.1x) 103.5 ms (0.1x)
Bitset Read 19.1 ms (0.4x) 21.2 ms (0.3x) 20.2 ms (0.4x) 20.0 ms (0.4x) 20.1 ms (0.4x)
Int to Int Write 76.5 ms (0.2x) 81.8 ms (0.1x) 78.5 ms (0.2x) 76.9 ms (0.1x) 77.9 ms (0.2x)
Int to Int Read 25.3 ms (0.3x) 29.0 ms (0.3x) 25.5 ms (0.3x) 25.5 ms (0.3x) 25.2 ms (0.3x)
Int to Mixed Write 111.4 ms (0.2x) 103.3 ms (0.2x) 95.1 ms (0.2x) 101.7 ms (0.2x) 101.2 ms (0.2x)
Int to Mixed Read 29.8 ms (0.3x) 30.4 ms (0.3x) 28.1 ms (0.4x) 28.0 ms (0.4x) 27.9 ms (0.3x)
Int to Packed Write 119.2 ms (0.2x) 128.6 ms (0.1x) 112.6 ms (0.2x) 121.3 ms (0.1x) 119.4 ms (0.1x)
Int to Packed Read 56.9 ms (0.2x) 68.2 ms (0.1x) 56.5 ms (0.2x) 58.1 ms (0.2x) 57.7 ms (0.2x)
String to Int Write 239.8 ms (0.1x) 300.0 ms (0.1x) 236.3 ms (0.1x) 234.3 ms (0.1x) 234.2 ms (0.1x)
String to Int Read 84.5 ms (0.1x) 107.2 ms (0.1x) 82.7 ms (0.2x) 78.2 ms (0.2x) 80.7 ms (0.1x)
String to Mixed Write 275.1 ms (0.1x) 336.4 ms (0.1x) 277.4 ms (0.1x) 280.4 ms (0.1x) 281.6 ms (0.1x)
String to Mixed Read 82.5 ms (0.2x) 107.1 ms (0.1x) 83.5 ms (0.2x) 81.7 ms (0.2x) 83.8 ms (0.2x)
String to Int (Hash) Write 415.0 ms (0.1x) 479.3 ms (0.1x) 417.2 ms (0.1x) 414.0 ms (0.1x) 417.2 ms (0.1x)
String to Int (Hash) Read 62.3 ms (0.2x) 61.5 ms (0.2x) 63.6 ms (0.2x) 63.3 ms (0.2x) 64.4 ms (0.2x)
String to Mixed (Hash) Write 558.0 ms (0.0x) 651.3 ms (0.0x) 559.5 ms (0.1x) 571.3 ms (0.1x) 563.5 ms (0.1x)
String to Mixed (Hash) Read 65.6 ms (0.2x) 62.7 ms (0.2x) 67.2 ms (0.2x) 65.2 ms (0.2x) 66.4 ms (0.2x)
String to Int (Adaptive) Write 400.1 ms (0.1x) 435.2 ms (0.1x) 369.6 ms (0.1x) 368.5 ms (0.1x) 370.3 ms (0.1x)
String to Int (Adaptive) Read 66.7 ms (0.2x) 60.7 ms (0.2x) 61.5 ms (0.2x) 61.0 ms (0.2x) 61.3 ms (0.2x)
String to Mixed (Adaptive) Write 566.9 ms (0.0x) 674.9 ms (0.0x) 578.3 ms (0.1x) 580.9 ms (0.0x) 571.3 ms (0.1x)
String to Mixed (Adaptive) Read 64.1 ms (0.2x) 61.9 ms (0.2x) 64.5 ms (0.2x) 63.5 ms (0.2x) 65.0 ms (0.2x)

API — Windows

Operation PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
putAll() 73.5 ms (1.2x) 83.5 ms (1.2x) 73.4 ms (1.2x) 75.8 ms (1.2x) 73.7 ms (1.2x)
fromArray() 73.7 ms (1.2x) 83.6 ms (1.2x) 73.5 ms (1.2x) 74.5 ms (1.2x) 73.9 ms (1.2x)
toArray() 82.2 ms (1.2x) 82.3 ms (1.2x) 80.3 ms (1.3x) 80.0 ms (1.3x) 80.3 ms (1.3x)
getAll() 2.5 ms (1.6x) 3.1 ms (1.4x) 2.3 ms (1.7x) 2.6 ms (1.6x) 2.8 ms (1.4x)
keys() 26.0 ms (1.7x) 27.7 ms (1.6x) 22.4 ms (1.9x) 22.8 ms (1.8x) 22.6 ms (1.7x)
values() 26.8 ms (1.6x) 27.8 ms (1.5x) 22.7 ms (1.8x) 23.2 ms (1.8x) 23.1 ms (1.7x)
sumValues() 17.4 ms (1.9x) 20.6 ms (1.8x) 17.7 ms (1.9x) 17.4 ms (1.9x) 17.4 ms (1.9x)
populationCount() 0.00 ms (333502x) 0.00 ms (358002x) 0.00 ms (345062x) 0.00 ms (347607x) 0.00 ms (333919x)
deleteRange() 140.7 ms (1.2x) 167.5 ms (1.1x) 141.3 ms (1.2x) 139.6 ms (1.2x) 141.0 ms (1.2x)
equals() 32.2 ms (2.7x) 39.0 ms (2.3x) 31.9 ms (2.8x) 31.3 ms (2.8x) 31.5 ms (2.7x)
union() Bitset 181.1 ms (0.1x) 119.2 ms (0.1x) 170.6 ms (0.1x) 178.5 ms (0.1x) 176.3 ms (0.0x)
intersect() Bitset 69.7 ms (0.1x) 50.1 ms (0.2x) 66.7 ms (0.1x) 69.9 ms (0.1x) 70.8 ms (0.1x)
diff() Bitset 66.0 ms (0.1x) 48.8 ms (0.1x) 64.5 ms (0.1x) 69.4 ms (0.1x) 66.9 ms (0.1x)
xor() Bitset 137.8 ms (0.2x) 99.9 ms (0.2x) 131.3 ms (0.1x) 138.8 ms (0.1x) 139.2 ms (0.1x)
mergeWith() Int→Int 168.2 ms (~1.0x) 186.2 ms (~1.0x) 166.4 ms (1.1x) 167.7 ms (1.1x) 167.2 ms (~1.0x)
mergeWith() Str→Int 646.5 ms (~1.0x) 731.8 ms (~1.0x) 616.7 ms (~1.0x) 642.3 ms (~1.0x) 626.0 ms (~1.0x)
forEach() Int→Int 39.0 ms (~1.0x) 40.7 ms (0.9x) 41.6 ms (0.9x) 40.5 ms (0.9x) 41.1 ms (0.9x)
filter() Int→Int 94.6 ms (0.9x) 104.4 ms (0.8x) 90.2 ms (0.9x) 91.2 ms (0.9x) 91.5 ms (0.9x)
map() Int→Int 111.5 ms (1.2x) 135.0 ms (0.9x) 109.2 ms (1.1x) 115.4 ms (~1.0x) 109.7 ms (~1.0x)
forEach() Str→Int 127.5 ms (0.9x) 166.5 ms (0.9x) 126.9 ms (0.9x) 134.1 ms (0.9x) 129.1 ms (0.9x)
filter() Str→Int 284.5 ms (0.8x) 361.7 ms (0.8x) 279.7 ms (0.8x) 283.7 ms (0.8x) 281.3 ms (0.8x)
map() Str→Int 357.0 ms (~1.0x) 453.4 ms (~1.0x) 349.3 ms (~1.0x) 352.5 ms (~1.0x) 351.9 ms (~1.0x)
Windows results (PHP 8.5)

Memory Efficiency (Windows, PHP 8.5)

Type Judy PHP array Savings
Bitset 160 B 10.0 MB 65536.5x less
Int to Int 160 B 10.0 MB 65536.5x less
Int to Mixed 7.6 MB 10.0 MB 1.3x less
Int to Packed 13.3 MB 10.0 MB 0.8x
String to Int 64.2 KB 20.0 MB 319.2x less
String to Mixed 7.7 MB 20.0 MB 2.6x less
String to Int (Hash) 64.2 KB 20.0 MB 319.2x less
String to Mixed (Hash) 7.7 MB 20.0 MB 2.6x less
String to Int (Adaptive) 64.2 KB 20.0 MB 319.2x less
String to Mixed (Adaptive) 7.7 MB 20.0 MB 2.6x less

API Speedups (Windows, PHP 8.5)

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

Operation Judy PHP Speedup
putAll() 73.7 ms 87.7 ms 1.2x
fromArray() 73.9 ms 87.7 ms 1.2x
toArray() 80.3 ms 101.5 ms 1.3x
getAll() 2.8 ms 3.8 ms 1.4x
keys() 22.6 ms 39.4 ms 1.7x
values() 23.1 ms 39.2 ms 1.7x
sumValues() 17.4 ms 32.3 ms 1.9x
populationCount() 0.00 ms 33.4 ms 333919x
deleteRange() 141.0 ms 162.4 ms 1.2x
equals() 31.5 ms 86.0 ms 2.7x
union() Bitset 176.3 ms 8.7 ms 0.0x
intersect() Bitset 70.8 ms 7.6 ms 0.1x
diff() Bitset 66.9 ms 4.2 ms 0.1x
xor() Bitset 139.2 ms 17.5 ms 0.1x
mergeWith() Int→Int 167.2 ms 171.9 ms ~1.0x
mergeWith() Str→Int 626.0 ms 623.3 ms ~1.0x
forEach() Int→Int 41.1 ms 37.6 ms 0.9x
filter() Int→Int 91.5 ms 78.0 ms 0.9x
map() Int→Int 109.7 ms 110.6 ms ~1.0x
forEach() Str→Int 129.1 ms 120.2 ms 0.9x
filter() Str→Int 281.3 ms 238.1 ms 0.8x
map() Str→Int 351.9 ms 352.1 ms ~1.0x

Core Types (Windows, PHP 8.5)

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

Type Op Judy PHP array Speedup
Bitset Write 103.5 ms 11.8 ms 0.1x
Bitset Read 20.1 ms 8.3 ms 0.4x
Bitset Iter 28.3 ms 7.1 ms 0.3x
Bitset Free 0.01 ms 0.54 ms 45.7x
Int to Int Write 77.9 ms 11.8 ms 0.2x
Int to Int Read 25.2 ms 8.6 ms 0.3x
Int to Int Iter 30.8 ms 7.1 ms 0.2x
Int to Int Free 0.61 ms 0.55 ms 0.9x
Int to Mixed Write 101.2 ms 15.5 ms 0.2x
Int to Mixed Read 27.9 ms 9.7 ms 0.3x
Int to Mixed Iter 34.3 ms 7.8 ms 0.2x
Int to Mixed Free 34.6 ms 5.0 ms 0.1x
String to Int Write 234.2 ms 25.3 ms 0.1x
String to Int Read 80.7 ms 11.7 ms 0.1x
String to Int Iter 114.4 ms 8.5 ms 0.1x
String to Int Free 43.0 ms 2.0 ms 0.0x
String to Int (Adaptive) Write 370.3 ms 25.4 ms 0.1x
String to Int (Adaptive) Read 61.3 ms 11.7 ms 0.2x
String to Int (Adaptive) Iter 175.5 ms 8.5 ms 0.0x
String to Int (Adaptive) Free 97.7 ms 1.9 ms 0.0x
All types detail

Core Types (Windows, PHP 8.5) — All

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

Type Op Judy PHP array Speedup
Bitset Write 103.5 ms 11.8 ms 0.1x
Bitset Read 20.1 ms 8.3 ms 0.4x
Bitset Iter 28.3 ms 7.1 ms 0.3x
Bitset Free 0.01 ms 0.54 ms 45.7x
Int to Int Write 77.9 ms 11.8 ms 0.2x
Int to Int Read 25.2 ms 8.6 ms 0.3x
Int to Int Iter 30.8 ms 7.1 ms 0.2x
Int to Int Free 0.61 ms 0.55 ms 0.9x
Int to Mixed Write 101.2 ms 15.5 ms 0.2x
Int to Mixed Read 27.9 ms 9.7 ms 0.3x
Int to Mixed Iter 34.3 ms 7.8 ms 0.2x
Int to Mixed Free 34.6 ms 5.0 ms 0.1x
Int to Packed Write 119.4 ms 15.9 ms 0.1x
Int to Packed Read 57.7 ms 9.7 ms 0.2x
Int to Packed Iter 63.5 ms 7.9 ms 0.1x
Int to Packed Free 28.2 ms 4.9 ms 0.2x
String to Int Write 234.2 ms 25.3 ms 0.1x
String to Int Read 80.7 ms 11.7 ms 0.1x
String to Int Iter 114.4 ms 8.5 ms 0.1x
String to Int Free 43.0 ms 2.0 ms 0.0x
String to Mixed Write 281.6 ms 28.5 ms 0.1x
String to Mixed Read 83.8 ms 14.0 ms 0.2x
String to Mixed Iter 127.4 ms 9.7 ms 0.1x
String to Mixed Free 146.5 ms 8.8 ms 0.1x
String to Int (Hash) Write 417.2 ms 25.6 ms 0.1x
String to Int (Hash) Read 64.4 ms 11.7 ms 0.2x
String to Int (Hash) Iter 176.2 ms 8.6 ms 0.0x
String to Int (Hash) Free 98.6 ms 2.0 ms 0.0x
String to Mixed (Hash) Write 563.5 ms 28.5 ms 0.1x
String to Mixed (Hash) Read 66.4 ms 14.2 ms 0.2x
String to Mixed (Hash) Iter 200.6 ms 9.7 ms 0.0x
String to Mixed (Hash) Free 285.1 ms 8.2 ms 0.0x
String to Int (Adaptive) Write 370.3 ms 25.4 ms 0.1x
String to Int (Adaptive) Read 61.3 ms 11.7 ms 0.2x
String to Int (Adaptive) Iter 175.5 ms 8.5 ms 0.0x
String to Int (Adaptive) Free 97.7 ms 1.9 ms 0.0x
String to Mixed (Adaptive) Write 571.3 ms 28.6 ms 0.1x
String to Mixed (Adaptive) Read 65.0 ms 14.1 ms 0.2x
String to Mixed (Adaptive) Iter 202.0 ms 9.4 ms 0.0x
String to Mixed (Adaptive) Free 292.8 ms 8.5 ms 0.0x

Release Comparison (2.3.0 → 2.4.0)

Baseline: v2.3.0 via pie install on same CI runner. Comparing Linux PHP 8.5. Threshold: ±10% (entries < 2 ms ignored).

Summary: 6 faster, ⚠️ 1 regressions, 13 unchanged, 50 new

Benchmark Baseline Current Delta Status
core.bitset.free 0.01 ms 0.01 ms 0.0% ~same
core.bitset.iter 95.2 ms 19.2 ms -79.8% FASTER
core.bitset.read 11.8 ms 11.4 ms -3.6% ~same
core.bitset.write 19.2 ms 19.9 ms +4.1% ~same
core.int_to_int.free 0.58 ms 0.21 ms 0.0% ~same
core.int_to_int.iter 96.5 ms 20.5 ms -78.7% FASTER
core.int_to_int.read 13.0 ms 13.4 ms +3.0% ~same
core.int_to_int.write 24.2 ms 29.2 ms +20.6% ⚠️ SLOWER
core.int_to_mixed.free 16.3 ms 14.5 ms -10.7% FASTER
core.int_to_mixed.iter 96.1 ms 21.3 ms -77.8% FASTER
core.int_to_mixed.read 16.5 ms 16.7 ms +1.1% ~same
core.int_to_mixed.write 42.8 ms 40.0 ms -6.5% ~same
core.string_to_int.free 22.1 ms 22.5 ms +1.6% ~same
core.string_to_int.iter 182.1 ms 63.9 ms -64.9% FASTER
core.string_to_int.read 41.2 ms 41.6 ms +0.8% ~same
core.string_to_int.write 104.0 ms 102.8 ms -1.2% ~same
core.string_to_mixed.free 77.8 ms 76.2 ms -2.1% ~same
core.string_to_mixed.iter 195.5 ms 74.0 ms -62.1% FASTER
core.string_to_mixed.read 44.7 ms 45.6 ms +2.0% ~same
core.string_to_mixed.write 130.7 ms 127.4 ms -2.6% ~same

Memory Comparison

Memory: ⚠️ 3 regressions, 2 unchanged

Type Baseline Current Delta Status
Bitset 128 B 160 B +25.0% ⚠️ WORSE
Int to Int 128 B 160 B +25.0% ⚠️ WORSE
Int to Mixed 7.6 MB 7.6 MB 0.0% ~same
String to Int 128 B 64.2 KB +51225.0% ⚠️ WORSE
String to Mixed 7.6 MB 7.7 MB +0.8% ~same
50 new benchmarks (not in baseline)
Benchmark Current
adv.filter.int_to_int 55.9 ms
adv.filter.string_to_int 148.9 ms
adv.forEach.int_to_int 27.6 ms
adv.forEach.string_to_int 76.3 ms
adv.map.int_to_int 57.8 ms
adv.map.string_to_int 174.1 ms
api.deleteRange.int_to_int 52.5 ms
api.equals.int_to_int 13.9 ms
api.fromArray.int_to_int 25.7 ms
api.getAll.int_to_int 1.5 ms
api.increment.int_to_int 29.6 ms
api.keys.int_to_int 11.5 ms
api.mergeWith.int_to_int 68.1 ms
api.mergeWith.string_to_int 291.7 ms
api.populationCount.int_to_int 0.00 ms
api.putAll.int_to_int 25.9 ms
api.setop.diff.bitset 16.8 ms
api.setop.diff.int_to_int 20.9 ms
api.setop.diff.string_to_int 135.6 ms
api.setop.intersect.bitset 16.8 ms
api.setop.intersect.int_to_int 21.3 ms
api.setop.intersect.string_to_int 137.4 ms
api.setop.union.bitset 37.3 ms
api.setop.union.int_to_int 53.9 ms
api.setop.union.string_to_int 368.2 ms
api.setop.xor.bitset 33.9 ms
api.setop.xor.int_to_int 42.7 ms
api.sumValues.int_to_int 7.5 ms
api.toArray.int_to_int 24.8 ms
api.values.int_to_int 12.1 ms
core.int_to_packed.free 9.4 ms
core.int_to_packed.iter 39.1 ms
core.int_to_packed.read 33.9 ms
core.int_to_packed.write 51.5 ms
core.string_to_int_adaptive.free 46.0 ms
core.string_to_int_adaptive.iter 79.2 ms
core.string_to_int_adaptive.read 26.9 ms
core.string_to_int_adaptive.write 163.4 ms
core.string_to_int_hash.free 46.2 ms
core.string_to_int_hash.iter 80.0 ms
core.string_to_int_hash.read 27.3 ms
core.string_to_int_hash.write 180.6 ms
core.string_to_mixed_adaptive.free 137.8 ms
core.string_to_mixed_adaptive.iter 98.0 ms
core.string_to_mixed_adaptive.read 30.7 ms
core.string_to_mixed_adaptive.write 259.5 ms
core.string_to_mixed_hash.free 138.1 ms
core.string_to_mixed_hash.iter 99.2 ms
core.string_to_mixed_hash.read 30.8 ms
core.string_to_mixed_hash.write 263.9 ms
Full benchmark output

Linux — PHP 8.5

══════════════════════════════════════════════════════════════════════════════════════════
  php-judy Benchmark Suite — 500,000 elements, 7 iterations (median)
  PHP 8.5.3 | Judy ext 2.4.0 | Linux x86_64
  Suite: all
══════════════════════════════════════════════════════════════════════════════════════════

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)          Heap
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  BITSET                           19.95         11.39         19.21          0.01         160 B
    PHP array                       7.02          3.84          3.12          0.32        8.0 MB
  INT_TO_INT                       29.23         13.41         20.53          0.21         160 B
    PHP array                       7.35          3.89          3.11          0.31        8.0 MB
  INT_TO_MIXED                     39.99         16.66         21.31         14.52        7.6 MB
    PHP array                      11.30          5.78          3.83          4.08        8.0 MB
  INT_TO_PACKED                    51.51         33.89         39.06          9.37       13.3 MB
    PHP array                      10.54          6.00          4.12          4.17        8.0 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)          Heap
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                   102.77         41.56         63.92         22.50       64.2 KB
    PHP array                      16.54          7.19          4.58          1.20       20.0 MB
  STRING_TO_MIXED                 127.36         45.61         74.04         76.20        7.7 MB
    PHP array                      20.00          9.48          6.24          6.85       20.0 MB
  STR_TO_INT_HASH                 180.63         27.28         79.96         46.24       64.2 KB
    PHP array                      16.37          7.24          4.58          1.14       20.0 MB
  STR_TO_MIX_HASH                 263.90         30.82         99.23        138.06        7.7 MB
    PHP array                      19.69          9.52          6.30          6.64       20.0 MB
  STR_INT_ADAPTIVE                163.38         26.86         79.21         46.02       64.2 KB
    PHP array                      16.65          7.29          4.57          1.19       20.0 MB
  STR_MIX_ADAPTIVE                259.51         30.74         98.00        137.85        7.7 MB
    PHP array                      20.43          9.61          6.24          6.54       20.0 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               25.90         35.24        1.4x
  fromArray()                            25.74         35.24        1.4x
  toArray()                              24.84         38.66        1.6x
  getAll(50100 keys)                      1.46          2.42        1.7x
  increment(500000 ops/1000 keys)         29.59         10.72        0.4x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      11.47         24.96        2.2x
  values() INT_TO_INT                    12.08         25.05        2.1x
  sumValues() INT_TO_INT                  7.51         19.22        2.6x
  populationCount() INT_TO_INT            0.00         19.99   285500.3x
  deleteRange() INT_TO_INT mid50%         52.49         69.17        1.3x
  equals() INT_TO_INT                    13.91         52.18        3.8x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                         37.26          4.86        0.1x
  intersect() BITSET                     16.79          5.91        0.4x
  diff() BITSET                          16.78          3.24        0.2x
  xor() BITSET                           33.86         11.38        0.3x
  union() INT_TO_INT                     53.86         95.30        1.8x
  intersect() INT_TO_INT                 21.33         45.42        2.1x
  diff() INT_TO_INT                      20.92         47.46        2.3x
  xor() INT_TO_INT                       42.66         95.45        2.2x
  union() STRING_TO_INT                 368.19        354.79        1.0x
  intersect() STRING_TO_INT             137.36        154.45        1.1x
  diff() STRING_TO_INT                  135.59        153.39        1.1x
  mergeWith() INT_TO_INT                 68.05         75.96        1.1x
  mergeWith() STRING_TO_INT             291.66        282.14        1.0x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   27.62         23.25        0.8x
  forEach() STRING_TO_INT                76.33         65.92        0.9x
  filter() INT_TO_INT (50% pass)         55.90         43.04        0.8x
  filter() STRING_TO_INT (50% pass)        148.90        123.24        0.8x
  map() INT_TO_INT (*2)                  57.78         54.17        0.9x
  map() STRING_TO_INT (*2)              174.10        171.10        1.0x

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

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (short keys)          76.86 ms      133.42 ms      117.00 ms       1.7x
  read (random access)         18.61 ms       14.76 ms       14.83 ms       1.3x
  foreach (iterate all)        50.11 ms       86.60 ms       85.41 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)          129.52 ms      222.47 ms      199.62 ms       1.7x
  read (random access)         42.04 ms       35.60 ms       33.16 ms       1.3x
  foreach (iterate all)        97.04 ms      113.19 ms      110.62 ms       1.2x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-03-02 23:31:31
  All timings: median of 7 iterations via hrtime(true), 1 warmup run
══════════════════════════════════════════════════════════════════════════════════════════

  JSON results written to: bench.json

Windows — PHP 8.5

══════════════════════════════════════════════════════════════════════════════════════════
  php-judy Benchmark Suite — 500,000 elements, 7 iterations (median)
  PHP 8.5.3 | Judy ext 2.4.0 | WINNT AMD64
  Suite: all
══════════════════════════════════════════════════════════════════════════════════════════

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)          Heap
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  BITSET                          103.49         20.13         28.27          0.01         160 B
    PHP array                      11.80          8.31          7.08          0.54       10.0 MB
  INT_TO_INT                       77.87         25.24         30.83          0.61         160 B
    PHP array                      11.76          8.60          7.07          0.55       10.0 MB
  INT_TO_MIXED                    101.24         27.89         34.26         34.55        7.6 MB
    PHP array                      15.52          9.67          7.82          5.01       10.0 MB
  INT_TO_PACKED                   119.36         57.70         63.45         28.21       13.3 MB
    PHP array                      15.93          9.74          7.94          4.86       10.0 MB

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

  Type                         Write(ms)      Read(ms)      Iter(ms)      Free(ms)          Heap
  ────────────────────────  ────────────  ────────────  ────────────  ────────────  ────────────
  STRING_TO_INT                   234.21         80.67        114.36         42.97       64.2 KB
    PHP array                      25.28         11.67          8.54          1.98       20.0 MB
  STRING_TO_MIXED                 281.64         83.76        127.41        146.55        7.7 MB
    PHP array                      28.54         13.99          9.65          8.76       20.0 MB
  STR_TO_INT_HASH                 417.21         64.39        176.24         98.60       64.2 KB
    PHP array                      25.55         11.73          8.55          2.00       20.0 MB
  STR_TO_MIX_HASH                 563.52         66.44        200.58        285.09        7.7 MB
    PHP array                      28.54         14.18          9.72          8.21       20.0 MB
  STR_INT_ADAPTIVE                370.34         61.27        175.48         97.67       64.2 KB
    PHP array                      25.36         11.66          8.49          1.91       20.0 MB
  STR_MIX_ADAPTIVE                571.35         64.96        202.00        292.79        7.7 MB
    PHP array                      28.59         14.14          9.37          8.50       20.0 MB

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  putAll()                               73.74         87.65        1.2x
  fromArray()                            73.85         87.65        1.2x
  toArray()                              80.30        101.49        1.3x
  getAll(50100 keys)                      2.75          3.85        1.4x
  increment(500000 ops/1000 keys)         58.44         24.01        0.4x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  keys() INT_TO_INT                      22.56         39.37        1.7x
  values() INT_TO_INT                    23.14         39.21        1.7x
  sumValues() INT_TO_INT                 17.38         32.29        1.9x
  populationCount() INT_TO_INT            0.00         33.39   333919.0x
  deleteRange() INT_TO_INT mid50%        140.97        162.39        1.2x
  equals() INT_TO_INT                    31.53         86.00        2.7x

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

  Operation                           Judy(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  union() BITSET                        176.28          8.74        0.0x
  intersect() BITSET                     70.79          7.60        0.1x
  diff() BITSET                          66.86          4.21        0.1x
  xor() BITSET                          139.17         17.50        0.1x
  union() INT_TO_INT                    149.10        205.74        1.4x
  intersect() INT_TO_INT                 55.57         87.63        1.6x
  diff() INT_TO_INT                      54.64         90.75        1.7x
  xor() INT_TO_INT                      112.61        182.48        1.6x
  union() STRING_TO_INT                 770.22        752.63        1.0x
  intersect() STRING_TO_INT             299.38        325.65        1.1x
  diff() STRING_TO_INT                  285.24        314.76        1.1x
  mergeWith() INT_TO_INT                167.25        171.89        1.0x
  mergeWith() STRING_TO_INT             626.04        623.27        1.0x

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

  Operation                        C-level(ms)       PHP(ms)     Speedup
  ──────────────────────────────  ────────────  ────────────  ──────────
  forEach() INT_TO_INT                   41.07         37.63        0.9x
  forEach() STRING_TO_INT               129.11        120.24        0.9x
  filter() INT_TO_INT (50% pass)         91.51         77.99        0.9x
  filter() STRING_TO_INT (50% pass)        281.34        238.08        0.8x
  map() INT_TO_INT (*2)                 109.69        110.63        1.0x
  map() STRING_TO_INT (*2)              351.86        352.11        1.0x

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

  Operation                   STR_TO_INT    STR_INT_HASH   STR_INT_ADPTV    Best ratio
  ──────────────────────  ──────────────  ──────────────  ──────────────  ────────────
  insert (short keys)         173.97 ms      312.15 ms      266.20 ms       1.8x
  read (random access)         20.40 ms       20.46 ms       18.95 ms       1.1x
  foreach (iterate all)        83.90 ms      140.56 ms      129.14 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)          314.95 ms      538.94 ms      472.25 ms       1.7x
  read (random access)         57.71 ms       49.30 ms       52.64 ms       1.2x
  foreach (iterate all)       185.13 ms      268.59 ms      268.61 ms       1.5x

══════════════════════════════════════════════════════════════════════════════════════════
  Benchmark complete — 2026-03-02 23:34:06
  All timings: median of 7 iterations via hrtime(true), 1 warmup run
══════════════════════════════════════════════════════════════════════════════════════════

  JSON results written to: bench.json

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request bumps the version to 2.4.0 in package.xml, php_judy.h, and updates the corresponding test expectations. The release notes in package.xml are also updated to reflect the features and improvements for version 2.4.0. The changes are correct and consistent. While reviewing package.xml, I noticed that many <file> entries are missing the md5sum attribute. Although this is outside the direct changes in this PR, it would be beneficial to add these checksums in a follow-up to ensure package integrity for PECL.

@orieg orieg force-pushed the release/v2.4.0 branch 2 times, most recently from f79f616 to f3337d4 Compare February 28, 2026 23:53
@orieg
Copy link
Owner Author

orieg commented Mar 1, 2026

Rebased onto current main (all 5 optimization PRs #52#56 now merged). 167/167 tests pass.

package.xml updated in both <notes> and <changelog>:

  • Added 4 PERF bullets (iterator overhaul, packed scalar fast-path, O(1) count, bulk-insert dispatch bypass)
  • Test count updated 158 → 167

Gemini's md5sum note: the missing checksums are on example/doc files added without them (pre-existing pattern throughout the file). No md5sum change in this PR; can be addressed in a follow-up packaging pass if PECL submission is planned.

- Update PHP_JUDY_VERSION to "2.4.0"
- Update package.xml: version, date, comprehensive release notes
  covering PRs #38-59 and #61
- Add 16 new test files and 3 new example files to package.xml
- Update test expectation in tests/001.phpt
orieg added 7 commits March 2, 2026 10:25
Replace 6 separate benchmark script invocations (run-benchmarks,
batch-operations, set-operations, all-types, phase2-api, phase3-advanced)
with a single unified runner (judy-bench.php) that outputs structured JSON
alongside human-readable tables.

Add judy-bench-compare.php for cross-version regression detection with
configurable threshold and CI-gatable exit codes.

Update CI workflow (Linux + Windows) to use the unified runner and rewrite
the report-results Python script to consume bench.json instead of parsing
text files with fragile regexes.
- Fix fromArray() bug: record .php counterpart using putAll baseline,
  fix stale $t_php display in stdout
- Rewrite CI report with consistent speedup direction everywhere
  (PHP time / Judy time; >1x = Judy faster; bold when Judy wins)
- Show absolute ms values alongside speedup factors instead of
  bare ratios
- Separate memory efficiency from speed tables
- Use human-readable type labels (e.g. "String to Int (Hash)")
- Progressive disclosure: Linux latest PHP expanded, cross-version
  and Windows details in collapsible sections
- Add Run Status grid for at-a-glance validation across all
  platform/version combinations
Add a "Release Comparison" section that diffs the current run against
a committed baseline (baselines/latest.json), showing FASTER/SLOWER/
~same/NEW status for each benchmark with delta percentages.

- Store initial v2.4.0-dev baseline (174 entries, Linux PHP 8.1)
- Add actions/checkout with sparse-checkout to report-results job
  so the Python script can read the baseline file
- Compare only .judy entries (Judy's own perf across releases),
  skip heap-only entries, use ±5% threshold
- Show summary counts, regressions with warning icons, and new
  benchmarks in a collapsible section
- Replace v2.4.0-dev baseline with proper PECL-installed v2.3.0 release
- Increase benchmark iterations from 3 to 7 for more stable medians
- Raise cross-release comparison threshold from ±5% to ±10%
- Use defined() checks for type detection instead of try/catch
- Add $has_hash guard so hash type benchmarks are skipped on older versions
- Note PECL baseline source in the release comparison report header
- Install released v2.3.0 via PIE on the PHP 8.4 Linux runner
- Run baseline benchmarks (core suite) on same hardware as PR benchmarks
- Report prefers CI-generated baseline; falls back to committed file
- Header indicates "same CI runner" or warns about cross-architecture
- Skip sub-2ms benchmarks in comparison (e.g., 0.2ms→0.7ms = +247% noise)
- Run baseline benchmarks before PR benchmarks for fairer ordering
- Note minimum threshold in report header
Compare heap_bytes between baseline (v2.3.0) and current for all
core types, using a ±1% threshold. Memory is deterministic so any
meaningful change is a real regression or improvement.
@orieg
Copy link
Owner Author

orieg commented Mar 2, 2026

Memory & Performance Analysis (Updated post-lazy key_scratch)

Memory comparison: v2.3.0 → v2.4.0

The CI report flags 3 memory "regressions". Only String‑to‑Int shows a meaningful increase; the integer‑keyed types are now nearly flat thanks to the lazy key_scratch optimization shipped in this PR.

Type v2.3.0 v2.4.0 Delta Explanation
Bitset 128 B 160 B +32 B Struct fields only (no key_scratch)
Int‑to‑Int 128 B 160 B +32 B Struct fields only (no key_scratch)
Int‑to‑Mixed 7.6 MB 7.6 MB ~same key_scratch dwarfed by per-element zval heap
String‑to‑Int 128 B 64.2 KB +64 KB key_scratch heap buffer — expected
String‑to‑Mixed 7.6 MB 7.7 MB ~same key_scratch dwarfed by per-element zval heap

Why 32 bytes more for integer-keyed types?

v2.4.0 added new struct fields to judy_object for type-flag dispatch and adaptive type support:

  • hs_array pointer (8 B) — JudyHS backing store for adaptive types
  • key_scratch pointer (8 B) — set to NULL for integer types (lazy allocation)
  • 6 boolean flags: is_integer_keyed, is_string_keyed, is_mixed_value, is_packed_value, is_hash_keyed, is_adaptive (6 B + alignment)

These flags eliminate repeated switch/if chains on the hot path, trading 32 bytes of struct overhead for faster type dispatch.

Why key_scratch (64 KB) for string types?

v2.3.0 stack-allocated uint8_t key[65536] inside individual functions. v2.4.0 moves this to a single heap-allocated buffer per object:

v2.3.0 v2.4.0
Buffer location Stack (64 KB per call frame) Heap (64 KB per object, lazy)
Visible to memory_get_usage()? No Yes
Fiber-safe? No (overflows 8 KB default Fiber stack) Yes

The actual memory footprint is comparable — it simply moved from the unmeasured C stack to the PHP-tracked heap. Integer-keyed types skip allocation entirely (intern->key_scratch = NULL).

Performance: iteration 62–80% faster, one write regression

Wins — the C-level php_judy_iterator rewrite delivers major iteration speedups:

Benchmark v2.3.0 v2.4.0 Delta
bitset.iter 95.2 ms 19.2 ms −80%
int_to_int.iter 96.5 ms 20.5 ms −79%
int_to_mixed.iter 96.1 ms 21.3 ms −78%
string_to_int.iter 182.1 ms 63.9 ms −65%
string_to_mixed.iter 195.5 ms 74.0 ms −62%
int_to_mixed.free 16.3 ms 14.5 ms −11%

Regressionint_to_int.write: 24.2 ms → 29.2 ms (+20.6%). This is at the margin of CI noise (the ±10% threshold catches it). The write path for INT_TO_INT is a single JLI call with no code changes in v2.4.0 — the added type-flag conditionals are in the string-keyed branches and don't execute for integer keys. Likely runner load variance; worth monitoring across runs but not a code-level regression.

New API benchmarks (v2.4.0 features)

Highlights from the 50 new benchmarks not present in v2.3.0:

Operation Judy PHP equiv Speedup
populationCount() 0.00 ms 20.0 ms ~200,000× (O(1) maintained counter)
equals() 13.9 ms 52.2 ms 3.8×
sumValues() 7.5 ms 19.2 ms 2.6×
keys() 11.5 ms 25.0 ms 2.2×
values() 12.1 ms 25.1 ms 2.1×
getAll() 1.5 ms 2.4 ms 1.7×
toArray() 24.8 ms 38.7 ms 1.6×
mergeWith() Int→Int 68.1 ms 76.0 ms 1.1×
mergeWith() Str→Int 291.7 ms 282.1 ms ~1.0×

mergeWith() is roughly on par with the PHP foreach equivalent for both key types, which validates its value as an in-place alternative to union() (which allocates a new array).

orieg added 3 commits March 2, 2026 15:09
Bug fixes for adaptive types (STRING_TO_*_ADAPTIVE):
- Clone handler: clone all 3 backing stores (JudyL, JudyHS, JudySL)
- has_dimension: NULL guard allows long-key-only isset() lookups
- unset_dimension: same NULL guard fix for long-key-only unset
- has_dimension check_empty: add adaptive types so empty() works
- write_dimension: add adaptive types to append-error guard
- slice(): add adaptive type branch dispatching SSO vs long keys

New feature:
- mergeWith(Judy $other): in-place merge registered as public API

Optimizations:
- Lazy key_scratch: allocate 64KB buffer only for string-keyed types
- Lazy iterator key_scratch: same conditional allocation
- Replace 6 stack-allocated 64KB buffers with intern->key_scratch
Benchmarks in-place mergeWith() vs manual foreach loop in the API
set operations section. Also adds entries to CI report api_ops so
results appear in the PR comment.
judy_init_type_flags() was static in php_judy.c but called from
judy_handlers.c (separate translation unit), causing undefined symbol
at runtime on Linux and LNK2019 on Windows. Move to php_judy.h as
static inline.

Also update test expectations for the append error message that now
includes adaptive types, and fix missing echo in adaptive slice test.
@orieg
Copy link
Owner Author

orieg commented Mar 2, 2026

/gemini review

@orieg orieg self-assigned this Mar 2, 2026
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request bumps the project version to 2.4.0, incorporating new features, performance improvements, and bug fixes, including the mergeWith method and new benchmark scripts. However, a security audit identified critical issues related to inconsistent object state during cloning, which can lead to counter corruption and process crashes due to mishandling of allocation failures, as highlighted by an existing repository rule. Additionally, there's a potential path traversal vulnerability in the benchmark scripts due to a lack of sanitization for file paths provided via command-line arguments. Minor issues were also noted, such as incorrect dates (year 2026) in metadata files (baselines/latest.json and package.xml), which appear to be typos.

@orieg orieg merged commit 0addbb0 into main Mar 3, 2026
13 checks passed
@orieg orieg deleted the release/v2.4.0 branch March 3, 2026 01:39
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