現在的編譯器還需要手動展開迴圈嗎 C 之迴圈展開

2021-10-14 11:41:36 字數 2394 閱讀 9987

什麼是迴圈展開?

迴圈展開,英文中稱loop unwinding或loop unrolling,是一種犧牲程式的尺寸來加快程式的執行速度的優化方法。可以由程式設計師完成,也可由編譯器自動優化完成。迴圈展開最常用來降低迴圈開銷,為具有多個功能單元的處理器提供指令級並行。也有利於指令流水線的排程。

迴圈展開對程式效能的影響

#include #include int main() auto end = std::chrono::system_clock::now(); std::chrono::duration dura = end - start; std::cout <
類似於上面的這段**是我們平常工作中經常見到的,函式目的就是求得1+2+……+9998+9999的累加和,每次迴圈把i累加到sum變數上,迴圈次數一共10000次。**執行結果如下:

可以看出**執行耗時0.0000279秒。下面我們將迴圈展開一次,即把上述**中的迴圈改為如下**:

for(int i = 0;i < count;i += 2)
即每次迴圈將i和i+1一起累加到sum變數上,這樣可以把迴圈次數從10000次降低到5000次,由於cpu的高度流水線化,連續兩個加法指令增加耗時很低,所以此版本**可以一定程度上提高程式執行速度,執行結果如下:

**執行耗時0.0000159秒,相較於未優化**速度快了將近一倍。當然,我們可以繼續增加迴圈展開次數以進一步提高程式執行速度,但是這個增加迴圈展開次數也是有限度的,當達到了cpu的最高吞吐量之後,繼續增加迴圈展開次數是沒有意義的。

上述迴圈展開後的**依然有進一步優化的空間,那就是消除連續指令的相關性,以達到指令級並行,我們可以看到迴圈展開後的**,迴圈體中有兩條語句:sum += i 和 sum += i+1,第二條語句sum += i+1依賴於第一條命來sum += i的執行結果,所以這兩條語句只能依次執行,限制了cpu進一步提高效能的可能。如果我們將迴圈體改為如下**:

int sum1=0,sum2=0;for(int i=0;i < count;i+=2)sum = sum1 + sum2;
我們新建了兩個變數sum1和sum2用於儲存迴圈展開時兩個累加語句的累加結果,最後在迴圈體外將兩部分結果相加得到最終結果。該**中兩個累加語句之間是互不相關的,所以cpu可以並行執行這兩條指令,以達到效能的進一步提高。下面是執行結果:

**執行耗時0.0000073秒,相較於只進行迴圈展開的**速度又快了將近一倍。

總結

由上面三段**的執行速度對比可以看出,迴圈展開對程式效能有著很重要的影響,可以減少分支**錯誤次數,增加取消資料相關進一步利用並行執行提高速度的機會。但是,並不建議大家進行手動的迴圈展開,在**中進行迴圈展開會導致程式的可讀性下降,**膨脹。為了直觀感受迴圈展開對效能的影響,上述**執行結果均是在不開編譯器優化的情況下進行的測試,其實在我們開啟了編譯器優化的時候,編譯器會自動對我們的迴圈**進行迴圈展開,讓我們可以在保持了**可讀性的同時,又能享受到迴圈展開對我們程式效能的提高。

還需要學習的十二種CSS選擇器

在前面的文章中,我們在大前端介紹了 五種必須知道的css選擇器 現在向大家介紹,還需要學習的另外十二種css選擇器。如果你還沒有用過,就好好學習一下,如果你已經熟知了就當是溫習。一 x link x visited x hover x active 偽類 a link a visted 偽類選擇器,...

java實現C編譯器 for 迴圈語句的解析和執行

int a 0 我們當前的解析器是解析不了的,必須把上面的語句轉換為 int a a 0 要想使當前的直譯器能夠解析並執行變數宣告同時付初值的功能,實現起來較為複雜,所以我決定先把這個功能放一放,把精力先使用在更為重要的實現上。本節我們要為直譯器增加的功能是對for迴圈進行解析和執行,for迴圈的語...

LCC編譯器的源程式分析 32 for迴圈語句

c 語言中的 for語句使用最為靈活,不僅可以用於迴圈次數已經確定的情況,而且可以用於迴圈次數不確定而只給出迴圈結束條件的情況。因此,這個語句的使用頻率是最高的,當然它的處理情況比上面兩種迴圈要複雜一些。它的形式如下 for 表示式1 表示式2 表示式3 語句 1 它的執行過程是先求解表示式 1的值...