2010-08-02 50 views
3

可能重複:
What does the code do?這是一個有效的C代碼嗎?

void duff(register char *to, register char *from, register int count) 
{ 
    register int n=(count+7)/8; 
    switch(count%8) 
    { 
     case 0: do{ *to++ = *from++; 
     case 7: *to++ = *from++; 
     case 6: *to++ = *from++; 
     case 5: *to++ = *from++; 
     case 4: *to++ = *from++; 
     case 3: *to++ = *from++; 
     case 2: *to++ = *from++; 
     case 1: *to++ = *from++; 
     }while(--n >0); 
    } 
} 

是上述有效的C代碼?如果是這樣,它試圖達到什麼目的,爲什麼會有人做類似上述的事情?

+0

最好使'count'無符號,或者添加'7U'而不是'7'。否則,鴻溝會很慢。 – 2010-08-02 05:58:30

+1

[代碼是做什麼的絕對重複?](http://stackoverflow.com/questions/1723270/what-does-the-code-do) – paxdiablo 2010-08-02 06:04:29

+1

@R ..:呃,不。你真的認爲編譯器太笨了,無法「優化」整數除法? – GManNickG 2010-08-02 06:17:38

回答

9

它被稱爲達夫的設備,你可以閱讀關於它on wikipedia

它解決了展開循環中的一個問題:可能需要一個非整數數量的傳遞。一種方法是在主循環之外處理這種情況,但使用Duff的設備會更有效,該設備使用非常快的跳轉表,並避免處理奇數操作的額外循環開銷。

在您的例子,這是一個記憶,請比較天真的版本:

void memcpy(char* dst, char* src, size_t count) 
{ 
    begin: 
    if (count-- == 0) return; 
    *(dst++) = *(src++); 
    goto begin; 
} 

要複製15個字節,這樣做如下:

測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試算, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試算, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數, 副本, 循環, 測試計數

注了多少次「測試計數「和」循環「操作必須完成。

使用達夫的版本,你表明,這是很簡單的:基於計數, 複製, 複製, 複製, 複製, 複製, 複製, 副本, 測試

跳算, 循環, 副本,副本 , 副本,副本 , 副本,副本 , 副本, 副本, 測試計數

節省超過一半的步驟

5

這是有效的。這是一個非常古老的循環展開。

基本上,不是檢查每個正在複製的字符的計數以確定何時停止,它只需檢查ceil(n/8)次。

register關鍵字只是一個編譯器提示,暗示編譯器試圖將該值保存在寄存器中,而不是將其清理出主內存。

顯然這樣的東西不再是真的必需了(memcpy()可能在你編碼的任何機器上有非常快的實現),但是像這樣的技巧實際上提供了相當不錯的性能勝利。

+0

對'memcpy'使用這個實現是愚蠢的,因爲這個庫會通過一次拷貝多個字節而不是多個字節拷貝來進行優化,但是對於其中循環開銷很大的重複操作,它可以是一個很大的改進。例如,爲數組中的每個元素添加一個常量。增加速度非常快,循環邏輯實際上是最大的成本,所以展開速度提高了很多。 – 2010-08-02 05:44:13

+0

Duff的設備仍然可能贏得很少對齊的非常小的memcpy操作,例如圍繞小部分字符串移動。 – 2010-08-02 05:55:41

+1

+1表示沒有必要。現在的問題比「慢代碼」要難得多:「不可讀的蹩腳的不可維護的代碼」:-) – paxdiablo 2010-08-02 06:03:41