【发布时间】:2015-03-18 15:10:24
【问题描述】:
考虑Juicer类中的以下方法:
Juicer >> juiceOf: aString
| fruit juice |
fruit := self gather: aString.
juice := self extractJuiceFrom: fruit.
^juice withoutSeeds
它生成以下字节码
25 self ; 1
26 pushTemp: 0 ; 2
27 send: gather:
28 popIntoTemp: 1 ; 3
29 self ; 4
30 pushTemp: 1 ; 5
31 send: extractJuiceFrom:
32 popIntoTemp: 2 ; 6 <-
33 pushTemp: 2 ; 7 <-
34 send: withoutSeeds
35 returnTop
现在注意 32 和 33 抵消了:
25 self ; 1
26 pushTemp: 0 ; 2
27 send: gather:
28 popIntoTemp: 1 ; 3 *
29 self ; 4 *
30 pushTemp: 1 ; 5 *
31 send: extractJuiceFrom:
32 storeIntoTemp: 2 ; 6 <-
33 send: withoutSeeds
34 returnTop
接下来考虑 28、29 和 30。它们在 gather 的结果下方插入 self。通过在发送第一条消息之前推送self 可以实现相同的堆栈配置:
25 self ; 1 <-
26 self ; 2
27 pushTemp: 0 ; 3
28 send: gather:
29 popIntoTemp: 1 ; 4 <-
30 pushTemp: 1 ; 5 <-
31 send: extractJuiceFrom:
32 storeIntoTemp: 2 ; 6
33 send: withoutSeeds
34 returnTop
现在取消 29 和 30
25 self ; 1
26 self ; 2
27 pushTemp: 0 ; 3
28 send: gather:
29 storeIntoTemp: 1 ; 4 <-
30 send: extractJuiceFrom:
31 storeIntoTemp: 2 ; 5
32 send: withoutSeeds
33 returnTop
临时文件 1 和 2 已写入但未读取。所以,除了调试时,可以跳过它们导致:
25 self ; 1
26 self ; 2
27 pushTemp: 0 ; 3
28 send: gather:
29 send: extractJuiceFrom:
30 send: withoutSeeds
31 returnTop
最后一个版本,它节省了 7 个堆栈操作中的 4 个,对应于表达力和清晰的源代码:
Juicer >> juiceOf: aString
^(self extractJuiceFrom: (self gather: aString)) withoutSeeds
还要注意,Pharo(我没有检查过 Squeak)还没有实现其他可能的优化(例如,跳转链)。这些优化将鼓励 Smalltalk 程序员更好地表达他们的意图,而无需支付成本额外的计算。
我的问题是这些改进是否是一种幻觉。具体来说,Pharo/Squeak 中缺少字节码优化是因为它们被认为没有什么相关性,还是它们被认为是有益的但尚未解决?
编辑
使用寄存器+堆栈架构的一个有趣优势 [cf. A Smalltalk Virtual Machine Architectural Model by Allen Wirfs-Brock 和 Pat Caudill] 是寄存器提供的额外空间使得为了优化而对字节码的操作更容易。当然,即使这些类型的优化不像方法内联或多态内联缓存那样相关,正如下面的答案所指出的那样,它们也不应该被忽视,特别是当与 JIT 编译器实现的其他优化结合使用时。另一个需要分析的有趣话题是 破坏性 优化(即需要取消优化以支持调试器的优化)是否真的有必要,或者 非破坏性 是否可以获得足够的性能提升em> 技术。
【问题讨论】: