现在的编译器还需要手动展开循环吗_性能 - 如果有的话,循环展开仍然有用吗?...
性能 - 如果有的話,循環展開仍然有用嗎?
我一直試圖通過循環展開來優化一些極其性能關鍵的代碼(一種快速排序算法,在蒙特卡羅模擬中被稱為數百萬次)。 這是我試圖加速的內循環:
// Search for elements to swap.
while(myArray[++index1] < pivot) {}
while(pivot < myArray[--index2]) {}
我嘗試展開類似的東西:
while(true) {
if(myArray[++index1] < pivot) break;
if(myArray[++index1] < pivot) break;
// More unrolling
}
while(true) {
if(pivot < myArray[--index2]) break;
if(pivot < myArray[--index2]) break;
// More unrolling
}
這完全沒有區別所以我把它改成了更易讀的形式。 我曾經嘗試過循環展開,但我有類似的經歷。 鑒于現代硬件上的分支預測器的質量,何時(如果有的話)循環展開仍然是一個有用的優化?
9個解決方案
101 votes
如果你可以打破依賴鏈,循環展開是有意義的。 這使得無序或超標量CPU可以更好地安排事情并因此運行得更快。
一個簡單的例子:
for (int i=0; i
{
sum += data[i];
}
這里參數的依賴鏈非常短。 如果因為數據陣列上有緩存未命中而導致停頓,那么cpu除了等待之外什么也做不了。
另一方面這段代碼:
for (int i=0; i
{
sum1 += data[i+0];
sum2 += data[i+1];
sum3 += data[i+2];
sum4 += data[i+3];
}
sum = sum1 + sum2 + sum3 + sum4;
可以跑得更快。 如果在一次計算中遇到緩存未命中或其他停頓,則仍有三個其他依賴鏈不依賴于停頓。 亂序CPU可以執行這些。
Nils Pipenbrinck answered 2019-04-08T19:08:35Z
20 votes
那些沒有任何區別,因為你正在進行相同數量的比較。 這是一個更好的例子。 代替:
for (int i=0; i<200; i++) {
doStuff();
}
寫:
for (int i=0; i<50; i++) {
doStuff();
doStuff();
doStuff();
doStuff();
}
即便如此,它幾乎肯定無關緊要,但你現在正在做50次比較而不是200次(想象一下比較更復雜)。
然而,手動循環展開通常是歷史的工件。 這是一個很好的編譯器會在重要的時候為你做的事情中的另一個。 例如,大多數人都懶得寫x <<= 1或x += x而不是x *= 2.您只需編寫x *= 2并且編譯器將為您優化它以獲得最佳效果。
基本上,對猜測編譯器的猜測越來越少。
cletus answered 2019-04-08T19:09:28Z
13 votes
無論現代硬件上的分支預測如何,大多數編譯器都會為您循環展開。
值得了解一下編譯器為您做了多少優化。
我發現Felix von Leitner的演講在這個問題上非常有啟發性。 我建議你閱讀它。 簡介:現代編譯器非常聰明,因此手動優化幾乎從未有效。
Peter Alexander answered 2019-04-08T19:10:12Z
2 votes
據我所知,現代編譯器已經在適當的情況下展開循環 - 一個例子是gcc,如果傳遞優化標記,那么手冊說它會:
展開其編號為的循環?? 迭代可以確定?? 編譯時或進入?? 環。
所以,在實踐中,你的編譯器很可能會為你做一些簡單的案例。 因此,您需要確保盡可能多的循環對于編譯器來說很容易確定需要多少次迭代。
Rich Bradshaw answered 2019-04-08T19:10:51Z
2 votes
無論是手動展開還是編譯器展開,循環展開通常都會適得其反,特別是對于最新的x86 CPU(Core 2,Core i7)。 結論:在您計劃部署此代碼的任何CPU上,使用和不使用循環展開您的代碼。
Paul R answered 2019-04-08T19:11:16Z
1 votes
不知道的嘗試不是這樣做的方法。
這種類型占總體時間的比例很高嗎?
所有循環展開都會減少遞增/遞減的循環開銷,比較停止條件和跳轉。 如果你在循環中所做的事情比循環開銷本身需要更多的指令周期,那么你不會在百分比方面看到太多改進。
以下是如何獲得最佳性能的示例。
Mike Dunlavey answered 2019-04-08T19:12:24Z
1 votes
循環展開在特定情況下可能會有所幫助。 唯一的好處是不會跳過一些測試!
例如,它可以允許標量替換,有效插入軟件預取......你會驚訝地發現它有多么有用(通過積極展開,你可以輕松地在大多數循環中獲得10%的加速,即使使用-O3)。
如前所述,它在很大程度上取決于循環,編譯器和實驗是必要的。 制定規則很難(或者展開的編譯器啟發式是完美的)
Kamchatka answered 2019-04-08T19:13:03Z
0 votes
循環展開完全取決于您的問題大小。 它完全取決于您的算法能夠將大小縮小為較小的工作組。 你上面做的不是那樣的。 我不確定monte carlo模擬是否可以展開。
循環展開的好方案是旋轉圖像。 因為您可以旋轉單獨的工作組。 要使其工作,您必須減少迭代次數。
jwendl answered 2019-04-08T19:13:35Z
0 votes
如果循環中和循環中存在大量局部變量,則循環展開仍然有用。 要重用這些寄存器而不是為循環索引保存一個。
在您的示例中,您使用少量局部變量,而不是過度使用寄存器。
如果比較很重(即非指令),則比較(到循環結束)也是一個主要缺點,特別是如果它依賴于外部函數。
循環展開也有助于提高CPU對分支預測的意識,但無論如何都會發生這種情況。
LiraNuna answered 2019-04-08T19:14:28Z
總結
以上是生活随笔為你收集整理的现在的编译器还需要手动展开循环吗_性能 - 如果有的话,循环展开仍然有用吗?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一叶障目下一句是什么呢?
- 下一篇: arm shell 获取本地键盘输入值_