这个C代码做什么[Duff的设备]?

Mai*_*nID 10 c++

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ner 18

这是Duff的用于复制内存缓冲区的设备.

  • 链接答案很糟糕! (6认同)
  • @Seth为什么链接答案不好?我之前从未听说过Duff的设备,并且在最常见的答案中找到了维基百科文章的链接. (6认同)
  • 链接的内容会随着时间的推移而消失.可能不是维基百科的一个大问题,但通常最好将相关的片段复制到你的答案中,并添加链接作为参考. (5认同)

Pet*_*ham 17

这是Duff的设备.这是一种展开循环的方法,避免了必须添加辅助修复循环来处理循环迭代次数不知道是展开因子的精确倍数的时间.

由于这里的大多数答案似乎总体上是积极的,我会反对它.

使用此代码,编译器将很难将任何优化应用于循环体.如果您只是将代码编写为一个简单的循环,那么现代编译器应该能够为您处理展开.这样,您可以保持可读性和性能,并希望将其他优化应用于循环体.

其他人引用的维基百科文章甚至说,当这个"模式"从Xfree86源代码中删除时,性能实际上得到了提升.

这种结果通常是盲目地优化您认为可能需要它的任何代码.它可以防止编译器正常工作,使代码可读性降低,更容易出错并且通常会降低编码速度.如果你在第一时间以正确的方式做事,即编写简单的代码,然后分析瓶颈,然后进行优化,你甚至不会想到使用这样的东西.无论如何,不​​是使用现代CPU和编译器.

理解它很好,但如果你真的使用它我会感到惊讶.


Ste*_*fen 7

这种交换语句和while循环的混合被称为"Duff的设备".这是一种展开循环的方法,这是以前经常使用的优化.

因此,此代码仍然将内存内容从一个位置复制到另一个位置,但它可能更有效.请注意,在今天的架构上,你应该总是测量它,因为有了缓存局部性和快速的CPU循环展开通常是一个坏主意.


3Da*_*ave 5

这在功能上与下面的代码相同:

for(int i=0;i<n;i++)
{
  *to++=*from++;
}
Run Code Online (Sandbox Code Playgroud)

不同之处在于您的代码展开循环,因此复制的每8个整数只需要1次循环迭代.由于任何情况都没有中断,因此从每个案例标签到下一个案例标签都会执行.

当count%8 == 0时,在第一次迭代的循环内执行8个副本

当count%8 == 7时,第一次迭代执行7个副本

等等.在使用%8副本进行第一次迭代后,每次迭代恰好会发生8个副本.

通过以这种方式展开循环,循环开销显着降低.重要的是要注意案例值(0,7,6,5,4,3,2,1)的顺序,它们有助于编译器将其转换为跳转表.

更新

OP发布的示例代码的问题是计数值为0将导致发生8个副本,可能导致缓冲区溢出.

  • 你的解释似乎有点不清楚.如果我没有弄错,那么该开关仅用于确定在第一次迭代期间要执行多少展开的分配(以补偿缓冲区长度可能无法被8整除). (2认同)