如何使用 AVX2 向量化這個 C 函式?
static void propogate_neuron(const short a, const int8_t *b, int *c) {
for (int i = 0; i < 32; i){
c[i] = a * b[i];
}
}
uj5u.com熱心網友回復:
您需要添加restrict限定符來標記c它不能與b.
問題是,根據嚴格的別名規則,int8_t它可能signed char可以與任何其他型別別名。因此編譯器不能確定設定c[i]不會被修改b[i]。強制編譯器在每次迭代時獲取資料。
的存在const并不意味著什么,因為它只是限制程式員通過指標修改資料b。
將原型替換為:
void propogate_neuron(const short a, const int8_t *b, int * restrict c)
代碼被矢量化。見神箭
uj5u.com熱心網友回復:
GCC 已經通過檢查重疊來自動矢量化。承諾使用沒有重疊int *restrict c讓 GCC 洗掉該檢查,并讓 clang 決定自動矢量化。
但是,clang 擴展到 32 位并vpmulld在 Haswell 及更高版本上使用2 uop。(雖然它在 Zen 上是完全有效的。)GCC 使用vpmullw和vpmulhw來獲得 16 位全乘法的低半和高半,并將它們混在一起。(Godbolt)這是一個非常笨拙的戰略,特別是-march=znver2其中vpmulld的一個微指令。
GCC 確實只有 4 個單 uop 乘法指令,但要實作它需要花費大量的 shuffle。我們可以做得更好:
由于我們只需要 8x16 => 32 位乘法,我們可以改為vpmaddwd在 Haswell/Skylake 和 Zen 上使用單 uop。
不幸的是,我們無法利用 add 部分,因為我們需要添加一個完整的 32 位值。我們需要每對 16 位元素的高半部分為零,以將其用作每個 32 位元素內的 16x16 => 32 位乘法。
#include <immintrin.h>
void propogate_neuron_avx2(const short a, const int8_t *restrict b, int *restrict c) {
__m256i va = _mm256_set1_epi32( (uint16_t)a ); // [..., 0, a, 0, a] 16-bit elements
for (int i = 0 ; i < 32 ; i =8) {
__m256i vb = _mm256_cvtepi8_epi32( _mm_loadl_epi64((__m128i*)&b[i]) );
__m256i prod = _mm256_madd_epi16(va, vb);
__m256i sum = _mm256_add_epi32(prod, _mm256_loadu_si256((const __m256i*)&c[i]));
_mm256_storeu_si256((__m256i*)&c[i], sum);
}
}
神箭:
# clang13.0 -O3 -march=haswell
movzx eax, di
vmovd xmm0, eax # 0:a 16-bit halves
vpbroadcastd ymm0, xmm0 # repeated to every element
vpmovsxbd ymm1, qword ptr [rsi] # xx:b 16-bit halves
vpmaddwd ymm1, ymm0, ymm1 # 0 a*b in each 32-bit element
vpaddd ymm1, ymm1, ymmword ptr [rdx]
vmovdqu ymmword ptr [rdx], ymm1
... repeated 3 more times, 8 elements per vector
vpmovsxbd ymm1, qword ptr [rsi 8]
vpmaddwd ymm1, ymm0, ymm1
vpaddd ymm1, ymm1, ymmword ptr [rdx 32]
vmovdqu ymmword ptr [rdx 32], ymm1
如果每個向量乘法保存一個 uop 會產生可測量的性能差異,那么在源中手動向量化的麻煩可能是值得的。
GCC / clang 在自動矢量化純 C 代碼時首先不會這樣做,這是一個錯過的優化。
如果有人想報告此事,請在此處發表評論。否則我可能會解決它。IDK,如果這樣的模式足夠頻繁,GCC / LLVM 的優化器想要尋找這種模式。特別是 clang 已經做出了一個合理的選擇,但由于 CPU 怪癖(32x32 => 32 位 SIMD 乘法在最近的 Intel 微架構上比 2x 16x16 => 32 位與水平加法的成本更高)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/348666.html
上一篇:在表單部分中保存的下拉依賴問題
