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