So I'm new to Clojure, and have been working through how to make Clojure code performant (i.e. what approaches are faster than others, how to profile, etc) by writing a (embarrassingly) simple ray-tracer.
In a ray tracer there is a tight loop that runs per pixel, where you determine which of a list of object in the scene intersects with the ray for that pixel, and which is closest. At one point I was using partial as part of that logic. Changing partial to #() made the rendering time for a specific scene go down from 4.7s to 4.1s. #() is ugly though, and partial, as people has said, is more readable, because it speaks more to how that bit of code is going to be used. So I wrote the following macro: (defmacro partialn [n f & args] (let [params (map (fn [_] (gensym "fnp-")) (range n))] `(fn [~@params] (~f ~@args ~@params)))) This is just a first cut and I'm sure there are rough edges, but you get the idea: we're using a macro to hide the creation of a 'static' anonymous function instead of writing it ourselves. When using this in my code in place of #(), the performance result is essentially identical. Apologies in advance if I've missed something obvious here, but is this something other people are interested in? Would this be useful in core? As far as I can tell it's something that is-- if you know the number of args you expect-- a strict upgrade to partial, though I'm willing to plead ignorance here. You could even re-write partial so supported both signatures (f & args) and (f n & args), though I'm not sure if that's idiomatic. Below is the specific data from bench. Note that these benchmarks aren't particularly scientific, they've all been running on a 2009MBP, from the REPL, with a :reload-all in between each run to swap code around. Each of these calls was essentially (map (partial f x) list-of-ys) -> (f x y), so one arg passed on creation and one at runtime. The laptop was at the very least left alone while they were running, so any variance wasn't explicitly introduced by me. ; for (partial ..) Evaluation count : 60 in 60 samples of 1 calls. Execution time mean : 4.788510 sec Execution time std-deviation : 39.595196 ms Execution time lower quantile : 4.744957 sec ( 2.5%) Execution time upper quantile : 4.884646 sec (97.5%) Overhead used : 18.475768 ns Found 3 outliers in 60 samples (5.0000 %) low-severe 1 (1.6667 %) low-mild 2 (3.3333 %) Variance from outliers : 1.6389 % Variance is slightly inflated by outliers ; for #(..) Evaluation count : 60 in 60 samples of 1 calls. Execution time mean : 4.106831 sec Execution time std-deviation : 54.371302 ms Execution time lower quantile : 4.010274 sec ( 2.5%) Execution time upper quantile : 4.191842 sec (97.5%) Overhead used : 18.475768 ns Found 1 outliers in 60 samples (1.6667 %) low-severe 1 (1.6667 %) ; for (partialn 1 ..) Evaluation count : 60 in 60 samples of 1 calls. Execution time mean : 4.200536 sec Execution time std-deviation : 56.566857 ms Execution time lower quantile : 4.104481 sec ( 2.5%) Execution time upper quantile : 4.304463 sec (97.5%) Overhead used : 18.475768 ns Found 2 outliers in 60 samples (3.3333 %) low-severe 1 (1.6667 %) low-mild 1 (1.6667 %) Variance from outliers : 1.6389 % Variance is slightly inflated by outliers On Wednesday, 14 August 2013 06:50:43 UTC+12, John Hume wrote: > > Though in some cases the performance impact could be significant, my > concern is readability. My understanding of the concept of partial function > application is that it's about supplying some but not all of the arguments. > So when I see `partial` in code, I expect more arguments to be supplied > later, which is confusing when that's not the case. (Obviously context can > make it easy to see that there will be no more arguments, but often that > context is not present.) > > > > On Tue, Aug 13, 2013 at 7:47 AM, Jay Fields <j...@jayfields.com<javascript:> > > wrote: > >> Say you have a simple function: (defn do-work [f] (f)) >> >> When you want to call do-work you need a function, let's pretend we >> want to use this function: (defn say-hello [n] (println "hello" n)) >> >> Which of the following solutions do you prefer? >> >> (do-work (partial say-hello "bob")) >> (do-work #(say-hello "bob")) >> >> I'd been using partial (which I font-lock**), but a teammate recently >> pointed out that partial's documentation explicitly calls out the fact >> that the number of args to partial should be less than the number of >> args to f. In practice it's been working 'fine', but I can't help but >> wonder if I'm sacrificing something I'm not aware of (performance?) >> >> ** with a font-lock, using partial *displays* the same number of chars >> as the reader macro solution, and I find it more readable when >> everything is in the parenthesis. - >> http://blog.jayfields.com/2013/05/emacs-lisp-font-lock-for-clojures.html >> >> -- >> -- >> 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<javascript:> >> 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 <javascript:> >> 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 <javascript:>. >> For more options, visit https://groups.google.com/groups/opt_out. >> >> >> > > > -- > http://elhumidor.blogspot.com/ > -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.