Skip to content

Commit 9d6344a

Browse files
mbaumanN5N3
andauthored
optimize length(::OrdinalRange) for large bit-ints (#58864)
Split from #58793, this coalesces nearly all the branches in `length`, allowing it to inline and generally perform much better while retaining the exact same functionality. --------- Co-authored-by: N5N3 <2642243996@qq.com>
1 parent 11eeed3 commit 9d6344a

File tree

2 files changed

+8
-12
lines changed

2 files changed

+8
-12
lines changed

base/range.jl

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -802,21 +802,15 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128},
802802
# slightly more accurate length and checked_length in extreme cases
803803
# (near typemax) for types with known `unsigned` functions
804804
function length(r::OrdinalRange{T}) where T<:bigints
805-
@inline
806805
s = step(r)
807806
diff = last(r) - first(r)
808807
isempty(r) && return zero(diff)
809-
# if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should
810-
# therefore still be valid (if the result is representable at all)
811-
# n.b. !(s isa T)
812-
if s isa Unsigned || -1 <= s <= 1 || s == -s
813-
a = div(diff, s) % typeof(diff)
814-
elseif s < 0
815-
a = div(unsigned(-diff), -s) % typeof(diff)
816-
else
817-
a = div(unsigned(diff), s) % typeof(diff)
818-
end
819-
return a + oneunit(a)
808+
# Compute `(diff ÷ s) + 1` in a manner robust to signed overflow
809+
# by using the absolute values as unsigneds for non-empty ranges.
810+
# Note that `s` may be a different type from T and diff; it may not
811+
# even be a BitInteger that supports `unsigned`. Handle with care.
812+
a = div(unsigned(flipsign(diff, s)), s) % typeof(diff)
813+
return flipsign(a, s) + oneunit(a)
820814
end
821815
function checked_length(r::OrdinalRange{T}) where T<:bigints
822816
s = step(r)

test/ranges.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ end
755755
@test length(typemin(T):typemax(T)) == T(0)
756756
@test length(zero(T):one(T):typemax(T)) == typemin(T)
757757
@test length(typemin(T):one(T):typemax(T)) == T(0)
758+
@test length(StepRange{T,BigInt}(zero(T), 1, typemax(T))) == typemin(T)
759+
@test length(StepRange{T,BigInt}(typemin(T), 1, typemax(T))) == T(0)
758760
@test_throws OverflowError checked_length(zero(T):typemax(T))
759761
@test_throws OverflowError checked_length(typemin(T):typemax(T))
760762
@test_throws OverflowError checked_length(zero(T):one(T):typemax(T))

0 commit comments

Comments
 (0)