我在使用的時候有注意到在參數化型別中使用值的方式與傳統封裝的方式有效能上的差異。
所以我就做了一些測試。
在參數化型別中使用值:
傳統型別封裝:
全文出現的程式碼為實際測試程式碼
因為 Julia 有提供好用的 @code_llvm 及 @code_native 來觀察一行程式碼實際被轉換成 LLVM 或是組合語言的時候會產生多少行的程式碼,藉此我們可以用低階程式碼來評估是否有效率。程式碼的行數愈少是越有效率的。
建立
我們來測試一個物件被建立需要多少行的程式碼。
A - LLVM
1 2 3 4 5 6 7
| define nonnull %jl_value_t addrspace(10)* @japi1_Type_12238(%jl_value_t addrspace(10)*, %jl_value_t addrspace(10)**, i32) #0 { top: %3 = alloca %jl_value_t addrspace(10)**, align 8 store volatile %jl_value_t addrspace(10)** %1, %jl_value_t addrspace(10)*** %3, align 8 ret %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 140407726014496 to %jl_value_t*) to %jl_value_t addrspace(10)*) }
|
B - LLVM
1 2 3 4 5 6
| define { i64 } @julia_Type_12221(%jl_value_t addrspace(10)*, i64) { top: %.fca.0.insert = insertvalue { i64 } undef, i64 %1, 0 ret { i64 } %.fca.0.insert }
|
A - Assembly
1 2 3 4 5
| ; ┌ @ REPL[1]:3 within `Type' movq %rsi, -8(%rsp) movabsq $140407726014496, %rax # imm = 0x7FB338A20020 retq ; └
|
B - Assembly
1 2 3 4 5
| ; ┌ @ REPL[1]:2 within `Type' movq %rsi, %rax retq nopw %cs:(%rax,%rax) ; └
|
取值
接著測試從物件當中取值出來的效能。
定義取值的方法:
1 2
| get_value(::A{T}) where {T} = T get_value(b::B) = b.x
|
事先建立好物件:
A - LLVM
1 2 3 4 5
| define i64 @julia_get_value_12274() { top: ret i64 5 }
|
B - LLVM
1 2 3 4 5 6 7 8 9
| define i64 @julia_get_value_12630({ i64 } addrspace(11)* nocapture nonnull readonly dereferenceable(8)) { top:
%1 = getelementptr inbounds { i64 }, { i64 } addrspace(11)* %0, i64 0, i32 0
%2 = load i64, i64 addrspace(11)* %1, align 8 ret i64 %2 }
|
A - Assembly
1
| @code_native get_value(a)
|
1 2 3 4 5
| ; ┌ @ REPL[8]:1 within `get_value' movl $5, %eax retq nopw %cs:(%rax,%rax) ; └
|
B - Assembly
1
| @code_native get_value(b)
|
1 2 3 4 5
| ; ┌ @ REPL[5]:1 within `get_value' movq (%rdi), %rax retq nopw %cs:(%rax,%rax) ; └
|
給大家參考。