Потеря производительности при исполнении: в Smalltalk (особенно в Squeak) - PullRequest
5 голосов
/ 08 мая 2009

Насколько медленнее можно ожидать, что perform: будет в среднем медленнее, чем отправка буквального сообщения? Следует ли мне избегать посылки perform: в цикле, аналогично предостережению, данному программистам Perl / Python, избегать вызова eval("...") (Compiler evaluate: в Smalltalk) в цикле?

Меня интересует в основном Squeak, но меня интересуют и другие Smalltalks. Кроме того, увеличиваются ли издержки с вариантами perform:with:? Спасибо

Ответы [ 2 ]

8 голосов
/ 08 мая 2009

#perform: не похоже на eval(). Проблема с eval() (в любом случае с точки зрения производительности) заключается в том, что он должен скомпилировать код, который вы отправляете во время выполнения, что является очень медленной операцией. Smalltalk #perform:, с другой стороны, эквивалентен Ruby's send() или Objective-C performSelector: (на самом деле оба этих языка были сильно вдохновлены Smalltalk). Такие языки уже ищут методы на основе их имени - #perform: просто позволяет вам указать имя во время выполнения, а не во время записи. Он не должен анализировать синтаксис или компилировать что-либо вроде eval().

Это будет немного медленнее (стоимость как минимум одного дополнительного вызова метода), но это не похоже на eval(). Кроме того, варианты с большим количеством аргументов не должны показывать никакой разницы в скорости по сравнению с простым perform:whatever. Я не могу говорить с таким большим опытом конкретно о Squeak, но именно так он обычно работает.

2 голосов
/ 25 августа 2010

Вот некоторые цифры с моей машины (это 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

Обратите внимание на большую разницу между «выполнить:» и «оценить:»; Оценка вызывает компилятор, чтобы проанализировать строку, сгенерировать одноразовый метод (байт-код), выполнить его (он соединяется при первом вызове) и, наконец, отбросить. Компилятор на самом деле написан для использования в основном для IDE и для fileIn-кода из внешних потоков; у него есть код для сообщения об ошибках, предупреждающих сообщений и т. д. В общем случае eval - это не то, что вам нужно, когда производительность критична.

Сроки от Dell Vostro; Ваш пробег может варьироваться, но соотношение нет. Я пытался получить чистое время выполнения, измеряя время пустого цикла и вычитая; Кроме того, я выполнил тесты 10 раз и взял лучшее время, чтобы устранить ОС / сеть / диск / электронную почту или любые другие проблемы. Тем не менее, я не очень заботился о машине без нагрузки. Код меры был (заменил второй timesRepeat-arg на вышеприведенный материал):

callFoo2
    |t1 t2|

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

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

    Transcript showCR:t2-t1

EDIT: PS: я забыл упомянуть: это время из среды IDE (то есть выполнение с байтовым кодом). Статически скомпилированный код (с использованием stc-компилятора) будет обычно немного быстрее (20-30%) в этих микроуровнях низкого уровня из-за лучшего алгоритма распределения регистров.

РЕДАКТИРОВАТЬ: я пытался воспроизвести эти числа на днях, но получил совершенно другие результаты (8 нс для простого вызова, но 9 нс для выполнения). Поэтому будьте очень осторожны с этими микро-таймингами, так как они полностью исчерпывают кэш первого уровня (а пустые сообщения даже пропускают настройку контекста или становятся встроенными) - они обычно не очень отражают общую производительность.

...