Skip to content

Commit 92f8d7e

Browse files
vtjnashjrevels
authored andcommitted
perf: optimize append_any function (#30248)
Reimplement a larger portion of the optimizations in jl_f__apply in the fallback function, so we can reduce the performance wall in more cases. General fix for #29133-like performance issues
1 parent 9c3ba19 commit 92f8d7e

File tree

7 files changed

+91
-46
lines changed

7 files changed

+91
-46
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector
149149
length(argtypes) >= nargs || return Any
150150
haveconst = false
151151
for a in argtypes
152-
a = maybe_widen_conditional(a)
152+
a = widenconditional(a)
153153
if has_nontrivial_const_info(a)
154154
# have new information from argtypes that wasn't available from the signature
155155
if !isa(a, Const) || (isa(a.val, Symbol) || isa(a.val, Type) || (!isa(a.val, String) && isimmutable(a.val)))
@@ -181,7 +181,7 @@ function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector
181181
if !istopfunction(f, :getproperty) && !istopfunction(f, :setproperty!)
182182
# in this case, see if all of the arguments are constants
183183
for a in argtypes
184-
a = maybe_widen_conditional(a)
184+
a = widenconditional(a)
185185
if !isa(a, Const) && !isconstType(a)
186186
return Any
187187
end
@@ -532,7 +532,7 @@ end
532532

533533
function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState)
534534
for i = 2:length(argtypes)
535-
a = maybe_widen_conditional(argtypes[i])
535+
a = widenconditional(argtypes[i])
536536
if !(isa(a, Const) || isconstType(a))
537537
return false
538538
end
@@ -551,7 +551,7 @@ function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(a
551551
return false
552552
end
553553

554-
args = Any[ (a = maybe_widen_conditional(argtypes[i]); isa(a, Const) ? a.val : a.parameters[1]) for i in 2:length(argtypes) ]
554+
args = Any[ (a = widenconditional(argtypes[i]); isa(a, Const) ? a.val : a.parameters[1]) for i in 2:length(argtypes) ]
555555
try
556556
value = Core._apply_pure(f, args)
557557
# TODO: add some sort of edge(s)
@@ -1089,7 +1089,7 @@ function typeinf_local(frame::InferenceState)
10891089
end
10901090
elseif hd === :return
10911091
pc´ = n + 1
1092-
rt = maybe_widen_conditional(abstract_eval(stmt.args[1], s[pc], frame))
1092+
rt = widenconditional(abstract_eval(stmt.args[1], s[pc], frame))
10931093
if !isa(rt, Const) && !isa(rt, Type) && (!isa(rt, PartialTuple) || frame.cached)
10941094
# only propagate information we know we can store
10951095
# and is valid inter-procedurally

base/compiler/inferenceresult.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function matching_cache_argtypes(linfo::MethodInstance, given_argtypes::Vector)
3636
@assert isa(linfo.def, Method) # ensure the next line works
3737
nargs::Int = linfo.def.nargs
3838
@assert length(given_argtypes) >= (nargs - 1)
39-
given_argtypes = anymap(maybe_widen_conditional, given_argtypes)
39+
given_argtypes = anymap(widenconditional, given_argtypes)
4040
if linfo.def.isva
4141
isva_given_argtypes = Vector{Any}(undef, nargs)
4242
for i = 1:(nargs - 1)

base/compiler/tfuncs.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ add_tfunc(ifelse, 3, 3,
223223
return tmerge(x, y)
224224
end, 1)
225225
function egal_tfunc(@nospecialize(x), @nospecialize(y))
226-
xx = maybe_widen_conditional(x)
227-
yy = maybe_widen_conditional(y)
226+
xx = widenconditional(x)
227+
yy = widenconditional(y)
228228
if isa(x, Conditional) && isa(yy, Const)
229229
yy.val === false && return Conditional(x.var, x.elsetype, x.vtype)
230230
yy.val === true && return x
@@ -841,7 +841,7 @@ function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt))
841841
u = headtype
842842
for i = 2:length(argtypes)
843843
isa(u, UnionAll) || return false
844-
ai = maybe_widen_conditional(argtypes[i])
844+
ai = widenconditional(argtypes[i])
845845
if ai === TypeVar
846846
# We don't know anything about the bounds of this typevar, but as
847847
# long as the UnionAll is not constrained, that's ok.
@@ -922,7 +922,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
922922
tparams = Any[]
923923
outervars = Any[]
924924
for i = 1:largs
925-
ai = maybe_widen_conditional(args[i])
925+
ai = widenconditional(args[i])
926926
if isType(ai)
927927
aip1 = ai.parameters[1]
928928
canconst &= !has_free_typevars(aip1)
@@ -1010,7 +1010,7 @@ end
10101010
# convert the dispatch tuple type argtype to the real (concrete) type of
10111011
# the tuple of those values
10121012
function tuple_tfunc(atypes::Vector{Any})
1013-
atypes = anymap(maybe_widen_conditional, atypes)
1013+
atypes = anymap(widenconditional, atypes)
10141014
all_are_const = true
10151015
for i in 1:length(atypes)
10161016
if !isa(atypes[i], Const)

base/compiler/typeinfer.jl

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,6 @@ function widen_all_consts!(src::CodeInfo)
223223
return src
224224
end
225225

