Joseph Yourine
Joseph Yourine

Reputation: 1331

Pass Clojure functions to java efficiently

I would like to implement a naive non-lazy map in Java with a Java loop. My main concern is function invocation in java from Clojure.

Here is my code :

A class called NaiveClojure to implement functions using Java

package java_utils;

import java_utils.ApplyFn;

public class NaiveClojure {

    public static Object[] map (ApplyFn applyfn, Object function, Object[] coll) {

        int len = coll.length;

        for (int i = 0 ; i < len ; i++) {
            coll[i] = applyfn.apply(function, coll[i]);
        }

        return coll;
    }
}

An abstract class called ApplyFn

package java_utils;

public abstract class ApplyFn {

  public abstract Object apply (Object function, Object value);

}

So in Clojure I have

(defn java-map [f coll]
  (let [java-fn (proxy [ApplyFn] []
                  (apply [f x]
                         (f x)))]
    (seq (NaiveClojure/map java-fn f (to-array coll)))))

I tried

(doall (map inc (range 0 10000))) ;; 3.4 seconds for 10000 operations
(java-map inc (range 0 10000) ;; 5.4 seconds

My point is not to outperform map (I implemented it as an example), I just want to do things like that with specific functions (not to reinvent the wheel of Clojure).

Is there a better way to pass functions like that ? (as an easy and faster way) And to improve my coding in general (I have a poor theoritical knowledge), do you know what is killing perf here ? I would say general typing like Object but I do not see anything else

Thanks

Upvotes: 1

Views: 257

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

You have no cause for concern here, the way you are doing it is fine and efficient.

coll[i] = applyfn.apply(function, coll[i]);

This is a very normal way to go about this. When measuring it do please, as Valentin Waeselynck points out, remember to use a reliable microbenchmarking function and also keep in mind that benchmarking this kind of small code chunk in isolation is tricky.

When you generate a clojure function it produces a "normal" java class, with a method called apply. This will not be any slower to call because you are calling a function that was originally written in Clojure than it would be to call a method on a class that was written in the normal Java syntax. Once the Hotspot JIT finishes warming it up and inlining, it will likely be as fast as it would be without the method call (which is why benchmarking this kind of thing is harder than it intuitively should be).

Upvotes: 1

Related Questions