David Nolen <dnolen.li...@gmail.com> writes:

Hi David,

>> I think it might be a good idea to discuss why the Java interfaces
>> are created for protocols.
>
> I don't see how polyfns can be as fast as protocol functions that are
> defined inline and thus backed by an Java interface. It has not been
> my experience that this is true at all. extending to a type later is
> expressive but takes a measurable performance hit as far as I've seen.

Just tested that.

--8<---------------cut here---------------start------------->8---
user> (defprotocol PFoo (foo [this]))
PFoo
user> (extend-protocol PFoo
        String (foo [this] :string)
        Number (foo [this] :number))
nil
user> (let [x 1, y "foo"]
        (time (dotimes [_ 1000000]
                (foo x) (foo y))))
"Elapsed time: 69.700106 msecs"
nil
user> (deftype X [] PFoo (foo [this] :x))
user.X
user> (deftype Y [] PFoo (foo [this] :y))
user.Y
user> (let [x (->X), y (->Y)]
        (time (dotimes [_ 1000000]
                (foo x) (foo y))))
"Elapsed time: 7.794859 msecs"
nil
--8<---------------cut here---------------end--------------->8---

So indeed type dispatch with inline implementations seems to be nearly
an order of magnitude faster than the cache-lookup based dispatch you
have when extending protocols using extend.

And multimethods are even much slower:

--8<---------------cut here---------------start------------->8---
user> (defmulti multifoo class)
nil
user> (defmethod multifoo String [s] :string)
#<MultiFn clojure.lang.MultiFn@3ae95911>
user> (defmethod multifoo Number [n] :number)
#<MultiFn clojure.lang.MultiFn@3ae95911>
user> (let [x 1, y "x"]
        (time (dotimes [_ 1000000]
                (multifoo x) (multifoo y))))
"Elapsed time: 348.201964 msecs"
nil
user> (let [x 1, y "x"]
        (time (dotimes [_ 1000000]
                (multifoo x) (multifoo y))))
"Elapsed time: 338.420101 msecs"
nil
--8<---------------cut here---------------end--------------->8---

Well, but I still see a use-case for polyfns namely in the situation
where you'd usually use a protocol and extend it to existing (java)
types you have no control over, and you don't need the generated
interface.  That applies to many of my protocols.

But on the other hand, many of my protocols aren't open but merely an
implementation detail, and they are extended upon only a few java
classes (many only 2).  For those it seems to be more efficient to use a
plain function explicitly dispatching on type using `instance?'...

--8<---------------cut here---------------start------------->8---
user> (defn foofn [x]
        (cond
          (instance? String x) :string
          (instance? Number x) :number
          :else (throw (RuntimeException. "BANG"))))
#'user/foofn
user> (time (let [o1 1, o2 "x"]
              (dotimes [_ 1000000]
                (foofn o1) (foofn o2))))
"Elapsed time: 13.263767 msecs"
nil
--8<---------------cut here---------------end--------------->8---

Bye,
Tassilo

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