Ответ 1

Вы можете написать быстрый или медленный код на любом языке: -)

Основываясь на быстрой проверке кода Clojure, я бы сказал, что основная причина разницы в производительности заключается в том, что тестовый код Clojure еще не полностью оптимизирован для использования самых быстрых доступных языковых функций.

Например, следующие функции в Clojure являются очень крутыми и полезными для удобства разработки, но при этом возникают некоторые издержки выполнения:

  • Ленивые последовательности и списки
  • Динамическая совместимость с использованием отражения
  • Состав функции выполнения/функции первого класса
  • Мультиметоды/динамическая отправка
  • Динамическая компиляция с eval или REPL
  • Арифметика BigInteger

Если вам нужна абсолютная максимальная производительность (за счет некоторой дополнительной сложности), вы бы захотели переписать код, чтобы избежать этого, и использовать такие вещи, как:

  • Статический тип намека (во избежание отражения)
  • Перепады
  • Макросы (для манипуляции с временным кодом компиляции)
  • Протоколы
  • Java-примитивы и массивы
  • loop/recur для итерации

При разумном использовании вышеизложенного я обнаружил, что, как правило, можно очень быстро приблизиться к производительности Java в Clojure 1.2+, например. рассмотрите следующий код, чтобы сделать миллион дополнений:

Неоптимизированный Clojure, используя ленивую последовательность и арифметику большой очереди. Это приятно и функционально, но это не особенно быстро:

(reduce 
  (fn [acc val] (unchecked-int (unchecked-add (int acc) (int val)))) 
  (range 0 1000000))

=> "Elapsed time: 65.201243 msecs"

Оптимизированный Clojure с примитивной арифметикой и loop/recur:

(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.

Ответ 2

Clojure - динамический язык, он должен выполнять кучу дополнительной проверки времени выполнения.

Изменить, теперь в менее легкомысленном настроении: не глядя на фактические реализационные тесты Clojure, разницу в производительности можно, вероятно, также записать на то, что идиоматический код Clojure избегает изменчивого состояния, а другой при равных условиях алгоритмы, использующие изменчивое состояние, часто превосходят те, которые этого не делают.

Является ли данный программист способным писать код, который правильно использует изменчивое состояние, является другим вопросом, и, конечно, декларативный характер чисто функционального кода означает, что в теории возможно как можно больше оптимизаций.

Ответ 3

Часть этого из-за самого теста, а именно, что вы должны написать свой код (каким бы языком он ни был), чтобы выполнить те же самые шаги реализации, что и версия Java. О, у вас есть другой, возможно более быстрый способ сделать X на вашем языке? Слишком плохо.

Когда основой для теста являются петли и избиение массива, и общий императивный стиль, требующий нескольких плавающих блоков кода, вы получите бессмысленные числа.

Ответ 4

Написание некоторых более быстрых версий выглядит так, как будто это может быть весело...

Ответ 5

Многие из тестов медленнее, потому что до недавнего времени для получения хорошей производительности из Clojure потребовалось гораздо больше работы и экспертных знаний. Эти ориентиры ориентированы в основном на 1,2.

Clojure 1.3 имеет немало улучшений, которые значительно упрощают запись кода исполнения. Тем не менее, не так много людей в сообществе знакомы с улучшением производительности кода Clojure, поэтому это будет постепенный процесс, поскольку люди подают лучшие версии.

Ответ 6

Scala функции вводились обычно понемногу, и в большинстве случаев указывали соответствующий Java-код. Это преобразует Scala в эволюцию Java, которая учитывает баланс между новыми функциями и производительностью.

Clojure, с другой стороны, был разработан с совершенно другим подходом к программированию и имеет функции для взаимодействия с Java. Однако несоответствие импеданса между обоими языками (и JVM подготовлено для Java), может сделать Clojure немного медленнее. Для некоторых проблем Clojure может быть быстрее (например, с использованием неизменяемых коллекций), но, возможно, не для большинства приложений.