Hey Aria,

If you really want a mutable Counter, IMO you might as well do things
your "Java" way with a HashMap ... or at least that's how I would do
it.

A more Clojure way would be a totally safe, functional counter:

(deftype ClojureCounter2 [counts total]
  Counter
    (getCount [k] (get counts k 0.0))
    (incCount! [k v] (ClojureCounter2. (assoc counts k (+ v (get
counts k 0.0))) (+ total v)))
    (totalCount [] total))

(defn make-cc2 []
  (ClojureCounter2 {} 0.0))

And, by my measurements, it's twice as fast as your mutable Clojure
version when run in the following harness:

(defn testFunctionalCounter [counter]
  (let [r (java.util.Random. 0)]
    (loop [counter counter i (int 1000000)]
      (when-not (== i (int 0))
        (recur (incCount! counter (.nextInt r 100000) (.nextDouble r))
(dec i))))))

If you want to keep to a functional style but don't care if old copies
of the counter get trashed (be careful!), you can easily switch to
transients:

(deftype ClojureCounter3 [counts total]
  Counter
    (getCount [k] (get counts k 0.0))
    (incCount! [k v] (ClojureCounter3. (assoc! counts k (+ v (get
counts k 0.0))) (+ total v)))
    (totalCount [] total))

(defn make-cc3 []
  (ClojureCounter3 (transient {}) 0.0))

which gives you another > 2x speedup, bringing it to within ~50% of
the Java version.

HTH, Jason


On Feb 17, 2:40 am, aria42 <ari...@gmail.com> wrote:
> Hi all, I was playing with the defprotocol/deftype/reify stuff in 1.2,
> and I wanted to test how a common abstraction I use would look if I
> did with java data structures vs. clojure ones. I've pasted the code
> below. I'll wait for you to take a look....So on my test, I have the
> Clojure version about 5x slower than the Java one only one cpu. Now I
> know that JavaMapCounter isn't thread safe, while ClojureCounter is,
> and that if I had sufficiently many cpus, the clojure version would be
> faster. But is there a better way to do what I'm doing with clojure to
> get the performance a bit more up to par with javas?
>
> Thanks, Aria
>
> (defprotocol Counter
>   (getCount [_ k])
>   (incCount! [_ k v])
>   (totalCount [_]))
>
> (defn JavaMapCounter []
>   (let [counts (java.util.HashMap.)
>         total (org.apache.commons.lang.mutable.MutableDouble.)]
>     (reify :as self
>      Counter
>      (getCount [k] (.get counts k))
>      (incCount! [k v]
>        (let [cur-v (if-let [x (getCount self k)] x 0.0)]
>         (.put counts k (+ v cur-v)))
>        (.setValue total (+ (.doubleValue total) v)))
>      (totalCount [] (.doubleValue total)))))
>
> (defn ClojureCounter []
>   (let [state (atom {:counts (hash-map) :total 0.0})]
>     (reify :as self
>      Counter
>      (getCount [k] (if-let [x (get-in @state [:counts,k])] x 0.0))
>      (incCount! [k v]
>        (swap! state
>          (fn [data]
>            (let [add-v (fn [x] (if x (+ x v) v))]
>               (-> data (update-in  [:counts,k] add-v)
>                        (update-in [:total] add-v))))))
>      (totalCount [] (:total @state)))))
>
> (defn testCounter [counter]
>   (let [r (java.util.Random. 0)]
>     (dotimes  [_ 1000000]
>       (incCount! counter (.nextInt r 100000) (.nextDouble r)))))
>
> (time (testCounter (JavaMapCounter)))
> (time (testCounter (ClojureCounter)))

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