6

perform:平均して、リテラル メッセージの送信よりもどれくらい遅くなると合理的に期待できますか? perform:Perl/Python プログラマーがeval("...")( Compiler evaluate:Smalltalk で) をループで呼び出さないようにという警告と同様に、ループでの送信を避けるべきですか?

私は主に Squeak に関心がありますが、他の Smalltalk にも関心があります。また、perform:with:バリアントではオーバーヘッドが大きくなりますか? ありがとうございました

4

2 に答える 2

9

#perform:のようではありませんeval()。(とにかく、パフォーマンスの面で)問題eval()は、実行時に送信するコードをコンパイルする必要があることです。これは非常に遅い操作です。#perform:一方、Smalltalkは、Rubysend()またはObjective-Cと同等ですperformSelector:(実際、これらの言語はどちらもSmalltalkに強く影響を受けています)。このような言語は、すでに名前に基づいてメソッドを検索しています#perform:。書き込み時ではなく、実行時に名前を指定するだけです。構文を解析したり、のようなものをコンパイルしたりする必要はありませんeval()

少し遅くなりますが(少なくとも1つの追加のメソッド呼び出しのコスト)、そうではありませんeval()。また、より多くの引数を持つバリアントは、単純なものと速度の違いを示すべきではありませんperform:whatever。Squeakについてこれほど多くの経験を具体的に話すことはできませんが、これが一般的にどのように機能するかです。

于 2009-05-07T23:57:05.223 に答える
3

これが私のマシンからのいくつかの数値です(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:」の大きな違いに注意してください。Evaluationは、コンパイラを呼び出して文字列を解析し、スローアウェイメソッド(バイトコード)を生成して実行し(最初の呼び出しで変更されます)、最後に破棄されます。コンパイラは実際には、主にIDEと外部ストリームからのfileInコードに使用されるように作成されています。エラー報告、警告メッセージなどのコードが含まれています。一般に、パフォーマンスが重要な場合、evalは必要なものではありません。

DellVostroからのタイミング。あなたのマイレージは変わるかもしれませんが、比率は変わりません。空のループ時間を測定して減算することにより、正味の実行時間を取得しようとしました。また、OS /ネットワーク/ディスク/電子メールなどの障害を排除するために、テストを10回実行し、最善の時間を費やしました。しかし、私は無負荷のマシンをあまり気にしませんでした。メジャーコードは次のとおりです(2回目のRepeat-argを上記のものに置き換えました):

callFoo2
    |t1 t2|

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

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

    Transcript showCR:t2-t1

編集:PS:言及するのを忘れました:これらはIDE内からの時間です(つまり、バイトコードで実行されます)。静的にコンパイルされたコード(stc-compilerを使用)は、レジスタ割り当てアルゴリズムが優れているため、これらの低レベルのマイクロベンチマークでは一般に少し高速(20〜30%)になります。

編集:先日、これらの数値を再現しようとしましたが、まったく異なる結果が得られました(単純な呼び出しでは8ns、実行では9ns)。したがって、これらのマイクロタイミングは第1レベルのキャッシュを完全に使い果たしてしまうため、十分に注意してください(空のメッセージでは、コンテキストセットアップが省略されたり、インライン化されたりします)。通常、これらは全体的なパフォーマンスをあまり表していないためです。

于 2010-08-25T12:34:52.830 に答える