Ah - one mystery down. Thanks Andy! -- paul.butcher->msgCount++
Snetterton, Castle Combe, Cadwell Park... Who says I have a one track mind? http://www.paulbutcher.com/ LinkedIn: http://www.linkedin.com/in/paulbutcher MSN: p...@paulbutcher.com AIM: paulrabutcher Skype: paulrabutcher On 28 Sep 2013, at 16:26, Andy Fingerhut <andy.finger...@gmail.com> wrote: > I do not know about the most important parts of your performance > difficulties, but on a more trivial point I might be able to shed some light. > > See the ClojureDocs page for pmap, which refers to the page for future, > linked below. If you call (shutdown-agents) the 60-second wait to exit > should go away. > > http://clojuredocs.org/clojure_core/clojure.core/future > > Andy > > Sent from my iPhone > > On Sep 28, 2013, at 1:41 AM, Paul Butcher <p...@paulbutcher.com> wrote: > >> On 28 Sep 2013, at 00:27, Stuart Halloway <stuart.hallo...@gmail.com> wrote: >> >>> I have posted an example that shows partition-then-fold at >>> https://github.com/stuarthalloway/exploring-clojure/blob/master/examples/exploring/reducing_apple_pie.clj. >>> >>> I would be curious to know how this approach performs with your data. With >>> the generated data I used, the partition+fold and partition+pmap approaches >>> both used most of my cores and had similar perf. >> >> Hey Stuart, >> >> Thanks for getting back to me. >> >> I've updated the code for my word count example on GitHub with (I believe) >> something that works along the lines you suggest: >> >> https://github.com/paulbutcher/parallel-word-count >> >> Here are some sample runs on my machine (a 4-core retina MacBook Pro). Each >> of these runs counts the words in the first 10000 pages of Wikipedia: >> >>> $ lein run 10000 ~/enwiki.xml sequential >>> "Elapsed time: 23630.443 msecs" >>> $ lein run 10000 ~/enwiki.xml fold >>> "Elapsed time: 8697.79 msecs" >>> $ lein run 10000 ~/enwiki.xml pmap 10000 >>> "Elapsed time: 27393.703 msecs" >>> $ lein run 10000 ~/enwiki.xml pthenf 10000 >>> "Elapsed time: 37263.193 msecs" >> >> As you can see, the the foldable-seq version gives an almost 3x speedup >> relative to the sequential version, and both the partition-then-pmap and >> partition-then-fold versions are significantly slower. >> >> The last argument for the partition-then-pmap and partition-then-fold >> versions is a partition size. I've tried various different sizes with no >> material effect: >> >>> $ lein run 10000 ~/enwiki.xml pthenf 1000 >>> "Elapsed time: 43187.385 msecs" >>> $ lein run 10000 ~/enwiki.xml pthenf 100000 >>> "Elapsed time: 35702.651 msecs" >>> $ lein run 10000 ~/enwiki.xml pmap 1000 >>> "Elapsed time: 34087.314 msecs" >>> $ lein run 10000 ~/enwiki.xml pmap 100000 >>> "Elapsed time: 47340.969 msecs" >> >> The performance of the partition-then-pmap version is actually much worse >> than the numbers above suggest. There's something very weird going on with >> (I guess) garbage collection - it takes a *long* time to finish after >> printing the elapsed time and the performance is completely pathological >> with larger page counts. >> >> Bottom line: the only way I've been able to obtain any kind of speedup >> remains foldable-seq. >> >> I'd be very grateful indeed if you could take a look at how I've implemented >> partition-then-fold to make sure that I've correctly captured your intent. >> Or if you have any suggestions for anything else that might work, or to >> explain the poor performance of partition-then-pmap and partition-then-fold. >> >> My guess is that the problem with partition-then-fold is the copying that's >> going on during the (into [] %). I can see that it is operating in parallel >> because the number of cores in use goes up, but the net effect is an overall >> slowdown rather than a speedup. >> >> That it performs worse than foldable-seq isn't surprising to me, given that >> it introduces an unnecessary copy. >> >> I still think that it's a crying shame to disallow folding over sequences - >> as the above shows, the gains both in performance and programming ease are >> significant, and it would only take a small modification to the reducers API >> to fix the holding-onto-head problem. What would be the downside of making >> this modification and allowing foldable sequences? >> >> -- >> paul.butcher->msgCount++ >> >> Snetterton, Castle Combe, Cadwell Park... >> Who says I have a one track mind? >> >> http://www.paulbutcher.com/ >> LinkedIn: http://www.linkedin.com/in/paulbutcher >> MSN: p...@paulbutcher.com >> AIM: paulrabutcher >> Skype: paulrabutcher >> >> On 28 Sep 2013, at 00:27, Stuart Halloway <stuart.hallo...@gmail.com> wrote: >> >>> Hi Paul, >>> >>> I have posted an example that shows partition-then-fold at >>> https://github.com/stuarthalloway/exploring-clojure/blob/master/examples/exploring/reducing_apple_pie.clj. >>> >>> I would be curious to know how this approach performs with your data. With >>> the generated data I used, the partition+fold and partition+pmap approaches >>> both used most of my cores and had similar perf. >>> >>> Enjoying your book! >>> >>> Stu >>> >>> >>> On Sat, May 25, 2013 at 12:34 PM, Paul Butcher <p...@paulbutcher.com> wrote: >>> I'm currently working on a book on concurrent/parallel development for The >>> Pragmatic Programmers. One of the subjects I'm covering is parallel >>> programming in Clojure, but I've hit a roadblock with one of the examples. >>> I'm hoping that I can get some help to work through it here. >>> >>> The example counts the words contained within a Wikipedia dump. It should >>> respond well to parallelisation (I have Java and Scala versions that >>> perform excellently) but I've been incapable of coming up with a nice >>> solution in Clojure. >>> >>> The code I'm working with is checked into GitHub: >>> >>> The basic sequential algorithm is: >>> >>>> (frequencies (mapcat get-words pages)) >>> >>> >>> If I run that on the first 10k pages in Wikipedia dump, it takes ~21s on my >>> MacBook Pro. >>> >>> One way to parallelise it is to create a parallel version of frequencies >>> that uses reducers: >>> >>>> (defn frequencies-fold [words] >>>> (r/fold (partial merge-with +) >>>> (fn [counts word] (assoc counts word (inc (get counts word 0)))) >>>> >>>> words)) >>> >>> And sure enough, if I use that, along with use the foldable-seq utility I >>> posted about here are while ago it runs in ~8s, almost a 3x speedup, not >>> bad given that the parallel version is unable to use transients. >>> >>> Unfortunately, as they currently stand, reducers have a fatal flaw that >>> means that, even with foldable-seq, they're basically useless with lazy >>> sequences. Reducers always hold onto the head of the sequence they're >>> given, so there's no way to use this approach for a complete Wikipedia dump >>> (which runs to around 40GiB). >>> >>> So the other approach I've tried is to use pmap: >>> >>>> (defn frequencies-pmap [words] >>>> >>>> (reduce (partial merge-with +) >>>> >>>> (pmap frequencies >>>> >>>> (partition-all 10000 words)))) >>>> >>> >>> But, for reasons I don't understand, this performs dreadfully - taking >>> ~26s, i.e. significantly slower than the sequential version. >>> >>> I've tried playing with different partition sizes without materially >>> affecting the result. >>> >>> So, what I'm looking for is either: >>> >>> a) An explanation for why the pmap-based solution performs so badly >>> >>> b) A way to fix the "holding onto head" problem that's inherent within >>> reducers. >>> >>> With the last of these in mind, it strikes me that the problem >>> fundamentally arises from the desire for reducers to follow the same basic >>> API as "normal" code. So: >>> >>> (reduce (filter ... (map ... coll))) >>> >>> becomes: >>> >>> (r/fold (r/filter ... (r/map ... coll))) >>> >>> A very small change to the reducers API - passing the collection to the >>> reduce and/or fold - would avoid the problem: >>> >>> (r/fold (r/filter ... (r/map ...)) coll) >>> >>> Anyway - I'd be very grateful for any insight into either of the above >>> questions. Or for suggestions for an alternative approach that might be >>> more fruitful. >>> >>> Many thanks in advance, >>> >>> -- >>> paul.butcher->msgCount++ >>> >>> Snetterton, Castle Combe, Cadwell Park... >>> Who says I have a one track mind? >>> >>> http://www.paulbutcher.com/ >>> LinkedIn: http://www.linkedin.com/in/paulbutcher >>> MSN: p...@paulbutcher.com >>> AIM: paulrabutcher >>> Skype: paulrabutcher >>> >>> >>> -- >>> -- >>> 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 >>> --- >>> You received this message because you are subscribed to the Google Groups >>> "Clojure" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to clojure+unsubscr...@googlegroups.com. >>> For more options, visit https://groups.google.com/groups/opt_out. >>> >>> >>> >>> >>> -- >>> -- >>> 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 >>> --- >>> You received this message because you are subscribed to the Google Groups >>> "Clojure" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to clojure+unsubscr...@googlegroups.com. >>> For more options, visit https://groups.google.com/groups/opt_out. >> >> >> -- >> -- >> 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 >> --- >> You received this message because you are subscribed to the Google Groups >> "Clojure" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to clojure+unsubscr...@googlegroups.com. >> For more options, visit https://groups.google.com/groups/opt_out. > > > -- > -- > 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 > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. -- -- 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 --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.