I’m definitely not a professional programmer and I don’t have a degree in computer engineering, so please forgive any naive questions — this post comes purely from curiosity.
I’ve been using Julia for research for a few years now, and I have some experience with compiled languages like C++ and Fortran. Lately, I’ve noticed a lot of discussion around efforts to create a native compiler for Julia — in particular, I’m referring to juliac.
Unfortunately, I couldn’t find much up-to-date information about its current state, so I was hoping the community could shed some light on the status and direction of this project.
I also have a beginner-level question that I’m hoping someone can clarify. From what I understand, the idea (at least for now) is that it will be possible to compile Julia code only if there are no heap allocations or external runtime dependencies. That is, the runtime and garbage collector won’t be included in the final executable.
Why is this the case? Is it due to fundamental limitations in the language design? Or is this just a first step toward a future where a more complete Julia runtime might be included in the compiled output?
I also have a beginner-level question that I’m hoping someone can clarify. From what I understand, the idea (at least for now) is that it will be possible to compile Julia code only if there are no heap allocations or external runtime dependencies
This isn’t correct. Juliac supports allocations (and the runtime is provided by the Julia binary like normal)
Yeah, this can be a little confusing. There are quite a few different ways of running Julia and more are coming.
There’s the julia REPL. It’s already a native compiler! As you type in Julia code, it’ll get compiled to your architecture on the fly! Of course not a very great distribution mechanism.
There’s julia script.jl. Julia will parse the script and similarly generate native code on the fly. But again, every time you run this, it’ll re-do the compilation work.
There’s julia --project=@script script.jl that’ll use the packages defined in the Project.toml that appears alongside the script. These packages will precompile the first time you instantiate them — down to native code! And then all those packages — particularly if they use PrecompileTools.jl — will preemptively cache native code for you, so Julia only needs to on-demand compile the stuff that appears in script.jl or is missing from the package precompiles. But of course distributing this is still hard — it’s not “just” an .exe (or equivalent).
There’s PackageCompiler.jl, which can create a native executable given a particular set of packages and workflow. This can make distributing much easier! But… it gathers up everything you might possibly call. It’s slow and big. The outputs in the hundreds of MB or even GB range.
Then, finally, there’s juliac and in particular juliac --experimental --trim. These are still experimental, but the latter will trim out everything you don’t call, leading to much more reasonable executable sizes. The big limitation on getting a small binary is with type stability, not allocations — if things aren’t type stable, Julia won’t be able to predict what methods you’ll need to compile. The best place to see info here is in live talks; see Jeff at JuliaCon 2024 and PyData 2024.
I wonder to what extent a Julia-lang blog post on the pros and cons of the different ways to run Julia would be useful. It’s getting harder and harder to keep track of all them.
What happens if your code isn’t completely typestable / contains dynamic dispatch? Is this code then out of scope for juliac? Or will it do a best effort but you won’t get very small binaries? Or can the user provide something similar to a precompile workload and promise the compiler that there won’t be any other method calls (accepting runtime errors otherwise)?
I think juliac has a heuristic cutoff for when it considers a call site to be “too dynamic”, so some amount of dynamic dispatch is allowed, but the set of possible callees cannot be unbounded. It’s like the small union optimization where Union{Nothing,Something} is also not considered type-unstable per se.