Skip to content

Conversation

@LilithHafner
Copy link
Member

  • Implement a cmp specialization for ranges
  • Add tests (gen AI helped populate const EXAMPLE_RANGES)

Fixes #60334

@LilithHafner LilithHafner added performance Must go faster ranges Everything AbstractRange sorting Put things in order labels Dec 7, 2025

@testset "cmp(::AbstractRange, ::AbstractRange)" begin
for a in EXAMPLE_RANGES, b in EXAMPLE_RANGES
@test try cmp(a, b) catch e; e end == try cmp(collect(a), collect(b)) catch e; e end
Copy link
Contributor

@Seelengrab Seelengrab Dec 8, 2025

Choose a reason for hiding this comment

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

Suggested change
@test try cmp(a, b) catch e; e end == try cmp(collect(a), collect(b)) catch e; e end
@test cmp(a, b) == cmp(collect(a), collect(b))

Isn't exception handling done by @test already?

Copy link
Contributor

@Seelengrab Seelengrab Dec 8, 2025

Choose a reason for hiding this comment

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

Or is this to compare that the errors are the same..? If so, IMO that should be done in a test explicit for that case, rather than mixing it into the general one. Alternatively we don't check this at all, since just comparing the thrown object doesn't mean that it's the same error - the location it was thrown from can be different too.

@adienes
Copy link
Member

adienes commented Dec 8, 2025

here is another good stressor

julia> r1 = LinRange(0.3376448676263234, 1.509664528429199, 3);

julia> r2 = range(0.3376448676263234, step=0.5860098304014378, length=3);

julia> isless(r1, r2) || isless(r2, r1) || isequal(r1, r2)
false

I think forcing the fast path to have the same concrete types will resolve this type of edge case

Comment on lines +1175 to +1178
# Assume that ranges are monotonic and use the last shared element as a high precision proxy for step.
x1, x2 = last(zip(r1, r2))
x1 != x2 && return cmp(x1, x2)
cmp(length(r1), length(r2))
Copy link
Member

Choose a reason for hiding this comment

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

last(zip(r1, r2)) is still the abstract iterator's O(n) isn't it? And if we're doing an O(n) thing, we might as well compare everything, no? The goal here should be to detect the "fast" outs that are definitively correct, falling back to O(n) iteration if we can't do that.

The worst case is where step is identical, but, yeah, it's tricky to rely on step(r). Perhaps this should just be done for OrdinalRanges.

Copy link
Member

Choose a reason for hiding this comment

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

last(::Zip) is actually O(1) in most cases but I agree it will be challenging for any generic implementation to work for all ranges

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Must go faster ranges Everything AbstractRange sorting Put things in order

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extremely slow comparison of large ranges

6 participants