So it looks I made a young programmer mistake (and I am not that young
anymore!) I *assumed* that creating a defrecord would result in faster code
than extending an existing one and therefore was looking to perform an early
(and thus unnecessary) optimization. According to the code and results
below, it would seem that I was wrong by an order of magnitude no less and
that calling the method on an extended protocol is over 10x faster than
calling the same method on a record . I have reviewed this test a few times,
but I can't see any flaw.

Toni.

(defprotocol P
  (m1 [this p11])
  (m2 [this p21 p22]))

(defrecord simple-P-impl []
  P
  (m1 [this p11] (str "simple m1 called with p11=" p11) nil ) ;; returning
nil on all methods so that I don't get a lot of output and end up measuring
how fast is Emacs at scrolling the REPL.
  (m2 [this p21 p22] (str "simple m2 called with p21=" p21 "and p22=" p22)
nil))

(defrecord empty-record [])

(extend empty-record
  P {:m1 (fn [this p11] (str "extended m1 called with p11=" p11) nil)
     :m2 (fn [this p21 p22] (str "extended m2 called with p21=" p21 "and
p22=" p22) nil)})

(def my-empty-record (empty-record.))
(def my-simple-P (simple-P-impl.))

(dotimes [_ 10] (time (dotimes [_ 10000] (.m1 my-simple-P "hello")))) ;

"Elapsed time: 131.973 msecs"
"Elapsed time: 142.72 msecs"
"Elapsed time: 95.51 msecs"
"Elapsed time: 95.724 msecs"
"Elapsed time: 83.646 msecs"
"Elapsed time: 97.44 msecs"
"Elapsed time: 79.077 msecs"
"Elapsed time: 94.275 msecs"
"Elapsed time: 78.541 msecs"
"Elapsed time: 95.75 msecs"

(dotimes [_ 10] (time (dotimes [_ 10000] (m1 my-empty-record "hello"))))

"Elapsed time: 6.257 msecs"
"Elapsed time: 35.18 msecs"
"Elapsed time: 5.674 msecs"
"Elapsed time: 2.615 msecs"
"Elapsed time: 3.372 msecs"
"Elapsed time: 5.169 msecs"
"Elapsed time: 3.651 msecs"
"Elapsed time: 3.429 msecs"
"Elapsed time: 4.706 msecs"
"Elapsed time: 2.878 msecs"


(note: similar results if calling m2)

On Wed, Jul 21, 2010 at 11:32 AM, Toni Batchelli <tbatche...@gmail.com>wrote:

> Hi Meikel,
>
> This is awesome! You just did a big chunk of what I was about to try
> to do :). Sorry for the late response, I've been off the grid for a
> few days...
>
> With your second proposed solution, the defrecord-with-defaults macro,
>  one can achieve very good performance while keeping some of the
> features that implementation inheritance provides. I'll look into your
> macro more closely and see if I can make it a seamless substitute for
> defmacro.
>
> Toni.
>
> On Sat, Jul 17, 2010 at 4:02 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
> > Hi,
> >
> > one way to do that is using extend.
> >
> > (def defaults
> >  {fn1 (fn ...)
> >   fn2 (fn ...)
> >   fn3 (fn ...)})
> >
> > (defrecord R1 [...])
> > (def R1-fns
> >  {fn1 (fn ...)})
> >
> > (defrecord R2 [...])
> > (def R2-fns
> >  {fn2 (fn ...)
> >   fn3 (fn ...)})
> >
> > (extend YourProtocol
> >  R1 (merge defaults R1-fns)
> >  R2 (merge defaults R2-fns))
> >
> > Another way is the following macro:
> >
> > (defmacro defrecord-with-defaults
> >  [record fields & protocol-default-fns]
> >  (let [process-protocol
> >        (fn [[protocol defaults fns]]
> >          (let [defaults (when defaults
> >                           (->> defaults
> >                             resolve
> >                             var-get
> >                             (map (fn [[k f]]
> >                                    (vector
> >                                      k (cons (symbol (name k)) (next
> f)))))
> >                             (into {})))]
> >            (list* protocol
> >                   (->> fns
> >                     (map #(array-map (-> % first name keyword) %))
> >                     (apply merge defaults)
> >                     (map second)))))
> >        split-protocols
> >        (fn split-protocols
> >          [p-d-fs]
> >          (lazy-seq
> >            (when-let [[p d & fs] (seq p-d-fs)]
> >              (let [[fs next-p-d-fs] (split-with (complement symbol?) fs)]
> >                (cons [p d fs] (split-protocols next-p-d-fs))))))]
> >    `(defrecord ~record ~fields
> >       ~@(mapcat process-protocol (split-protocols
> protocol-default-fns)))))
> >
> > Usage:
> >
> > (def foo-defaults
> >  `{:bar (fn ([this# that#] nil))
> >    :baz (fn [this# that#] nil)})
> >
> > (defrecord-with-defaults R [a b]
> >  Foo foo-defaults
> >  (bar [this that] nil)
> >  Frob nil
> >  (frobnicator [this] nil))
> >
> > Note: you have to syntax-quote the functions in the default map (as for
> definline). And you can have only one arity per function. Here the
> corresponding expansion:
> >
> > (clojure.core/defrecord FooImpl1 [a b]
> >  Foo
> >  (bar [this that] nil)
> >  (baz [this__711__auto__ that__712__auto__] nil)
> >  Frob
> >  (frobnicator [this] nil))
> >
> > Sincerely
> > Meikel
> >
> > --
> > 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<clojure%2bunsubscr...@googlegroups.com>
> > For more options, visit this group at
> > http://groups.google.com/group/clojure?hl=en
>
>
>
> --
> Antoni Batchelli
> - twitter: @tbatchelli , @disclojure
> --- email: tbatche...@gmail.com
> ----- web: tbatchelli.org , disclojure.org
>



-- 
Antoni Batchelli
- twitter: @tbatchelli , @disclojure
--- email: tbatche...@gmail.com
----- web: tbatchelli.org , disclojure.org

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