-
-
Notifications
You must be signed in to change notification settings - Fork 285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce time to first from_flat_kwargs
#2005
Conversation
Try this Pull Request!Open Julia and type: julia> import Pkg
julia> Pkg.activate(temp=true)
julia> Pkg.add(url="https://github.com/fonsp/Pluto.jl", rev="rh/from-flat-kwargs-perf")
julia> using Pluto |
But!! We currently use Configurations.jl for an awesome feature: the Pluto configuration becomes a subconfiguration of PlutoSliderServer: https://github.com/JuliaPluto/PlutoSliderServer.jl#2-plutodeploymenttoml So if we take this PR, we need to find a way to still support that 😯 |
What do you think about improving Configurations.jl itself? It feels like theoretically, the macro could return exactly the code that you wrote, which would give the same performance? Or do macros block precompilation? |
I've looked into it, but based on my experience with
You've spotted a flaw in my text in the first post. Compilation can go through macro's in some cases (Julia 1.7.2): f() = 3
macro call_f()
return :(f())
end
g() = @call_f()
function is_precompiled(f::Function)
method = only(methods(f))
return !isempty(method.specializations)
end
@show precompile(g, ())
@show is_precompiled(g)
@show is_precompiled(f)
Anyway, I'll think a bit about this how we can move forward. Will probably take some days. |
The generated function was made to improve parsing speed. If the latency is the main concern we can consider bringing the old dynamic parser back. |
Hmm. Yes I did say that that takes the most time, but that wouldn't be a problem if Julia could infer everything because that generated thing would be compiled during the precompilation phase anyway. Upon further inspection, it turns out that type inference starts to propagate julia> using Cthulhu, Pluto
julia> @descend Pluto.Configuration.from_flat_kwargs(; port=8000) and disabled optimization (press
Configurations.from_dict($(Expr(:static_parameter, 1)), d) where The Here, al lot of stuff is of type |
I think for Pluto case parsing speed is unlikely to be a concern, so what if we just do a dynamic version. Have you checked any old version of Configurations for your benchmark? yeah |
Yes. It saved only 20% whereas this PR saves 97% or so.
Well, like I said I don't have time unfortunately to dive into that. I'll see whether I can fix the problem at the PlutoSliderServer side and then see whether the Pluto developers are fine with merging this PR. |
I've checked it and the tests in https://github.com/JuliaPluto/PlutoSliderServer.jl/blob/f8a561785503582a2f089852b1352a7143de6fe9/test/configuration.jl#L1-L12 did indeed fail after my changes in this PR. I've now fixed that in e45b11c by putting |
Can we put extra macros/generated functions/magic into Configurations.jl to generate this code automatically? Or is this what you mean with the dynamic version? |
What means this here (both times)? |
Yeah that was confusing :) It sounds like we get the big performance gain from writing this function by hand: Lines 209 to 278 in 973b0b2
Configurations.from_flat_kwargs . Can this be automated by Configurations.jl? In theory, a macro like @give_me_the_optimized_from_flat_kwargs_function(Options) should be able to generate it with the same result, right? (Obviously we want nicer API)
|
Yes that's definitely possible. The current from kwargs interface is not specialized to each type, but just reuse the from_dict mechanism. we can write a codegen to specialize it. |
@Roger-luo Hey roger! We discussed this PR on the call today, and because of the pretty large performance benefit, we were thinking of merging this PR now, and reverting it in the future, after this has been fixed in Configurations.jl. |
For me, Julia 1.7.0:
|
Yeah, I think you can merge this first, you can always specialize the Configurations API to bypass bad generated code. I probably won't have time to work on things recently since I'm busy with a few other things. |
For future reference. Current state of allocations from the Julia 1.7 Ubuntu logs:
So at least 3.62GiB still to go. This number doesn't include the allocations for some parts of Pluto such as the loader. For Julia 1.8-beta3, the allocations are as follows (note: don't take running time too serious, GitHub Runners don't all have the same CPU):
|
🎶 Another second bites the dust. Another second bites the dust. And another one gone and another one gone. 🎶
This PR reduces the time to first
from_flat_kwargs
by a few seconds (120 MB allocations). In effect, this reduces the time toPluto.run
by a few seconds.The reason that this PR allows the reduction is as follows. The function
from_flat_kwargs
used to useConfigurations.jl
to pass keyword arguments for different structs to them via metaprogramming. Specifically, the profiler showed that most time was spent inhttps://github.com/Roger-luo/Configurations.jl/blob/5a23569281345564b082f7b0c09a26720ff68e76/src/from_dict.jl#L274-L336
Metaprogramming is all fun and games and very powerful, but terrible for the compiler. During the precompilation phase, the compiler will compile all kinds of things except when they are hidden behind a non-trivial
eval
on some expression. This makes sense because the compiler cannot know whether evaluating some random expression will cause unexpected side-effects. To avoid this metaprogramming problem, I've taken the boring road of handling the keyword arguments manually. This makes the compiler very happy, that is, it is now much easier to infer the types which makes it much easier to precompile everything. Hence, the reduction in time to first X.Benchmark
main
this PR