【Microarchitecture of Intel and AMD CPU】 2 Out-of-order execution

Intel 的第六代微處理器,從PPro開始,提供了一種重要的技術提升,亂序執行。如果一條指令的輸入因爲某些原因在當前時刻還未準備好,那麼我們可以嘗試執行這條指令後面的指令。當然,處理器需要檢查時候後面的指令需要前面的指令的執行結果。如果每一條指令都依賴於前一條指令的結果,那麼這就叫做依賴鏈。

判斷輸入依賴關係的邏輯,和一旦其所依賴的數據一旦準備好,就立即執行機制已經就緒,這使得處理器可以在同一時間並行做多件事。如果我們需要進行加法或者乘法,並且他們指令互不依賴,那麼我們就可以同時執行他們,因爲我們使用兩種不同的執行單元。但是我們不能同時執行兩個乘法指令,如果我們只有一個乘法單元。

處理器一般按照順序進行流水,以增加吞吐量。如果浮點加法需要4拍,並且執行單元完全pipeline,那麼我們可以在時間T開始執行一個加法,在T+4時,將會得到加法的結果;在T+1時執行另一筆加法,在T+5時得到下一筆的結果。這個技術的優勢將會得到最大化,如果code被組織成連續之間沒有依賴關係的形式。

2.1 指令分解爲uops

亂序執行的微處理器將會將執行分解爲微操做,簡記爲uops. 一個簡單的指令,比如 ADD EAX,EBX 通常是一條uop,然而ADD EAX,[MEM1] 通常會被分解爲兩條指令:

1)從MEM1處加載數據到臨時寄存器;

2)將臨時寄存器和EAX的值累加到EAX上。

指令ADD [MEM1],EAX則會分解爲三條uop

1)從MEM1處加載數據到臨時寄存器;

2)將臨時寄存器和EAX的值累加到臨時寄存器上;

3)將臨時寄存器的值寫回memory。

這個操作的優勢就是uops可以被亂序執行

; Example 2.1. Out of order processing
mov eax, [mem1]
imul eax, 5
add eax, [mem2]
mov [mem3], eax

此處,ADD EAX,[MEM2] 指令會被分解爲兩條微指令。這個的好處就是可以同時加載mem2處的數據,同時執行imul的乘法。如果這些數據都不在cache中,微處理器將會在讀取mem1處的數據之後,立刻讀取mem2處的數據,可以遠早於乘法的執行。

將指令分解爲微指令也可以使得棧工作的效率提升,考慮下面的sequence

; Example 2.2. Instructions split into μops
push eax
call func

push eax指令會分解爲兩條uop:SUB ESP,4和MOV [ESP],EAX。這樣做的優勢就是SUB ESP,4可以提前執行,即使此時EAX還沒有準備好。CALL指令需要ESP的最新值(將EBP設置爲ESP的值)。 如果 PUSH指令沒有被分解爲兩條微指令,那麼CALL指令需要等待PUSH指令的執行完畢。幸好我們使用了微指令,棧指針的基本不會耽誤我們的程序。

2.2 寄存器重命名

考慮下面的例子:

; Example 2.3. Register renaming
mov eax, [mem1]
imul eax, 6
mov [mem2], eax
mov eax, [mem3]
add eax, 2
mov [mem4], eax

這個代碼片段中執行了兩個毫不相關的指令,【mem1】*6 和【mem2】+2。如果最後三條指令使用不同的寄存器,那麼它們就明顯的不相關。實際上,微處理器可以實現這個技術。它會爲後面的三條指令使用不同的臨時寄存器,這樣就可以同時執行加法和乘法了。IA32指令只給了我們7個32bit的通用寄存器,通常我們會用光。所以我們沒法給每個計算都配置一個新的寄存器。但是微處理器有大量的臨時寄存器可以使用。微處理器可以重命名任意的臨時寄存器,以代表邏輯架構寄存器EAX。

寄存器重命名可以十分的自動化並且以非常簡單的方式工作。每次一條指令想要寫入邏輯寄存器,處理器都會爲這個邏輯寄存器分配一個新的臨時寄存器。上面例子中的第一條指令要寫如EAX,所以一個新的臨時寄存器會被分配。換句話說,乘法指令將要使用兩個不同的寄存器,一個用於輸入,一個用於輸出。(譯者注:原例 imul eax 6 是將eax的值乘6後再寫入EAX)下面的例子介紹了這樣做的優勢

; Example 2.4. Register renaming
mov eax, [mem1]
mov ebx, [mem2]
add ebx, eax
imul eax, 6
mov [mem3], eax
mov [mem4], ebx

假如【MEM1】在cache中,但是【MEM2】不在。這意味着乘法將會在加法之前啓動。使用臨時寄存器的優勢就是,即使後面再執行加法,那麼我們也不會破壞eax原始的值。如果我們使用同一個寄存器既做輸入又做輸出,那麼乘法需要等待ebx的加載完畢,然後加法才能執行。

在所有的指令執行完畢之後,代碼段中最新的臨時寄存器中的值將會被寫入邏輯架構EAX寄存器。這個階段又叫做retirement(退休)。

所有的上述通用寄存器,棧寄存器,標誌位,浮點寄存器和向量寄存器和段寄存器都可以被重命名。多數處理器不允許控制字和浮點狀態字被重命名,這就是代碼修改這些寄存器較慢的原因。


翻譯自【Microarchitecture of Intel and AMD CPU  An optimization guide for assembly programmers and compiler makers

                                                                                                                    -------------------歡迎關注我的公衆號《處理器與AI芯片》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章