执行的性能开销:在Smalltalk(特别是Squeak)中

时间:2009-05-07 23:14:22

标签: smalltalk squeak

平均而言,我可以合理地期望perform:比文字信息发送慢多少?我是否应该避免在循环中发送perform:,类似于为Perl / Python程序员提供的警告,以避免在循环中调用eval("...")(Smalltalk中的Compiler evaluate:)?

我主要关注Squeak,但也对其他Smalltalks感兴趣。此外,perform:with:变体的开销是否更大?谢谢

2 个答案:

答案 0 :(得分:8)

#perform:eval()不同。 eval()(性能方面,无论如何)的问题是它必须编译你在运行时发送它的代码,这是一个非常慢的操作。另一方面,Smalltalk的#perform:相当于Ruby的send()或Objective-C的performSelector:(实际上,这两种语言都受到了Smalltalk的强烈启发)。这些语言已根据其名称查找方法 - #perform:只允许您在运行时指定名称而不是写入时间。它不必解析任何语法或编译eval()之类的任何内容。

它会慢一点(至少一个额外的方法调用的成本),但它不像eval()。此外,具有更多参数的变体不应该显示速度与普通perform:whatever之间的任何差异。我不能特别谈谈有关Squeak的那些经验,但这是它通常的工作方式。

答案 1 :(得分:2)

以下是我的机器上的一些数字(它是Smalltalk / X,但我猜这些数字是可比的 - 至少应该是比率):

被调用的方法“foo”和“foo:”是一个noops(即由^ self组成):

self foo                               ...  3.2 ns
self perform:#foo                      ...  3.3 ns
[self foo] value                       ... 12.5 ns (2 sends and 2 contexts)
[ ] value                              ...  3.1 ns (empty block)
Compiler valuate:('TestClass foo')     ...  1.15 ms

self foo:123                           ...  3.3 ns
self perform:#foo: with:123            ...  3.6 ns
[self foo:123] value                   ...   15 ns (2 sends and 2 contexts)
[self foo:arg] value:123               ...   23 ns (2 sends and 2 contexts)
Compiler valuate:('TestClass foo:123') ...  1.16 ms

注意“perform:”和“evaluate:”之间的巨大差异; evaluate正在调用编译器来解析字符串,生成一个抛弃方法(字节码),执行它(它在第一次调用时被jitted)并最终被丢弃。实际编写的编译器主要用于IDE和来自外部流的fileIn代码;它有错误报告,警告消息等的代码。 通常,当性能至关重要时,eval不是您想要的。

来自戴尔Vostro的计时;你的milage可能会有所不同,但比例不是。 我试图通过测量空循环时间和减去来获得净执行时间; 此外,我运行了10次测试,并采取了最佳时间,以消除操作系统/网络/磁盘/电子邮件或任何干扰。但是,我并不真正关心无负载的机器。 测量代码是(用上面的东西替换第二次重复的方法):

callFoo2
    |t1 t2|

    t1 :=
        TimeDuration toRun:[
            100000000 timesRepeat:[]
        ].

    t2 :=
        TimeDuration toRun:[
            100000000 timesRepeat:[self foo:123]
        ].

    Transcript showCR:t2-t1

编辑: PS:我忘了提到:这些是IDE内部的时间(即字节码执行)。由于更好的寄存器分配算法,静态编译的代码(使用stc编译器)在这些低级微基准测试中通常会快一些(20-30%)。

编辑:我前几天尝试重现这些数字,但得到了完全不同的结果(简单调用为8ns,执行为9ns)。所以要非常小心这些微时序,因为它们完全从第一级缓存中运行(空信息甚至省略了上下文设置,或者内联) - 它们通常不能很好地代表整体性能。