Почему функции в ядре работают быстрее, если они не связаны локально?
В этом примере говорится все:
user> (time (dotimes [i 10000000] (inc i)))
"Elapsed time: 413.948711 msecs"
nil
user> (time (let [new-inc inc] (dotimes [i 10000000] (new-inc i))))
"Elapsed time: 1034.722729 msecs"
nil
Ответы
Ответ 1
Я считаю, что компилятор строит некоторые основные функции, такие как inc
, особенно применительно к примитивным аргументам.
Когда вы используете inc
как обычную функцию (например, переходя к функциям более высокого порядка, сглаживание с помощью let
и т.д.), производительность может быть хуже, потому что она теряет способность встроить. Дополнительные накладные расходы связаны с выполнением дополнительного вызова функции, возможно, также стоимость бокса по одному или нескольким аргументам.
Это не ограничение Clojure, просто отражает тот факт, что компилятор еще не очень изощрен с его оптимизацией. Вероятно, вы, вероятно, ожидаете, что в будущих версиях Clojure ситуация будет намного лучше.
Ответ 2
Просто добавьте к тому, что mikera упомянула о inlining. inc
- это var, привязанный к функции. Если вы посмотрите на meta
в var inc
i.e (meta #'inc)
, вы обнаружите, что у него есть ключ :inliner
, значение которого компилятор может использовать для встраивания кода функции, где он вызывается с помощью inc var. Когда вы используете let
для привязки локально, вы просто привязываете объект функции к новому имени в локальной области видимости, а объект функции не имеет никакой вложенной информации, это была inc
var, которая имеет эту информацию, и, следовательно, компилятор не может встроить его.