Почему функции в ядре работают быстрее, если они не связаны локально?

В этом примере говорится все:

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, которая имеет эту информацию, и, следовательно, компилятор не может встроить его.