2009-07-09 79 views
4

我知道 - 過早優化。
但我有代碼,應該找出位置是否改變與緩存位置。比較在C++中的效率? (abs(X)> 1 vs abs(x)!= 0)

當前的代碼:

if(abs(newpos-oldpos) > 1){ 
    ..... 
} 

它是更有效地使用下面的?

if(abs(newpos-oldpos) != 0){ 
    .... 
} 

爲什麼或爲什麼不呢?我目前正在辯論它,我的頭更具可讀性,並且懷疑我是否缺少性能差異。

+0

Clarification-所有位置是多頭(因此ABS()代替晶圓廠()) – Luciano 2009-07-09 19:00:00

+6

林假設你的意思> 0不> 1個,否則它是不一樣的考驗? 反正。你的編譯器比你聰明。它會將你使用的任何形式轉換成最高效的彙編代碼(編譯器很聰明,它們不會逐字將C++/C代碼翻譯成彙編) – nos 2009-07-09 19:01:04

回答

13

爲什麼不是這個?

if (newpos != oldpos) { 
    ... 
} 

由於缺少abs()以及更清晰的引導,兩者效率都較高。

+1

(newpos!= oldpos)不同於(abs(newpos- oldpos)> 1)。 – 2009-07-09 19:01:56

+2

但它與(abs(newpos-oldpos)!= 0)相同。問題是關於平等,任何一個代碼都是錯誤的(我的賭注是第一個),或者Luciano比比較運算符的效率更大。 – 2009-07-09 19:06:29

+0

我同意(newpos!= oldpos)在各方面都比卓越(ABS(newpos-oldpos)!= 0)。然而,將(abs(newpos-oldpos)> 1)引入混合中我認爲會改變答案應該是什麼(特別是因爲它是你想要保留行爲的原始代碼)。 – 2009-07-09 19:21:53

2

不要試圖優化比較運算符以平等運算符;他們應該有相同的時間,只有整數相同的值。

如果你要在所有的優化,儘量

if ((newpos - oldpos > 1) || (oldpos - newpos > 1)) 

