Desempenho do Clojure: REPL versus uberjar

Eu queria fazer algumas renderizações gráficas em tempo real e estava tentando fazer vários cálculos por pixel por quadro. Logo percebi rapidamente que isso era muito lento e comecei na mesma base: com que rapidez posso percorrer todos os pixels?

Encontrei dotimes razoavelmente rápido, mas quando faço isso em um REPL, é muito lento:

user=> (dotimes [_ 10] (time (dotimes [_ 1e7] (+ 1 1))))
"Elapsed time: 409.177477 msecs"
"Elapsed time: 417.755502 msecs"
"Elapsed time: 418.939182 msecs"
"Elapsed time: 420.131575 msecs"
"Elapsed time: 419.83529 msecs"
"Elapsed time: 417.612003 msecs"
"Elapsed time: 420.749229 msecs"
"Elapsed time: 418.918554 msecs"
"Elapsed time: 414.403957 msecs"
"Elapsed time: 417.729624 msecs"
nil
user=>

Então eu coloquei isso em um projeto de Leiningen. Quando eu faço um "lein run", é tão lento quanto. Mas quando eu crio o uberjar e o executo com o comando java, é muito mais rápido:

% java -jar target/looping-0.1.0-SNAPSHOT-standalone.jar 
"Elapsed time: 122.006758 msecs"
"Elapsed time: 3.667653 msecs"
"Elapsed time: 3.60515 msecs"
"Elapsed time: 4.008436 msecs"
"Elapsed time: 3.961558 msecs"
"Elapsed time: 3.60212 msecs"
"Elapsed time: 3.592532 msecs"
"Elapsed time: 4.573949 msecs"
"Elapsed time: 3.959568 msecs"
"Elapsed time: 3.607495 msecs"

Embora a primeira corrida ainda seja muito mais lenta. Qual é a diferença? Nos dois casos, o código é compilado, não há Clojure interpretado, certo? É JIT, algumas otimizações ou algumas opções especiais da JVM definidas para o REPL?

Obrigado por todas as idéias.

questionAnswers(1)

yourAnswerToTheQuestion