Skip to content

Commit 3e172d6

Browse files
committed
Check happy path first when comparing gem version:
- During resolution, Gem::Version are compared against each other. Since comparing versions is a very hot path we can micro optimize it to check the happy path first. The speed gain on the overall resolution isn't significant but the ips gain is quite substantial. The diff chunk is small so I figure it's worth it anyway. ```ruby a = Gem::Version.new("5.3.1") b = Gem::Version.new("5.3.1") Benchmark.ips do |x| x.report("equal regular:") { a <=> c } x.report("equal optimized:") { a <=> c } x.hold!("equal_temp_results") x.compare!(order: :baseline) end ``` ``` Warming up -------------------------------------- equal optimized: 1.268M i/100ms Calculating ------------------------------------- equal optimized: 12.738M (± 1.5%) i/s (78.50 ns/i) - 64.680M in 5.078754s Comparison: equal regular:: 9866605.0 i/s equal optimized:: 12738310.3 i/s - 1.29x faster ``` ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [arm64-darwin25]
1 parent 30e2aa9 commit 3e172d6

File tree

1 file changed

+41
-43
lines changed

1 file changed

+41
-43
lines changed

lib/rubygems/version.rb

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -345,60 +345,58 @@ def approximate_recommendation
345345
# other types may raise an exception.
346346

347347
def <=>(other)
348-
if String === other
349-
return unless self.class.correct?(other)
350-
return self <=> self.class.new(other)
351-
end
352-
353-
return unless Gem::Version === other
354-
355-
# Fast path for comparison when available.
356-
if @sort_key && other.sort_key
357-
return @sort_key <=> other.sort_key
358-
end
359-
360-
return 0 if @version == other.version || canonical_segments == other.canonical_segments
348+
if Gem::Version === other
349+
# Fast path for comparison when available.
350+
if @sort_key && other.sort_key
351+
return @sort_key <=> other.sort_key
352+
end
361353

362-
lhsegments = canonical_segments
363-
rhsegments = other.canonical_segments
354+
return 0 if @version == other.version || canonical_segments == other.canonical_segments
364355

365-
lhsize = lhsegments.size
366-
rhsize = rhsegments.size
367-
limit = (lhsize > rhsize ? rhsize : lhsize)
356+
lhsegments = canonical_segments
357+
rhsegments = other.canonical_segments
368358

369-
i = 0
359+
lhsize = lhsegments.size
360+
rhsize = rhsegments.size
361+
limit = (lhsize > rhsize ? rhsize : lhsize)
370362

371-
while i < limit
372-
lhs = lhsegments[i]
373-
rhs = rhsegments[i]
374-
i += 1
363+
i = 0
375364

376-
next if lhs == rhs
377-
return -1 if String === lhs && Numeric === rhs
378-
return 1 if Numeric === lhs && String === rhs
365+
while i < limit
366+
lhs = lhsegments[i]
367+
rhs = rhsegments[i]
368+
i += 1
379369

380-
return lhs <=> rhs
381-
end
370+
next if lhs == rhs
371+
return -1 if String === lhs && Numeric === rhs
372+
return 1 if Numeric === lhs && String === rhs
382373

383-
lhs = lhsegments[i]
374+
return lhs <=> rhs
375+
end
384376

385-
if lhs.nil?
386-
rhs = rhsegments[i]
377+
lhs = lhsegments[i]
387378

388-
while i < rhsize
389-
return 1 if String === rhs
390-
return -1 unless rhs.zero?
391-
rhs = rhsegments[i += 1]
392-
end
393-
else
394-
while i < lhsize
395-
return -1 if String === lhs
396-
return 1 unless lhs.zero?
397-
lhs = lhsegments[i += 1]
379+
if lhs.nil?
380+
rhs = rhsegments[i]
381+
382+
while i < rhsize
383+
return 1 if String === rhs
384+
return -1 unless rhs.zero?
385+
rhs = rhsegments[i += 1]
386+
end
387+
else
388+
while i < lhsize
389+
return -1 if String === lhs
390+
return 1 unless lhs.zero?
391+
lhs = lhsegments[i += 1]
392+
end
398393
end
399-
end
400394

401-
0
395+
0
396+
elsif String === other
397+
return unless self.class.correct?(other)
398+
self <=> self.class.new(other)
399+
end
402400
end
403401

404402
# remove trailing zeros segments before first letter or at the end of the version

0 commit comments

Comments
 (0)