I've been running the genetic programming system that I mentioned in an earlier post (http://hampshire.edu/lspector/clojush/) on a dual quad-core mac and seeing some very nice concurrency, with up to 1300% CPU utilization in some configurations. (Apparently there's some magic that lets the 8 cores in this machine act like 16 to some extent -- I don't know the details but it's nice.)
However, sometimes I get more and sometimes I get less, and I also get decreasing amounts of it over time, which I find puzzling. For example, running the system on a hard problem over night last night I was initially getting peaks of about 1100% CPU (between the synchronization bottlenecks), but this morning it was still running but the peaks were only about 300%. The things that are happening concurrently in this program never have to write to the same places, but sometimes they do have to read from the same places and I'm wondering if this is responsible for some of what I'm seeing. I think that there may be two different issues here: 1. I declare a bunch of parameters globally as vars with def, and these are accessed (but never changed) by code that will be running in different threads. Is this a possible source of slowdown? Thinking that it might be I tried using "binding" to create thread-local copies but this didn't help, possibly (I'm reaching here) because a similar slowdown occurs when all of the threads (of which there are several thousand, all initiated by "send"s to agents) try to read the same vars to create their local bindings. Or maybe I'm missing something here, but I do think that my initial peak utilizations have been a little lower since I added a few more of these vars, so I'd like to know if this kind of reading from the same var from multiple threads has any cost that I could avoid. BTW these vars are all constants after I set them in my main thread. 2. On the gradual slowdowns, my theory is that this may be related to increased sharing of structure over time. I'm evolving programs (in the Push language) which are represented as nested lists, and over time these are mutated in multiple ways (in the genetic algorithms sense of mutation, not in the programming languages sense) and also recombined. So the programs in the population share more and more list structure as the generations proceed. The persistent data structures in Clojure make this very nice insofar as I don't have to worry that it's unsafe and there's little cost in terms of time or memory for copying anything. But is it degrading my concurrency? After a couple hundred generations most programs will share structure with many many others, and so the chances are good that multiple threads will simultaneously be walking parts of the same structures (for reading only). Might this also be slowing things down? If so then it might be worthwhile to pay the up-front cost of making complete copies of the Push programs whenever I mutate or recombine them, to avoid the cost of multiple reads. But I don't know (A) if it's even possible that this is what's causing the slowdown or (B) how to make a copy of a (nested) list in Clojure! I realize this is all probably pretty naive -- it's my first foray into this kind of concurrent programming -- but I'd appreciate any advice or pointers that you can provide. Thanks, -Lee -- Lee Spector, Professor of Computer Science School of Cognitive Science, Hampshire College 893 West Street, Amherst, MA 01002-3359 [email protected], http://hampshire.edu/lspector/ Phone: 413-559-5352, Fax: 413-559-5438 Check out Genetic Programming and Evolvable Machines: http://www.springer.com/10710 - http://gpemjournal.blogspot.com/ -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to [email protected] Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/clojure?hl=en
