Julia 學習筆記 - 計時
這裡我們要介紹 julia 計算程式執行時間的指令.
簡單來說,julia 計算程式運行效能有幾個指令:
- @time顯示出程式運行的秒數以及記憶體的使用狀況
- @timed顯示程式執行回傳值,秒數以及記憶體的使用狀況
- @timev顯示- @time一樣的資訊,最後顯示程式執行回傳值
- @elapsed顯示出某程式運行的秒數
BUT, 真正要計算程式效能, 還是推薦用 BenchmarkTools.jl 這個 package.
以下範例中我們需要使用兩個 package: LinearAlgebra 以及 BenchmarkTools
using LinearAlgebra
using BenchmarkTools
Example 1: Matrix-matrix multiplication
我們以矩陣乘法 $C=A\times B$ 為例:$A:n\times p$, $B:p\times m$, $C:n\times m$
f1 這個函數會產生兩個隨機矩陣 $A$, $B$ 並且將他們相乘:
function f1(n, p, m)
   A=rand(n,p)
   B=rand(p,m)
   C=zeros(Float64,n,m)
   for i in 1:n
      for j in 1:m
         for k in 1:p
            C[i,j] = C[i,j]+A[i,k]*B[k,j]
         end
       end
    end
end
f1 (generic function with 1 method)
我們先試試看以 julia 既有的計算時間指令來看結果如何
@time f1(100,10,200)
  0.076423 seconds (161.79 k allocations: 8.380 MiB)
@timed f1(100,10,200)
(nothing, 0.000448109, 184496, 0.0, Base.GC_Diff(184496, 1, 0, 5, 2, 0, 0, 0, 0))
@timev f1(100,10,200)
  0.005932 seconds (8 allocations: 180.172 KiB, 92.28% gc time)
elapsed time (ns): 5931535
gc time (ns):      5473727
bytes allocated:   184496
pool allocs:       5
non-pool GC allocs:2
malloc() calls:    1
GC pauses:         1
@elapsed f1(100,10,200)
0.000561533
這些指令的表現如同一開始我們所介紹的, 顯示出程式執行時間以及其他資訊.
不過一般而言一個程式在第一次跑及之後幾次跑的運行效能會不同, 這是由於初始化的關係. 所以我們通常要跑好幾次來算平均, 已得知一個程式的真正效能.
benchmark 就提供了這樣的功用, 他會跑好幾次程式, 並且計算最大最小時間.
@benchmark f1(100,10,200)
BenchmarkTools.Trial:
  memory estimate:  180.02 KiB
  allocs estimate:  4
  --------------
  minimum time:     437.243 μs (0.00% GC)
  median time:      485.922 μs (0.00% GC)
  mean time:        513.114 μs (3.90% GC)
  maximum time:     44.383 ms (98.89% GC)
  --------------
  samples:          9711
  evals/sample:     1
接著我們看一下如果用 LinearAlgebra 裡的矩陣乘法指令效能如何.
function f2(n, p, m)
    A=rand(n,p)
    B=rand(p,m)
    C=zeros(Float64,n,m)
    mul!(C,A,B);
end
f2 (generic function with 1 method)
@benchmark f2(100,10,200)
BenchmarkTools.Trial:
  memory estimate:  180.02 KiB
  allocs estimate:  4
  --------------
  minimum time:     41.088 μs (0.00% GC)
  median time:      91.270 μs (0.00% GC)
  mean time:        106.954 μs (19.04% GC)
  maximum time:     45.286 ms (99.75% GC)
  --------------
  samples:          10000
  evals/sample:     1
看起來似乎比我們自己寫的 for-loop 效能好一些.
type-stability
julia 語言號稱效能非常好, 不過常常我們自己寫一個 julia 程式發現跑得沒有很快, 這通常是由於所謂的 “型別穩定性 type-stability”. 這樣的問題可以藉由以下幾個 julia 內建的巨集程式來診斷.
@code_warntype f1(10,10,10)
@code_typed f1(10,10,10)
@code_native f1(10,10,10)
這方面更深入的說明可見 julia: performance tips
Example 2: Vector 2-norm
接著我們看一下算向量 2-norm 的效能, 一樣我們比較一下自己寫的以及 call 函數的方式.
function t1(n)
    vec = rand(1,n);
    vec*vec';
end
t1 (generic function with 1 method)
@benchmark t1(10^5)
BenchmarkTools.Trial:
  memory estimate:  781.42 KiB
  allocs estimate:  3
  --------------
  minimum time:     423.973 μs (0.00% GC)
  median time:      504.538 μs (0.00% GC)
  mean time:        606.699 μs (9.87% GC)
  maximum time:     47.376 ms (98.43% GC)
  --------------
  samples:          8213
  evals/sample:     1
function t2(n)
    vec = rand(1,n);
    sum(abs2, vec);
end
t2 (generic function with 1 method)
@benchmark t2(10^5)
BenchmarkTools.Trial:
  memory estimate:  781.33 KiB
  allocs estimate:  2
  --------------
  minimum time:     141.160 μs (0.00% GC)
  median time:      195.940 μs (0.00% GC)
  mean time:        335.496 μs (19.51% GC)
  maximum time:     64.500 ms (99.17% GC)
  --------------
  samples:          10000
  evals/sample:     1
Example 3: Solving linear system
這裡我們看一下解線性系統, 也就是 matlab 常見的"左除", 的效率如何.
function s1(n)
    A=rand(n,n);
    b=rand(n);
    A\b;
end
s1 (generic function with 1 method)
@benchmark s1(1000)
BenchmarkTools.Trial:
  memory estimate:  15.28 MiB
  allocs estimate:  8
  --------------
  minimum time:     13.463 ms (0.00% GC)
  median time:      16.817 ms (10.20% GC)
  mean time:        18.148 ms (9.36% GC)
  maximum time:     64.226 ms (77.63% GC)
  --------------
  samples:          276
  evals/sample:     1
