Duff的设备是一个技巧,它可以直接在C或C++中循环展开循环,而不需要额外的代码来处理剩余的部分循环。诀窍是使用一个开关语句,其中所有的情况下只有一个标签是在中间的while循环。此外,所有案例都会在while循环结束时结束。尽管有印象,它使Duff的设备是合法的C和C++代码(但是,它在java中无效)。
它有什么用处?
达夫的设备有两个关键点。
- 首先,循环展开。通过避免检查循环是否完成以及跳回循环顶部所涉及的一些开销,我们可以获得更快的速度。当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<stdio.h> #include<string.h> #include <stdlib.h> // 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<size; i++) src[i] = rand ()%2; copyBoolArray(src, dest, size); for (i=0; i<size ; i++) printf ( "%d %d" , src[i], dest[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数组复制到目标数组。它将运行的过程数也取决于源阵列的大小。
对于第一个过程,执行从计算出的case标签开始,然后进入每个连续的赋值语句,就像任何其他switch语句一样。在最后一个case标签之后,执行到达循环的底部,然后跳回到顶部。循环的顶部在switch语句中,因此不再重新计算开关。
原始循环被展开八次,因此迭代次数除以八。如果要复制的字节数不是8的倍数,那么还有一些字节剩余。大多数一次复制字节块的算法将在最后处理剩余字节,但达夫的设备在开始时处理它们。该函数计算switch语句的count%8,以计算剩余的字节数,跳转到大小写标签中的字节数,并复制它们。然后,循环继续在左边的过程中复制八个字节的组。
第一关:
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
本文由 尼希尔·库马尔·夏尔马 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。