Analysis of variance (ANOVA) starts from the analysis of variance which is caught by the model or not. If the variance is modeled, then the variance must be explained by the model, I mean of which is “caught” by the model. If it is not, variance is left unexplained as noises. These two concepts forms explained variance (or between-group variance in categorical factor) and unexplained variance (or within-group variance in categorical factor).

繼續閱讀

先前看到了 Programmer Competency Matrix,所以就自己幫自己評比一下。

  • Computer Science
    • data structures: level 3
    • algorithms: level 3
    • systems programming: level 2
  • Software Engineering
    • source code version control: level 3
    • build automation: level 2
    • automated testing: level 2
  • Programming
    • problem decomposition: level 3
    • systems decomposition: level 2
    • communication: level 3
    • code organization within a file: level 2
    • code organization across files: level 2
    • source tree organization: level 3
    • code readability: level 3
    • defensive coding: level 2
    • error handling: level 1
    • IDE: level 2
    • API: level 1
    • frameworks: level 3
    • requirements: level 2
    • scripting: level 2
    • database: level 2
  • Experience
    • languages with professional experience: level 1~2 (familiar with OO language with functional patterns)
    • platforms with professional experience: level 2
    • years of professional experience: level 2
    • domain knowledge: level 2
  • Knowledge
    • tool knowledge: level 2
    • languages exposed to: level 2~3 (used Prolog before)
    • codebase knowledge: level 3 (I started a project before)
    • knowledge of upcoming technologies: level 2
    • platform internals: level 1~2 (not sure)
    • books: level 2 (actually used and read books)
    • blogs: level 3 (but not regularly update blog posts)

留言與分享

Name binding 是一個將資料或是程式碼綁定(binding)到識別符(identifiers)的一個過程。一個識別符綁定到一個物件代表這個識別符會參考(reference)某個物件。

Name binding 在程式語言中式相當重要而複雜的一個議題,而它牽涉到作用域(scope)的問題,物件存在於程式碼的位置(語意)及物件的生命周期(時間)。

繼續閱讀

很多人在寫程式的時候會有些壞習慣,如:

1
2
3
4
5
6
7
X = rand(3, 4)
for i = 1:3
for j = 1:4
X[i, j]
...
end
end

可能多數人看以上這段程式碼並沒有什麼特別的感覺,但是如果要維護的時候就會發現你突然不太理解這段程式碼。

有人知道這邊的 3 是什麼意思嗎?嗯…或許可以從上下文猜出來是陣列的列數的意思。

一旦要更改陣列的大小的時候勢必就要更改這些數字,甚至這些數字散落在程式碼的各個角落就會更加頭痛。

這些數字稱為魔術數字(magic numbers),因為沒有人知道他的意義是什麼!

解法一:使用常數

如果這些數字很常被使用到,而且不會在程式中被變更,請使用常數,像:

1
2
3
4
5
6
7
8
9
const ROWS = 3
const COLUMNS = 4
X = rand(ROWS, COLUMNS)
for i = 1:ROW
for j = 1:COLUMNS
X[i, j]
...
end
end

如此,以後要更改陣列大小只需要更改常數即可,也讓程式碼的可讀性上升。

如果你的程式會更改到這些數字,那麼就用變數。

1
2
3
4
5
6
7
8
9
rows = 3
columns = 4
X = rand(rows, columns)
for i = 1:rows
for j = 1:columns
X[i, j]
...
end
end

解法二:動態

如果陣列的大小不是事先知道的,或是需要動態取得,那麼可以用 size

1
2
3
4
5
6
for i = 1:size(X, 1)
for j = 1:size(X, 2)
X[i, j]
...
end
end

如此可以用在未知大小的陣列上。

留言與分享

有在做套件開發的開發者們應該不陌生 @inbounds 這個 macro,在很多現代程式語言中也有。

在存取陣列時,為了安全性與正確性的考量,避免存取到陣列範圍以外的記憶體位置,很多語言都設置了邊界檢查(bounds check)。

