Functions can only be compared by identity, not value, eg (not=
(constantly 1) (constantly 1)). When you attach metadata to a
function, you cause it to no longer be = to other non-meta instances
of that function, so memoize can no longer use previous values.

user=> (def mp (memoize println))
#'user/mp
user=> (def x (constantly 1))
#'user/x
user=> (mp x)
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@11e31ea>
nil
user=> (mp x)
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@507369>
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@1d71e34>
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@1fac733>
nil

Note that the printed representation of the function is changing, too:
it has a different @address because it is a new object with different
metadata.

On Mar 10, 3:33 pm, Tassilo Horn <tass...@member.fsf.org> wrote:
> Hi all,
>
> I'm struggeling with some really strange problem.  I have a function
> `p-apply', which can be used find all reachable vertices in a graph
> matching a regular expression modeled as nested vector of functions, to
> which the results are chained through.  Here's an example call:
>
>   (p-apply v1 [p-seq --> --> [p-alt --<>
>                                     [p-+ [--> :cls 'Foo]]]])
>
> That means, starting from v1 you first have to traverse 2 outgoing
> edges, and then you may either traverse an aggregation from part to
> whole side, or you may iterate one or many outgoing edges of type Foo.
>
> Sometimes, memoization can bring a performance win, so I use a global
> var with a macro to dispatch to internal p-apply functions.
>
> Here's the relevant parts of the code.  You can basically jump to the
> last function.
>
> --8<---------------cut here---------------start------------->8---
> (def ^{:private true}
>   *memoize-path-eval* false)
>
> (defn- p-apply-normal
>   [v p]
>   (cond
>    ;; funs -->
>    (fn? p) (p v)
>    ;; funs with params [--> :cls 'Foo]
>    (coll? p) (apply (first p) v (next p))
>    ;; adjacences / role names
>    (or (string? p) (keyword? p) (symbol? p))
>    (let [vs (into-oset v)]
>      (apply into-oset (map #(adjacences % p) vs)))
>    :else (throw (RuntimeException.
>                  (format "Don't know how to apply %s."
>                          p)))))
>
> (def ^{:private true}
>   p-apply-memoized
>   (memoize p-apply-normal))
>
> (defmacro with-path-eval-memoization
>   [& body]
>   `(binding [*memoize-path-eval* true]
>      ~@body))
>
> (defn- p-apply-internal
>   [v p]
>   (if *memoize-path-eval*
>     (let [vs (into-oset v)
>           r (map #(p-apply-memoized % p) vs)]
>       (if (seq r)
>         (apply into-oset r)
>         (ordered-set)))
>     (p-apply-normal v p)))
>
> (defn p-apply
>   [v p]
>   (let [counter (atom -1)]
>     (p-apply-internal
>      v
>
>      ;;; Using p with metadata somehow makes things SLOW
>      ;(clojure.walk/postwalk #(if (fn? %)
>      ;                              (with-meta % {:state (swap! counter inc)})
>      ;                              %) p)
>
>      ;;; using just p is FAST
>      p
>      )))
> --8<---------------cut here---------------end--------------->8---
>
> Because I'd like to extend the search to collect also the shortest paths
> to all reachable vertices that conform the given regular expression, I
> wanted to put metadata on the function symbols that are given to
> `p-apply'.
>
> But as soon as I comment `p' and uncomment the `postwalk' call returning
> `p' with metadata on the function symbols, memoization seems to stop
> working.
>
> With the code above (`p' passed to `p-apply-internal'), I get these
> timings with memoization turned on:
>
> With memoization
> ================
> cbo-q-sequential
> "Elapsed time: 6805.37119 msecs"
> cbo-q-parallel
> "Elapsed time: 212.276041 msecs"
>
> With memoization 2
> ==================
> cbo-q-sequential
> "Elapsed time: 178.102362 msecs"
> cbo-q-parallel
> "Elapsed time: 172.027562 msecs"
>
> So the first function, which in effect fills the memoization cache and
> doesn't much benefit from it itself runs ~7 secs, but repeating the
> queries is lightning fast.
>
> If I comment `p' and pass it first through `postwalk' to put metadata on
> the functions, then the timings are these:
>
> With memoization
> ================
> cbo-q-sequential
> "Elapsed time: 8247.753366 msecs"
> cbo-q-parallel
> "Elapsed time: 8962.930698 msecs"
>
> With memoization 2
> ==================
> cbo-q-sequential
> "Elapsed time: 11010.302911 msecs"
> cbo-q-parallel
> "Elapsed time: 9098.196259 msecs"
>
> What's the matter?  Why does it get that slow only because of metadata?
>
> 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