Performance question (newbie)

97 views
Skip to first unread message

Dragan

unread,
Jul 15, 2009, 7:51:32 AM7/15/09
to Clojure
Hi,

I am trying to compare the performance of java loops to clojure reduce
function. Noting special, Since I am just learning.
Java code is something like:

[code]
long sum = 0;
for (int i = 1; i < 1000000; i++ ){
sum = sum + i;
}
[/code]

while in Clojure I used:

[code]
(reduce + (range 1 i))
[/code]

Execution time for the Java version is 7 ms, while Clojure needs 60 -
160 ms.
Now, that is expected, since Clojure runs in REPL.
Next, I tried the gen-class of the Clojure, and called that Class from
Java (so no REPL involved), but the code haven't speed up at all!
Am I missing something or such 10x performance penalty is usual for
such operations?

Meikel Brandmeyer

unread,
Jul 15, 2009, 9:26:07 AM7/15/09
to Clojure
Hi,

On Jul 15, 1:51 pm, Dragan <draga...@gmail.com> wrote:
> [code]
> long sum = 0;
> for (int i = 1; i < 1000000; i++ ){
>     sum = sum + i;}
>
> [/code]
>
> while in Clojure I used:
>
> [code]
> (reduce + (range 1 i))
> [/code]

Comparing such a loop with reduce is a bit unfair,
since the loop iteration heavily dominates the
execution. reduce gives you much more convenience
to handle complex data structures, which - I think -
a long is not.

For such tight loops, you want something like this:

(loop [sum (long 0)
i (long 1)]
(if (< i 100000)
(recur (+ sum i) (inc i))
sum))

This should give you higher performance.

> Now, that is expected, since Clojure runs in REPL.
> Next, I tried the gen-class of the Clojure, and called that Class from
> Java (so no REPL involved), but the code haven't speed up at all!
> Am I missing something or such 10x performance penalty is usual for
> such operations?

There is no difference of code run at the Repl and code,
which is compiled (which is not equivalent with gen-class btw.).
Clojure code always gets compiled before it is executed.
Of course there is a slight compilation overhead at the Repl.

Sincerely
Meikel

Frantisek Sodomka

unread,
Jul 15, 2009, 9:37:38 AM7/15/09
to Clojure
If you make it into a function and use type hints, that should help:

(defn sum [n]
(let [n (int n)]
(loop [ret (long 0) i (int 1)]
(if (< i n)
(recur (+ ret i) (inc i))
ret))))

user=> (time (reduce + (range 1 1000000)))
"Elapsed time: 116.959837 msecs"
499999500000

user=> (time (sum 1000000))
"Elapsed time: 6.151341 msecs"
499999500000

Frantisek


On Jul 15, 1:51 pm, Dragan <draga...@gmail.com> wrote:

Frantisek Sodomka

unread,
Jul 15, 2009, 9:39:57 AM7/15/09
to Clojure
PS: Read tips on:
http://clojure.org/java_interop


On Jul 15, 1:51 pm, Dragan <draga...@gmail.com> wrote:

Adrian Cuthbertson

unread,
Jul 15, 2009, 10:01:02 AM7/15/09
to clo...@googlegroups.com
It's also worth searching this group for 'performance' and checking
out the discussions over the past few months. There've been lots of
queries about many different aspects of performance and some really
good advice dispensed.

- Adrian.

Aaron Cohen

unread,
Jul 15, 2009, 10:13:24 AM7/15/09
to clo...@googlegroups.com
Another note is that these kind of micro-benchmarks are a little difficult to do correctly in most modern VMs, including Hotspot.  In particular, the kind of tight loop you're doing there where the result isn't used can sometimes be optimized away by the JIT to go "infinitely" fast.  A pretty good presentation about this from JavaONE can be found here: http://www.azulsystems.com/events/javaone_2009/session/2009_J1_Benchmark.pdf
-- Aaron

B Smith-Mannschott

