On Apr 9, 2009, at 2:57 AM, Eric Tschetter wrote:
Might I suggest (defn as-str ([] "") ([& args](apply str (map #(if (instance? clojure.lang.Named %) (name %) %) args)))
I like it for its simplicity. It can actually be a little simpler yet as the general case gives the same result when passed no arguments as the special case does:
(defn as-str [& args](apply str (map #(if (instance? clojure.lang.Named %) (name %) %) args)))
This code works well, but performs much less well than the code I posted. Just by code inspection, we can see that it's doing a lot more work, invoking a lot more machinery, especially in the one-argument case.
We can run some micro-benchmarks to get some idea how that affects its performance:
For a one argument case: ; as-str using apply user=> (time (dotimes [i 1000000] (as-str :a))) "Elapsed time: 496.361 msecs" ; as-str not using apply user=> (time (dotimes [i 1000000] (as-str :a))) "Elapsed time: 22.178 msecs" The non-apply version is 22 times as fast. For a two argument case: ; as-str using apply user=> (time (dotimes [i 1000000] (as-str :a 1))) "Elapsed time: 1057.922 msecs" ; as-str not using apply user=> (time (dotimes [i 1000000] (as-str :a 1))) "Elapsed time: 317.483 msecs" The non-apply version is 3.3 times as fast.From looking at this difference, my conclusion is that the version I posted is "simple enough" to be preferred over the apply version given that its performance, especially in the one argument case, is so much better.
Criticisms of that conclusion:- These are micro-benchmarks, they tell us little about how either version will really perform when the processor is doing more than just repeating the same operation over and over.
- This is premature optimization, there's no evidence that this difference will matter in practice.
- The apply version is simple, functional, and pretty. In the long run that matters more than performance.
Thoughts on the criticisms:micro-benchmark: that's right, although, combined with thinking about how the code works, it does give some indication of the performance difference which is not 10%, or 60%, but 2100%. This is library code that should run well in a large variety of circumstances. A factor of 22 in performance is hard to ignore in that scenario.
premature: that's right. It's only the "this is a low-level operation that may be used heavily in circumstances we don't yet imagine" argument that pushes me to consider this optimization in the absence of a real performance test that shows it matters.
simple: The apply version is certainly pretty. The version I posted, though, is also written in a straightforward way that I think is quite readable and understandable. It trades that last measure of simplicity for a healthy measure of improved performance.
Since arguing with myself is only so productive, I'd appreciate hearing other thoughts about these ideas.
--Steve
smime.p7s
Description: S/MIME cryptographic signature