Skip to content

Commit ee50eee

Browse files
committed
ensure more iterators stay type-stable
This is a frequent performance trap. Perhaps because Tuple/NamedTuple/Pair/default-constructor usage is so much easier than `@nospecialize`. Dunno what else to do but fix them manually.
1 parent b6d81e3 commit ee50eee

File tree

4 files changed

+38
-19
lines changed

4 files changed

+38
-19
lines changed

base/abstractdict.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -672,11 +672,11 @@ _oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any,
672672

673673
start(d::IdDict) = _oidd_nextind(d.ht, 0)
674674
done(d::IdDict, i) = (i == -1)
675-
next(d::IdDict{K,V}, i) where {K, V} = (Pair{K,V}(d.ht[i+1], d.ht[i+2]), _oidd_nextind(d.ht, i+2))
675+
next(d::IdDict{K, V}, i) where {K, V} = (Pair{K, V}(d.ht[i + 1]::K, d.ht[i + 2]::V), _oidd_nextind(d.ht, i + 2))
676676

677677
length(d::IdDict) = d.count
678678

679-
copy(d::IdDict) = IdDict(d)
679+
copy(d::IdDict) = typeof(d)(d)
680680

681681
get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} = (d[key] = get(d, key, default))::V
682682

@@ -689,14 +689,14 @@ mutable struct IdSet{T} <: AbstractSet{T}
689689
dict::IdDict{T,Nothing}
690690

691691
IdSet{T}() where {T} = new(IdDict{T,Nothing}())
692-
IdSet{T}(s::IdSet{T}) where {T} = new(IdDict{T,Nothing}(s.dict))
692+
IdSet{T}(s::IdSet{T}) where {T} = new(copy(s.dict))
693693
end
694694

695695
IdSet{T}(itr) where {T} = union!(IdSet{T}(), itr)
696696
IdSet() = IdSet{Any}()
697697

698-
copy(s::IdSet{T}) where {T} = IdSet{T}(s)
699-
copymutable(s::IdSet{T}) where {T} = IdSet{T}(s)
698+
copymutable(s::IdSet) = typeof(s)(s)
699+
copy(s::IdSet) = typeof(s)(s)
700700

701701
isempty(s::IdSet) = isempty(s.dict)
702702
length(s::IdSet) = length(s.dict)

base/expr.jl

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,33 @@ end
3232

3333
## expressions ##
3434

35-
copy(e::Expr) = (n = Expr(e.head);
36-
n.args = copy_exprargs(e.args);
37-
n.typ = e.typ;
38-
n)
35+
function copy(e::Expr)
36+
n = Expr(e.head)
37+
n.args = copy_exprargs(e.args)
38+
n.typ = e.typ
39+
return n
40+
end
3941

4042
# copy parts of an AST that the compiler mutates
41-
copy_exprs(x::Expr) = copy(x)
4243
copy_exprs(@nospecialize(x)) = x
44+
copy_exprs(x::Expr) = copy(x)
4345
function copy_exprs(x::PhiNode)
4446
new_values = Vector{Any}(undef, length(x.values))
45-
for i = 1:length(x.edges)
47+
for i = 1:length(x.values)
48+
isassigned(x.values, i) || continue
49+
new_values[i] = copy_exprs(x.values[i])
50+
end
51+
return PhiNode(copy(x.edges), new_values)
52+
end
53+
function copy_exprs(x::PhiCNode)
54+
new_values = Vector{Any}(undef, length(x.values))
55+
for i = 1:length(x.values)
4656
isassigned(x.values, i) || continue
4757
new_values[i] = copy_exprs(x.values[i])
4858
end
49-
PhiNode(copy(x.edges), new_values)
59+
return PhiCNode(new_values)
5060
end
51-
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(a) for a in x]
61+
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(x[i]) for i in 1:length(x)]
5262

5363
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
5464
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)

base/iterators.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,10 @@ length(v::Pairs) = length(v.itr)
226226
axes(v::Pairs) = axes(v.itr)
227227
size(v::Pairs) = size(v.itr)
228228
@inline start(v::Pairs) = start(v.itr)
229-
@propagate_inbounds function next(v::Pairs, state)
229+
@propagate_inbounds function next(v::Pairs{K, V}, state) where {K, V}
230230
indx, n = next(v.itr, state)
231231
item = v.data[indx]
232-
return (Pair(indx, item), n)
232+
return (Pair{K, V}(indx, item), n)
233233
end
234234
@inline done(v::Pairs, state) = done(v.itr, state)
235235

base/pair.jl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,27 @@ foo
2828
7
2929
```
3030
"""
31-
struct Pair{A,B}
31+
struct Pair{A, B}
3232
first::A
3333
second::B
34+
function Pair{A, B}(@nospecialize(a), @nospecialize(b)) where {A, B}
35+
@_inline_meta
36+
# if we didn't inline this, it's probably because the callsite was actually dynamic
37+
# to avoid potentially compiling many copies of this, we mark the arguments with `@nospecialize`
38+
# but also mark the whole function with `@inline` to ensure we will inline it whenever possible
39+
# (even if `convert(::Type{A}, a::A)` for some reason was expensive)
40+
return new(a, b)
41+
end
3442
end
43+
Pair(a::A, b::B) where {A, B} = Pair{A, B}(a, b)
3544
const => = Pair
3645

3746
start(p::Pair) = 1
3847
done(p::Pair, i) = i>2
39-
next(p::Pair, i) = (getfield(p,i), i+1)
40-
eltype(p::Type{Pair{A,B}}) where {A,B} = Union{A,B}
48+
next(p::Pair, i) = (getfield(p, i), i+1)
49+
eltype(p::Type{Pair{A, B}}) where {A, B} = Union{A, B}
4150

42-
indexed_next(p::Pair, i::Int, state) = (getfield(p,i), i+1)
51+
indexed_next(p::Pair, i::Int, state) = (getfield(p, i), i+1)
4352

4453
hash(p::Pair, h::UInt) = hash(p.second, hash(p.first, h))
4554

0 commit comments

Comments
 (0)