Roadmap for a faster time-to-first-plot?

UnicodePlots is also very fast if all your need is bar plot or simple line plots


#edit previously I claim that PackageCompilerX is abandoned, but I am totally wrong, it is under active development. Since I am not a compiler specialist, please forget what I have said and forgive my ignorance.

C++ is AOT so it is simply solved by first compiling and then running the binary. And compile times for template heavy C++ code are pretty bad.


Please consider whether you really have the depth of knowledge to lay out the situation here. Things like claiming a package is abandoned without knowing it is highly counterproductive (you claim PackageCompilerX is abandoned, which it definitely isn’t, the kc/wip branch was last committed to 15 hours ago - on the contrary it represents one of our best shots at real progress here). And concluding comments such as “I am sceptical of this approach” and “But I think we can’t gain much from this method” adds no new information and does not help the discussion.


No, he is correct, I did claim that"I think PackageCompilerX is abandoned". I am really sorry for that. I hope my claim doesn’t mislead anyone.


I was checking the latest news, I saw this video for new AMD CPU with 32 cores:

They compare the time that is cut from compiling of Android, Chromium, and a game engine by adding more Cores.

@ChrisRackauckas pointed that Julia’s compiler is serial now, but we can use ORCJIT features of LLVM to parallelize that.
I guess this should be considered in the road map

Also, talked about here: New LLVM JIT Features


Actual LLVM compile time is not the major issue for many packages, rather type inference or other issues with actually generating the LLVM code. So multithreaded LLVM compiler is unlikely to help.


Not just the LLVM.
It might seem impossible now, but I feel that even code inference can be done in parallel for different functions (I don’t know the details of implantation).

Well IIRC Plots LLVM time is like 3 seconds. Leon’s latest work gets us down to 8.2 seconds. Our goal is getting to 5. When it was 26 seconds LLVM time didn’t matter, but now it’s something that’s interesting again. But even then, it’s still not the majority of the time, and as the total amount of compilation is cut down, this is cut down automatically as well, so it may still not be a major factor but always a small gain that could be had.

Fixing this issue will be a multi-prong approach:

  • Better precompilation of packages
  • Static compilation of things when necessary?
  • Less method invalidation
  • A “grown sysimage” for users that pools precompilation of their most used packages (this a vast simplification of something from recent in-person discussions)
  • The compiler giving up earlier on things that are not inferrable anyways
  • Making the interpreter be used in cases where things already aren’t inferred (this “give up” case)
  • Multithreading the compilation so that way long compiles are handled better (large functions have O(n^2) compilation behavior in LLVM IIRC)

etc. The nice thing is that there have been like 20 things identified that could help. It’s just about trying a bunch of things, and in conglomeration the issue will be completed. You won’t see a single silver bullet take a standard package from 28 seconds to 1 second compile times (other than maybe static compilation), but as these approaches become available you’ll see each one take a slice out of it incrementally.


Later on please consider writing up a summary of various approaches and the speedup they gave, possibly as a blog post. I think it would be very instructive to other developers.


I think Leon’s work so far was mostly to come up with a set of tasks for Plots to execute and then run SnoopCompile. In other words, this magic can be widely copied.


Indeed, but it was coupled with some ways to instrument the compiler and find out precisely what’s taking the most time to compile, and some dives into what’s causing times to grow and discussions of method invalidation. It would be good to write this down, but we’re really just getting started.


I’m a bit confused what’s changed. In Julia v0.5 or so I used SnoopCompile on all tests to generate precompiles and this worked quite well. Then at some point the precompiles stopped showing any noticeable improvement and I deleted the precompiles.

But now you are saying use SnoopCompile To generate precompile statements, but not on the tests?

It never really changed. It just has to do with how much gets invalidated and how much of it can be isolated to specific packages. Precompiles which “share types” are currently just dropped (probably one of the biggest limitations to even using something like SnoopCompile)

We need to time more, but my feeling is that if Gadfly/Compose and Plots both dropped their dependency on Measurements.jl and instead embedded a version into themselves as a package so that all of those weird number types can precompile and then a snoop file is used for that, I think it could do quite a bit.

(In fact, if anyone wants to contribute, this is a very nice thing to try. Make Measurements.jl a submodule of Plots by pasting it in, and use the SnoopCompile stuff from Leon’s PR to add precompiles that utilize this module. See what happens. Beginner friendly!)


I guess this can be done with just a few lines, without copying the whole codebase?

include(Base.locate_package(Base.PkgId(Base.UUID("eff96d63-e80a-5855-80a2-b1b0885c5ab7"), "Measurements")))
using .Measurements  # notice the leading dot

(Note that, strictly speaking, this requires Measurements.jl to make deps a part of public API.)

Are you talking about


Yes Measures

You don’t really have to do any diving now with @snoopi. The old @snoopc measured codegen time (dominated by LLVM time), which isn’t terribly relevant. With @snoopi you both measure inference time and can then figure out how well your intervention “worked.”


It’s already in a submodule so this should be very easy


I just tried that and could not notice any significant change.


julia> @time using Plots
11.759940 seconds (22.34 M allocations: 1.123 GiB, 3.96% gc time)

julia> @time display(plot(rand(10)))
Plot{Plots.GRBackend() n=1}
14.330170 seconds (28.71 M allocations: 1.385 GiB, 3.41% gc time)


(with code from Measures.jl copied into a submodule of Plots)

julia> @time using Plots
 12.408045 seconds (22.58 M allocations: 1.137 GiB, 4.08% gc time)

julia> @time display(plot(rand(10)))
Plot{Plots.GRBackend() n=1}
 14.317090 seconds (28.69 M allocations: 1.384 GiB, 3.63% gc time)