I’ve read a lot about JIT and Intperpreters so far, because I am very curious about how Julia works under the hood. However being completely new to compiler technology, some things still remain a mystery for me.
To my knowledge, Julia is a mixture of an Interpreter and a Just In Time Compiler, is that true? When a new Function is called, Julia knows the arguments et cetera and generates an LLVM code. The LLVM library (which is shipped with Julia but not part of the project I guess? I haven’t heard of LLVM before, but read a little about it until now) then generates machine code depending on the architecture it runs on. This makes the first function call slow (-> there is issued a compile chain at runtime: Julia->LLVM->machine code) and all further function calls fast (executing the cached function call). Is this true so far? I’ve read (spread across some discussions and issues), that the cached code can become very big, and therefore is not stored after exiting a Julia session. Where is it actually stored as long as the session runs? In RAM only, or also in some files?
On the other hand, in global scope the compiler knows nothing and everything is very dynamic. Therefore each instruction must be executed line by line and can’t be pre-compiled, just like Python does this. Therefore stuff in global scope is slower. Did I understand this correctly? Would an interpreter actually be faster, if you would call a function only once (and therefore there is no reuse of the cached machine code)? So you could get rid of the compile-overhead. But somehow an Interpreter also must generate machine code at some point. It is just not optimized, I guess, because it is not possible to optimize when looking only at a single line at once. So why is this actually called an Interpreter, and not a (unoptimizing) Compiler. Or asking the question differently: What is the difference between an Interpreter, an Just in Time Compiler, and Julia, exactly?
My second question is: When Julia compiles just in time, why is there a need for compiling modules, before
using them? Is this due to external, non-Julia dependencies only? Or does a pure Julia module also do some valuable pre-compilation? Also (I asked this question already, but didn’t get too much reaction until now), why does
using XY take so long sometimes? Is it due to loading the source file into RAM, or is there happening some other stuff as well? For example, when I use ControlSystems, the first call takes 9 seconds, the second call takes 2 seconds, and only the third call takes 0.3 ms. What is happening in the second call? Why does it not return instantly as in the third call?
I know this is a lot to ask in one thread. But at the moment I try to wrap my mind around how Julia works, and I found stuff is not so easy as it looks on the first glance. But understanding this really helps me when talking about Julia to others (who don’t know Julia yet), and also in using Julia correctly. So if someone could enlighten me a bit more than I am recently, that would be very much appreciated!