Nice! A few more days of work and I've time to play with these kind of
things again. Here are some comments, based on your description.

As game of life is a cellular automata, you do not need any blocking
at all, so you could use agents, rather than refs. It does become an
asynchronous CA then, but that fits for game of life :).

Every cell would be an agent. Agents read the state of their
neighbouring cells and update their own state. If world is a vector of
agents, then (doseq [a world] (send a update)) (apply await world)
would update the whole grid once, using all the processors that java
can find.

On Mar 16, 5:31 am, Scott Fraser <scott.e.fra...@gmail.com> wrote:
> I have taken Larry's "Game of Life" example that he originally posted
> here:
>
> http://groups.google.com/group/clojure/msg/fdfc88f1ba95bdee
>
> ...and updated it to use all the CPU's your JVM has access to. My
> first attempts ran into the classic map -> pmap slowdown. My next
> attempt had too much dosync, in that there were many threads but they
> were all waiting for each other.
>
> I finally rewrote the calc-state routine to batch the units of work
> into meaty sizes, and to minimize the dosync granularity. It gets the
> batches of new-state together, then goes and applies these in batches.
> In both events I use pmap.
>
> Now it is running really fast on my 4-core Intel i7. You will really
> notice a difference at larger grid sizes. On my i7 it keeps the 8 (due
> to hyperthreading) "cpus" busy at about 60% when I run a huge grid.
>
> I also updated paint-cells with a type hint that greatly reduces
> reflection in that performance critical code.
>
> I am very new to clojure and would appreciate feedback. I am concerned
> I may have overcomplicated things with my usage of the map/pmap form.
> I am guessing there may be a simpler way to write what I did.
>
> Note at the start it will automatically select how many "available-
> procs" you have. Try tweaking this on your hardware to see how it
> impacts performance. Watching the threads with a profiler is
> interesting.
>
> Here is is:
>
> (def cells (ref {}))
> (def running (ref false))
> ;(def x-cells ( * 32 4))
> ;(def y-cells ( * 48 4))
> (def x-cells 32)
> (def y-cells 32)
> (def range-cells (for [x (range x-cells) y (range y-cells)] [x y]))
> (def length-range-cells (count range-cells))
> (def cell-size 10)
> (def life-delay 0)
> (def life-initial-prob 3)
> (def available-procs (.. java.lang.Runtime getRuntime
> availableProcessors))
>
> (defn determine-initial-state [x y]
>   (= 0 (rand-int life-initial-prob)))
>
> (defn determine-new-state [x y]
>   (let [alive (count (for [dx [-1 0 1] dy [-1 0 1]
>                            :when (and (not (= 0 dx dy))
>                                    (cells [ (mod (+ x dx) x-cells)
> (mod (+ y dy) y-cells)]))]
>                        :alive))]
>     (if (cells [x y])
>       (< 1 alive 4)
>       (= alive 3))))
>
> (defn update-batch-of-new-cells [new-cells list-of-batches]
>   (dosync
>     (dorun (map #(commute new-cells assoc (first %) (second %))
>              list-of-batches))
>     ))
>
> (defn calc-batch-of-new-cell-states [cell-state batch-cells]
>   (doall (map
>            #(let [new-cell-state (cell-state (first %) (second %))]
>               [[(first %) (second %)] new-cell-state])
>            batch-cells)))
>
> (defn calc-state [cell-state]
>   (let [new-cells (ref {})]
>     (dorun (pmap #(update-batch-of-new-cells new-cells %)
>              (pmap #(calc-batch-of-new-cell-states cell-state %)
>                (for [cpu (range available-procs)] (take-nth available-
> procs (drop cpu range-cells))))))
>     (dosync (ref-set cells @new-cells))))
>
> (defn paint-cells [#^java.awt.Graphics graphics]
>   (doseq [[[x,y] state] @cells]
>     (doto graphics
>       (. setColor (if state Color/RED Color/WHITE))
>       (. fillRect (* cell-size x) (* cell-size y) cell-size cell-
> size))))
>
> (defn toggle-thread [#^JPanel panel button]
>   (if @running
>     (do (dosync (ref-set running false))
>       (. button (setText "Start")))
>     (do (dosync (ref-set running true))
>       (. button (setText "Stop"))
>       (. (Thread.
>            #(loop []
>               (calc-state determine-new-state)
>               (. panel repaint)
>               (if life-delay (Thread/sleep life-delay))
>               (if @running (recur))))
>         start))))
>
> (defn -main[]
>
>   (calc-state determine-initial-state)
>
>   (let [f (JFrame.)
>         b (JButton. "Start")
>         panel (proxy [JPanel] [] (paint [graphics] (paint-cells
> graphics)))]
>
>     (doto f
>       (. setLayout (BorderLayout.))
>       (. setLocation 100 100)
>       (. setPreferredSize (Dimension. (* cell-size x-cells) (+ 60 (*
> cell-size y-cells))))
>       (. add b BorderLayout/SOUTH)
>       (. add panel BorderLayout/CENTER)
>       (. setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
>       (. pack)
>       (. setVisible true))
>
>     (. b addActionListener
>       (proxy [ActionListener] []
>         (actionPerformed [evt] (toggle-thread panel b))))))
--~--~---------~--~----~------------~-------~--~----~
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
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