Вы можете написать быстрый или медленный код на любом языке: -)
Основываясь на быстрой проверке некоторых кодов Clojure, я бы сказал, что основная причина различий в производительности заключается в том, что тестовый код Clojure еще не был полностью оптимизирован для использования самых быстрых доступных языковых функций.
Например, следующие функции в Clojure очень крутые и полезные для удобства разработки, но приводят к некоторому снижению производительности во время выполнения:
- Ленивые последовательности и списки
- Динамическая совместимость Java с использованием отражения
- Композиция функций времени выполнения / функции первого класса
- Мультиметоды / динамическая отправка
- Динамическая компиляция с eval или на REPL
- BigInteger арифметика
Если вам нужна абсолютная максимальная производительность (за счет некоторой дополнительной сложности), вам следует переписать код, чтобы избежать этого, и использовать такие вещи, как:
- Статический тип хинтинга (чтобы избежать отражения)
- 1026 * Переходные *
- Макросы (для манипулирования кодом времени компиляции)
- Протоколы
- Java-примитивы и массивы
- цикл / повтор для итерации
При разумном использовании вышесказанного я обнаружил, что, как правило, в Clojure 1.2+ можно очень близко приблизиться к производительности Java, например. рассмотрим следующий код, чтобы сделать миллион дополнений:
Неоптимизированное Clojure с использованием ленивой последовательности и арифметики бигинтергеров. Это красиво и функционально, но не очень быстро:
(reduce
(fn [acc val] (unchecked-int (unchecked-add (int acc) (int val))))
(range 0 1000000))
=> "Elapsed time: 65.201243 msecs"
Оптимизированное Clojure с примитивной арифметикой и циклом / рекурсом:
(loop [acc (int 0) i (int 0)]
(if (>= i (int 1000000))
acc
(recur (unchecked-add acc i) (unchecked-inc i)) ))
=> "Elapsed time: 0.691474 msecs"
Java код, довольно стандартный итерационный цикл:
public static int addMillion() {
int result=0;
for (int i=0; i<1000000; i++) {
result+=i;
}
return result;
}
=> "Elapsed time: 0.692081 msecs"
p.s. Я использовал unchecked-add вместо + в коде Clojure, чтобы он соответствовал поведению целочисленного переполнения Java.