[#^"[Ljava.lang.String;" a1 #^"[Ljava.lang.String;" a2]
to [^objects a1 ^objects a2] didn't seem to have any negative impact on
performance. Can anyone explain why hinting ^objects is just as good as
specifying that it's an array of Strings? What are you hinting with ^objects that Clojure doesn't already know?On Feb 20, 2013 5:55 AM, "Phillip Lord" <philli...@newcastle.ac.uk> wrote:
> (do
> (assoc curr (inc j) 0)
> (recur (inc j) max-len)))
Here you're discarding the result of assoc, a pure function, which changes the code's nature significantly.
> (do
> (assoc! curr (inc j) 0)
> (recur (inc j) max-len)))
Nor is it safe to discard the result of calling assoc!; see how assoc! is used in other online examples.
--
Stephen Compall
If anyone in the MSA is online, you should watch this flythrough.
--
--
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
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Whatever the final performance achieved, the fact remains that the original Java code was much cleaner, simpler, and more comprehensible than the big ball of mud the performant Clojure version is turning into.
On Thu, Feb 21, 2013 at 4:55 AM, Marko Topolnik <marko.t...@gmail.com> wrote:Whatever the final performance achieved, the fact remains that the original Java code was much cleaner, simpler, and more comprehensible than the big ball of mud the performant Clojure version is turning into.To my eyes the deftype version is about as clean, simple, comprehensible, as the Java version. But I've been doing Clojure for a while now.
Christophe's version also has the advantage that it can pretty much compile down to efficient JavaScript via ClojureScript and probably an efficient ClojureCLR program as well. This may or may not matter to you.
My 5-year experience with Clojure (since 0.9) hasn't helped me to see it that way.
Christophe's version also has the advantage that it can pretty much compile down to efficient JavaScript via ClojureScript and probably an efficient ClojureCLR program as well. This may or may not matter to you.
In a Java project it clearly does not matter. In library code it quite likely could matter a lot. That is of course a subject completely separate from the discussion on the pitfalls involved in getting Clojure performance right.
Apparently even Cristophe broke quite a bit of sweat to come up with his second solution, and did also wander around searching for bottlenecks (like .equals against =). ^:unsynchronized-mutable is something I've never layed my eyes on before and I've spent quite a bit of time working on optimized Clojure, googling for any trick I could find. What is the most trivially obvious way to solve a probelm in Java takes the most obscure features of Clojure to emulate.
Finally, Cristophe's solution, as well as all other optimized Clojure code, seems to be just barely making it for the use case involved. In my code, for example, I struggle with such things as an array of Strings (received from a Java method) failing when used in amap, which needs an array of Objects. I'm sure I could come up with yet another layer of obscurity which would make this work, but, as I said, after several months of struggling I'm ready to settle for 100 lines of clean, elegant, obvious Java.
It's right there in the docstring of deftype, but OK.
are present only to facilitate the building of higher level constructs, such as Clojure's reference types, in Clojure itself.
Other than that, you are right, it's there in the docstring :)
Fair enough. My point was simply that Clojure implementations have a small learnable subset that performs well when performance is desired - primitives, loops, arrays, deftypes, etc regardless of host. It's unfortunate that the host, in this case the JVM, requires quite a bit of thinking about type hints and casts. I think most of the challenges around are writing fast Clojure JVM code lie here. I note these issues are not present in ClojureScript ;)
Finally, Cristophe's solution, as well as all other optimized Clojure code, seems to be just barely making it for the use case involved. In my code, for example, I struggle with such things as an array of Strings (received from a Java method) failing when used in amap, which needs an array of Objects. I'm sure I could come up with yet another layer of obscurity which would make this work, but, as I said, after several months of struggling I'm ready to settle for 100 lines of clean, elegant, obvious Java.Perhaps it's because I lack considerable experience with Java that I don't find it so challenging. Yes it's a bit finicky, yes it could be improved, but it still beats writing Java in my mind.
Fair enough. My point was simply that Clojure implementations have a small learnable subset that performs well when performance is desired - primitives, loops, arrays, deftypes, etc regardless of host. It's unfortunate that the host, in this case the JVM, requires quite a bit of thinking about type hints and casts. I think most of the challenges around are writing fast Clojure JVM code lie here. I note these issues are not present in ClojureScript ;)OK, but does that mean that at the end of the day, optimized ClojureScript performance is head-to-head with optimized JVM Clojure in a desktop/server side application?
I'll give you one specific item that I keep tripping upon: the lack of reassignable, stack-based locals. Without them I can't efficiently get more than one value out of a loop. Another item is that I can get zero primitive values out of a loop. How do you cope with those?
When on the subject, the times are getting ripe for a full-length book not about how handsome and elegant and FP-y Clojure is (we have plenty of those), but a "terrorist's handbook" on writing performant Clojure. Can you recommend any current material in that direction?
-Marko
I'll give you one specific item that I keep tripping upon: the lack of reassignable, stack-based locals. Without them I can't efficiently get more than one value out of a loop. Another item is that I can get zero primitive values out of a loop. How do you cope with those?Some ideas for the first point:* atoms
* implement efficient tuple type which uses mutation for multiple value return
I suspect the second option will be pretty fast. Annoying, but that's a tradeoff with Clojure's mostly functional design.
Second point, I think 1.5.0 addresses that to some degree thanks to Christophe.
Heh, I always point out test.benchmark - http://github.com/clojure/test.benchmark. There's some horrifying stuff in there ;)
* implement efficient tuple type which uses mutation for multiple value returnBasically, this is the ^:unsynchronized-mutable that we just mentioned :) This is much better, but still it's not stack-based, so incurs the cost of allocation and GC.
Annoying and slower than Java's locals, unfortunately. Most of the time it won't make a huge dent in the performance, but I just happen to have an inner loop that iterates between zero and three times only, zero being by far the most common case, and is entered many times from the surrounding loop. The performance drop is considerable.
Er re: assigning stack based locals. Forget wasting time making a tuple type, probably best to just do that with a small mutable array. This worked ok for us when porting some Java persistent data structure code to ClojureScript.
Isn't that always the way, though? Build your program in a powerful, expressive language, then profile it, find the critical parts, and optimise them - where possible in the same language, and where that's too ugly/painful, drop down a layer to a lower level language.
I did lots of this in the late '80s - I wrote programs in C, found where they were slow, optimised the C where it was viable, and re-implemented the core bits in assembler. This was great for high performance on a single cpu architecture, but over the years CPUs got faster and CPUs changed - among other things they optimised differently, so speed tweaks for an 80386 were actually slower on a Pentium.
I tend to think clojure is in a similar position - fast enough for the vast majority of things (ymmv of course - depending on what your domain is) and if you meet a situation like this where optimising the clojure becomes too ugly, you can drop down to Java (or indeed C!) - but you lose interoperability with ClojureScript, and hopefully in time language and VM improvements will make this less necessary.
- Korny
--
Sent from my geek device... Spelling mistakes can be blamed on Google
Isn't that always the way, though? Build your program in a powerful, expressive language, then profile it, find the critical parts, and optimise them - where possible in the same language, and where that's too ugly/painful, drop down a layer to a lower level language.
I tend to think clojure is in a similar position - fast enough for the vast majority of things (ymmv of course - depending on what your domain is) and if you meet a situation like this where optimising the clojure becomes too ugly, you can drop down to Java (or indeed C!)
the two strongest voices—Steele and Gabriel—were feeling their oats over their ability to write a powerful compiler to foil the complexities of Common Lisp. One often heard them, and later Moon, remark that a “sufficiently smart compiler” could solve a particular problem. Pretty soon the core group was quoting this “SSC” argument regularly.
Forgot to mention another hugely important factor: heap-allocated objects spell disaster for CPU cachelines. With today's architectures the difference between a cache hit and a cache miss is like the difference between catching your bus to work and having to walk instead. In our example I would have to be careful to reuse the same deftype instance for all inner loop runs, but then I'd need even more code to make sure the values are reset before entering. The result would be messy, brittle code with higher-level logic scattered between all the housekeeping constructs.
Hello,
I am cross-posting my Clojure question from StackOverflow. I am trying to get an algorithm in Clojure to match Java speed and managed to get the performance to within one order of magnitude and wondering if more is possible. The full question is here:
http://stackoverflow.com/questions/14949705/clojure-performance-for-expensive-algorithms
Thank you.
Man, this is exactly how I feel after all this tinkering! It was great for learning Clojure a bit more in depth, but in the end I am going to stick with the Java solution. Especially since it's so easy to mix Java and Clojure in the same project! I just specify :java-source-paths ["src/java"] in my project.clj and I just call that one method when I need it and the rest of the project is in Clojure. I think when performance if critical idiomatic Clojure is to just drop down to Java :)
Christophe's second function actually achieves Java speed or very close (within 5-10%), but it's ugly and there's a bit more to my algorithm which would make it even uglier if I were to go that route.
FWIW, I, for one, am really glad that Clojure allows us to select precisely which nice tools we want (have to) throw away (persistent data structures, dynamic typing, synchronized) when the need arises. Reminds me of the "don't pay for what you don't use" motto of C++ except done right (i.e. the other way around, because you don't want to pay wrt simplicity rather than performance, cf. "premature optimization…")
At the moment I don't find the Clojure solution simple, but again this may simply be to lack of exposure. Have you written a lot of performance optimized Clojure?
Take a look at any of the Common Lisp or Haskell submissions to the Computer Language Benchmarks Game web site, and you will see some programs that are nowhere near what people typically write in those languages, and certainly not what people would write if they weren't concerned with squeezing out the last drop of performance. Lots of mutable data structures in both, and lots of type declarations in Common Lisp.
Then again, even for C and Java programs on that site, people will do some pretty amazing tricks they wouldn't normally do in those languages, either, e.g. performing I/O in parallel with computation, even when it makes the computation code more complex to give the correct answer.
For Clojure, I'd recommend doing the same thing I'd recommend in just about any language: write the code that occurs to you first to get it correct. If it is fast enough for your purposes, whatever those are, you are done. If not, use a profiler to see where most of the time is spent, and then start working on optimizations in the same language if they are worth your time to do so. If they get too difficult in the original language, dropping down to a lower-level language (e.g. Java, C, assembler) is often a choice you can make, depending upon your deployment restrictions.
> from what I hear, idiomatic Haskell is a performance devil as well
Does this mean very good, or very bad?
On a related note, is there currently any way to get the Clojure compiler to tell you where boxing is occurring, like *warn-on-reflection* does for reflection?
Take a look at any of the Common Lisp or Haskell submissions to the Computer Language Benchmarks Game web site, and you will see some programs that are nowhere near what people typically write in those languages, and certainly not what people would write if they weren't concerned with squeezing out the last drop of performance. Lots of mutable data structures in both, and lots of type declarations in Common Lisp.Can you really get mutability in Haskell? I thought that was impossible; hence the notorious State monad.
Then again, even for C and Java programs on that site, people will do some pretty amazing tricks they wouldn't normally do in those languages, either, e.g. performing I/O in parallel with computation, even when it makes the computation code more complex to give the correct answer.Yes, I've already got frustrated with that site several times. For example, Scala beats Java by a wide margin on some benchmarks. Turns out it's because it uses JNI to solve the problem with the C bignum library. What relevance could that have?
For Clojure, I'd recommend doing the same thing I'd recommend in just about any language: write the code that occurs to you first to get it correct. If it is fast enough for your purposes, whatever those are, you are done. If not, use a profiler to see where most of the time is spent, and then start working on optimizations in the same language if they are worth your time to do so. If they get too difficult in the original language, dropping down to a lower-level language (e.g. Java, C, assembler) is often a choice you can make, depending upon your deployment restrictions.The important consideration is, just how many times the idiomatic code is slower? In my book it is a worthwhile goal to improve from 100x slower to 10x slower, even if that outcome still means it's quite a bit slower.
FWIW, I, for one, am really glad that Clojure allows us to select precisely which nice tools we want (have to) throw away (persistent data structures, dynamic typing, synchronized) when the need arises. Reminds me of the "don't pay for what you don't use" motto of C++ except done right (i.e. the other way around, because you don't want to pay wrt simplicity rather than performance, cf. "premature optimization…")
I'm no Haskell expert, but it doesn't take much Googling to give strong evidence that the answer is "yes, you can get mutability in Haskell". Search through this Haskell program on the Benchmarks Game site for occurrences of the string "unsafe".
In my experience writing Clojure programs for the Benchmarks Game, getting within 10x is fairly easy, and doesn't require much knowledge other than eliminating Clojure reflection, and using a decent algorithm that doesn't throw in an extra factor of N by accident. It often helps to use mutable Java arrays and Clojure loops, too.
As I mentioned before, I'm generally happy with Clojure's performance, but the few times I've had performance problems, I ended up rewriting the code at least three different ways in order to try to find the magic combination that would boost performance.
Fortunately, dropping down to Java is relatively painless. But I still wonder whether there might be some benefit to having a "low-level DSL" within Clojure, a mode that lets you choose to write your code in a way where the semantics are closer to the underlying platform.
I haven't used Clojurescript much, but I get the impression that Clojurescript is already a step in that direction, with a simpler story regarding how mutation, arrays, and primitives are translated to the underlying platform, arguably making it easier to get good performance when you need it.
(defmacro arr-max"return maximum value of `expr` over the indicesand values of array `arr`, where `idx-symb` and `val-symb`are bound to index and values of `arr`"[arr idx-symb val-symb expr]`(let [arr# ~arrn# (alength arr#)](loop [~idx-symb 0 max-val# java.lang.Long/MIN_VALUE](if (= ~idx-symb n#)max-val#(let [~val-symb (aget arr# ~idx-symb)val# ~expr](recur (inc ~idx-symb)(if (> val# max-val#)val# max-val#)))))))(defn lcs [^objects a1 ^objects a2](let [prev-ref (atom (long-array (inc (alength a2))))cur-ref (atom (long-array (inc (alength a2))))](arr-max a1 i v1(let [^longs prev @prev-ref^longs cur @cur-refmax-len (arr-max a2 j v2(let [match-len (if (.equals v1 v2)(inc (aget prev j))0)](aset cur (inc j) match-len)match-len))](reset! prev-ref cur)(reset! cur-ref prev)(long max-len)))))
For example, Scala beats Java by a wide margin on some benchmarks. Turns out it's because it uses JNI to solve the problem with the C bignum library.
Yeah, I wish the Benchmarks allowed for idiomatic submissions and finely tuned submissions.
Along those lines this older post did an interesting analysis on the benchmark solutions where it explored the tension that exists between expressiveness and performance across the various languages:
http://blog.gmarceau.qc.ca/2009/05/speed-size-and-dependability-of.html
[…] Here, I think it's a macro you'll probably use all over the place, arr-max, which will find the largest value of an expression looping over an array's index and values. Then lcs is just nested uses of arr-max that I think is pretty reasonable. The thing which clutters the remaining code are the prev/cur swapping which I don't have a slick way of handling.
(defmacro arr-max"return maximum value of `expr` over the indicesand values of array `arr`, where `idx-symb` and `val-symb`are bound to index and values of `arr`"[arr idx-symb val-symb expr]`(let [arr# ~arrn# (alength arr#)](loop [~idx-symb 0 max-val# java.lang.Long/MIN_VALUE]
--
--
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
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Things don't look very rosy for Clojure: it turns out to be about as verbose as Java and significantly slower (this confirms my experience; slightly slower than regular Java code, significantly slower than highly optimized Java). If idiomatic Clojure was used, it would move it to the left and upwards; Clojure would hang out with JRuby.
Again, only the fastest entries are shown.
If idiomatic Clojure was used...
Why insist on getting Clojure to be at par with languages that may offer a performance
boost on narrow problems at the expense of making parallel processing and code
in general more complex everywhere else ?
If idiomatic Clojure was used...
The problem, of course, is that: the code one-person considers to be idiomatic; another person considers to be idiotic, naïve.
(defn blank? [s] (every? #(Character/isWhitespace %) s))Have you ever wondered about its performance? Here you go:user> (time (dotimes [_ 10000] (blank? " ")))"Elapsed time: 3887.578 msecs"
Now that reduce can be short-circuited, redifining every?, some and al on top of it would yield some interesting gains:(defn revery? [pred coll](reduce (fn [t x](if (pred x)t(reduced false))) true coll))(defn rblank? [s] (revery? #(Character/isWhitespace ^char %) s))(defn blank? [s] (every? #(Character/isWhitespace ^char %) s))
Christophe
Hang out with JRuby? Seriously?
Probably because none of these things will ever reveal Clojure performance for non-trivial applications.
On Wednesday, February 27, 2013 5:19:20 AM UTC+1, Isaac Gouy wrote:If idiomatic Clojure was used...
The problem, of course, is that: the code one-person considers to be idiomatic; another person considers to be idiotic, naïve.Not really. Take Stuart Halloway's opening example in the section entitled Why Clojure?(defn blank? [s] (every? #(Character/isWhitespace %) s))Have you ever wondered about its performance?
There's no magic. You cannot win on all fronts.
(defn blank? [s] (every? #(Character/isWhitespace %) s))Have you ever wondered about its performance?
No. Why would I wonder about the performance of a one line code snippet that was written without concern for performance?
-Marko
--
--
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
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
If you wanted to create a collection of idiomatic Clojure programs for solving a particular set of problems, e.g. the Benchmarks Game problems, as soon as more than one person submitted a program and/or reviewed a program, there could arise arguments over which ones are idiomatic and which are not.If one person is maintaining the collection, they can make judgement calls on this, and/or keep multiple different submissions around to solve the same problem as all equally idiomatic, even though they use different code constructs to do it.
However, if someone comes along with (let [m (HashMap.)] (loop []...(recur (.put m ...))) claiming that is in fact idomatic, he's just being unreasonable---by everyone's agreement.