Hi,
For pedagogical purposes, I wrote a Scheme interpreter in Julia, which I then updated to become a toy Julia interpreter written in Julia. So far, everything OK, without any relevant performance problems, despite being written in a highly recursive style. Then, I converted the interpreter to continuation-passing style (using closures as continuations) and noticed huge performance slowdowns even for the simplest examples, such as interpreting 1+2. Subsequent executions run very efficiently, but the first attempt at evaluating 1+2 takes around 30 secs before the result is returned. As the complexity of the evaluated code increases, execution times start to take minutes, then hours, and then, sometimes, I get a serious exception and Julia dies. Note that the expressions being evaluated by the interpreter are quite trivial. For example, ((x,y)->x+y)(1,2) is instantly evaluated when the evaluator is written in direct style but, when the interpreter is written in continuation-passing style, more than one hour later (and the amount of RAM used oscillating between 7 GB and 20 GB), the result is not yet available (although I can see through my logging that the interpreter is (very slowly) making progress).
I tried to create a MWE but failed to do so: as I start to remove unnecessary code, the performance problems disappear. BTW, it seems that this is related to the interaction of several functions, as redefining just one of them does not cause any performance problems. However, redefining a few at the same time then causes the massive slowdown at the next use of the evaluator.
Profiling the code shows that most of the time, type inference and abstract interpretation are the culprit, as the following flat profile shows:
10324 …CPSEvaluator.jl 38 eval_expr(::Expr)
14971 .\compiler\typeinfer.jl 574 typeinf_ext(::Core.MethodInstance, ::Core.Compiler.Params)
14971 .\compiler\typeinfer.jl 605 typeinf_ext(::Core.MethodInstance, ::UInt64)
14988 .\loading.jl 1075 include_string(::Module, ::String, ::String)
14988 .\logging.jl 491 with_logger
14988 .\logging.jl 395 with_logstate(::Atom.var"##117#122"{String,Int64,String}, ::Base.CoreLoggin…
14988 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 92 #116
14988 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 93 #117
14988 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 94 (::Atom.var"##118#123"{String,Int64,String})()
14988 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 46 withpath(::Function, ::String)
14988 C:\Users\aml.julia\packages\Atom\Wouyw\src\repl.jl 75 hideprompt(::Atom.var"##116#121"{String,Int64,String})
14988 C:\Users\aml.julia\packages\CodeTools\xGemk\src\eval.jl 30 include_string(::Module, ::String, ::String, ::Int64)
14988 C:\Users\aml.julia\packages\CodeTools\xGemk\src\utils.jl 30 withpath(::Atom.var"##118#123"{String,Int64,String}, ::String)
14989 .\task.jl 333 (::Atom.var"##19#21"{Array{Any,1}})()
14989 C:\Users\aml.julia\packages\Atom\Wouyw\src\comm.jl 164 handlemsg(::Dict{String,Any}, ::Dict{String,Any})
14989 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 86 (::Atom.var"##115#120")(::Dict{String,Any})
14989 C:\Users\aml.julia\packages\Atom\Wouyw\src\eval.jl 91 macro expansion
14989 C:\Users\aml.julia\packages\Media\ItEPc\src\dynamic.jl 24 macro expansion
60721 .\compiler\abstractinterpretation.jl 21 abstract_call_gf_by_type(::Any, ::Array{Any,1}, ::Any, ::Core.Compiler.Infe…
60757 .\compiler\abstractinterpretation.jl 873 abstract_eval_call(::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Core.C…
279725 .\compiler\abstractinterpretation.jl 846 abstract_call(::Any, ::Nothing, ::Array{Any,1}, ::Array{Any,1}, ::Core.Comp…
281133 .\compiler\abstractinterpretation.jl 875 abstract_eval_call(::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Core.C…
281136 .\compiler\abstractinterpretation.jl 636 abstract_call(::Any, ::Array{Any,1}, ::Array{Any,1}, ::Array{Any,1}, ::Core…
332732 .\compiler\typeinfer.jl 488 typeinf_edge(::Method, ::Any, ::Core.SimpleVector, ::Core.Compiler.Inferenc…
336843 .\compiler\abstractinterpretation.jl 396 abstract_call_method(::Method, ::Any, ::Core.SimpleVector, ::Bool, ::Core.C…
338689 .\compiler\abstractinterpretation.jl 93 abstract_call_gf_by_type(::Any, ::Array{Any,1}, ::Any, ::Core.Compiler.Infe…
341973 .\compiler\abstractinterpretation.jl 945 abstract_eval(::Any, ::Array{Any,1}, ::Core.Compiler.InferenceState)
342067 .\compiler\abstractinterpretation.jl 1204 typeinf_local(::Core.Compiler.InferenceState)
342781 .\compiler\abstractinterpretation.jl 1260 typeinf_nocycle(::Core.Compiler.InferenceState)
343472 .\compiler\typeinfer.jl 12 typeinf(::Core.Compiler.InferenceState)
BTW, I noticed similar behavior in Julia 1.1.0, 1.2.0, and 1.3.0-rc1.
Do you have any suggestions for understanding what is going on?
Thanks.