天天看點

《深入了解計算機系統》——(一)

一、條件資料傳送和條件控制轉移

首先我們先從代碼層面看下這兩者的不同點,先給出結論:條件資料傳送性能>條件控制轉移性能

public class IfTest {
    private static int absDiff(int x, int y) {
        int result;
        if (x < y) {
            result = y - x;
        } else {
            result = x - y;
        }
        return result;
    }


    private static int cmovDiff(int x, int y) {
        int rval = y - x;
        int eval = x - y;
        boolean nTest = x >= y;
        if (nTest) {
            rval = eval;
        }
        return rval;
    }

    public static void main(String[] args) {
        int x = 998, y = 999;
        System.out.println("absDiff start");
        long startTime = System.currentTimeMillis();
        System.out.println(absDiff(x, y));
        long endTime = System.currentTimeMillis();
        System.out.println("absDiff start time=" + (endTime - startTime));

        System.out.println("cmovDiff start");
        startTime = System.currentTimeMillis();
        System.out.println(cmovDiff(x, y));
        endTime = System.currentTimeMillis();
        System.out.println("cmovDiff start time=" + (endTime - startTime));
    }
}
           

自己可以試驗下兩者的所用時間比較,下面來具體分析下原因:

處理器通過使用流水線獲得高性能,在流水線中,一條指令的處理要經過一系列的階段,每個階段執行所需操作的一小部分(例如,從記憶體中取指令,确定指令類型,從 記憶體讀資料,執行算數運算,向記憶體寫資料,以及更新程式計數器等)。這種方法是通過重疊連續指令的步驟來提高性能,例如,在取一條指令的同時,執行他前面的一條指令的算數運算。但是要做到這一點,就需要事先确定要執行的指令序列,這樣才能保證流水線中充滿了待執行的指令。當機器遇到條件分支的時候,隻有當條件求值完成之後才能确定走哪個分支。但是現在機器不會那麼傻的自己去等待而不做别的事,他會采用非常精密的分支預測邏輯猜測每條跳轉邏輯是否會執行,然後将機率最大的那個分支的指令放在流水線中,如果在成功率比較高的情況下,其實效率還是比較高的,但是如果是上圖中的x<y這樣的結果就非常的不好預測,基本隻有50%的機率,一旦猜測錯誤,就會要求處理器丢掉它為該跳轉指令後所做的所有事情,然後開始從正确的位置開始去填充流水線。而對于條件資料傳送,是先将資料計算好處理,放在寄存器中,最後才會處理判斷分支,使得整個控制流不依賴與資料,更容易保持流水線是滿的,所有平均來看條件資料傳送效率會更高,性能會更好。

二、switch語句

正常我們在開發的過程中,如果遇到條件分支都是建議使用switch語句,話說是效率會高點,那為什麼呢?其實也不是什麼情況都适用,隻有在需要處理具有多種可能結果的預測時,這種語句才會特别有用,接下來一探究竟:

首先switch的底層資料結構是跳轉表。跳轉表是一個數組,表項i是一個代碼段的位址,這個代碼段實作當開關索引值等于i時程式應該采取的動作。程式代碼用開關索引值來執行一個跳轉表内的數組引用,确定跳轉表的目标,和使用一組很長的if-else相比,使用跳轉表的優點就是執行開關語句的時間與開關情況的數量無關。GCC根據開關情況的數量和開關情況值的稀疏程度來翻譯開關語句,當開關情況數量比較多時(4個以上),并且值的跨度比較小時,就會使用跳轉表。

繼續閱讀