{"id":1456,"date":"2009-06-29T12:19:39","date_gmt":"2009-06-29T11:19:39","guid":{"rendered":"http:\/\/www.walkingrandomly.com\/?p=1456"},"modified":"2009-07-02T10:37:59","modified_gmt":"2009-07-02T09:37:59","slug":"a-parallel-programming-tip-for-mathematica","status":"publish","type":"post","link":"https:\/\/walkingrandomly.com\/?p=1456","title":{"rendered":"A Parallel programming tip for Mathematica"},"content":{"rendered":"<p>Someone emailed me recently complaining that the ParallelTable function in Mathematica didn&#8217;t work.\u00a0 In fact it made his code slower!\u00a0 Let&#8217;s take a look at an instance where this can happen and what we can do about it.  I&#8217;ll make the problem as simple as possible to allow us to better see what is going on.<\/p>\n<p>Let&#8217;s say that all we want to do is generate a list of the prime numbers as quickly as possible using Mathematica.\u00a0 The code to generate the first 20 million primes is very straightforward.<\/p>\n<pre>\r\nprimelist = Table[Prime[k], {k, 1, 20000000}];\r\n<\/pre>\n<p>To time how long this list takes to calculate we can use the AbsoluteTime function as follows.<\/p>\n<pre>\r\nt = AbsoluteTime[];\r\nprimelist = Table[Prime[k], {k, 1, 20000000}];\r\ntime2 = AbsoluteTime[] - t\r\n<\/pre>\n<p>This took about 63 seconds on my machine.  Taking a quick look at the Mathematica documentation we can see that the parallel version of Table is, obviously enough, ParallelTable.  What could be simpler?  So, to spread this calculation over both processors in a dual-core laptop we simply need to do the following<\/p>\n<pre>\r\nt = AbsoluteTime[];\r\nprimelist = ParallelTable[Prime[k], {k, 1, 20000000}];\r\ntime2 = AbsoluteTime[] - t\r\n<\/pre>\n<p>The result?  98 seconds!\u00a0 So, it seems that the parallel version is more than <strong>50% SLOWER<\/strong>!  My correspondent was right &#8211; ParallelTable doesn&#8217;t seem to work at all.  <\/p>\n<p>Before we send a message to Wolfram Tech Support though, let&#8217;s dig a little deeper.  It turns out that a single evaluation of<br \/>\nPrime[k] for any given k is very quick &#8211; even for high k.  Prime[100000000] evaluates in a tiny fraction of a second for example (try it! &#8211; it really is astonishingly quick.)<\/p>\n<p>What I suspect is happening here is that when you move to the parallel version, the kernels spend most of the time communicating with each other rather than actually calculating anything.\u00a0 I think that ParallelTable does something roughly like the following.  <\/p>\n<p>Kernel1:Give me a k.<br \/>\nMaster:have k=1.<br \/>\nKernel2:Give me a k.<br \/>\nMaster:have k=2<br \/>\nKernel1:I&#8217;ve done k=1 and the answer is 2, give me another k<br \/>\nMaster: have k=3<br \/>\nkernel2:I&#8217;ve done k=2 and the answer is 3, give me another k etc<\/p>\n<p>So, the kernels spend more time talking about the work that needs to be done rather than actually doing it.  Also, for various reasons, kernel1 and kernel2 might get out of sync and so the Master kernel may end up with a list out of order.\u00a0 If so then it will need to reorder the lists at the end of the calculations and so that&#8217;s even more time taken.<\/p>\n<p>So, my approach was to try and reduce the amount of communication between kernels to something like<\/p>\n<p>Master: Kernel 1 &#8211; you go away and get me first 10 million primes.  Don&#8217;t bother me until it&#8217;s done.<br \/>\nMaster: kernel 2 &#8211; you\u00a0go away and get me the second 10 million.  Don&#8217;t bother me until it&#8217;s done.<\/p>\n<p>The following Mathematica code does this.<\/p>\n<pre>\r\nt = AbsoluteTime[];\r\njob1 = ParallelSubmit[Table[Prime[k], {k, 1, 10000000}]];\r\njob2 = ParallelSubmit[Table[Prime[k], {k, 10000001, 20000000}]];\r\n{a1, a2} = WaitAll[{job1, job2}];\r\ntime2 = AbsoluteTime[] - t\r\n<\/pre>\n<p>This works in 40 seconds compared to the original time of 62 seconds.  Not a 2x speedup but not too shabby for so little extra work on our part.<\/p>\n<p>The moral of the story?  Make sure that your code spends more time actually doing work than it does just talking about it.<\/p>\n<p><strong>Disclaimer:<\/strong> I am still learning about Mathematica&#8217;s parallel tools myself so don&#8217;t take this stuff as gospel.  Also, there are almost certainly more efficient ways of getting a list of primes than using the Prime[k] function.  I am only using Prime[k] here because it is an example of a function that evaluates very quickly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Someone emailed me recently complaining that the ParallelTable function in Mathematica didn&#8217;t work.\u00a0 In fact it made his code slower!\u00a0 Let&#8217;s take a look at an instance where this can happen and what we can do about it. I&#8217;ll make the problem as simple as possible to allow us to better see what is going [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[4,8,7],"tags":[],"class_list":["post-1456","post","type-post","status-publish","format-standard","hentry","category-math-software","category-mathematica","category-programming"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3swhs-nu","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/1456","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1456"}],"version-history":[{"count":13,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/1456\/revisions"}],"predecessor-version":[{"id":1469,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/1456\/revisions\/1469"}],"wp:attachment":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}