Hello, 2009/3/16 bOR_ <boris.sch...@gmail.com>
> > 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. I'm not very used to concurrent programming, so I have a few questions you may find naïve, but well, let's just pretend they're interesting ... : It seems to me that the game of life works in "increments" of its "world". So I don't see at first what can be gained by using one agent for each cell. Because then, you'll still have to coordinate the work of the agents in your code. I suspect you suggested to use agents because by using agents, you gain an abstraction for parallel programming for free, and that you are not using agent for what I think they are for in the first place: asynchronized computing. Regards, -- Laurent > > 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 -~----------~----~----~----~------~----~------~--~---