如何编译Duff的设备代码?

Ben*_*min 6 c c++ switch-statement duffs-device

我理解为什么Duff的设备比普通的循环代码更快,可以展开但没有优化.但我无法理解代码是如何编译的.
我想这是关于切换语法的一个技巧.但不是了.

怎样才能做到,而句子中存在开关句子?很奇怪.
有没有人可以解释这个?

编辑: 另一个问题.为什么duff使用8?它可能是16,65536或其他什么.因为代码大小?还有其他原因吗?例如,缓存或流水线操作的好处.

Ste*_*314 11

"它的工作原理"很简单.

C和C++都是编译语言,通常编译为平台机器代码.机器代码没有块结构的概念 - 所有块结构必须转换为使用(实际上)某些无条件和条件gotos混合的形式.

C语法规则允许switch语句和循环以不是真正的分层块结构的方式组合,但是纠缠控制流.只要编译器可以处理这个(任何好的编译器都应该),底层机器代码就没有问题.结果将是"spaghetti",但生成的机器代码通过优化器总是意大利面 - 它不是人类可读的,所以它不是问题.这里的问题是源代码也是spaghetti,即使gotos已被"隐藏".

注意 - 尽管任何好的编译器都应该处理Duffs设备,正如其他人已经评论过的那样,但这并不意味着它能够很好地应对以正确地优化它 - 只能很好地生成正确的可执行代码.这是曾经有目的的一些古老奇怪的习语,但现在更有可能混淆你的编译器并破坏它生成有效代码的能力.

编辑

以下是与Duffs设备有关,可能有助于说明基本思路......

switch (count & 1)
{
  case 0 : goto lbl0;
  case 1 : goto lbl1;
}

lbl0:

while (count != 0)
{
  handle_one ();
  count--;
lbl1:
  handle_one ();
  count--;
}
Run Code Online (Sandbox Code Playgroud)

在循环中包含case子句在概念上与在循环中具有goto-target标签没有什么不同,如上所述.

警告 - 这纯粹是为了说明一个想法,不应该复制到现实代码中.


Mic*_*urr 6

Duff的Device编译原因的简单解释是switch语句的语法对switch语句块可能需要采用的形式并不特别具体.有一些限制,受控语句中允许的一些限制是在switch(casedefault标签)之外不允许的.但除此之外,受控声明只是任何其他声明,可能存在switch目标标签.

这是C99的语法:

switch ( expression ) statement
Run Code Online (Sandbox Code Playgroud)

除了语法之外,标准还强加了一些约束:

  • 控制表达式必须具有整数类型
  • 关于VLA在switch语句中可能出现的位置存在限制
  • case label表达式必须是整型常量表达式
  • 不能有重复的case标签表达或default标签

除此之外,语句块中允许的任何构造都应该被允许在受控语句中(添加casedefault标签都可以).请记住,case并且default只是标签交换机跳到基于控制表达式和case标签表达式. 正如Potatoswatter所说,switch只是一个计算goto.所以就像goto跳进循环的中间一样,也可以switch.

此外,我认为你可能会看到Duff设备的好处的情况今天非常罕见(我认为它们甚至在1980年代也很少见).不要忘记Tom Duff自己在他的描述中说了以下内容:

  • "恶心,不是吗?"
  • "在这一发现中,我感到自豪和反感的结合."

甚至比最初描述时更多,Duff的设备应该被认为是一种好奇而不是使用的工具.