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