在Smalltalk的同一语句中分配和比较的效率

use*_*565 1 benchmarking smalltalk visualworks pharo gnu-smalltalk

一个以前的SO问题提出了有关这成语是在执行efficency方面更好的时间问题:

[ (var := exp) > 0 ] whileTrue: [ ... ]
Run Code Online (Sandbox Code Playgroud)

[ var := exp. 
  var > 0 ] whileTrue: [ ... ]
Run Code Online (Sandbox Code Playgroud)

直观地看,第一种形式似乎在执行期间更有效,因为它节省了一个额外的声明(第二种形式).在大多数Smalltalks中都是如此吗?

试着用两个愚蠢的基准测试:

| var acc |
var := 10000.
[ [ (var := var / 2) < 0  ] whileTrue: [ acc := acc + 1 ] ] bench.

| var acc |
var := 10000.
[ [ var := var / 2. var < 0  ] whileTrue: [ acc := acc + 1 ] ] bench
Run Code Online (Sandbox Code Playgroud)

显示两个版本之间没有重大差异.

还有其他意见吗?

Lea*_*lia 5

所以问题是:我应该用什么来实现更好的执行时间?

temp := <expression>.
temp > 0
Run Code Online (Sandbox Code Playgroud)

要么

(temp := <expression>) > 0
Run Code Online (Sandbox Code Playgroud)

在像这样的情况下,得出结论的最好方法是在抽象层次上迈出一步.换句话说,我们需要更好地了解幕后发生的事情.

a的可执行部分CompiledMethod由其字节码表示.当我们保存一个方法时,我们正在做的是编译成一系列低级指令,以便VM能够在每次调用时执行该方法.那么,让我们来看看上面每个案例的字节码.

由于<expression>在两种情况下都是相同的,所以让我们大大减少它以消除噪音.另外,我们把我们的代码的方法,以便有一个CompiledMethod一起玩

Object >> m
  | temp |
  temp := 1.
  temp > 0
Run Code Online (Sandbox Code Playgroud)

现在,让我们看看CompiledMethod它的超类是否有一些消息可以向我们展示字节码Object >> #m.选择器应包含子字节码,对吗?

...

在这里#symbolicBytecodes!现在让我们评估(Object >> #m) symbolicBytecodes得到:

pushConstant: 1
popIntoTemp: 0
pushTemp: 0
pushConstant: 0
send: >
pop
returnSelf
Run Code Online (Sandbox Code Playgroud)

请注意我们的temp变量如何Temp: 0在字节码语言中重命名为.

现在重复另一个并得到:

pushConstant: 1
storeIntoTemp: 0
pushConstant: 0
send: >
pop
returnSelf
Run Code Online (Sandbox Code Playgroud)

不同的是

popIntoTemp: 0
pushTemp: 0
Run Code Online (Sandbox Code Playgroud)

storeIntoTemp: 0
Run Code Online (Sandbox Code Playgroud)

这揭示了在两种情况下temp都以不同的方式从堆栈中读取.在第一种情况下,我们的结果从执行堆栈中<expression>弹出temp,然后temp再次按下以恢复堆栈.A pop后跟一个push相同的东西.在第二种情况下,相反,没有pushpop发生,temp只是从堆栈中读取.

所以结论是,在第一种情况下,我们将生成两个取消指令,pop然后是push.

这也解释了为什么差异如此难以衡量:pushpop指令有直接翻译成机器代码,CPU将执行它们的真快.

但请注意,没有什么能阻止编译器自动优化代码并实现其实pop + push相当于storeInto.通过这样的优化,Smalltalk片段将导致完全相同的机器代码.

现在,您应该能够决定您喜欢哪种形式.我认为这样的决定应该只考虑你更喜欢的编程风格.考虑到执行时间是无关紧要的,因为差异很小,并且可以通过实现我们刚才讨论的优化来轻松地减少到零.顺便说一下,对于那些愿意理解无与伦比的Smalltalk语言的低级领域的人来说,这将是一个很好的练习.

  • 在VisualWorks中,您的代码示例都编译为相同的字节代码,因此Leandro建议自动优化代码实际上已经在VisualWorks中进行了.因此,两个语句的反编译导致(var:= exp)> 0.我个人更喜欢将语句分开,因为它更容易理解.与这个基准测试问题没有直接关系,但是一般建议:请尝试避免使用Smalltalk中的while/for循环,与典型的迭代方法(如#do:,#debug:,#select)相比,它们通常难以理解:和#collect:. (2认同)