編譯器與程式碼產生

自動向量化(auto-vectorization)

一般指令一次做一件事:把這兩個數相加、儲存這一個值。但現代處理器還有寬的 SIMD 指令(單指令多資料,Single Instruction Multiple Data),對一整批值同時做同一個操作——一口氣加八對浮點數。自動向量化就是編譯器自動把一個純量迴圈(每次迭代處理一個元素)改寫成使用這些寬指令、每次迭代處理好幾個元素的迴圈。

具體而言,看一個逐元素相加兩個陣列的迴圈:for (i = 0; i < n; i++) c[i] = a[i] + b[i];。如果處理器的 SIMD 單元一次處理 8 個浮點數,編譯器可以把它轉換成一個載入 a 的 8 個值、b 的 8 個值,用一個 SIMD 指令加全部 8 對、再儲存 8 個結果的迴圈——然後迴圈 n/8 次,尾巴用一個純量餘數迴圈。對合適的程式碼,這能快上好幾倍。但向量化只有在迭代彼此獨立時才合法:元素 i 的結果不能依賴元素 i-1 的結果,而記憶體區域也不能偷偷重疊。

它重要在於:這是你不必手寫內建函式(intrinsics)就能得到 SIMD 速度的方式,但它也以脆弱著稱,謹慎的工程師應該理解它「為什麼」失敗。兩大殺手主宰一切。相依性(dependencies):若 c[i] = c[i-1] + a[i],每次迭代都需要前一次的結果,所以這一批不能平行跑。別名(aliasing):在 C 中,如果 a、b、c 是普通指標,編譯器不能假設它們指向不重疊的記憶體——c 可能別名 a——所以它必須假設最壞情況、拒絕向量化,除非你用 restrict 修飾詞另行保證。其他阻礙包括迴圈內的函式呼叫、複雜的流程控制、非連續的記憶體存取。誠實的總結:自動向量化是盡力而為、並非保證;查看編譯器的向量化報告,而不要假設它發生了。

void add(float *a, float *b, float *c, int n) { for (int i = 0; i < n; i++) c[i] = a[i] + b[i]; } // 可能「不會」向量化:c 可能別名 a 或 b。用 restrict 給出保證來修正: void add(float * restrict a, float * restrict b, float * restrict c, int n) { ... }

沒有 restrict,編譯器必須假設陣列可能重疊、可能拒絕 SIMD;restrict 保證它們不會。

自動向量化是盡力而為、並非保證:迴圈攜帶的相依性使它不可能,指標別名使它無法證明,於是編譯器默默保留純量迴圈——請讀向量化報告(例如 -Rpass=loop-vectorize),別假設它觸發了。

又称
automatic vectorizationSIMD-izationloop vectorization自動 SIMD 化