unread,
Jul 15, 2009, 11:39:01 AM7/15/09
to clo...@googlegroups.com
On Wed, Jul 15, 2009 at 13:51, Dragan<drag...@gmail.com> wrote:
>
> Hi,
>
> I am trying to compare the performance of java loops to clojure reduce
> function. Noting special, Since I am just learning.
> Java code is something like:
>
> [code]
> long sum = 0;
> for (int i = 1; i < 1000000; i++ ){
>    sum = sum + i;
> }
> [/code]
>
> while in Clojure I used:
>
> [code]
> (reduce + (range 1 i))
> [/code]
>
> Execution time for the Java version is 7 ms, while Clojure needs 60 -
> 160 ms.
> Now, that is expected, since Clojure runs in REPL.

Code run from the REPL won't run any slower (or faster) than code
that's compile ahead of time. Clojure compiles all code to java
classes before execution, regardless of how it is loaded.

> Next, I tried the gen-class of the Clojure, and called that Class from
> Java (so no REPL involved), but the code haven't speed up at all!
> Am I missing something or such 10x performance penalty is usual for
> such operations?

You're probably seeing the cost of boxing the numbers and of using
BigInteger to represent the sum. Being something of a newbie myself, I
tried to speed up your code:

(defn sum-of-range-1 [range-limit]
(reduce + (range 1 range-limit)))

When I ran (sum-of-range-1 1000000) one thousand times, it took 34.54
s on my machine

An explicit loop with some type hints is faster, though likely not as
fast as Java:

(defn sum-of-range-4 [range-limit]
(loop [i (int 1) s (long 0)]
(if (< i range-limit)
(recur (inc i) (+ s i))
s)))

This took 20.275s for the same scenario.

// Ben

sum.clj

John Harrop

unread,
Jul 15, 2009, 2:22:23 PM7/15/09
to clo...@googlegroups.com
On Wed, Jul 15, 2009 at 11:39 AM, B Smith-Mannschott <bsmit...@gmail.com> wrote:
An explicit loop with some type hints is faster, though likely not as
fast as Java:

(defn sum-of-range-4 [range-limit]
 (loop [i (int 1) s (long 0)]
   (if (< i range-limit)
     (recur (inc i) (+ s i))
     s)))

This took 20.275s for the same scenario.

Use unchecked-add and unchecked-inc in place of + and inc and you should get equivalent speed. 

Dragan

unread,
Jul 15, 2009, 4:48:53 PM7/15/09
to Clojure
Guys, thanks very much for the fast responses. Of course, this is an
unscientific and totally ad-hock benchmark. I am just *learning*
Clojure and functional programming and I wanted to get some feeling
of how fast Clojure is comparing to java when equivalent idioms are
used . Of course, loop and reduce are not really equivalent, but I
would use them for similar tasks, that 's why I wanted to compare them
(instead for example loop vs loop).
That's why it's great to hear those "duh" tips :)

On Jul 15, 5:39 pm, B Smith-Mannschott <bsmith.o...@gmail.com> wrote:
>  sum.clj
> < 1KViewDownload

Rich Hickey

unread,
Jul 15, 2009, 10:24:27 PM7/15/09
to Clojure


On Jul 15, 2:22 pm, John Harrop <jharrop...@gmail.com> wrote:
> On Wed, Jul 15, 2009 at 11:39 AM, B Smith-Mannschott
> <bsmith.o...@gmail.com>wrote:
>
> > An explicit loop with some type hints is faster, though likely not as
> > fast as Java:
>
> > (defn sum-of-range-4 [range-limit]
> >  (loop [i (int 1) s (long 0)]
> >    (if (< i range-limit)
> >      (recur (inc i) (+ s i))
> >      s)))
>
> > This took 20.275s for the same scenario.
>
> Use unchecked-add and unchecked-inc in place of + and inc and you should get
> equivalent speed.

Frantisek's solution is the right way, and will run within 5-10% of
the Java version, Going to unchecked-* is generally not worth it and I
don't recommend it. You should only use it when you desire the
wrapping behavior.

Rich

Wilson MacGyver

unread,
Jul 16, 2009, 1:28:40 PM7/16/09
to clo...@googlegroups.com
This link reminded me of this discussion.

http://www.cnn.com/2009/US/07/15/quadrillion.dollar.glitch/index.html?iref=newssearch

as Rich said, unchecked is generally a bad idea. :)
--
Omnem crede diem tibi diluxisse supremum.
Reply all
Reply to author
Forward
0 new messages