邊界檢查會檢查所存取的索引值是否在陣列的範圍內,但是這樣的檢查會有些微的效能損耗,尤其在迴圈內的情況更有可能被累積而放大,關於 Julia 的 邊界檢查可以參考官方文件 Bounds checking

如果可以確定所存取的索引值一定在範圍內,我們就可以把邊界檢查給移除,以加速陣列的存取。如以下範例:

1
2
3
4
A = rand(3, 4)
@inbounds for i = 1:size(A, 1)
println(A[i, :])
end

或是

1
2
3
4
A = rand(3, 4)
for i = 1:size(A, 1)
@inbounds println(A[i, :])
end

@inbounds 會將程式碼區塊中的邊界檢查給移除,可以參考 @inbounds官方文件。使用時必須注意存取的索引值,否則小則存取的值錯誤,大則可能導致程式崩潰。

先養成好的索引習慣,再考慮將效能提升,加入 @inbounds。相關的資訊也紀錄在官方的效能建議中。

留言與分享

剛好看到一些跟編譯器相關的議題,所以來紀錄一下。

在一些語言中會有行內函式(inline function)的設計,使用的話一般會讓程式的效能變好。

最知名應該是 C 跟 C++ 的 inline

Inline function 會在編譯時期直接將函式內容展開到程式碼中,不過展開與否是由編譯器決定的,inline 的標記只是告訴編譯器這個函式可以成為 inline function。

Inline expansion 就是編譯時期會由編譯器執行的一個動作,看起來與 macro expansion 相似,但不同的是 macro expansion 是在前處理(preprocessing)時期做的,會直接展開在原始碼裡頭,而 inline expansion 則是在編譯時期做的,會在呼叫位點(call site)直接展開。

展開後編譯器便可以進行最佳化,執行時,就不需要做函式呼叫,也不會在 function stack 上多配置空間。一般使用在短小的函式上會有好處,在巨大的函式上使用不一定會有好處。然而過多的 inline function 反而可能造成過多的指令快取的消耗,造成反效果。

在 Julia 中,編譯器會自動偵測哪些函式可以被展開,會自動做 inline expansion。一般短小的函式會自動被編譯器判定要 inline,不過也可以由程式設計師自己指定哪些巨大函式可以 inline,可以參考[文件]](https://docs.julialang.org/en/v1.2/base/base/#Base.@inline)。

除了 @inline 以外,還有 @noinline。為了避免過多的 inline 反而傷害效能,也可以標記一些短小的函式不要 inline。

範例:

1
2
3
@inline function bigfunc(x)
...
end

Julia 什麼時候 inline expansion?

我們來實驗看看,以下有兩個函式,foobar。其中讓 foo 為 inline。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
julia> @inline foo(x) = 3x
foo (generic function with 1 method)

julia> bar(x) = foo(x)^2
bar (generic function with 1 method)

julia> bar(5)
225

julia> @code_lowered bar(5)
CodeInfo(
1 ─ %1 = Main.foo(x)
│ %2 = Core.apply_type(Base.Val, 2)
│ %3 = (%2)()
│ %4 = Base.literal_pow(Main.:^, %1, %3)
└── return %4
)

julia> @code_typed bar(5)
CodeInfo(
1 ─ %1 = Base.mul_int(3, x)::Int64
│ %2 = Base.mul_int(%1, %1)::Int64
└── return %2
) => Int64

你會發現在 lower 的階段仍保留有 foo 的函式呼叫的過程,但是到了 typed 的階段就已經剩下計算的部份了。

所以其實在進入 LLVMIR 以前就已經先做完 inline expansion 了。

相關技術:Inline caching

留言與分享

來分享一下最近貢獻開源專案的小心得,雖然我自己貢獻開源專案的次數跟時間不是很多,不過一個 PR 可以產生不少文字跟討論算是值得紀錄一下的。

我自己在 Julia 的一個統計相關的專案上發了一個 PR,希望補齊在標準化上的一些功能,並且可以支援一維的陣列標準化。

繼續閱讀

Yueh-Hua Tu

目標是計算生物學家!
Systems Biology, Computational Biology, Machine Learning
Julia Taiwan 發起人


研發替代役研究助理


Taiwan