4

我在讀this article,我注意到了jz指令。這讓我思考:「Jump if zero」(jz)更快嗎?

請問這代碼組件,以票面價值

for (int i=max;i!=0;--i){ 
    //Some operation 
} 

採取超越這個代碼?

for (int i=0;i<max;++i){ 
    //Some operation 
} 

只要你不關心你的數據與增加i處理,不存在語義差別。緩存未命中也不會受到影響,因爲它們可以按順序工作。

我在彙編時不夠好寫例子,但我會認爲第一個例子只會使用jz。第二個將使用cmp,然後使用jg,並且還需要另一個變量max。第一個例子只需要循環計數器,因爲0是隱含的。

這也可能是編譯器用來優化的東西,但我可以想象它無法進行優化的情況。

+0

是的,我認爲這取決於每個不同跳轉指令所需的週期數。我不知道離手,但是如果你查看你的目標指令集,你應該能夠找到每條指令使用的循環次數的枚舉。 – JustKevin 2014-09-05 04:48:47

+0

它可能仍然需要cmp指令,因爲它是存儲位置的比較。循環計數器也可以在稍後的編譯器中自動分配給寄存器。 – JustKevin 2014-09-05 04:52:02

+0

另請參閱:http://stackoverflow.com/a/22466475 – harold 2014-09-06 09:50:21

回答

2

假設我們有cmp a,b

處理器將暫時減去操作數(不影響它們的值),正確設置標誌,並在此之後評估跳轉操作。

因此,例如製作jz,而不是cmpjmp的速度會更快。

+0

我忘了減量將設置零標誌。好點子。 – JustKevin 2014-09-05 14:29:57

0

我認爲這主要與現代編譯器solved problem。或者,如果不是解決了,那麼至少通常正確,應用啓發式來產生更好的循環變換。對於x86 [-64],還有更重要的考慮因素,例如循環體是否適合指令緩存?跳轉目標是否合適對齊?分支預測是否有效?

有多種方法可以用x86實現循環。例如,

  • 使用j[e|r]cxz指令,避免標誌寄存器 - 儘管慢於解碼。

  • 使用subadd之前j<cond>而非decinc,以避免局部的標誌寄存器攤位。

operation也很重要。編譯時已知是max

for (int i = 0; i < max/4; i++) 
{ 
    operation; 
    operation; 
    operation; 
    operation; 
} 

operation如果足夠簡單(例如,浮點運算),它可能受益於這種n路的調度。如果每個操作都依賴於以前的操作,則不會。

看一看像GMPmpn/x86[-64]目錄爲不同的微架構優化循環。 Agner Fog's optimization manuals是一個很好的資源。

0

有時,循環倒計數可以釋放寄存器,這可能會比跳轉條件中的任何差異更有用(特別是在x86-32上,寄存器很少)。

但是,在循環中倒數可以防止自動矢量化。如果你的計算可以用SSE/AVX /做什麼的,你可能需要編寫代碼就像

for (i=end; i;){ 
    i-=4; 
    val[i+0] = foo(i); 
    val[i+1] = foo(i); 
    val[i+2] = foo(i); 
    val[i+3] = foo(i); 
    } 

,然後你必須確保end%4 == 0,都保存一個寄存器。