226-
maybe_widen_conditional(@nospecialize vt) = vt
227-
function maybe_widen_conditional(vt::Conditional)
228-
if vt.vtype === Bottom
229-
return Const(false)
230-
elseif vt.elsetype === Bottom
231-
return Const(true)
232-
else
233-
return Bool
234-
end
235-
end
236-
237226
function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1})
238227
head = e.head
239228
i0 = 1
@@ -256,7 +245,7 @@ end
256245
function visit_slot_load!(sl::Slot, vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1})
257246
id = slot_id(sl)
258247
s = vtypes[id]
259-
vt = maybe_widen_conditional(s.typ)
248+
vt = widenconditional(s.typ)
260249
if s.undef
261250
# find used-undef variables
262251
undefs[id] = true
@@ -312,7 +301,7 @@ function type_annotate!(sv::InferenceState)
312301
if gt[j] === NOT_FOUND
313302
gt[j] = Union{}
314303
end
315-
gt[j] = maybe_widen_conditional(gt[j])
304+
gt[j] = widenconditional(gt[j])
316305
end
317306

318307
# compute the required type for each slot

base/compiler/typelattice.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ end
217217
@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NOT_FOUND || (n !== NOT_FOUND && !(n o))
218218
@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n, o)))
219219

220+
widenconditional(@nospecialize typ) = typ
220221
function widenconditional(typ::Conditional)
221222
if typ.vtype == Union{}
222223
return Const(false)

base/essentials.jl

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -415,27 +415,6 @@ Stacktrace:
415415
"""
416416
sizeof(x) = Core.sizeof(x)
417417

418-
function append_any(xs...)
419-
# used by apply() and quote
420-
# must be a separate function from append(), since apply() needs this
421-
# exact function.
422-
out = Vector{Any}(undef, 4)
423-
l = 4
424-
i = 1
425-
for x in xs
426-
for y in x
427-
if i > l
428-
_growend!(out, 16)
429-
l += 16
430-
end
431-
arrayset(true, out, y, i)
432-
i += 1
433-
end
434-
end
435-
_deleteend!(out, l-i+1)
436-
out
437-
end
438-
439418
# simple Array{Any} operations needed for bootstrap
440419
@eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i)
441420

@@ -641,6 +620,72 @@ function isassigned(v::SimpleVector, i::Int)
641620
return x != C_NULL
642621
end
643622

623+
624+
# used by ... syntax to access the `iterate` function from inside the Core._apply implementation
625+
# must be a separate function from append(), since Core._apply needs this exact function
626+
function append_any(xs...)
627+
@nospecialize
628+
lx = length(xs)
629+
l = 4
630+
i = 1
631+
out = Vector{Any}(undef, l)
632+
for xi in 1:lx
633+
x = @inbounds xs[xi]
634+
# handle some common cases, where we know the length
635+
# and can inline the iterator because the runtime
636+
# has an optimized version of the iterator
637+
if x isa SimpleVector
638+
lx = length(x)
639+
if i + lx - 1 > l
640+
ladd = lx > 16 ? lx : 16
641+
_growend!(out, ladd)
642+
l += ladd
643+
end
644+
for j in 1:lx
645+
y = @inbounds x[j]
646+
arrayset(true, out, y, i)
647+
i += 1
648+
end
649+
elseif x isa Tuple
650+
lx = length(x)
651+
if i + lx - 1 > l
652+
ladd = lx > 16 ? lx : 16
653+
_growend!(out, ladd)
654+
l += ladd
655+
end
656+
for j in 1:lx
657+
y = @inbounds x[j]
658+
arrayset(true, out, y, i)
659+
i += 1
660+
end
661+
elseif x isa Array
662+
lx = length(x)
663+
if i + lx - 1 > l
664+
ladd = lx > 16 ? lx : 16
665+
_growend!(out, ladd)
666+
l += ladd
667+
end
668+
for j in 1:lx
669+
y = arrayref(true, x, j)
670+
arrayset(true, out, y, i)
671+
i += 1
672+
end
673+
else
674+
for y in x
675+
if i > l
676+
_growend!(out, 16)
677+
l += 16
678+
end
679+
arrayset(true, out, y, i)
680+
i += 1
681+
end
682+
end
683+
end
684+
_deleteend!(out, l - i + 1)
685+
return out
686+
end
687+
688+
644689
"""
645690
Colon()
646691

src/cgutils.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ static std::pair<Value*, bool> emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x,
11061106
}
11071107

11081108
// intersection with Type needs to be handled specially
1109-
if (jl_has_intersect_type_not_kind(type)) {
1109+
if (jl_has_intersect_type_not_kind(type) || jl_has_intersect_type_not_kind(intersected_type)) {
11101110
Value *vx = maybe_decay_untracked(boxed(ctx, x));
11111111
Value *vtyp = maybe_decay_untracked(literal_pointer_val(ctx, type));
11121112
if (msg && *msg == "typeassert") {
@@ -1150,6 +1150,16 @@ static std::pair<Value*, bool> emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x,
11501150
return std::make_pair(ctx.builder.CreateICmpEQ(emit_typeof_boxed(ctx, x),
11511151
maybe_decay_untracked(literal_pointer_val(ctx, intersected_type))), false);
11521152
}
1153+
jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(intersected_type);
1154+
if (jl_is_datatype(dt) && !dt->abstract && jl_subtype(dt->name->wrapper, type)) {
1155+
// intersection is a supertype of all instances of its constructor,
1156+
// so the isa test reduces to a comparison of the typename by pointer
1157+
return std::make_pair(
1158+
ctx.builder.CreateICmpEQ(
1159+
mark_callee_rooted(emit_datatype_name(ctx, emit_typeof_boxed(ctx, x))),
1160+
mark_callee_rooted(literal_pointer_val(ctx, (jl_value_t*)dt->name))),
1161+
false);
1162+
}
11531163
// everything else can be handled via subtype tests
11541164
return std::make_pair(ctx.builder.CreateICmpNE(
11551165
ctx.builder.CreateCall(prepare_call(jlsubtype_func),

0 commit comments

Comments
 (0)