Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

Clojure が遅いという話

Clojure が遅いという話

遅いケースもあるという認識も持っておいてもらおう。 比較のために今回はPythonと比べてみる。

Clojure

(dotimes [i 5]
  (time
   (reduce unchecked-add (map unchecked-add (range 1000000) (range 1000000)))))

Python

import timeit

def f (x):
    return reduce(lambda x, y: x + y, (map (lambda x, y: x + y, xrange(1, x), xrange(1, x))))

r = timeit.timeit("f(1000000)", "from __main__ import f",  number = 1)

print(r*1000)

結果

両者、かなり似たコードになっているが結果はどうか?

Clojure は初回どうしても遅くなるので 5 回計測。

Clojure

"Elapsed time: 936.786616 msecs"
"Elapsed time: 922.82911 msecs"
"Elapsed time: 923.171342 msecs"
"Elapsed time: 931.944958 msecs"
"Elapsed time: 935.293865 msecs"

Python

352.871894836

よく unchecked-xxx 使えみたいな話がありますが、使ってもなお遅いということを覚えておいた方がよさそうですね。

Clojure (Java) はやはり速かったという話

Clojure (Java) はやはり速かったという話

あまりにも遅すぎなのでは?と思ったので調べたらやはり計測方法に問題があっ たみたい。

Java がこんなに遅いわけない。

遅かった原因

いつも通り nrepl 経由で適当に実行していたのが原因。

leiningen から nrepl を立ち上げていたのだが、以下を見れば遅い理由がわかる。

java -client -Xbootclasspath/a:/home/ma2/.lein/self-installs/leiningen-2.3.3-standalone.jar -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Dfile.encoding=UTF-8 -Dmaven.wagon.http.ssl.easy=false -Dleiningen.original.pwd=/home/ma2 -Dleiningen.script=/home/ma2/bin/lein -classpath /home/ma2/.clojure:/home/ma2/.lein/self-installs/leiningen-2.3.3-standalone.jar clojure.main -m leiningen.core.main repl :headless :port 60000

-client オプション久々に見た!

leiningen は前から起動がクソ遅いと文句を言われていたので jvm のオプションを指定している。

起動速度をあげるために以下のオプションで起動している。

  • -client

  • -XX:+TieredCompilation

  • -XX:TieredStopAtLevel=1

oh …

実行速度が遅くて当たり前である。

lein スクリプト内

...

export LEIN_JVM_OPTS="${LEIN_JVM_OPTS-"-XX:+TieredCompilation -XX:TieredStopAtLevel=1"}"

...

export TRAMPOLINE_FILE
"$LEIN_JAVA_CMD" -client \
    "${BOOTCLASSPATH[@]}" \
    $LEIN_JVM_OPTS \
    -Dfile.encoding=UTF-8 \
    -Dmaven.wagon.http.ssl.easy=false \
    -Dleiningen.original.pwd="$ORIGINAL_PWD" \
    -Dleiningen.script="$SCRIPT" \
    -classpath "$CLASSPATH" \

がっつり固定で書かれている…

grench から起動すればこんなの気にしなくていいので -server モードで起動するようにすべきである。

server モード で計測

lein スクリプトを修正して以下のオプションで立ちあげてみる。

java -server -Xbootclasspath/a:/home/ma2/.lein/self-installs/leiningen-2.3.3-standalone.jar -Xms512m -Xmx1024m -Dfile.encoding=UTF-8 -Dmaven.wagon.http.ssl.easy=false -Dleiningen.original.pwd=/home/ma2 -Dleiningen.script=/home/ma2/bin/lein -classpath /home/ma2/.clojure:/home/ma2/.lein/self-installs/leiningen-2.3.3-standalone.jar clojure.main -m leiningen.core.main repl :headless :port 60000

計測してみよう。

λ grench eval '(time (reduce unchecked-add (map unchecked-add (range 1000000) (range 1000000))))'
"Elapsed time: 366.023913 msecs"
999999000000

ほぼ Python と変わらないくらいになった。

僕のおんぼろ X200s だとこれぐらいですが、会社の i7 搭載マシンだと Python より速くなります。

まとめ

パフォーマンスを計測する場合、leiningen 使うとダメです! nrepl 経由で楽せずちゃんと測りましょう!