假設第二個循環中省略的花括號僅僅是一個錯字,並且在for循環中有一個錯字,並且您詢問了乘法浮點數的問題,但是您的代碼顯示了整數數組,這樣甚至不會獲得很好的向量化如果編譯器看到它。雖然編譯器可能會將來自A和B的4個值加載爲單個指令,並在一條指令中執行4次乘法,但代碼會強制編譯器提取4個產品中的每一個,然後依次對它們進行求和,並逐個獲取SIMD寄存器中的值通常很慢。
如果在另一方面,你這樣做
float A[100000];
float B[100000];
float C0=0, C1=0, C2=0, C3=0;
for (size_t i=0; i < 100000/4; i += 4)
{
C0 += A[i+0] * B[i+0];
C1 += A[i+1] * B[i+1];
C2 += A[i+2] * B[i+2];
C3 += A[i+3] * B[i+3];
}
float C = (C0 + C1) + (C2 + C3);
然後一個很好的編譯器可以像現在vectorise這個它認爲每個循環中它加載兩個SIMD寄存器,它們相乘,則可以添加結果一個SIMD寄存器的總和,並且只提取這4個數字並且在最後總計它們。
矢量化編譯可以用SIMD來做到這一點,它不會改變個別和的評估順序(FP數學不是關聯的)。由於這個原因,編譯器通常不被允許改變FP數學的順序(不是沒有一些額外的標誌允許它在技術上違反語言標準),所以上面的代碼可以用SIMD指令精確地表示,並且運行得更快(事實上我會放鬆循環,因爲乘法會成爲瓶頸)。
這是SIMD的一個技巧,你必須理解,然後思考如何用矢量指令最好地實現該操作,然後編寫代碼來執行相同的操作序列,並希望編譯器能夠找到你已經完成了。
或者你也可以用intrinsics自己編寫矢量指令,或者使用OpenMP或類似的方式來更明確地告訴編譯器該做什麼。
SIMD優於線程這種操作的優勢在於,您正在單個內核中使用更多的硅片......因此您不會阻止另一個線程獲取週期。在我們的計算網格中,我們通常在任何一臺機器上運行多個單線程進程,以保持所有內核在任何時候都處於繁忙狀態......在這種情況下,使用更多內核來實現這一點是一種虛假的經濟,您只需要竊取另一個線程可能會有用地運行另一項工作。
來源
2017-08-01 15:55:26
Tim
那麼,矢量化優化器可能會或者很多都不會優化任一循環,但在考試環境中,我期望看到更加明確的映射到底層架構,以闡明學生已經理解了系統的原理。特別是水平加法應該在最後進行最終摺疊時作爲矢量加法執行,特別是由於缺乏關聯性,可能會禁止等效的浮點格式。爲了避免優化器阻塞代碼複雜性,陣列的顯式對齊規範也不會受到影響。 – doynax
對於考試,我希望看到這個手工編碼的目標架構的程序集,說實話。給定一箇中途體面的編譯器,儘管這個代碼應該最終被優化(在過去做了一些類似的HPC矢量,並驗證了GCC的彙編輸出)。 OP應該明確地分解二進制文件並檢查SIMD指令...... – madscientist159
同意,內在函數將成爲這裏的一種方式,並且優化編譯器可以使用糾結的代碼創造奇蹟(相反,隨機分解出於難理解的原因)。在這裏最讓我困擾的是提到了「單精度浮點值」,在這種情況下,將'int'替換爲'float',剩下一個固有的串行依賴鏈,用於累積,除非使用像' - 快速數學「,並打破了過程中的大部分數字算法。老實說,除非使用並行累積緩衝區,否則我無法在考試中看到答案被接受。 – doynax