2016-09-27 73 views
2

寄存器是彙編代碼中存儲和處理數據的最強大的地方,但是與主存儲器相比,它的空間有限。因此,我認爲確定何時將數據移動到寄存器以及何時移出數據對優化彙編代碼非常重要,特別是在需要大量寄存器使用時。一個變量應該存儲在一個寄存器中多久?

因此,將數據存入寄存器之前需要多長時間才能將數據存入主存儲器供以後使用(在寄存器中處理後)?或者當我沒有更多的寄存器供我處理新數據時,是否將它們放入內存中? (我個人不認爲這是適當的:P)

考慮下面的代碼(1碼):

MOV EBX,SomeAddressForLaterUse 
;...-imagine 37 lines of assembly code here 
MOV ESI,SomeAddress 
MOV EDI,EBX 
MOV ECX,SIZE_IN_BYTES 
REP MOVSB 

現在考慮這個其他代碼(第二代碼);

MOV EBX,SomeAddressForLaterUse 
PUSH EBX 
;...-imagine 37 lines of assembly code here 
MOV ESI,SomeAddress 
POP EDI 
MOV ECX,SIZE_IN_BYTES 
REP MOVSB 

上面,我覺得這是很明顯的是,第二碼具有優勢,節省多了一個寄存器中的37行的彙編代碼(除非這37行的彙編代碼不使用多少個寄存器)使用,但有時候在這兩種方法之間選擇是非常困惑的,例如,如果它是10行代碼而不是37代碼呢?

總而言之,在確定將數據從寄存器移開時是否存在某種規則?

+1

你給了一個規則,並說你不喜歡它。你能指出這個規則有什麼不好的地方,在任何特定的時候它會給出不好的結果?一旦您發現該規則的具體問題,您就有了改進的基礎。 –

+0

不確定你的問題有一個簡單的答案,但有一點需要注意:訪問大多數處理器的L1高速緩存通常可以在不到一個時鐘週期內完成,因此你可以將整個空間視爲寄存器。所以在現代CPU上這通常是32k或更多。此外,諸如算法選擇,cache-awarenss等問題通常會使您在指令級別從微優化中獲得的收益變得微不足道。這是什麼類型的項目? – QuadrupleA

+0

@QuadrupleA:當然,但是CPU在單個時鐘週期內試圖退出多條指令,並且它們都不可能執行L1緩存訪問。 –

回答

4

你正在看着這完全倒退。當內存泄漏到內存時的規則就像「當你用完寄存器時」一樣簡單。

複雜的部分是決定哪些註冊重用

一旦你決定,你在代碼中確定了兩個關鍵點 - 這個寄存器的最後一次使用是爲了它的舊目的,並且是第一次用於新目的。泄漏事件可能發生在這些事件之間的任何時間點,在這裏你會考慮處理器特性,正在執行哪些其他指令需要內存訪問,依賴鏈在計算最終值的時間長短 - 最終會提出執行溢出以避免流水線延遲等待內存控制器的最佳時間。

除此之外,調用約定的注意事項,比如哪些寄存器必須被調用的函數保留,哪些被認爲是暫存空間,並且在很多情況下葉函數不需要對內存做任何溢出。

在某些架構上,處理器特性差異很大,以至於在訂購指令流時無法獲得優化所需的信息。 (x86是一個很好的例子)。在這種情況下,CPU本身可能會有一個亂序執行引擎,其中有大量邏輯專用於以特定微架構最佳的方式對指令進行重新排序(或將它們分成的微操作)。此執行時間優化還可以考慮分支預測統計信息,這是提前編譯只能在收集分析跟蹤時執行的操作。

3

你真正想要做的是計算/放置寄存器中的值,並且理想情況是永遠不會將它們移動到「很遠」並因此訪問速度慢的內存(「溢出」)。

許多編譯器處理這種情況的方法是,首先假設計算值在代碼中的某個點處在寄存器中。這需要一個無限的(好的,無限的)數量的寄存器,這顯然比機器更多,所以它不能真正工作。是的,您可能在某個時間點上有10,000個值用於真正大型的程序。 (甚至更多!)

但是,如果你能決定哪個那些無界的人都是最不重要的,你可以迫使他們潑灑/在內存中,現在你有一個包含值較少的寄存器。如果你泄漏了足夠的東西,你會發現剩下的東西會適合真正的寄存器。

這是由所謂的graph-coloring algorithm完成。這個想法是,你建立一個圖形,其節點是在代碼中的一個點上註冊的值,以及連接節點的弧線,這些節點的值必須同時存在;這些被稱爲「干涉弧」。

什麼着色算法所做的是顏色(當然,寄存器號碼)來裝飾的節點。假設你的機器有8個寄存器,並且你想在某個寄存器中有變量X,圖中有一個節點代表了這一點。

有兩個階段的過程可以做到這一點。第一階段通過擁有比寄存器少的鄰居來選擇可清晰着色的節點,並且解決了節點具有比寄存器更多鄰居的情況。第二階段分配顏色/寄存器。

  • 階段1:重複直到圖爲空:選擇一個節點,其中的鄰居數少於實際的寄存器;從圖中移除它(及其弧)並推入堆棧。如果沒有選擇,請選擇一個鄰居太多的節點[通常選擇具有最大數量鄰居的節點是好的],從圖中刪除,推入堆棧[這樣一個節點可能的寄存器溢出記憶在階段2]。

  • 階段2:重複,直到堆棧爲空:從棧中彈出一個節點,並添加它和它的弧回到圖。如果可能的話,給該節點分配一個顏色/寄存器,該顏色/寄存器不是它的任何相鄰顏色/寄存器;這是價值將使用的實際註冊表。 (這一步有時會成功着色一個可能要溢出的節點)如果不能選擇這樣一個顏色/寄存器,那麼這個節點就不能被分配給一個寄存器而沒有它或它的鄰居被泄漏;我們通過給這個節點分配一個任意寄存器來計算這個問題,並在某處存儲一個存儲位置來保存它的計算值,同時計算它的相鄰節點,我們解決了這個問題。

中提琴,你已經在泄漏只有少數寄存器在實踐的方式分配寄存器號的每個值。 This document gives more detailed explanation and basic algorithm

不,你不想做手工這一點,但它很容易建立一個程序來做到這一點。

This paper discusses how to revise this scheme to handle messy ("irregular") architectures like the x86 where the registers are not all equivalent

相關問題