【问题标题】:What does this C code do [Duff's device]? [duplicate]这个 C 代码有什么作用[Duff 的设备]? [复制]
【发布时间】:2010-12-15 22:44:45
【问题描述】:
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);
    }
}

【问题讨论】:

  • 正如其他人所说,达夫的设备。如果我不需要,我不会实现这个,太深奥/混淆了。我更喜欢可读的代码 ;-) 虽然,如果包装在这样的函数中,具有良好的 cmets/documentation,如果我不必触摸它,我会使用它。
  • 在适当的情况下使用 Duff 的设备不会有太大问题。它被深入讨论,需要的最大评论将是一个充分讨论它的 URL。
  • 哇 - 有人真的很费心使用 Duff 的设备进行优化,但没有将模和除法转换为移位???
  • do {} while() 语法不正确吗?
  • @Aaron: Any 体面的编译器会优化模数和除法,如果 r.h.s.是在编译时已知的 2 的幂。另一方面,手动循环展开仍然是偶尔有用的优化。

标签: c++


【解决方案1】:

switch 语句和 while 循环的这种混合称为“Duff 的设备”。这是一种展开循环的方法,这是早期经常使用的优化。

所以这段代码仍然将内存内容从一个地方复制到另一个地方,但它可能更有效。请注意,在当今的架构上,您应该始终衡量这一点,因为缓存局部性和令人眼花缭乱的 CPU 循环展开通常是一个坏主意。

【讨论】:

    【解决方案2】:

    Duff's device

    computer science 中,Duff 的设备 是一个串行副本的optimized implementation,它使用了在assembly language 中广泛应用于loop unwinding 的技术。它的发现归功于 1983 年 11 月的 Tom Duff,当时他为 Lucasfilm 工作。这可能是迄今为止C programming languagecase label fall-through 最引人注目的用法。 Duff 并没有声称发现 loop unrolling 的概念,只是在 C 中这种特殊的表达方式。

    【讨论】:

      【解决方案3】:

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

      for(int i=0;i<n;i++)
      {
        *to++=*from++;
      }
      

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

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

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

      等等。在 %8 个副本的第一次迭代之后,每次迭代恰好发生 8 个副本。

      通过以这种方式展开循环,循环开销显着减少。请务必注意 case 值 (0,7,6,5,4,3,2,1) 的顺序,这有助于编译器将其转换为跳转表。

      更新

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

      【讨论】:

      • 您的解释似乎有些不清楚。如果我没记错的话,该开关仅用于确定在第一次迭代期间要执行多少展开分配(以补偿缓冲区长度可能无法被 8 整除)。
      【解决方案4】:

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

      由于这里的大多数答案似乎总体上是肯定的,所以我要公开反对它。

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

      其他人引用的维基百科文章甚至说,当从 Xfree86 源代码中删除这种“模式”时,性能实际上得到了改善。

      这种结果是盲目手动优化您碰巧认为可能需要它的任何代码的典型结果。它会阻止编译器正常工作,使您的代码可读性降低并且更容易出现错误,并且通常会减慢它的速度。如果你一开始就以正确的方式做事,即编写简单的代码,然后分析瓶颈,然后进行优化,你甚至不会想到使用这样的东西。无论如何,现代 CPU 和编译器都不是。

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

      【讨论】:

        猜你喜欢
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-27
        • 2011-12-31
        • 1970-01-01
        相关资源
        最近更新 更多