在看《你必須知道的496個C語言問題》一書中,提到"達夫裝置"這個東西,主要是下面的代碼:
|
register n = (count + 7) / 8; /\* count > 0 assumed \*/
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);
} |
這是個很棒的迂迴迴圈展開法, 由 Tom Duff 在 Lucasfilm 時所設計。它的 ``傳統" 形態, 是用來複製多個位元組。
這裡 count 個位元組從 from 指向的數組複製到 to 指向的記憶體位址 (這是個記憶體映射的輸出寄存器, 這也是為什麼它沒有被增加)。它把 swtich 語句和複製 8 個位元組的迴圈交織在一起, 從而解決了剩餘位元組的處理問題 (當 count 不是 8 的倍數時)。相信不相信, 象這樣的把 case 標誌放在嵌套在 swtich 語句內的模組中是合法的。當他公布這個技巧給 C 的開發人員和世界時, Duff 注意到 C 的 swtich 文法, 特別是 ``跌落" 行為, 一直是被爭議的, 而 ``這段代碼在爭論中形成了某種論據, 但我不清楚是贊成還是反對"
函數包含一個switch語句,它的case語句同時位於一個while迴圈體內(有一個case語句在外面)。switch內的運算式計算被八除的餘數。執行開始於while迴圈內的哪個位置由這個餘數決定,最終迴圈退出,(沒有break)。Duff's Device這樣就簡單漂亮地解決了邊界條件的問題。順便提一下,為什麼"case 0"標記在迴圈外面呢?這樣不是打破了對稱的美觀嗎?這樣做的唯一理由是為了處理空序列。當餘數為零,"case 0"內就需要執行一個多餘的測試來判斷空序列的可能性。總之,這是個很酷的演算法。
達夫裝置是一個加速迴圈語句的C編碼技巧。其基本思想是--減少迴圈測試的執行次數。
如果在一個for迴圈中,其中操作執行得如果足夠快(比如說,一個賦值)——那麼測試迴圈條件佔用了迴圈所用時間的很大部分。迴圈應該被部分解開,這樣數個操作一次完成,測試操作也做的較少。其實,是通過switch語句將要進行的連續迴圈操作的次數進行了預判(根據擦case語句的位置)然後依次執行,而不必每次都去進行測試條件。
在這裡Duff's Device是個新穎的,有創造力的解決方案。這裡有一個使用該模型的一個執行個體:快速拷貝和填充。
Duff's Device對效率的負面影響可能來自於代碼膨脹(一些處理器更善於處理緊湊的迴圈而不是大的迴圈)和特別的結構。最佳化器被做成當遇一些更加技巧性的結構時可能會不知所措從而產生比較保守的代碼。
其實達夫裝置是使用switch語句來控制進入迴圈的位置。
下面的程式是簡單驗證達夫裝置的執行:
|
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char**argv){
int n;
if(argc<2){
printf("not enough arugment\n");
return -1;
}
n=atoi(argv[1]);
switch(n){
case 0: do {printf("%d ",0);
case 1: printf("%d ",1);
case 2: printf("%d ",2);
case 3: printf("%d ",3);
case 4: printf("%d ",4);
}while(--n>0);
}
return 0;
} |
從上面的輸出結果我們可以清楚的看到,switch語句控制了進入迴圈的位置。 |