2015-04-03 75 views
6

我想用英特爾I64彙編器做一些長整數運算(128位),並且需要創建2的補碼。假設我的正面價值是RDX:RAX。長整數的二進制補碼

2的補碼是通過「翻轉位和加1」完成的。所以最幼稚的做法是(4個指令和14個字節的代碼):

NOT RAX 
    NOT RDX 
    ADD RAX,1 ; Can't use INC, it doesn't set Carry 
    ADC RDX,0 

當我使用RAX,而不是不NEG指令,但它的「+1」對我來說,但進是錯誤的,當RAX爲零時,NEG RAX清除進位,但我需要在這種情況下進位。因此,下一個最好的辦法可能是(4個指令和11個字節的代碼):

NOT RDX 
    NEG RAX 
    CMC 
    ADC RDX,0     ; fixed, thanks lurker 

還是4個指令。但是不是加+1,我可以減1,並且由於SBB將進位位加到減數中,當進位清零時,我將加1。因此,我的下一個最好的嘗試是這樣的,有3個指令和10個字節的代碼:

NOT RDX 
    NEG RAX 
    SBB RDX,-1 

正如你可以從我的長篇大論的文字看,這不明擺着理解。在彙編中有沒有更好的,更容易理解的方式來實現級聯二進制補碼?

+4

您似乎認爲「更好」等於「更短的代碼」,並且這不必適用於無序多分頻處理器,就像x86-64一樣。我會說你的實現最容易理解是第一個,如果他們全部都執行相同的時間,我不會感到驚訝。 – 2015-04-03 20:08:40

+0

順便說一句:你有沒有考慮過使用XMM寄存器?它們足夠寬以容納一個128位的數字,並且(我沒有檢查過)它們可能有整數指令來處理整數 – 2015-04-03 20:10:09

+2

@mcleod_ideafix它們沒有,所以你仍然留下攜帶進位的問題手動。 – harold 2015-04-03 20:49:19

回答

3

更短的指令或更少數量的指令並不一定意味着更快的執行。每條指令的延遲和吞吐量都不相同。諸如enterdad,aam等已過時的指令將會慢得多,並且它們僅用於向後兼容。即使inc is sometimes slower than add。與上面使用的cmc相同。可以在並行中執行的更長時間的一系列低延遲指令將工作得更快。編譯器的優化器總是知道這一點,並會選擇最合適的指令來發出。

對於這個代碼

__int128 x = some_value; 
__int128 y = -x; // line 12 

-O2ICC will generate the following instructions否定值

xor  esi, esi          #12.17 
    xor  edx, edx          #12.17 
    sub  rdx, r15          #12.17 
    sbb  rsi, rbx          #12.17 

我已經刪除了不相關的行其間,所以你可以看到,它的子0減去值/ SBB。這將比你的第二個解決方案更快。

您可以切換上面gcc.godbolt鏈接編譯器,看看各種方式通過不同的編譯器

否定

GCC:

neg rdx 
adc rcx, 0 
neg rcx 

鏘:

neg rbx 
mov esi, 0 
sbb rsi, r14 

正如你所看到的,他們也只使用3條指令。無論是更快還是更快,都需要仔細的基準測試。但在英特爾CPU上,英特爾編譯器(ICC)通常可以獲得比其他更高的性能,因爲它更好地理解架構。

+0

'爸爸'不是x86助記符,'aam'等在x86-64中無效。根據Ager Fog的指令表,自P4以來,每個英特爾/ AMD微架構上的「cmc」速度都很快。 – EOF 2015-04-04 09:41:49

+0

也許我應該嘗試GCC有時候,至少看看它生成的代碼。我使用的是Visual Studio 2013. – Rolf 2015-04-05 17:50:22

+0

https://gcc.godbolt.org/將幫助您查看幾個最常用編譯器的彙編輸出 – 2015-04-07 03:30:24