所以问题是:我应该使用什么来获得更好的执行时间?
temp := <expression>.
temp > 0
或者
(temp := <expression>) > 0
在这种情况下,得出结论的最佳方法是在抽象级别上降低一步。换句话说,我们需要更好地了解幕后发生的事情。
a 的可执行部分CompiledMethod
由它的字节码表示。当我们保存一个方法时,我们所做的就是将它编译成一系列低级指令,以便 VM 能够在每次调用它时执行该方法。那么,让我们来看看上面每种情况的字节码。
由于<expression>
在两种情况下都相同,因此让我们大幅降低它以消除噪音。另外,让我们将代码放在一个方法中,以便CompiledMethod
使用
Object >> m
| temp |
temp := 1.
temp > 0
现在,让我们看看CompiledMethod
它的超类中的一些消息,这些消息将向我们展示Object >> #m
. 选择器应该包含子字字节码,对吧?
...
在这里#symbolicBytecodes
!现在让我们评估(Object >> #m) symbolicBytecodes
得到:
pushConstant: 1
popIntoTemp: 0
pushTemp: 0
pushConstant: 0
send: >
pop
returnSelf
注意我们的temp
变量是如何Temp: 0
在字节码语言中重命名的。
现在重复另一个并得到:
pushConstant: 1
storeIntoTemp: 0
pushConstant: 0
send: >
pop
returnSelf
不同的是
popIntoTemp: 0
pushTemp: 0
相对
storeIntoTemp: 0
这表明,在这两种情况下,都temp
以不同的方式从堆栈中读取。在第一种情况下,我们的结果从执行堆栈<expression>
中弹出temp
,然后temp
再次压入以恢复堆栈。Apop
后面跟着一个push
相同的东西。相反,在第二种情况下,没有push
或pop
发生,temp
只是从堆栈中读取。
所以结论是,在第一种情况下,我们将生成两个取消指令pop
,然后是push
.
这也解释了为什么这种差异如此难以衡量:指令可以直接翻译成机器代码,CPU 会非常快地执行它们push
。pop
但是请注意,没有什么能阻止编译器自动优化代码并意识到实际上pop + push
等价于storeInto
. 通过这样的优化,两个 Smalltalk 片段将产生完全相同的机器代码。
现在,您应该能够决定您喜欢哪种形式。我认为这样的决定应该只考虑你更喜欢的编程风格。考虑到执行时间是无关紧要的,因为差异很小,并且可以通过实施我们刚刚讨论的优化轻松地将其减少到零。顺便说一句,对于那些愿意了解无与伦比的 Smalltalk 语言的低级领域的人来说,这将是一个很好的练习。