Почему программа Clojure Hello World работает так медленно по сравнению с Java и Python?

Обновление

Как было предложено многими людьми, похоже, что это произошло из-за того, что код clojure был сначала скомпилирован, а затем выполнен. Компиляция AOT должна помочь компенсировать это. Учитывая, что практический процесс компиляции clojure AOT немного затруднился (проблемы с classpath, проблемы с каталогом и др.), Я написал небольшой пошаговый процесс здесь, если кто-то заинтересован.


Привет всем,

Я читаю "Программирование Clojure", и я сравнивал некоторые языки, которые я использую для некоторого простого кода. Я заметил, что реализации clojure были самыми медленными в каждом случае. Например,

Python - hello.py

def hello_world(name):
  print "Hello, %s" % name

hello_world("world")

и результат,

$ time python hello.py
Hello, world

real    0m0.027s
user    0m0.013s
sys 0m0.014s

Java - hello.java

import java.io.*;

public class hello {
  public static void hello_world(String name) {
    System.out.println("Hello, " + name);
  }

  public static void main(String[] args) {
    hello_world("world");
  }
}

и результат,

$ time java hello
Hello, world

real    0m0.324s
user    0m0.296s
sys 0m0.065s

и, наконец,

Clojure - hellofun.clj

(defn hello-world [username]
  (println (format "Hello, %s" username)))

(hello-world "world")

и результаты,

$ time clj hellofun.clj 
Hello, world

real    0m1.418s
user    0m1.649s
sys 0m0.154s

Это целое, garangutan 1,4 секунды!

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

Что еще более важно - разве эта огромная разница в производительности в какой-то момент не будет проблемой? (Я имею в виду, скажем, я использовал clojure для производственной системы - коэффициент усиления, который я получаю при использовании lisp, полностью компенсируется проблемами производительности, которые я вижу здесь).


Используемая здесь машина - Macbook Pro 2007, работающая на Snow Leopard, 2,16 ГГц Intel C2D и 2G DDR2 SDRAM.

BTW, clj script Я использую от здесь и выглядит,

#!/bin/bash
JAVA=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java
CLJ_DIR=/opt/jars
CLOJURE=$CLJ_DIR/clojure.jar
CONTRIB=$CLJ_DIR/clojure-contrib.jar
JLINE=$CLJ_DIR/jline-0.9.94.jar
CP=$PWD:$CLOJURE:$JLINE:$CONTRIB

# Add extra jars as specified by `.clojure` file
if [ -f .clojure ]
then
  CP=$CP:`cat .clojure`
fi

if [ -z "$1" ]; then
  $JAVA -server -cp $CP \
      jline.ConsoleRunner clojure.lang.Repl
else
  scriptname=$1
  $JAVA -server -cp $CP clojure.main $scriptname -- $*
fi

Ответ 1

Вы не измеряете много здесь, кроме времени загрузки Clojure. Вы также запускаете свою программу таким образом, чтобы вы также измеряли время компиляции. Если вы хотите увидеть более быстрое время загрузки, вам необходимо скорректировать код в скором времени.

Закодировав бит в Python, я обнаружил, что Clojure является общим правилом намного, намного, намного быстрее, чем Python, и вы обычно можете получить программу Clojure, чтобы получить в пределах 2X-4X скорости чистой Java.

Ответ 2

Чтобы добавить ответ dnolen, при выполнении содержимого синхронизации Python vs Clojure вы можете захотеть упаковать свою "базовую единицу работы" в качестве функции, а затем использовать макрос time (в Clojure) или timeit.timeit (в Python или, еще лучше, использовать средства синхронизации IPython) в REPL. Результаты должны быть примерно сопоставимыми. (Обратите внимание, что код Clojure должен быть "разогрет", запустив его несколько раз, чтобы достичь полной производительности.)

Есть также некоторые комплекты тестов для Clojure, например Criterium; вы можете рассмотреть возможность использования одного из них.

Ответ 3

Также обратите внимание, что опция "-сервер" в вашем clj script будет использовать "серверную JVM", которая оптимизирована для длительных процессов за счет более медленного времени запуска.

В вашем примере java не указан этот параметр, поэтому, вероятно, он использует "клиентскую JVM", которая оптимизирована для более быстрого времени запуска.

Попробуйте запустить java -jar clojure.jar -i hellofun.clj для более справедливого сравнения.

Ответ 4

JVM в целом уже имеет несколько медленное время запуска, в отличие от родных или интерпретируемых языков. Кроме того, Clojure добавляет значительные затраты на время запуска, поскольку он компилирует и загружает довольно много кода при запуске. Даже при использовании AOT перед началом работы необходимо настроить Clojure.

Нижняя строка, не зависит от Clojure для короткоживущих процессов. Даже не полагайтесь на Java для этих случаев использования большую часть времени. Что-то родное или интерпретированное как Node.js, Python, Lua и т.д. Было бы намного лучше.

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

Теперь, если вам действительно нужно что-то быстрое для Clojure, я бы рекомендовал посмотреть lumo. Его ClojureScript REPL, который сам содержит и работает на загрузочном ClojureScript, поэтому JVM не видно.

time python -c "print(\"Hello World\")"
Hello World

real    0m0.266s
user    0m0.015s
sys     0m0.202s

time lumo -e "\"Hello World\""
"Hello World"

real    0m0.438s
user    0m0.000s
sys     0m0.203s

Как вы можете видеть, Lumo приближается к скорости запуска Cpy3k.

Альтернатива, которая больше не будет Clojure, но она по-прежнему будет Clojure вдохновлена ​​Lisp, Hy. Его a Lisp с синтаксисом Clojure, запущенным на Python.

time hy -c "(print \"Hello World\")"
Hello World

real    0m0.902s
user    0m0.000s
sys     0m0.171s

Время запуска немного медленнее, чем Cpy3k и Lumo, но дает вам весь Python в вашем распоряжении с синтаксисом и макросами Clojure.