On Wed, Dec 22, 2010 at 12:52 PM, Rayne <disciplera...@gmail.com> wrote:
> I have a piece of code, and I'd like to see how fast it can be.
>
> (defn count-num-chars [^String s]
>  (loop [s s acc 0]
>    (if (seq s)
>      (recur (rest s) (if (= (first s) \space) acc (inc acc)))
>      acc)))
>
> This is the fastest I've been able to get it. The function is very
> simple. It takes a string and counts the number of non-space
> characters inside of that string.
>
> I've been testing this code against a 460 non-space character string.
> Here is the entire source, benchmarking and all:
>
> (def s (apply str (repeat 20 "This is a really long string")))
>
> (defn count-num-chars [^String s]
>  (loop [s s acc 0]
>    (if (seq s)
>      (recur (rest s) (if (= (first s) \space) acc (inc acc)))
>      acc)))
>
> (println "Chars outputted:" (count-num-chars s))
>
> (let [before (System/nanoTime)]
>  (dotimes [_ 1000]
>    (count-num-chars s))
>  (let [after (System/nanoTime)]
>    (println "Time (in nanoseconds):" (/ (- after before) 1000.0))))
>
> Running it gives me around 137343.295 nanoseconds.

I get around half that.

Yours calls seq every loop. Writing

(defn count-num-chars [^String s]
 (loop [s (seq s) acc 0]
   (if s
     (recur (next s) (if (= (first s) \space) acc (inc acc)))
     acc)))

results in about 10% savings.

Using primitive math:

(defn count-num-chars [^String s]
 (loop [s (seq s) acc (int 0)]
   (if s
     (recur (next s) (if (= (first s) \space) acc (unchecked-inc acc)))
     acc)))

yields up another 10% or so.

That's maybe 40,000ns, still over 10x slower than the Java
implementations you mentioned, but it's about the limits of the
savings we'll get while still using the seq abstraction, I suspect. To
get closer to Java performance means getting closer to Java means of
doing it:

(defn count-num-chars [^String s]
  (let [l (int (.length s))]
    (loop [i (int 0) acc (int 0)]
      (if (< i l)
        (recur (unchecked-inc i) (if (= (.charAt s i) \space) acc
(unchecked-inc acc)))
        acc))))

15k nsec -- twice as fast as before. Still 5x slower than Java; I'm
not sure why. This is now directly equivalent to the obvious Java
implementation,

int l = s.length();
int acc = 0;
for (i = 0; i < l; ++i)
    if (s.charAt(i) == ' ') ++acc;

and since the string is type-hinted it shouldn't be using reflection
for the length or charAt calls.

-- 
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

Reply via email to