What about going from interpreter → Specialized code, instead of having a more generic code intermediate step? Would that be easier?
Yes, that would be easier and will be done first. But it’s an interesting observation that you can potentially do separate compilation of a slow generic version (but compiled) that would be much faster than interpretation but would still be slow compared to a fully specialized version. There’s a lot of code that would be too slow in the interpreter but doesn’t really need full specialization.
Technically speaking, even without templates you still need to “compile” the header file to correctly link to binary code. I don’t see how template causes any essential difference here. Besides, compiler can cache instantiations of templates (and header files) for further usage.
My point is that separate compilation is the direct result of usage of header files, not lack of templates. Recall that #include
is nothing but a macro which is expanded into the content of included file. There’s no way to guarantee that symbols defined in multiple files are actually the same definition. This is different to import
, where the symbols imported into different files are enforced to be the same thing. That also explains why Haskell doesn’t have C-like separate compilation. While Haskell compiler internally uses some forms of cache to achieve separate compilation, it doesn’t explicitly exposed to the user, which is unsafe, for people may link different version of codes together and break type system consistency.
If you are 100% sure that you will never get into the forementioned problem (and some other minor problems), that is, you can maintain dependency correctly and never link wrongly, you can achieve the same things in Julia, just like how C/C++ programmers use makefile. Makefile itself is nothing but a programmer-guided separate (and incremental) compilation.
The difference is that template-based programming in C++ requires that (a) most of the implementation is in the header file and (b) implementation is re-compiled (i.e. new object code is generated) when the template is used for any new type in the user’s code (which happens frequently).
In contrast, a traditional (pre-template) header file consists mostly or entirely of declarations. These must be parsed (though that can be cached) when the header file is included, but don’t require the generation of new object code associated with each new header inclusion.
(Of course, even without templates you can still have a header file full of enormous macros etcetera which produce new object code when they are used, but this is not a typical scenario.)
It may also be interesting to compare this to whole-program optimization, where there has been a lot of progress in LLVM and elsewhere on first compiling modules separately and generically and then incrementally (and in parallel) doing inter-module code specialization and other forms of link-time optimization (LTO).
(Small world here: my sister is a lead developer for incremental LTO in LLVM.)
If she gets tired of her current job - maybe you could see if she’d want to work for Julia Computing?
Sounds like she has the skills for all the important compiler work going on these days in Julia!
There can be pros and cons to saving the machine code - and I hope some of the issues get addressed.
If you have some sort of a cluster, where the code might be running on somewhat different processors (say different versions of Intel processors, some with AVX2 and others with AVX 512), then you need to be careful about which version(s) get stored in the .ji file.
What happens if have several networked machines, sharing the same ~/.julia directory, and some are Intel based, and others have M1 processors?
Presumably, the answer is to allow users to disable native code caching if they don’t want it.
Disabling native code caching, in order to handle multiple processor types, I don’t think would be a good thing though. I think people with clusters of machines would be more interested in getting the best performance.
If the absolute paths of the Julia executable and/or of the sysimage are different, no problem: https://github.com/JuliaLang/julia/blob/05340a8cdd813f8e5774c8a85d4be60b9555d0f6/base/loading.jl#L1488-L1489.
That’s not true, there is no general forbidding of templates in Google’s C++ Style Guide, though there is a vague admonition “Avoid complicated template programming.”. I’ve seen internal Google C++ code that used variadic generics to do the equivalent of type safe printf tricks, which I guess is template metaprogramming, and not just using templates for parametric polymorphism, which is the more common use that you see everywhere in that code base.