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
-
This is not a reliable way to make performance measurements (check out criterium), and a less than x2 slowdown is hardly 'killing perf'. The sole fact that you're wrapping Clojure functions could explain why it's slower (since you're doing more work).Valentin Waeselynck– Valentin Waeselynck2016年10月07日 09:42:20 +00:00Commented Oct 7, 2016 at 9:42
-
Oh thanks for the library name, it appears in fact that my function is faster than clojure map on sequences with both time and criterium bench. I just wondered if using IFn or a better approach would produce better resultsJoseph Yourine– Joseph Yourine2016年10月07日 10:01:17 +00:00Commented Oct 7, 2016 at 10:01
-
If it's faster it's probably because you're updating an array in-place (which is not the same semantics as clojure.core/map !)Valentin Waeselynck– Valentin Waeselynck2016年10月07日 10:14:05 +00:00Commented Oct 7, 2016 at 10:14
-
Yes of course, my point is to use this to achieve "precision" local work that does not need the flexibility and capabilities of Clojure mapJoseph Yourine– Joseph Yourine2016年10月10日 12:54:55 +00:00Commented Oct 10, 2016 at 12:54
1 Answer 1
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).