【问题标题】:Duff device in PHP not possible?无法使用 PHP 中的 Duff 设备?
【发布时间】:2011-11-12 14:13:36
【问题描述】:

有人告诉我,duff 设备不适用于 PHP,因为 switch 和 case 构造的工作方式不同。我在 php.net 上找到了这个 duff devive,我的问题是这个设备有什么问题?还是我不了解 duff 设备?在我的汇编器中,我可以用一个简单的命令展开一个循环,当它编译时,我得到一个展开的循环。

<?php
$n = $ITERATIONS % 8;
while ($n--) $val++;
$n = (int)($ITERATIONS / 8);
while ($n--) {
   $val++;
   $val++;
   $val++;
   $val++;
   $val++;
   $val++;
   $val++;
   $val++;
}
?>

【问题讨论】:

  • 您的问题与您的神秘switch 构造有何关系?我在您的代码中没有看到任何 switch...

标签: php micro-optimization duffs-device


【解决方案1】:

那不是达夫装置。它使用特殊的预循环对齐步骤(这正是 Duff 的设备旨在避免的)。

在真正的 Duff 设备中,有一段展开的代码最初被 switch 部分跳过。这个技巧减少了所需的代码量(仅循环)并减少了代码中的条件跳转次数。

您提供的代码只是一个手动展开的循环。


循环展开:

循环展开是一种优化技术,其中一次处理循环的多个迭代。所以而不是:

$number_of_iterations = 128;
for ($n = 0; $n !== $number_of_iterations; ++$n) {
    do_something();
}

你使用:

$number_of_iterations = 128;
for ($n = 0; $n !== (int)($number_of_iterations / 4); ++$n) {
    //Repeat do_something() four times.
    //Four is the "unrolling factor".
    do_something();
    do_something();
    do_something();
    do_something();
}

这样做的好处是速度。条件分支通常是一个相对昂贵的操作。与展开循环相比,第一个循环将通过条件分支 4 次。

不幸的是,这种方法有些问题。假设$number_of_iterations 不能被四整除——将劳动分工成更大的块将不再有效。对此的传统解决方案是有另一个循环,它以较小的块执行工作,直到剩余的工作量可以由展开的循环执行:

$number_of_iterations = 130;
//Reduce the required number of iterations
//down to a value that is divisible by 4
while ($number_of_iterations % 4 !== 0) {
    do_something();
    --$number_of_iterations
}
//Now perform the rest of the iterations in an optimised (unrolled) loop.
for ($n = 0; $n !== (int)($number_of_iterations / 4); ++$n) {
    do_something();
    do_something();
    do_something();
    do_something();
}

这样更好,但初始循环仍然不必要地低效。它再次在每次迭代中分支 - 这是一个昂贵的提议。在php 中,这是你能得到的最好的(??)。

现在进入达夫的设备。

达夫装置:

不是在进入有效展开区域之前执行紧密循环,另一种选择是直接进入展开区域,但最初会跳转到循环的中途。这叫做达夫装置。

我现在将语言切换为C,但代码的结构将保持非常相似:

//Note that number_of_iterations
//must be greater than 0 for the following code to work
int number_of_iterations = 130;
//Integer division truncates fractional parts
//counter will have the value which corresponds to the
//number of times that the body of the `do-while`
//will be entered.
int counter = (number_of_iterations + 3) / 4;
switch (number_of_iterations % 4) {
    case 0: do { do_something();
    case 3:      do_something();
    case 2:      do_something();
    case 1:      do_something();
            while (--counter > 0)
}

之前while ($number_of_iterations % 4 !== 0) 中的所有条件分支都已替换为单个计算跳转(来自开关)。


整个分析基于有缺陷的概念,即减少代码区域中的条件分支的数量总是会显着提高性能,而编译器将无法执行这些分类在适当的情况下自行进行微优化。在现代代码中应该避免手动循环展开和 Duff 的设备。

【讨论】:

  • 什么是真的?什么是“简单的手动展开循环”?
【解决方案2】:

您的代码实际上并不是 Duff 的设备。一个合适的 DD 在 switch 语句中会有一个 交错 的 while 或 do/while。

DD 的重点是删除这段代码:

$n = $ITERATIONS % 8;
while ($n--) $val++;

Duff Device 的第一步像 GOTO 一样处理到代码中:

send(to, from, count)
register short *to, *from;
register count;
{
        register 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);
        }
}

假设 count % 8 结果是 5。这意味着 switch 跳转到案例 5,然后一直下降到 while 结束,此时它开始以 8 为增量执行工作。

【讨论】:

  • 你能正确定义吗?我读过stackoverflow.com/questions/514118/how-does-duffs-device-work。 duff 设备使用开关吗?
  • 更新了答案。它不是没有 switch 语句的 Duff 设备,你只能在 C 中这样做,因为 PHP 语法规则不允许这样做。
  • 但结果是一样的,我的设备循环速度比简单的 for 循环快吗?我不明白这种命名的区别?这个有专利吗?此外,您可以说嵌套而不是交错,它具有相同的含义。
  • 它没有专利,只是它的名字。如果没有交错的 switch 语句,它只是一个展开的循环。 (见另一个答案。)
  • 很老的答案;但对于任何阅读的人 - @Phpdna Interlaced 并不意味着嵌套,请更仔细地查看代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
  • 2012-12-28
  • 1970-01-01
  • 2016-08-29
  • 2011-01-27
相关资源
最近更新 更多