Clojure performance optimization

502 views
Skip to first unread message

Michael Golovanov

unread,
Apr 19, 2011, 3:08:51 AM4/19/11
to Clojure
Hi everyone

I have the same task implementation on Java and Clojure. Task is very
simple: User input integers to the console. Program need print inputed
integers until user input is 42.
Clojure implementation is 10 times slower, how to optimize Clojure
implementation performance?

Java impl:

import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
do {
int num = sc.nextInt();
if (num == 42)
break;
System.out.println(num);
} while( true );
}
}

Clojure impl:

(import java.util.Scanner)

(defn getNextInt [scanner term f x]
(if (not= x nil)
(apply f [x]))

(let [val (. scanner nextInt)]
(if (not= val term)
(getNextInt scanner term f val))))

(getNextInt (Scanner. System/in) 42 (fn [x] (println x)) nil)

Thanx

Alan

unread,
Apr 19, 2011, 4:07:29 PM4/19/11
to Clojure
How much is ten times a microsecond? There's no way the user could
detect that kind of latency in a console app. If you're worried about
startup time, then the code of the actual task isn't really relevant.

On Apr 19, 12:08 am, Michael Golovanov <mike.golova...@gmail.com>
wrote:

Michael Golovanov

unread,
Apr 20, 2011, 7:14:08 AM4/20/11
to Clojure
Hi, Alan

Thanx for your answer.
I run Java and Clojure code in web IDE (two different online services)
on the same input data sets. Java 0,07sec, Clojure 0,87 per execution.
I know that is not correct method for performance measure and may be
Clojure execution time include compile time.
Do you recommend me some Clojure specific tools or performance
profiling approaches?

Peteris

unread,
Apr 20, 2011, 2:38:25 AM4/20/11
to Clojure
The task seems to be a Google Code Jam problem (which must satisfy
strict time requirements). I think Michael feels concerned that
Clojure programs in general would run ten times slower than equivalent
Java programs.

--
Peteris Erins

Michael Golovanov

unread,
Apr 20, 2011, 11:21:13 AM4/20/11
to Clojure
Hi, Peter

Yes, you are right. This is a synthetic test task from service like
Google Code Jam which must satisfy strict time requrements. And i am
trying to learn Clojure by this way.

I see Clojure vs Java performance tests. Clojure is fast and have many
features that simplify developing applications.

My question is how to find performance bottlenecks in Clojure
applications.
Second question is about my code. Where are the problem?
May be this is
1. Incorrect execution time measuring procedure
2. Bad algorithm
3. Slow input/output (read from System.in stream/ printing to
System.out) in Clojure

Thank you for help.

Aaron Cohen

unread,
Apr 20, 2011, 11:30:25 AM4/20/11
to clo...@googlegroups.com
On Wed, Apr 20, 2011 at 11:21 AM, Michael Golovanov
<mike.go...@gmail.com> wrote:

> My question is how to find performance bottlenecks in Clojure
> applications.
> Second question is about my code. Where are the problem?
> May be this is
> 1. Incorrect execution time measuring procedure
> 2. Bad algorithm
> 3. Slow input/output (read from System.in stream/ printing to
> System.out) in Clojure
>

One rule of thumb when performance tuning clojure code is to (set!
*warn-on-reflection* true) at the top of your code.

In your case, there is one reflection warning in your loop that I bet
will get a large part of your performance back.

Also, your recursive call is in tail-position, so you can use recur
which is basically compiled down to a goto rather than a function
call. This will save stack and probably run faster.

How does the following work for you?

(defn getNextInt [#^Scanner scanner term f x]


(if (not= x nil)
(apply f [x]))

(let [val (. scanner nextInt)]
(if (not= val term)

(recur scanner term f val))))

Laurent PETIT

unread,
Apr 20, 2011, 11:38:28 AM4/20/11
to clo...@googlegroups.com
Hello,

Try with this, which is closer to the java version:

; in a file scan/core.clj
(ns scan.core
(:import java.util.Scanner)
(:gen-class))

(defn -main []
(let [sc (Scanner. System/in)]
(loop [num (int (.nextInt sc))]
(when-not (= 42 num)
(println num)
(recur (.nextInt sc))))))

2011/4/19 Michael Golovanov <mike.go...@gmail.com>:

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

Laurent PETIT

unread,
Apr 20, 2011, 11:55:58 AM4/20/11
to clo...@googlegroups.com
You might be interested in micro-code-revue (not arguing about the
design, but simpler details) as well:

(apply f [x]) can be replaced by (f x)

(not= x nil) can be (almost) replaced by x if you know x cannot be the
value 'false' (sufficient in your case)

(. scanner nextInt) is a somewhat deprecated form. Write it (.nextInt scanner)

(fn [x] (println x)) is just println => you can pass println function "as is"

Try hard avoiding direct recursive function calls like (def f [...]
... (f ...))) => this fills the stack, and the JVM has a stack limited
in size.
Prefer the use of recur (when possible ! => it is in your case)

Side note.
If you were not in a case where you're seeking for the max perf., you
would probably address the problem differently : decompose the problem
in multisteps.

For example, here, you could have a function whose purpose is to
create a sequence of vals from the input:

(defn incoming-ints [in]
(let [sc (java.util.Scanner. in)]
(repeatedly #(.nextInt sc))))

Then you would walk over this sequence while its value is different from 42 :

(doseq [i (incoming-ints System/in) :while (not= i 42)]
(println i))

HTH,

--
Laurent

2011/4/19 Michael Golovanov <mike.go...@gmail.com>:

Michael Golovanov

unread,
Apr 20, 2011, 1:59:07 PM4/20/11
to Clojure
Ok. Problem solved.
Online service that i am using show sum of compile and execution time.
Simple empty function definition without any calls show 0,8 sec time
consuption. Therefore Clojure code is fast as Java.

I am appreciate for your help and coding tips. Thank yoy

Andy Fingerhut

unread,
Apr 20, 2011, 5:59:44 PM4/20/11
to clo...@googlegroups.com
There is a significant time required to run "Hello, world" on a JVM, significantly more so than the corresponding C program on the same machine, due to all kinds of initialization to start up the JVM, even if the compile time is negligible.

There is additional initialization time when you start up a JVM running Clojure, even if it is just a "Hello, world" Clojure program with negligible compile time.

If you are curious to see some run times for Hello, world in Java and Clojure for several different JVMs on the same hardware, but different OSs, I've published some measurements here:

http://homepage.mac.com/jafingerhut/Sites/files/clojure-benchmarks/2011-03-15-clojure-1.3-alpha6-vs-1.2-vs-java-perf.html

Click on the "table of hello world results" link.

The earlier links are for slight modifications of many Clojure programs submitted to the Computer Language Benchmarks Game web page, comparing their run time with Clojure 1.2 and Clojure 1.3alpha6.

Andy

Michael Golovanov

unread,
Apr 23, 2011, 3:53:40 PM4/23/11
to Clojure
Hi, all

I fix some discussed details.
Please review my code at https://github.com/mgoblin/GCS-puzzles/wiki/42-puzzle
Reply all
Reply to author
Forward
0 new messages