Jeffrey Cross
Jeffrey Cross

Duff的设备:循环展开解释语言

1983年,Tom Duff发明了一种非常奇怪的方法,使用C语言的switch和case语句来代码“展开”大循环的优化。作为示例,他描述了一个将数组复制到输出寄存器的简单循环:

寄送(来,从,计数)寄存器短*到*,从;登记计数; {do * to = * from ++;而( - 计数> 0); }

在循环的每次迭代中,除了正在执行的副本之外,count变量也会递减,并且会对0进行比较.Duff的Device允许您实现相同的结果,但只有8的开销:

寄送(来,从,计数)寄存器短*到*,从;登记计数; {register n =(count + 7)/ 8; switch(count%8){case 0:do {* to = * from ++;案例7:* to = * from ++;案例6:* to = * from ++;案例5:* to = * from ++;案例4:* to = * from ++;案例3:* to = * from ++;案例2:* to = * from ++;案例1:* to = * from ++; }而( - N> 0); }}

会发生的是循环展开8次,因此循环的每次迭代都会运行内部代码8次而不进行比较。 Duff设备的天才之处在于它利用了C开关/外壳结构的工作方式。第一次通过,如果迭代没有均匀地除以8,则循环代码被执行足够的次数以等于迭代的剩余部分/ 8。这有点奇怪,因为“do”语句出现在switch中,但“do”中有“case”语句。不过,它是有效的C.

在有人哭泣之前,请记住,当没有合适的,更好的算法可用时,这只适合加速内环的性能。如果您编写C代码,大多数现代编译器都足够聪明,可以自动优化代码并为您重新编译循环。

但是,对于像PHP或Javascript这样的解释语言,如果你想要挤出一些额外的性能,你有时需要自己做一些优化。幸运的是,这两种语言都有c风格的switch语句。

Andy King写了一个关于这个循环展开算法的略微改动的版本,该算法抛弃了switch语句并将正常的Duff设备分成两个独立的循环,一个用于剩余的循环,一个用于展开。在Javascript中,它仅在正常for循环的三分之一时间内执行简单的加法循环(testVal ++是正常循环的内部):

函数duffFasterLoop8(迭代){var testVal = 0; var n = iterations%8; if(n> 0){do {testVal ++; } while(--n); // n必须大于0} n = parseInt(iterations / 8);做{testVal ++;名为testVal ++;名为testVal ++;名为testVal ++;名为testVal ++;名为testVal ++;名为testVal ++;名为testVal ++; } while(--n); }

它不像Duff的设备那样语法聪明,但它是手动展开内循环并获得最佳性能的好方法。

Duff的设备Andy King为执行速度优化JavaScript

分享

发表评论