我在寫數學代碼,它需要快速乘以大數。它分解爲整數數組與單個整數的乘法運算。在C++中,這看起來像這樣(在無符號的):優化x64彙編器MUL循環
void muladd(unsigned* r, const unsigned* a, unsigned len, unsigned b) {
unsigned __int64 of = 0; // overflow
unsigned i = 0; // loop variable
while (i < len) {
of += (unsigned __int64)a[i] * b + r[i];
r[i] = (unsigned)of;
of >>= 32;
++i;
}
r[i] = (unsigned)of; // save overflow
}
我手動展開這個循環中,它轉換爲64位和製作的.ASM編譯器輸出,以進一步優化它。主要.ASM循環現在看起來是這樣的:
mov rax, rdi ; rdi = b
mul QWORD PTR [rbx+r10*8-64] ; rdx:rax = a[i] * b; r10 = i
mov rsi, QWORD PTR [r14+r10*8-64] ; r14 = r; rsi = r[i]
add rax, rsi
adc rdx, 0
add rax, r11 ; r11 = of (low part)
adc rdx, 0
mov QWORD PTR [r14+r10*8-64], rax ; save result
mov r11, rdx
; this repeats itself 8 times with different offsets
當我這個標杆,我覺得這需要大約6.3個週期上avarage每個增殖我的酷睿2四核。
我的問題是:我能以某種方式加快速度嗎?不幸的是,我看不到任何方法來避免其中的一個增加,並且乘法總是需要RDX:RAX,所以我需要移動數據並且不能排序「並行乘法」。
任何想法的人?
更新: 一些測試後,我已經成功地把速度可達約5.4個週期每64位MUL(包括所有添加,移動和循環開銷)。我想這是關於Core2的最佳選擇,因爲Core2沒有非常快速的MUL指令:吞吐量爲3,延遲爲6(7)個週期。吞吐量爲1,等待時間爲3(4)個週期,桑迪橋將會更好。
關於GMP的少得多的數字:我從他們的源代碼中得到了這一點,在我看來這是一個理論數字。但可以肯定的是,這是一個計算AMD K9 CPU的數字。從我讀過的內容來看,AMD收集的MUL單元比(舊的)英特爾芯片更快。
您可能想看看GMP中的一些裝配例程。他們有一個功能可以完成這個功能,並且可以彙編到大多數處理器(包括x64)中。 – Mysticial
GMP確實對快速mul_basecase有很好的支持,而且它的每MUL需要2.35個週期,非常好。如果我正確地理解了它們,它們會交錯地插入兩個向量,這似乎保持低依賴性並改善溢出處理。 – cxxl