這仍然是可讀的。 (以及一致性爲浮動pt #s)

編輯: ack!沒關係,如果你想知道位置是否已經被一些最小增量變化(我最初讀你的代碼的問題從字面上W/O看到你想達到的總體目標),使用此:

if ((newpos - oldpos > delta) || (oldpos - newpos > delta)) 

爲增量> 0,或該(如諾亞中號建議的)

if (newpos != oldpos) 

爲,δ= 0

+1

避免abs的開銷可能是一個更快的解決方案。 – 2009-07-09 19:12:22

0

的性能差異將是微乎其微,但第一個將是更有效的(來自我的猜測)b/C它涉及的操作少於!=。另外,這兩個語句意味着不同的事情,例如,嘗試abs(newpos - oldpos)= 0.5並且看到,除非這兩個變量是整數。

+0

我不這麼認爲:我不認爲它涉及的操作少於!=。 – ChrisW 2009-07-09 18:59:20

3

在大多數架構上,應該沒有區別。比較通常是在CPU內部通過執行減法並通過ALU設置條件代碼來完成的。通過測試條件碼來完成分支(即,不等於測試條件碼寄存器中的零位,分支更高通常測試零,負和溢出標誌)。

4

如果刪除不必要的abs(),第二個效率更高。如果您比較零,則差異是正面還是負面並不重要。另外,我認爲它更具可讀性。但是,這兩個條件似乎並不相同;如果abs(newpos-oldpos)== 1,會發生什麼情況?

13

的主要原因不改變

(abs(newpos-oldpos) > 1) 

(abs(newpos-oldpos) != 0) 

的是,他們在語義上的不同。

abs(newpos-oldpos) == 1你會得到不同的結果。這就是你爲什麼不願意改變事情的一個例子,「除了事實上無論如何不會有可衡量的性能差異(也可能沒有實際的差異)」。

1

在x86上,忽略它們並非等價操作的事實,您可能可以節省一個週期左右。

ABS(newpos)> 1

將是

  1. 減法
  2. 阿布斯
  3. 比較1
  4. JMP

ABS(newpos-oldpos )!= 0

  1. 減法
  2. 阿布斯
  3. JMP - 如果ABS是內聯和最後一個動作,適當設置零標誌。

如果這對您的程序有任何可衡量的影響,我會很驚訝 - 如果您的代碼已經運行得非常緊張,那麼您絕對應該得到讚揚。

3

除非我錯過了一些東西,否則他們不會做同樣的事情。 x> 1與x!= 0不一樣。

0

不是猜測編譯器會做什麼,爲什麼不直接看結果彙編代碼,或者測量它的執行?

0

我現在不是,如果這是我缺少的東西,但該代碼在每本關於浮點數的書上,並且打算在兩個稍微不同的數字之間獲得正匹配。

如果代碼必須比較兩個浮點數,那麼在大多數機器上沒有可能的優化,但重要的是,這不是過早優化,而是關於重構不完全理解的代碼。

1

因爲答案將取決於你的架構,讓我們來看看在X86-64生成的代碼(用gcc -O3):

#include <math.h> 

int t_gt(int x) { // note! not equivalent to the others 
    return abs(x) > 1; 
} 

int t_ge(int x) { 
    return abs(x) >= 1; 
} 

int t_ne(int x) { 
    return abs(x) != 1; 
} 

變爲:

Disassembly of section .text: 

0000000000000000 <t_gt>: 
#include <math.h> 

int t_gt(int x) { 
    0: 89 f8     mov %edi,%eax 
    2: c1 f8 1f    sar $0x1f,%eax 
    5: 31 c7     xor %eax,%edi 
    7: 29 c7     sub %eax,%edi 
    9: 31 c0     xor %eax,%eax 
    b: 83 ff 01    cmp $0x1,%edi 
    e: 0f 9f c0    setg %al 
    return abs(x) > 1; 
} 
    11: c3      retq 
    12: 66 66 66 66 66 2e 0f nopw %cs:0x0(%rax,%rax,1) 
    19: 1f 84 00 00 00 00 00 

0000000000000020 <t_ge>: 

int t_ge(int x) { 
    20: 89 f8     mov %edi,%eax 
    22: c1 f8 1f    sar $0x1f,%eax 
    25: 31 c7     xor %eax,%edi 
    27: 29 c7     sub %eax,%edi 
    29: 31 c0     xor %eax,%eax 
    2b: 85 ff     test %edi,%edi 
    2d: 0f 9f c0    setg %al 
    return abs(x) >= 1; 
} 
    30: c3      retq 
    31: 66 66 66 66 66 66 2e nopw %cs:0x0(%rax,%rax,1) 
    38: 0f 1f 84 00 00 00 00 
    3f: 00 

0000000000000040 <t_ne>: 

int t_ne(int x) { 
    40: 89 f8     mov %edi,%eax 
    42: c1 f8 1f    sar $0x1f,%eax 
    45: 31 c7     xor %eax,%edi 
    47: 29 c7     sub %eax,%edi 
    49: 31 c0     xor %eax,%eax 
    4b: 83 ff 01    cmp $0x1,%edi 
    4e: 0f 95 c0    setne %al 
    return abs(x) != 1; 
} 
    51: c3      retq 

正如你可以看出,有兩點區別:

  • set * ops上的條件碼是不同的。這可能不會影響速度。
  • 對於t_ge,一個兩字節的寄存器測試(AND)被使用,而另兩個使用CMP(減法)和一個字面一字節操作數到針對比較。

儘管各種set *變體之間,以及test和cmp之間的差異可能爲零,但對於cmp而言,額外的單字節操作數可能會使性能下降的幅度微乎其微。

當然,最佳的性能將被擺脫無謂的abs完全放棄():

0000000000000060 <t_ne2>: 

int t_ne2(int x) { 
    60: 31 c0     xor %eax,%eax 
    62: 85 ff     test %edi,%edi 
    64: 0f 95 c0    setne %al 
    return (x != 0); 
} 
    67: c3      retq 

請記住,這些發現可能無法在其他架構適用;然而,輸掉腹肌肯定會在任何地方變得更快。

0

至少在我使用在時刻(GCC 4.2)編譯器,它產生用於第一個表達組件代碼採用特技描述here。然後遞減結果並使用條件代碼來決定如何分支。

對於你的第二個,它是重寫的東西,基本上,newpos != oldpos

,你給的兩個表達式在含義略有不同。但無論哪種方式,我見過的最合理的編譯器都會部署一些非常有趣的技巧來微代碼優化。在這方面你很難超越編譯器。你最好的選擇是平常的建議:試試兩個版本,剖析代碼,看看哪個實際執行得更快。

順便說一句,如果你是這麼想abs(newpos - oldpos) >= 1第一次測試,它仍然會產生絕對值序列。這可能是因爲在減法中出現溢出的可能性,假設是二進制補碼系統。在我的機器,例如,abs(-2147483648 - 2147483647)給1,如果你正在尋找的說,2增量或更多,而不是即使他們顯然完全不同,其將失敗測試。即使在邊緣情況下,編譯器的優化器也必須小心保持這種行爲。