2010-10-01 113 views
2

這裏是我寫在64位linux機器上的函數。C代碼分析

void myfunc(unsigned char* arr) //array of 8 bytes is passed by reference 
{ 
    unsigned long a = 0; //8 bytes 
    unsigned char* LL = (unsigned char*) &a; 

    LL[0] = arr[6]; 
    LL[1] = arr[3]; 
    LL[2] = arr[1]; 
    LL[3] = arr[7]; 
    LL[4] = arr[5]; 
    LL[5] = arr[4]; 
    LL[6] = arr[0]; 
    LL[7] = arr[2]; 
} 

現在我的問題是:

  1. 威爾變量「a」被存儲在一個寄存器,使得它不會被一次又一次地從RAM或chache訪問?
  2. 在64位體系結構上工作時,我應該假設'arr'數組將被存儲在一個寄存器中,因爲函數參數存儲在一個64位體系結構的寄存器中?
  3. 指針類型轉換的效率如何?我的猜測是它應該效率低下?

任何幫助將appriciated。

問候

回答

3
  1. a無法存儲在寄存器中,因爲您已經使用了它的地址。 (valdo正確地指出,一個非常聰明的編譯器可能優化數組訪問到位操作,並將a留在一個寄存器中,但我從來沒有見過編譯器這樣做,我不確定它會變得更快)。
  2. arr(指針本身)存儲在一個寄存器(%edi,在amd64上)。內存中的的內容。
  3. 指針類型鑄造本身通常根本不產生任何代碼。然而,用類型轉換來做愚蠢的事情會導致非常低效的代碼,甚至導致代碼的行爲未定義。

它看起來像你試圖排列一個數組中的字節,然後將它們推到一個數字,並且你的例子生成的機器代碼不是非常糟糕的。 David建議使用shift和mask操作,而不是這樣(如果你的代碼需要在一個big-endian機器上運行,這也可以避免問題),並且還有SSE向量排列指令,但是我聽說他們很友善的痛苦使用。順便說一句,你應該使你的示例函數的返回類型爲unsigned long,並將return a;放在最後;那麼你可以使用gcc -O2 -S,看看你從編譯中得到了什麼。沒有改變返回a,GCC將愉快地優化功能的整個身體,因爲它沒有外部可見的副作用。

+0

好,所以'a'或'arr'都不會存儲在寄存器中。這個代碼中的緩存命中呢?我能否假設讀寫變量'arr'和'a'產生100%緩存命中? – 2010-10-01 18:31:11

+0

是的,這可能是一個安全的假設 - 我能想到的唯一的事情就是如果你在函數中間切換上下文非常不幸,那麼當控制回到你的過程時,它們可能不會再被緩存了。 – zwol 2010-10-01 18:34:24

2

你可能會做的更好使用顯式移位和掩碼指令來實現這一點,而不是使用數組索引。

數組操作將使編譯器難以使用寄存器,因爲通常沒有指令執行諸如「從寄存器A的第3個字節裝入8位」的指令。 (一個優化編譯器可能找出可以用shift/mask來做到這一點,但我不確定它有多可能)。

0
  1. 關於變量a將被存儲在寄存器中的問題是一個優化問題。由於沒有volatile修飾符恕我直言智能編譯器會做到這一點。

  2. 這是一個調用約定的問題。如果按照慣例,單個指針參數被傳送到一個寄存器中 - 那麼將是arr

  3. 指針類型轉換不是CPU解釋的操作。沒有爲它生成的代碼。它只是編譯器關於你的意思的信息。

(其實有時鑄造確實會產生額外的代碼,但是這涉及到多重繼承和多態)

+0

GCC不會將'a'優化成一個寄存器,而且我不確定在這種情況下它會更快。我提交了http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45861看看他們的想法。 – zwol 2010-10-01 18:35:43

+0

扎克!我在海灣合作委員會網站看到過你的帖子。您在C文件中編寫的第二個按位操作函數在char數組上進行了大量類型轉換。你能告訴我這些類型的鑄件應該是多麼膨脹嗎?對不起,我不太瞭解彙編代碼,所以無法讀取它。 – 2010-10-01 18:49:25

+0

你爲什麼如此擔心類型轉換的成本?一般來說,使用C來投射零或一條指令。它們完全不像高級語言中的轉換操作。 (在這種情況下,它們本身並不做任何事情,但是它們強制編譯器發出在寄存器的整個寬度上運行的移位指令 - 這是必要的,或者所有的移位都會產生零,而不是所有你想要的。) – zwol 2010-10-02 01:41:20

0

取決於你的優化級別。您可以檢查程序集以回答您的問題。使用gcc,使用「-S」標誌。

gcc -S -O0 -o /tmp/xx-O0.s /tmp/xx.c 
gcc -S -O3 -o /tmp/xx-O3.s /tmp/xx.c 

生成的組件是完全不同。 (請務必將return a;更改爲Zack建議的更改。)

另請參閱this message關於如何生成混合c /彙編列表(通過優化快速變得無用)的提示。