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 <[email protected]> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en