2016-06-20 38 views
5

當尋找在由Visual Studio(2015U2)在我看到的C代碼這個「手工優化」片被轉換回爲乘法/O2(釋放)模式所產生的組件:x86_64:IMUL快於2倍SHL + 2x ADD?

int64_t calc(int64_t a) { 
    return (a << 6) + (a << 16) - a; 
} 

組裝:

imul  rdx,qword ptr [a],1003Fh 

所以我在想,如果這是真的比做得它是書面的方式,像快:

mov   rbx,qword ptr [a] 
    mov   rax,rbx 
    shl   rax,6 
    mov   rcx,rbx 
    shl   rcx,10h 
    add   rax,rcx 
    sub   rax,rbx 

我一直認爲乘法總是比幾班/增加慢?現代英特爾x86_64處理器不再是這種情況嗎?

+4

「IMUL」通常是3-4個延遲的順序,每個時鐘的吞吐量爲1。所以,可能會更快。還要注意,至少最後3條指令相互依賴,這樣它本身就會產生一個等效的依賴鏈。 – Jester

+0

過去一直很好:8086 IMUL花費了大約100個時鐘,具體取決於尋址模式,寄存器大小等。 –

+1

如果您認爲內存中的負載是空閒的(因爲您必須等待它)*無論如何*)和寄存器「mov」可以被消除,「shl」可以並行執行,那麼理想情況下,移位代碼將與乘法一樣快。然而,它攻擊了很多功能單元,並且它更像是i-cache。 – EOF

回答

10

沒錯,現代x86 CPU(尤其是Intel)具有非常高的性能乘數。
imul r, r/mimul r, r/m, imm都是3週期延遲,Intel SnB系列和AMD Ryzen每個1c吞吐量都有一個,即使是64位操作數大小也是如此。

在AMD推土機系列中,延遲時間爲4c或6c,每2c或每4c的吞吐量爲1c。 (64位操作數大小的較慢時間)。

來自Agner Fog's instruction tables的數據。請參閱標記wiki中的其他內容。


現代CPU的晶體管預算是相當巨大的,並允許做一個64位乘法這樣的低延遲所需的硬件並行的量。 (It takes a lot of adders作出large fast multiplier)。

受功率預算限制,而不是晶體管預算,意味着只要不能同時切換(https://en.wikipedia.org/wiki/Dark_silicon),就可以擁有用於許多不同功能的專用硬件。例如您不能在英特爾CPU上同時飽和pext/pdep單元,整數乘法器和矢量FMA單元,因爲它們中的許多都在相同的執行端口上。

有趣的事實:imul r64也是3c,所以你可以在3個週期內得到完整的64 * 64 => 128b乘法結果。 imul r32是4c延遲和一個額外的uop,但。我的猜測是額外的uop/cycle將64位的結果從常規的64位乘法器拆分爲兩個32位的一半。


編譯器通常優化延遲,並且通常不知道如何優化短獨立依賴鏈的吞吐量與對延遲這個瓶頸長循環攜帶依賴性鏈。

gcc和clang3.8和更高版本使用多達兩個LEA指令而不是imul r, r/m, imm。不過,如果替代方案是3個或更多指令(不包括mov),我認爲gcc將使用imul

這是一個合理的調整選擇,因爲3指令dep鏈的長度與英特爾的imul相同。使用兩個單週期指令會花費額外的時間來縮短延遲1個週期。

clang3.7和更早版本傾向於贊成imul,除了只需要單個LEA或換檔的乘數。所以鐺最近更改爲優化延遲,而不是吞吐量乘以小常量。 (或者可能由於其他原因,例如不與僅與乘法器在同一端口上的其他事物競爭)。

例如this code on the Godbolt compiler explorer

int foo (int a) { return a * 63; } 
    # gcc 6.1 -O3 -march=haswell (and clang actually does the same here) 
    mov  eax, edi # tmp91, a 
    sal  eax, 6 # tmp91, 
    sub  eax, edi # tmp92, a 
    ret 

clang3.8,事後相同的代碼。

+0

這是否意味着'imul'雖然速度更快,但可能會消耗比4個ALU ops更多的能量(因爲乘法器會激活更多的晶體管)? – rustyx

+2

@rustyx:IDK。通過管道跟蹤4個ALU微處理器並將其結果轉發給對方需要比追蹤更多的能量,而這可能比操作乘法器花費更多!亂序執行很昂貴。我希望我有些想法是什麼數字,所以我可以猜測答案,但我沒有做任何功率優化(除了明顯的東西,如使用128位AVX矢量操作時,我不需要上128結果)。 –

+1

@rustyx:順便說一句,使用較少晶體管的高延遲乘法器可能每個乘法成本的能量仍然相似,因爲它仍然需要添加所有的部分和。它只需要更多的週期。 (這是一個巨大的過度簡化,可能實際上是錯誤的,我不是一個門邏輯的人,並沒有真正重讀這些鏈接的細節)。儘管如此,低延遲乘法器使其更經常使用它(例如,而不是兩個'lea'指令乘以10)。 –