Skip to content

Commit bb7091c

Browse files
authored
Speed up first and only for various types (#52296)
1 parent f761860 commit bb7091c

File tree

6 files changed

+24
-3
lines changed

6 files changed

+24
-3
lines changed

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ julia> firstindex(rand(3,4,5), 2)
446446
firstindex(a::AbstractArray) = (@inline; first(eachindex(IndexLinear(), a)))
447447
firstindex(a, d) = (@inline; first(axes(a, d)))
448448

449-
first(a::AbstractArray) = a[first(eachindex(a))]
449+
@propagate_inbounds first(a::AbstractArray) = a[first(eachindex(a))]
450450

451451
"""
452452
first(coll)

base/dict.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,8 @@ end
738738
isempty(t::Dict) = (t.count == 0)
739739
length(t::Dict) = t.count
740740

741+
@propagate_inbounds Iterators.only(t::Dict) = Iterators._only(t, first)
742+
741743
@propagate_inbounds function Base.iterate(v::T, i::Int = v.dict.idxfloor) where T <: Union{KeySet{<:Any, <:Dict}, ValueIterator{<:Dict}}
742744
i == 0 && return nothing
743745
i = skip_deleted(v.dict, i)
@@ -1049,3 +1051,5 @@ iterate(dict::PersistentDict, state=nothing) = HAMT.iterate(dict.trie, state)
10491051
length(dict::PersistentDict) = HAMT.length(dict.trie)
10501052
isempty(dict::PersistentDict) = HAMT.isempty(dict.trie)
10511053
empty(::PersistentDict, ::Type{K}, ::Type{V}) where {K, V} = PersistentDict{K, V}()
1054+
1055+
@propagate_inbounds Iterators.only(dict::PersistentDict) = Iterators._only(dict, first)

base/iterators.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const Base = parentmodule(@__MODULE__)
1111
using .Base:
1212
@inline, Pair, Pairs, AbstractDict, IndexLinear, IndexStyle, AbstractVector, Vector,
1313
SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo,
14-
@propagate_inbounds, @isdefined, @boundscheck, @inbounds, Generator,
14+
@propagate_inbounds, @isdefined, @boundscheck, @inbounds, Generator, IdDict,
1515
AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom,
1616
(:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, missing,
1717
any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex,
@@ -1547,7 +1547,9 @@ Stacktrace:
15471547
[...]
15481548
```
15491549
"""
1550-
@propagate_inbounds function only(x)
1550+
@propagate_inbounds only(x) = _only(x, iterate)
1551+
1552+
@propagate_inbounds function _only(x, ::typeof(iterate))
15511553
i = iterate(x)
15521554
@boundscheck if i === nothing
15531555
throw(ArgumentError("Collection is empty, must contain exactly 1 element"))
@@ -1559,6 +1561,15 @@ Stacktrace:
15591561
return ret
15601562
end
15611563

1564+
@inline function _only(x, ::typeof(first))
1565+
@boundscheck if length(x) != 1
1566+
throw(ArgumentError("Collection must contain exactly 1 element"))
1567+
end
1568+
@inbounds first(x)
1569+
end
1570+
1571+
@propagate_inbounds only(x::IdDict) = _only(x, first)
1572+
15621573
# Specific error messages for tuples and named tuples
15631574
only(x::Tuple{Any}) = x[1]
15641575
only(x::Tuple) = throw(

base/set.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ rehash!(s::Set) = (rehash!(s.dict); s)
175175

176176
iterate(s::Set, i...) = iterate(KeySet(s.dict), i...)
177177

178+
@propagate_inbounds Iterators.only(s::Set) = Iterators._only(s, first)
179+
178180
# In case the size(s) is smaller than size(t) its more efficient to iterate through
179181
# elements of s instead and only delete the ones also contained in t.
180182
# The threshold for this decision boils down to a tradeoff between

base/strings/basic.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ firstindex(s::AbstractString) = 1
179179
lastindex(s::AbstractString) = thisind(s, ncodeunits(s)::Int)
180180
isempty(s::AbstractString) = iszero(ncodeunits(s)::Int)
181181

182+
@propagate_inbounds first(s::AbstractString) = s[firstindex(s)]
183+
182184
function getindex(s::AbstractString, i::Integer)
183185
@boundscheck checkbounds(s, i)
184186
@inbounds return isvalid(s, i) ? (iterate(s, i)::NTuple{2,Any})[1] : string_index_err(s, i)

base/weakkeydict.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,6 @@ function iterate(t::WeakKeyDict{K,V}, state...) where {K, V}
213213
end
214214
end
215215

216+
@propagate_inbounds Iterators.only(d::WeakKeyDict) = Iterators._only(d, first)
217+
216218
filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d)

0 commit comments

Comments
 (0)