📜  Duff的装置如何运作?

📅  最后修改于: 2021-05-30 07:57:33             🧑  作者: Mango

Duff的设备是一种技巧,它可以直接用C或C++表达循环展开,而无需额外的代码来处理剩余的部分循环。技巧是使用switch语句,其中除case以外的所有标签都位于while循环的中间。此外,所有情况都落到while循环的末尾。尽管有这种印象,但它使Duff的设备是合法的C和C++代码(但是,在Java无效)。

有什么用?

Duff的设备有两个关键方面。

  1. 首先,展开循环。通过避免检查循环是否完成并跳回到循环顶部所涉及的一些开销,我们可以提高速度。当执行直线代码而不是跳跃时,CPU可以运行得更快。但是,代码大小变大。
  2. 第二个方面是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基础课程》。