(defmacro defchunk [name tps]
  `(def ~name (quote ~tps)))

(defmacro let-chunk [vname name val-vec & body]
  (let [chunk-def @(resolve name)
        types (map first chunk-def)
        part-names (map (comp symbol (partial str vname "!") second) chunk-def)]
    `(let [~vname ~val-vec
           ~@(interleave part-names (map #(list %1 (list vname %2))
types (iterate inc 0)))]
       ~...@body)))


user=> (defchunk foo [[int x][double y]])
#'user/foo
user=> (let-chunk afoo foo [1 1.7] afoo)
[1 1.7]
user=> (let-chunk afoo foo [1 1.7] afoo!x)
1
user=> (let-chunk afoo foo [1 1.7] afoo!y)
1.7

Simple enough, and afoo!x and afoo!y are primitive.

(defchunk complex [[double real][double imag]])

(defn cx-mult [w z]
  (let-chunk w complex w
    (let-chunk z complex z
      [(- (* w!real z!real) (* w!imag z!imag))
       (+ (* w!imag z!real) (* w!real z!imag))])))

(after a few runs to get it all JITted)

user=> (time (nth (iterate #(cx-mult % %) [0.0 1.0]) 10000))
"Elapsed time: 14.12232 msecs"
[1.0 -0.0]

Looks like one iteration taking less than 2 microseconds to me. And
that's with iterate. Loop gives this:

user=> (time
  (loop [c [0.0 1.0] n 10000]
    (if (= n 0)
      c
      (recur (cx-mult c c) (dec n)))))
"Elapsed time: 2.54112 msecs"
[1.0 -0.0]

Interestingly, using definline to define cx-mult slows this down
instead of speeding it up.

Another macro could give you a super-defn that let-chunks certain
function parameters, so the (let-chunk foo bar foo ...) stuff wrapping
the body of cx-mult disappears into a macro. Yet another could give
you a loop-chunk.

For truly high performance, though, the vector boxing is a problem.
JIT doesn't seem to be eliminating it, or the speed would be up to
1000x faster still. Defstruct/defrecord is probably what's needed
here; this would mean a) modifying defchunk to emit a suitable
defstruct/defrecord in addition to the type-partname pairs structure
and b) making the other macros destructure these instead of vectors.

But the above shows a working, if crude, first pass at implementing
the facility under discussion in this thread. Replacing vectors with
structmaps/records in it will probably produce a large speedup when
the things are passed across function boundaries.

The remaining bugbear will be that the let-chunk and loop-chunk will
create a heap allocated structmap/record even if it's unused. Having
the macros detect if it's unused might not be easy.

I'm unsure, though, that heap allocation is as horrid as everyone here
seems to be assuming it to be. It may be horribly slow in FORTRAN or
even in C but modern JVMs make heap allocation very cheap. I wouldn't
be surprised if using heap-allocated structmaps or records directly
will do fine. There are also supposed to be more optimizations for
primitive use, including passing across function boundaries, in the
forthcoming Clojure 1.3.

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