簡體   English   中英

布爾比較的效率? 在C.

[英]Efficiency of boolean comparisons? In C

我正在用C編寫一個循環,我只是想知道如何優化它。 這里並不重要,因為我只是練習,但為了進一步了解,我想知道:

在循環中,例如以下代碼段:

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

處理器是否每次迭代檢查(i < 10)(i == 10) 或者它只是檢查(i < 10) ,如果是真的,繼續?

如果它同時檢查兩者,則不會:

int i = 0;
while (i != 10) {
    printf("%d\n", i);
    i++;
}

更有效率?

謝謝!

兩者都將在單個匯編指令中翻譯。 對於EQUAL和NOT EQUAL,大多數CPU都有比LESS THAN更少或者等於的比較指令。

關於這些優化問題的一個有趣的事情是,它們經常說明為什么在編寫這些操作的性能影響之前應該編寫清晰度/正確性的代碼(這通常沒有任何區別)。

您的2個示例循環不具有相同的行為:

int i = 0;
/* this will print 11 lines (0..10) */
while (i <= 10) {
    printf("%d\n", i);
    i++;
}

和,

int i = 0;
/* This will print 10 lines (0..9) */
while (i != 10) {
    printf("%d\n", i);
    i++;
}

但是,為了回答你的問題,幾乎可以肯定這兩個結構的性能是相同的(假設你修復了問題所以循環計數是相同的)。 例如,如果您的處理器只能在兩個單獨的步驟(這將是一個非常不尋常的處理器)中檢查相等以及一個值是否小於另一個值,那么編譯器可能會將(i <= 10)轉換為(i < 11)測試 - 或者可能是(i != 11)測試。

這是早期優化的一個明顯的例子....恕我直言,這是程序員對他們的工藝新手的東西,容易擔心。 如果您必須擔心它,請學會對代碼進行基准測試和分析,以便您的擔憂基於證據而不是假設。

說到你的具體問題。 首先, <=沒有實現為在我職業生涯中遇到的任何C編譯器中單獨測試<==兩個操作。 這包括一些非常愚蠢的編譯器。 請注意,對於整數, a <= 5a < 6條件相同,並且如果目標體系結構要求僅使用< ,則代碼生成器將執行此操作。

你的第二個問題是, while (i != 10)可能更有效,但卻引發了一個有趣的防御性編程問題。 首先,在任何合理的目標架構中都沒有任何效率。 但是,它可能會導致小錯誤導致更大的失敗。 考慮一下:如果循環體內的某些代碼行修改了i ,比如說它大於10,可能會發生什么? 循環結束需要多長時間,並且會出現錯誤的其他后果嗎?

最后,當想知道這種事情時,通常有必要找出你正在使用的編譯器實際生成的代碼。 大多數編譯器都提供了執行此操作的機制。 對於GCC,了解-S選項將導致它直接生成匯編代碼而不是生成目標文件。

運算符<=和<是匯編中的單個指令,應該沒有性能差異。 請注意,在某些處理器上測試0可能比測試任何其他常量要快一些,因此使循環向后運行是合理的:

int i = 10;
while (i != 0) 
{
    printf("%d\n", i);
    i--;
}

請注意,像這樣的微優化通常只能獲得更多的性能,更好地利用您的時間來使用高效的算法。

取決於架構和編譯器。 在大多數體系結構中,有一條指令用於<=或相反,可以否定,因此如果將其轉換為循環,則比較很可能只是一條指令。 (在x86或x86_64上是一條指令)

編譯器可能會將循環展開為十倍於i++的序列,當只涉及常量表達式時,它甚至會優化++並且只留下常量。

而Ira是對的,如果涉及到printf ,那么比較確實會消失,執行時間可能是數百萬個時鍾周期。

處理器是否每次迭代檢查(i <10)和(i == 10)? 或者它只是檢查(i <10),如果是真的,繼續?

也不會,它很可能會檢查(i <11)。 <= 10就是為了給你的代碼提供更好的意義,因為11是一個神奇的數字 ,實際上意味着(10+1)

// Case I

int i = 0; 
while (i < 10) {
    printf("%d\n", i);
    i++;
    printf("%d\n", i);
    i++;
}

// Case II

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

Case I代碼占用更多空間但速度更快,Case II代碼占用的空間更少,但與Case I代碼相比較慢。 因為在編程空間中復雜性和時間復雜度總是相互成正比的。 這意味着你必須妥協空間或時間。
因此,通過這種方式,您可以優化時間復雜度或空間復雜度,

而且你的兩個代碼是一樣的。

我正在用C編寫一個循環,我只是想知道如何優化它。

如果在啟用優化的情況下進行編譯,則最大的優化將來自展開該循環。

使用-O2對代碼進行分析將很困難,因為對於簡單的函數,編譯器將展開循環,並且您將無法對比較中的實際差異進行基准測試。 在分析使用常量的測試用例時,應該小心,這些常量可能會在編譯器優化時使代碼變得微不足道。

拆卸。 根據處理器,優化和許多事情,這個簡單的示例代碼實際上展開或做的事情不能反映您的真實問題。 使用gcc -O1進行編譯雖然您提供的兩個示例循環都導致相同的匯編程序(用於arm)。

如果大於或等於循環的遠端,則小於C代碼通常會變成分支。 如果你的處理器沒有大於或等於它可能有一個分支如果大於和一個分支如果相等,兩個指令。

通常會有一個寄存器持有我。 將有一個增加i的指令。 然后一條指令將i與10進行比較,然后等於,大於或等於,並且通常在單個指令中完成,因此通常不會看到差異。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM