Duff的设备是一种技巧,它可以直接用C或C++表达循环展开,而无需额外的代码来处理剩余的部分循环。技巧是使用switch语句,其中除case以外的所有标签都位于while循环的中间。此外,所有情况都落到while循环的末尾。尽管有这种印象,但它使Duff的设备是合法的C和C++代码(但是,在Java无效)。
有什么用?
Duff的设备有两个关键方面。
- 首先,展开循环。通过避免检查循环是否完成并跳回到循环顶部所涉及的一些开销,我们可以提高速度。当执行直线代码而不是跳跃时,CPU可以运行得更快。但是,代码大小变大。
- 第二个方面是switch语句。它允许代码在第一次通过时跳入循环的中间。对于大多数人来说,令人惊讶的部分是允许这种事情。好吧,这是允许的。
例子
// C program to illustrate the working of
// Duff's Device. The program copies given
// number of elements bool array src[] to
// dest[]
#include
#include
#include
// Copies size bits from src[] to dest[]
void copyBoolArray(bool src[], bool dest[],
int size)
{
// Do copy in rounds of size 8.
int rounds = size / 8;
int i = 0;
switch (size % 8)
{
case 0:
while (!(rounds == 0))
{
rounds = rounds - 1;
dest[i] = src[i];
i = i + 1;
// An important point is, the switch
// control can directly reach below labels
case 7:
dest[i] = src[i];
i = i + 1;
case 6:
dest[i] = src[i];
i = i + 1;
case 5:
dest[i] = src[i];
i = i + 1;
case 4:
dest[i] = src[i];
i = i + 1;
case 3:
dest[i] = src[i];
i = i + 1;
case 2:
dest[i] = src[i];
i = i + 1;
case 1:
dest[i] = src[i];
i = i + 1;
};
}
}
// Driver code
int main()
{
int size = 20;
bool dest[size], src[size];
// Assign some random values to src[]
int i;
for (i=0; i
输出:
1 1
0 0
1 1
1 1
1 1
1 1
0 0
0 0
1 1
1 1
0 0
1 1
0 0
1 1
1 1
0 0
0 0
0 0
0 0
1 1
以上程序如何运作?
在上面的示例中,我们处理20个字节并将这20个随机位从src数组复制到目标数组。它要运行的遍数还取决于源数组的大小。
对于第一遍,执行从计算出的案例标签开始,然后一直执行到每个后续赋值语句,就像其他任何switch语句一样。在最后一个case标签之后,执行到达循环的底部,这时它跳回到顶部。循环的顶部位于switch语句内,因此不再对开关进行重新评估。
原始循环展开八次,因此迭代次数除以八。如果要复制的字节数不是8的倍数,则剩余一些字节。大多数一次复制字节块的算法将在末尾处理其余字节,但是Duff的设备在开始时处理它们。该函数为switch语句计算计数%8,以计算余数,然后跳转到该字节数的case标签,然后复制它们。然后循环继续在左遍中复制八个字节的组。
对于第一遍:
rounds = count / 8;
// rounds = 2 for count =20
i = 0;
switch(count % 8)
{
// The remainder is 4 (20 modulo 8)
// so jump to the case 4
case 0:
while( !(rounds == 0) )
{
//[skipped]
rounds = rounds - 1;
dest[i] = src[i];
i = i + 1;
case 7:
dest[i] = src[i];
i = i + 1; //[skipped]
case 6:
dest[i] = src[i];
i = i + 1; //[skipped]
case 5:
dest[i] = src[i];
i = i + 1; //[skipped]
case 4:
dest[i] = src[i];
i = i + 1; //Start here. Copy 1 byte (total 1)
case 3:
dest[i] = src[i];
i = i + 1; //Copy 1 byte (total 2)
case 2:
dest[i] = src[i];
i = i + 1; //Copy 1 byte (total 3)
case 1:
dest[i] = src[i];
i = i + 1; //Copy 1 byte (total 4)
};
}
对于第二遍:
rounds = count / 8;
i = 0;
switch(count % 8)
{
case 0:
while( !(rounds == 0) )
{
// rounds is decremented by 1 as while
// loop works, now rounds=1
rounds = rounds - 1;
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 5)
case 7:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 6)
case 6:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 7)
case 5:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 8)
case 4:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 9)
case 3:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 10)
case 2:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 11)
case 1:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 12)
}
}
第三遍:
rounds = count / 8;
i = 0;
switch(count % 8)
{
case 0:
while( !(rounds == 0) )
{
//now while loop works
rounds = rounds - 1; //rounds is decremented by 1, now rounds=0
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 13)
case 7:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 14)
case 6:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 15)
case 5:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 16)
case 4:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 17)
case 3:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 18)
case 2:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 19)
case 1:
dest[i] = src[i];
i = i + 1; // Copy 1 byte (total 20)
};
}
参考:
维基百科
http://www.lysator.liu.se/c/duffs-device.html
想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。