我的應用程序中有一個乘加內核,我想提高它的性能。C代碼循環性能
我使用了Intel Core i7-960(3.2GHz的時鐘),並已手動實現使用SSE內部函數如下內核:
for(int i=0; i<iterations; i+=4) {
y1 = _mm_set_ss(output[i]);
y2 = _mm_set_ss(output[i+1]);
y3 = _mm_set_ss(output[i+2]);
y4 = _mm_set_ss(output[i+3]);
for(k=0; k<ksize; k++){
for(l=0; l<ksize; l++){
w = _mm_set_ss(weight[i+k+l]);
x1 = _mm_set_ss(input[i+k+l]);
y1 = _mm_add_ss(y1,_mm_mul_ss(w,x1));
…
x4 = _mm_set_ss(input[i+k+l+3]);
y4 = _mm_add_ss(y4,_mm_mul_ss(w,x4));
}
}
_mm_store_ss(&output[i],y1);
_mm_store_ss(&output[i+1],y2);
_mm_store_ss(&output[i+2],y3);
_mm_store_ss(&output[i+3],y4);
}
我知道我可以使用打包FP載體,以提高性能和我已經做到了成功,但我想知道爲什麼單個標量代碼無法達到處理器的峯值性能。這個內核在我的機器上的性能是每個週期〜1.6個FP操作,而每個週期最大爲2個FP操作(因爲FP add + FP mul可以並行執行)。
如果我正確研究生成的彙編代碼,理想的時間表如下所示,其中mov
指令需要3個週期,從相關指令開始,從加載域到FP域的切換延遲需要2個週期,FP乘法需要4個週期,FP加法需要3個週期。 (請注意,乘法 - >添加的相關性不會導致任何切換延遲,因爲這些操作屬於同一個域)。
根據所測量的性能(〜最大理論性能的80%)存在的每8個週期〜3個指令的開銷。
我想要麼:
- 擺脫這種開銷,或
- 解釋它來自哪裏
當然有與緩存的問題錯過&數據錯位,其可以增加移動指令的延遲,但是還有其他因素可以在這裏起作用嗎?像寄存器讀取攤位什麼的?
我希望我的問題很清楚,在此先感謝您的回覆!
更新:內環的組裝如下所示:
...
Block 21:
movssl (%rsi,%rdi,4), %xmm4
movssl (%rcx,%rdi,4), %xmm0
movssl 0x4(%rcx,%rdi,4), %xmm1
movssl 0x8(%rcx,%rdi,4), %xmm2
movssl 0xc(%rcx,%rdi,4), %xmm3
inc %rdi
mulss %xmm4, %xmm0
cmp $0x32, %rdi
mulss %xmm4, %xmm1
mulss %xmm4, %xmm2
mulss %xmm3, %xmm4
addss %xmm0, %xmm5
addss %xmm1, %xmm6
addss %xmm2, %xmm7
addss %xmm4, %xmm8
jl 0x401b52 <Block 21>
...
它確實依賴於很多編譯器(甚至它的版本)以及您傳遞給它的優化標誌。如果數值性能對您來說如此重要,您可能還需要花時間和精力學習數值庫和/或OpenCL或CUDA(以利用GPGPU)。 Ther也是緩存考慮因素。在現在的處理器上預測循環的實際時間很困難。 – 2012-04-03 11:13:37
我不明白爲什麼你會認爲循環控制總是可以並行執行,而它實際上是在無序執行方案中創建完美的依賴關係鏈。 INC指令修改一個寄存器。 CMP指令必須等待INC完成以檢查該寄存器中的值並相應地修改標誌。然後,條件跳轉指令必須等待CMP寫入標誌以決定是否實際跳轉。恐怕沒有平行化。更不要說跳躍會導致流水線延遲 - 分支預測器會處理這一點。 – 2012-04-03 11:13:39
更不用說,INC指令必須等待修改標誌的先前指令才能保持CF標誌的狀態。只需將INC替換爲相應的ADD即可解決該問題。 – 2012-04-03 11:18:43