void Send(int * to, const int* from, const int count)
{
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);
}
}
答案 0 :(得分:17)
这是Duff's Device用于复制内存缓冲区。
答案 1 :(得分:16)
这是Duff's Device。这是一种展开循环的方法,避免了必须添加辅助修复循环来处理循环迭代次数不知道是展开因子的精确倍数的时间。
由于这里的大多数答案似乎总体上都是积极的,所以我会反对它。
使用此代码,编译器将很难将任何优化应用于循环体。如果您只是将代码编写为一个简单的循环,现代编译器应该能够为您处理展开。这样,您可以保持可读性和性能,并希望将其他优化应用于循环体。
其他人引用的维基百科文章甚至说,当这个“模式”从Xfree86源代码中删除时,性能实际上得到了提升。
这种结果通常是盲目地优化您认为可能需要它的任何代码。它可以防止编译器正常工作,使代码可读性降低,更容易出错并且通常会降低编码速度。如果你首先以正确的方式做事,即编写简单的代码,然后分析瓶颈,然后进行优化,你甚至不会想到使用这样的东西。无论如何,不是使用现代CPU和编译器。
理解它很好,但是如果你真的使用它我会感到惊讶。
答案 2 :(得分:7)
将switch语句和while循环混合称为“Duff的设备”。这是一种展开循环的方法,这是以前经常使用的优化。
因此,此代码仍将内存内容从一个位置复制到另一个位置,但它可能更有效。当心,在今天的架构上你应该总是测量它,因为有了缓存局部性和快速的CPU循环展开通常是一个坏主意。
答案 3 :(得分:5)
这在功能上与下面的代码完全相同:
for(int i=0;i<n;i++)
{
*to++=*from++;
}
不同之处在于您的代码展开循环,因此复制的每8个整数只需要1次循环迭代。由于任何个案都没有中断,因此从每个案例标签到下一个案件标签都会执行。
当计数%8 == 0时,在第一次迭代的循环内执行8个副本
当计数%8 == 7时,第一次迭代执行 7个副本
等等。在使用%8副本进行第一次迭代后,每次迭代恰好会发生8个副本。
通过以这种方式展开循环,循环开销显着降低。重要的是要注意案例值(0,7,6,5,4,3,2,1)的顺序,它们有助于编译器将其转换为跳转表。
<强>更新强>
OP发布的示例代码的一个问题是计数值为0将导致发生8个副本,可能导致缓冲区溢出。
答案 4 :(得分:4)
在computer science中, Duff的设备是一个optimized implementation的串行副本,它使用assembly language广泛应用于{{{}}的技术3}}。它的发现于1983年11月归功于loop unwinding,当时正在为Tom Duff工作。这可能是Lucasfilm迄今为止case label fall-through中最引人注目的用法。 Duff并没有因为发现C programming language的概念而声称有用,只是在C中它的这个特殊表达。
答案 5 :(得分:3)