Skip to content

Commit

Permalink
fix #16434, superlinear compiler run time on large functions
Browse files Browse the repository at this point in the history
improves the algorithm in `type_annotate!` to look only at uses
of variables instead of (all variables)*(all statements)
  • Loading branch information
JeffBezanson committed May 29, 2016
1 parent 6591216 commit 1dae0e0
Showing 1 changed file with 45 additions and 28 deletions.
73 changes: 45 additions & 28 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1966,14 +1966,32 @@ function finish(me::InferenceState)
nothing
end

function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, undefs)
function record_slot_type!(id, vt::ANY, slottypes)
if vt !== Bottom
otherTy = slottypes[id]
if otherTy === Bottom
slottypes[id] = vt
elseif otherTy !== Any && !typeseq(otherTy, vt)
slottypes[id] = Any
end
end
end

function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, undefs, pass)
if isa(e, Slot)
t = abstract_eval(e, vtypes, sv)
s = vtypes[e.id]
if s.undef
undefs[e.id] = true
id = (e::Slot).id
s = vtypes[id]
vt = widenconst(s.typ)
if pass == 1
# first pass: find used-undef variables and type-constant variables
if s.undef
undefs[id] = true
end
record_slot_type!(id, vt, sv.linfo.slottypes)
return e
end
return t === sv.linfo.slottypes[e.id] ? e : TypedSlot(e.id, t)
# second pass: add type annotations where needed
return vt === sv.linfo.slottypes[id] ? e : TypedSlot(id, vt)
end

if !isa(e,Expr)
Expand All @@ -1985,14 +2003,14 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::InferenceState, undefs)
if is(head,:static_typeof) || is(head,:line) || is(head,:const)
return e
elseif is(head,:(=))
e.args[2] = eval_annotate(e.args[2], vtypes, sv, undefs)
e.args[2] = eval_annotate(e.args[2], vtypes, sv, undefs, pass)
return e
end
i0 = is(head,:method) ? 2 : 1
for i=i0:length(e.args)
subex = e.args[i]
if !(isa(subex,Number) || isa(subex,AbstractString))
e.args[i] = eval_annotate(subex, vtypes, sv, undefs)
e.args[i] = eval_annotate(subex, vtypes, sv, undefs, pass)
end
end
return e
Expand All @@ -2013,38 +2031,31 @@ end
# annotate types of all symbols in AST
function type_annotate!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, nargs)
nslots = length(states[1])
for i = 1:nslots
nargs = linfo.nargs
for i = 1:nargs
linfo.slottypes[i] = widenconst(states[1][i].typ)
end
for i = nargs+1:nslots
linfo.slottypes[i] = Bottom
end
undefs = fill(false, nslots)
body = linfo.code::Array{Any,1}
nexpr = length(body)
for i=1:nexpr
# identify variables always used as the same type
st_i = states[i]
if st_i !== ()
for j = 1:nslots
vt = widenconst(st_i[j].typ)
if vt !== Bottom
otherTy = linfo.slottypes[j]
if otherTy === Bottom
linfo.slottypes[j] = vt
elseif otherTy !== Any && !typeseq(otherTy, vt)
linfo.slottypes[j] = Any
end
end
end
end
end
i = 1
optimize = sv.optimize::Bool
while i <= nexpr
st_i = states[i]
expr = body[i]
if st_i !== ()
# st_i === () => unreached statement (see issue #7836)
body[i] = eval_annotate(body[i], st_i, sv, undefs)
eval_annotate(expr, st_i, sv, undefs, 1)
if isa(expr, Expr) && expr.head == :(=) && i < nexpr && isa(expr.args[1],Slot) && states[i+1] !== ()
# record type of assigned slot by looking at the next statement.
# this is needed in case the slot is never used (which makes eval_annotate miss it).
id = expr.args[1].id
record_slot_type!(id, widenconst(states[i+1][id].typ), linfo.slottypes)
end
elseif optimize
expr = body[i]
if isa(expr, Expr) && expr_cannot_delete(expr::Expr)
i += 1
continue
Expand All @@ -2058,6 +2069,12 @@ function type_annotate!(linfo::LambdaInfo, states::Array{Any,1}, sv::ANY, nargs)
end
i += 1
end
for i = 1:nexpr
st_i = states[i]
if st_i !== ()
body[i] = eval_annotate(body[i], st_i, sv, undefs, 2)
end
end

# mark used-undef variables
for i = 1:nslots
Expand Down

3 comments on commit 1dae0e0

@mlubin
Copy link
Member

@mlubin mlubin commented on 1dae0e0 May 29, 2016

Choose a reason for hiding this comment

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

Is this a guarantee that compilation time should scale linearly now? If so, that is huge. Compilation time was a big issue for JuMP's AD until we rewrote it to not dynamically generate code...

@JeffBezanson
Copy link
Member Author

Choose a reason for hiding this comment

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

Probably not; there are certainly still super-linear algorithms in the compiler, but you won't always hit those cases. Would be interesting to see if this helps the AD code.

@toivoh
Copy link
Contributor

@toivoh toivoh commented on 1dae0e0 May 29, 2016

Choose a reason for hiding this comment

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

@mlubin: If you run into other superlinear cases, please file issues about them!
I guess that superlinear scaling is not always avoidable, but when it is I hope we might as well try to do it.

Please sign in to comment.