Clojure speed

564 views
Skip to first unread message

Gregory Petrosyan

unread,
Feb 2, 2009, 10:35:11 AM2/2/09
to Clojure
Hello everybody,

Althrough I am new to Clojure, I like it a lot. Because it is
advertised as native JVM language, I expected it to demostrate decent
performance in simple numeric tests, so I decided to compare it to
Python.

Clojure rev. 1173:
user=> (defn fac [#^Integer n] (reduce * (range 1 (+ 1 n))))
#'user/fac
user=> (time (reduce + (map fac (range 1000))))
"Elapsed time: 944.798019 msecs"

Python 3.0:
>>> import timeit
>>> t=timeit.Timer('sum(fac(n) for n in range(1000))', 'from functools import reduce; from operator import mul; fac = lambda n: reduce(mul, range(1, n+1), 1)')
>>> t.timeit(10)/10
0.35052159800038679

This is XP SP2 on Core2 Duo, with 3Gb of RAM.

As you can see, Python is almost 3 times faster on this
microbenchmark! Can anybody explain this to me? (No flame wars,
please, I am really interested in why the things are as they are, is
it considered ok or not, and what can be done to make Clojure faster
on similar tests).

Konrad Hinsen

unread,
Feb 2, 2009, 1:26:08 PM2/2/09
to clo...@googlegroups.com
On 02.02.2009, at 16:35, Gregory Petrosyan wrote:

> Althrough I am new to Clojure, I like it a lot. Because it is
> advertised as native JVM language, I expected it to demostrate decent
> performance in simple numeric tests, so I decided to compare it to
> Python.
>
> Clojure rev. 1173:
> user=> (defn fac [#^Integer n] (reduce * (range 1 (+ 1 n))))
> #'user/fac
> user=> (time (reduce + (map fac (range 1000))))
> "Elapsed time: 944.798019 msecs"

Timing on the JVM is notoriously difficult. Since good performance
depends on the JIT compiler, you can't expect anything to be fast the
first time you run it. So, define a function that runs your benchmark
and run it several times. But even then, microbenchmarks are not the
JVM's strongest point.

Konrad.

Christian Vest Hansen

unread,
Feb 2, 2009, 2:12:33 PM2/2/09
to clo...@googlegroups.com
It is safe to assume that Python uses the GMP library for its infinite
precision math, no? This could be a big part of the explanation as, if
the language shootouts are to be believed, BigInteger and BigDecimal
have inferior performance when compared to what can be achieved with
GMP.

For problems that can be solved within the constraints of 32 bits,
number boxing would be a likely candidate perfomance drain; the
function interfaces speak in Objects and therefor any number that
wishes to cross a function boundary, either as a parameter or as a
return value, must be a subclass of Object and that means boxing of
primitives, which in turn means object allocation and GC.

Also, the server (HotSpot) VM currently requires 10.000 calls of a
method before it will consider JIT'ing it.
--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

David Nolen

unread,
Feb 2, 2009, 2:12:48 PM2/2/09
to clo...@googlegroups.com
Please do the list a favor and read the very long threads about performance.  You cannot expect to enter a new language run a microbenchmark you would never see in a running programming and expect that to be anything like a real comparison.

Here's something unlikely but far less so (something like it might _actually_ appear in a real program):

t=timeit.Timer('class Sprite():\n\tx = 5\n\ty = 5\n\tvel = (5, 5)\n\tdef __init__(self, nx, ny, nvel):\n\t\tself.x=nx\n\t\tself.y=ny\n\t\tvel=nvel\n\n\nfor n in range(1000000):\n\tSprite(5, 5, (9, 10))')
> 1.1212704896926879

vs.

(defstruct sprite :x :y :vel)
(time (dotimes [x 1000000]
(struct sprite 5 5 [9 10])))
> 262.041ms

4X faster. But it really doesn't prove anything any more than your example does.  Play around with Clojure long enough, and you'll find for many many things, it is far faster than Python.

This is not a rag on Python, I enjoy Python hacking myself.

David Nolen

unread,
Feb 2, 2009, 2:29:50 PM2/2/09
to clo...@googlegroups.com
Heh, this is a more reasoned reply than my own as it points out an actual implementation difference between Python and Clojure. And of course you might need arbitrary precision arithmetic in your program, but again this just reinforces the insignificance of microbenchmarks without some context of what you are actually trying to achieve.

Gregory Petrosyan

unread,
Feb 3, 2009, 3:10:27 AM2/3/09
to Clojure
On Feb 2, 10:29 pm, David Nolen <dnolen.li...@gmail.com> wrote:
> Heh, this is a more reasoned reply than my own as it points out an actual
> implementation difference between Python and Clojure. And of course you
> might need arbitrary precision arithmetic in your program, but again this
> just reinforces the insignificance of microbenchmarks without some context
> of what you are actually trying to achieve.
>
> On Mon, Feb 2, 2009 at 2:12 PM, Christian Vest Hansen
> <karmazi...@gmail.com>wrote:
> > It is safe to assume that Python uses the GMP library for its infinite
> > precision math, no? This could be a big part of the explanation as, if
> > the language shootouts are to be believed, BigInteger and BigDecimal
> > have inferior performance when compared to what can be achieved with
> > GMP.

Well, Python uses home-grown arbitrary precision numbers:
http://svn.python.org/view/python/trunk/Objects/intobject.c?rev=68381&view=markup
http://svn.python.org/view/python/trunk/Objects/longobject.c?rev=68975&view=markup

Anyway, I am not trying to prove that Clojure is slow or anything like
that.

Out of curiosity: are there any public Clojure benchmarks available?
Anything like
Great Computer Languages Shootout benchmarks? It will be very
interesting to play
with them a bit, comparing them to Java ones, for example.

André Thieme

unread,
Feb 3, 2009, 4:50:38 AM2/3/09
to Clojure
On 2 Feb., 16:35, Gregory Petrosyan <gregory.petros...@gmail.com>
wrote:

> Clojure rev. 1173:
> user=> (defn fac [#^Integer n] (reduce * (range 1 (+ 1 n))))
> #'user/fac
> user=> (time (reduce + (map fac (range 1000))))
> "Elapsed time: 944.798019 msecs"
>
> Python 3.0:>>> import timeit
> >>> t=timeit.Timer('sum(fac(n) for n in range(1000))', 'from functools import reduce; from operator import mul; fac = lambda n: reduce(mul, range(1, n+1), 1)')
> >>> t.timeit(10)/10
>
> 0.35052159800038679
>
> This is XP SP2 on Core2 Duo, with 3Gb of RAM.
>
> As you can see, Python is almost 3 times faster on this
> microbenchmark! Can anybody explain this to me? (No flame wars,
> please, I am really interested in why the things are as they are, is
> it considered ok or not, and what can be done to make Clojure faster
> on similar tests).

Hi, welcome in the group.
Can you please write that program in Java and see how well it
performs for you?

Gregory Petrosyan

unread,
Feb 3, 2009, 12:00:48 PM2/3/09
to Clojure
On Feb 3, 12:50 pm, André Thieme <splendidl...@googlemail.com> wrote:

> Hi, welcome in the group.
> Can you please write that program in Java and see how well it
> performs for you?

Will try to compare Java and Clojure later.

Here http://leonardo-m.livejournal.com/75825.html you can find similar
microbenchmark. Java is more than 3х slower than Python's built-in
integers, and more than 10x slower than GMPY ones. Seems like Java's
BigIntegers have some problems with performance, hm?

David Nolen

unread,
Feb 3, 2009, 12:12:21 PM2/3/09
to clo...@googlegroups.com
Even more constructive is to take a real Python program that you've written where you actually care about it's performance.  Rewrite it Clojure.  Do some investigation about which parts seem slow to you.  Spend some time on this.  Come back with some code and questions and you'll probably get some great answers.  At that point you might start to understand what the performance characteristic of the language actually are.  Perhaps some idioms in Python are faster, perhaps you need to learn new idioms to express them more efficiently in Clojure.  Perhaps you'll find out some things are ungodly faster in Clojure and, of course, some things, like BigInteger math, aren't.

2009/2/3 Gregory Petrosyan <gregory....@gmail.com>

Christian Vest Hansen

unread,
Feb 3, 2009, 2:30:35 PM2/3/09
to clo...@googlegroups.com
2009/2/3 Gregory Petrosyan <gregory....@gmail.com>:

> Here http://leonardo-m.livejournal.com/75825.html you can find similar
> microbenchmark. Java is more than 3х slower than Python's built-in
> integers, and more than 10x slower than GMPY ones. Seems like Java's
> BigIntegers have some problems with performance, hm?

BigInteger have problems, but so does that benchmark:

No warm-up.
Includes startup time.
Using client VM.
And probably a number of less obvious things that commonly haunt Java
micro-benchmarks.

On the other hand, we have reason to hope that the big-math story will
improve significantly in JDK 7:
http://markmail.org/message/7conncsespvrlazn

Jon Harrop

unread,
Feb 3, 2009, 6:17:53 PM2/3/09
to clo...@googlegroups.com
On Monday 02 February 2009 19:12:48 David Nolen wrote:
> Please do the list a favor and read the very long threads about
> performance.

I would be interested to see a Clojure port of my ray tracer benchmark:

http://www.ffconsultancy.com/languages/ray_tracer/

--
Dr Jon Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/?e

Reply all
Reply to author
Forward
0 new messages