Skip to content

Performance optimizations and Version#== clarity fix#18

Merged
andrew merged 3 commits intomainfrom
performance-optimizations
Apr 3, 2026
Merged

Performance optimizations and Version#== clarity fix#18
andrew merged 3 commits intomainfrom
performance-optimizations

Conversation

@andrew
Copy link
Copy Markdown
Owner

@andrew andrew commented Apr 3, 2026

  • Memoize Version#to_s and Interval#empty? since both are immutable after construction
  • Optimize Interval#overlaps? with inline bounds check instead of allocating a full intersection Interval
  • Single-pass interval filtering in VersionRange constructor (replace compact.reject with select)
  • Use match? instead of match in hot paths to avoid MatchData allocation
  • Improve cache eviction strategy: drop oldest half instead of clearing everything, bump limits
  • Precompile OPERATOR_PREFIX_REGEX constant in Parser#sort_key_for_constraint
  • Fix Version#== operator precedence bug (was parsing as self <=> (other == 0) due to == binding tighter than <=>)
  • Add benchmark gem for Ruby 4.0.0 compatibility in rake tasks
  • Deduplicate Interval empty computation (validate_bounds! and empty? were doing the same comparison)
  • Replace fake memory benchmark with real allocation counting via ObjectSpace
  • 9 new tests covering equality edge cases, nil interval filtering, overlaps edge cases, and to_s memoization

Speed

Benchmark main this branch change
Native parsing (ranges/sec) 10,331 74,890 7.2x faster
VERS URI parsing (URIs/sec) 11,553 93,664 8.1x faster
Version comparison (ops/sec) 1,200,000 1,565,217 1.3x faster
Containment checks (ops/sec) 121,507 253,165 2.1x faster
to_s conversion (ops/sec) 222,222 1,619,049 7.3x faster
contains? repeated (ops/sec) 111,842 178,010 1.6x faster
empty? repeated (ops/sec) 11,333,467 16,999,871 1.5x faster
npm scheme parsing (ms/parse) 0.0039 0.0026 1.5x faster
maven scheme parsing (ms/parse) 0.0882 0.0231 3.8x faster
Complex npm stress (ops/sec) 45,263 115,088 2.5x faster
Gem pessimistic stress (ops/sec) 150,875 381,534 2.5x faster
Maven bracket stress (ops/sec) 51,403 121,124 2.4x faster
Python complex stress (ops/sec) 25,374 61,728 2.4x faster

Allocations

34 version ranges parsed from test suite data.

Metric main this branch change
Cold parse (total objects) 4,585 1,466 3.1x fewer
Cold parse (per range) 134.9 43.1 3.1x fewer
Cold parse strings 2,756 817 3.4x fewer
Cold parse arrays 1,044 347 3.0x fewer
Cold parse hashes 584 110 5.3x fewer
Cached parse (total objects) 3,478 970 3.6x fewer
Cached parse (per range) 102.3 28.5 3.6x fewer
to_s allocations 725 105 6.9x fewer
contains? allocations 1,473 642 2.3x fewer
empty? allocations 2 2 same
Retained memory (per object) 40 bytes 40 bytes same

andrew added 3 commits April 3, 2026 08:36
- Memoize Version#to_s and Interval#empty? since both are immutable
- Optimize Interval#overlaps? with inline bounds check instead of allocating intersection
- Single-pass interval filtering in VersionRange constructor
- Use match? instead of match to avoid MatchData allocation
- Improve cache eviction to drop oldest half instead of clearing everything
- Precompile OPERATOR_PREFIX_REGEX constant in Parser
- Add explicit parens in Version#== for clarity
- Add benchmark gem for Ruby 4.0.0 compatibility
- benchmark gem is only used in Rake tasks, not at runtime
- Replace validate_bounds! and lazy empty? memoization with a single
  compute_empty called at construction time
Use ObjectSpace.count_objects to measure actual object allocations
during cold parsing, cached parsing, and repeated operations instead
of hardcoding a ~300 bytes estimate.
@andrew andrew merged commit 175372c into main Apr 3, 2026
2 checks passed
@andrew andrew deleted the performance-optimizations branch April 3, 2026 08:00
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