官术网_书友最值得收藏!

How Julia works

(You can safely skip this section on a first reading.) Julia works with an LLVM JIT compiler framework that is used for JIT generation of machine code. The first time you run a Julia function, it is parsed, and the types are inferred. Then, LLVM code is generated by the JIT compiler, which is then optimized and compiled down to native code. The second time you run a Julia function, the native code that's already generated is called. This is the reason why, the second time you call a function with arguments of a specific type, it takes much less time to run than the first time (keep this in mind when doing benchmarks of Julia code).

This generated code can be inspected. Suppose, for example, that we have defined a f(x) = 2x + 5 function in a REPL session. Julia responds with the message f (generic function with one method); the code is dynamic because we didn't have to specify the type of x or f. Functions are, by default, generic in Julia because they are ready to work with different data types for their variables.

The code_llvm function can be used to see the JIT bytecode. This is the bytecode generated by LLVM, and it will be different for each target platform. For example, for the Intel x64 platform, if the x argument is of type Int64, it will be as follows:

julia> code_llvm(f, (Int64,)) 
 
; Function f 
; Location: REPL[7]:1 
; Function Attrs: uwtable 
define i64 @julia_f_33833(i64) #0 { 
top: 
; Function *; { 
; Location: int.jl:54 
  %1 = shl i64 %0, 1 
;} 
; Function +; { 
; Location: int.jl:53 
  %2 = add i64 %1, 5 
;} 
  ret i64 %2 
} 

The code_native function can be used to see the assembly code that was generated for the same type of x:

julia> code_native(f, (Int64,)) 
 
        .text 
; Function f { 
; Location: REPL[7]:1 
        pushq   %rbp 
        movq    %rsp, %rbp 
; Function +; { 
; Location: int.jl:53 
        leaq    5(%rcx,%rcx), %rax 
;} 
        popq    %rbp 
        retq 
        nopl    (%rax,%rax) 
;} 

Compare this with the code generated when x is of type Float64:

julia> code_native(f, (Float64,)) 
 
        .text 
; Function f { 
; Location: REPL[7]:1 
        pushq   %rbp 
        movq    %rsp, %rbp 
; Function *; { 
; Location: promotion.jl:314 
; Function *; { 
; Location: float.jl:399 
        vaddsd  %xmm0, %xmm0, %xmm0 
        movabsq $424735072, %rax        # imm = 0x1950F160 
;}} 
; Function +; { 
; Location: promotion.jl:313 
; Function +; { 
; Location: float.jl:395 
        vaddsd  (%rax), %xmm0, %xmm0 
;}} 
        popq    %rbp 
        retq 
        nopl    (%rax,%rax) 
;} 

Julia code is fast because it generates specialized versions of functions for each data type. Julia also implements automatic memory management. The user doesn't have to worry about allocating and keeping track of the memory for specific objects. Automatic deletion of objects that are not needed anymore (and hence, reclamation of the memory associated with those objects) is done using a garbage collector (GC).

The GC runs at the same time as your program. Exactly when a specific object is garbage collected is unpredictable. The GC implements an incremental mark-and-sweep algorithm. You can start garbage collection yourself by calling GC.gc(), or if you don't need it, you can disable it by calling GC.enable(false).

The standard library is implemented in Julia itself. The I/O functions rely on the libuv library for an efficient, platform-independent I/O. The standard library is contained in a package called Base, which is automatically imported when starting Julia.

主站蜘蛛池模板: 苗栗县| 上饶市| 城固县| 齐齐哈尔市| 大足县| 盐津县| 吴川市| 六安市| 鞍山市| 长春市| 连云港市| 四子王旗| 镇原县| 平舆县| 乌拉特前旗| 水富县| 太白县| 乌兰浩特市| 米脂县| 连城县| 长寿区| 珲春市| 建阳市| 扶风县| 楚雄市| 新巴尔虎左旗| 泸溪县| 钦州市| 嘉义县| 彰化市| 贵南县| 登封市| 莱西市| 玉门市| 平安县| 合阳县| 抚顺市| 尼勒克县| 宜兰市| 罗江县| 南江县|