<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/"></div></div><div><a data-url="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/" data-counturl="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/" data-count="horizontal" data-text="What is the cost of a progress bar in R?" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://peter.solymos.org/code/2016/09/11/what-is-the-cost-of-a-progress-bar-in-r.html"> Peter Solymos - R related posts</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>The <a target="_blank" href="http://cran.r-project.org/package=pbapply"><strong>pbapply</strong></a> R package adds progress bar to vectorized functions, like <code>lapply</code>. A <a target="_blank" href="https://github.com/psolymos/pbapply/issues/9">feature request</a> regarding progress bar for parallel functions has been sitting at the development GitHub repository for a few months. More recently, the author of the <a target="_blank" href="https://cran.r-project.org/web/packages/pbmcapply/index.html"><strong>pbmcapply</strong></a> package dropped a note about his implementation of forking functionality with progress bar for Unix/Linux computers, which got me thinking. How should we add progress bar to snow type clusters? Which led to more important questions: what is the real cost of the progress bar and how can we reduce overhead on process times?</p>
<h3>Parallel workflow</h3>
<p>Fist off, let’s review how a <a target="_blank" href="https://cran.r-project.org/web/packages/snow/index.html"><strong>snow</strong></a> type cluster works. (This is actually implemented in the <strong>parallel</strong> package, but I tend to refer to <strong>snow</strong> as that was the original package where the <strong>parallel</strong> code was taken from, see <a target="_blank" href="https://stat.ethz.ch/R-manual/R-devel/library/parallel/doc/parallel.pdf">here</a>.)</p>
<div>
<pre><code><span>library</span><span>(</span><span>parallel</span><span>)</span><span>
</span><span>parLapply</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## function (cl = NULL, X, fun, ...)
## {
##     cl &lt;- defaultCluster(cl)
##     do.call(c, clusterApply(cl, x = splitList(X, length(cl)),
##         fun = lapply, fun, ...), quote = TRUE)
## }
## &lt;bytecode: 0x7fa43b326fc0>
## &lt;environment: namespace:parallel>
</code></pre>
</div>
<p>The <code>cl</code> argument refers to a cluster object that can be conveniently set up via the <code>makeCluster</code> function. <code>X</code> is the ‘problem’ to be split among the <em>workers</em> defined by the cluster object. <code>fun</code> is the function that needs to be applied on elements of <code>X</code>. Pretty much the same as the <code>lapply</code> function except for the cluster argument.</p>
<p>Within the function, we see a nested expression that can be unpacked like this:</p>
<div>
<pre><code><span>x</span><span> </span><span>&lt;-</span><span> </span><span>splitList</span><span>(</span><span>X</span><span>,</span><span> </span><span>length</span><span>(</span><span>cl</span><span>))</span><span>
</span><span>z</span><span> </span><span>&lt;-</span><span> </span><span>clusterApply</span><span>(</span><span>cl</span><span>,</span><span> </span><span>x</span><span>,</span><span> </span><span>fun</span><span> </span><span>=</span><span> </span><span>lapply</span><span>,</span><span> </span><span>fun</span><span>)</span><span>
</span><span>do.call</span><span>(</span><span>c</span><span>,</span><span> </span><span>z</span><span>,</span><span> </span><span>quote</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>)</span><span>
</span></code></pre>
</div>
<p>The first line splits <code>X</code> so that it is now a list of lists. The list <code>x</code> is of length equal to the number of workers (i.e. <code>length(cl)</code>). Each element in the list <code>x</code> is a part of the original <code>X</code> vector. Let’s see how <code>splitList</code> works. It is an un-exported function from the <strong>parallel</strong> package that calls <code>splitIndices</code>. Suppose we have 10 ‘jobs’ we want to submit to 2 workers (note that these are just indices, not real values from <code>X</code>):</p>
<div>
<pre><code><span>splitIndices</span><span>(</span><span>nx</span><span> </span><span>=</span><span> </span><span>10</span><span>,</span><span> </span><span>ncl</span><span> </span><span>=</span><span> </span><span>2</span><span>)</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## [[1]]
## [1] 1 2 3 4 5
##
## [[2]]
## [1]  6  7  8  9 10
</code></pre>
</div>
<p>The first worker gets one half of the job, while the second gets the rest. In other words, the full problem is split into <code>ncl</code> subsets, and those are pushed to the workers in one batch. That is what the next line with <code>clusterApply</code> does (it uses <code>lapply</code> on each worker passing the <code>fun</code>ction etc.). The output object <code>z</code> has all the results in the form of list of lists. So the final <code>do.call</code> expression puts together the pieces in a single list/vector that can be mapped to the input object <code>X</code>.</p>
<p>Here is example taking squares of the numbers <code>11:20</code> (note: it is good practice to stop the child processes with <code>stopCluster</code> when all done):</p>
<div>
<pre><code><span>ncl</span><span> </span><span>&lt;-</span><span> </span><span>2</span><span>
</span><span>(</span><span>cl</span><span> </span><span>&lt;-</span><span> </span><span>makeCluster</span><span>(</span><span>ncl</span><span>))</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## socket cluster with 2 nodes on host 'localhost'
</code></pre>
</div>
<div>
<pre><code><span>(</span><span>X</span><span> </span><span>&lt;-</span><span> </span><span>11</span><span>:</span><span>20</span><span>)</span><span>
</span></code></pre>
</div>
<div>
<pre><code>##  [1] 11 12 13 14 15 16 17 18 19 20
</code></pre>
</div>
<div>
<pre><code><span>fun</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>x</span><span>^</span><span>2</span><span>
</span><span>(</span><span>x</span><span> </span><span>&lt;-</span><span> </span><span>parallel</span><span>:::</span><span>splitList</span><span>(</span><span>X</span><span>,</span><span> </span><span>length</span><span>(</span><span>cl</span><span>)))</span><span> </span><span># these are the real values
</span></code></pre>
</div>
<div>
<pre><code>## [[1]]
## [1] 11 12 13 14 15
##
## [[2]]
## [1] 16 17 18 19 20
</code></pre>
</div>
<div>
<pre><code><span>str</span><span>(</span><span>z</span><span> </span><span>&lt;-</span><span> </span><span>clusterApply</span><span>(</span><span>cl</span><span>,</span><span> </span><span>x</span><span>,</span><span> </span><span>fun</span><span> </span><span>=</span><span> </span><span>lapply</span><span>,</span><span> </span><span>fun</span><span>))</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## List of 2
##  $ :List of 5
##   ..$ : num 121
##   ..$ : num 144
##   ..$ : num 169
##   ..$ : num 196
##   ..$ : num 225
##  $ :List of 5
##   ..$ : num 256
##   ..$ : num 289
##   ..$ : num 324
##   ..$ : num 361
##   ..$ : num 400
</code></pre>
</div>
<div>
<pre><code><span>str</span><span>(</span><span>do.call</span><span>(</span><span>c</span><span>,</span><span> </span><span>z</span><span>,</span><span> </span><span>quote</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>))</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## List of 10
##  $ : num 121
##  $ : num 144
##  $ : num 169
##  $ : num 196
##  $ : num 225
##  $ : num 256
##  $ : num 289
##  $ : num 324
##  $ : num 361
##  $ : num 400
</code></pre>
</div>
<div>
<pre><code><span>stopCluster</span><span>(</span><span>cl</span><span>)</span><span>
</span></code></pre>
</div>
<h3>Progress bar for sequential jobs</h3>
<p>Here is a function that shows the basic principle behind the functions in the <strong>pbapply</strong> package:</p>
<div>
<pre><code><span>library</span><span>(</span><span>pbapply</span><span>)</span><span>
</span><span>pb_fun</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>X</span><span>,</span><span> </span><span>fun</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>X</span><span> </span><span>&lt;-</span><span> </span><span>as.list</span><span>(</span><span>X</span><span>)</span><span> </span><span># X as list
</span><span> </span><span>B</span><span> </span><span>&lt;-</span><span> </span><span>length</span><span>(</span><span>X</span><span>)</span><span> </span><span># length of X
</span><span> </span><span>fun</span><span> </span><span>&lt;-</span><span> </span><span>match.fun</span><span>(</span><span>fun</span><span>)</span><span> </span><span># recognize the function
</span><span> </span><span>out</span><span> </span><span>&lt;-</span><span> </span><span>vector</span><span>(</span><span>&quot;list&quot;</span><span>,</span><span> </span><span>B</span><span>)</span><span> </span><span># output list
</span><span> </span><span>pb</span><span> </span><span>&lt;-</span><span> </span><span>startpb</span><span>(</span><span>0</span><span>,</span><span> </span><span>B</span><span>)</span><span> </span><span># set up progress bar
</span><span> </span><span>for</span><span> </span><span>(</span><span>i</span><span> </span><span>in</span><span> </span><span>seq_len</span><span>(</span><span>B</span><span>))</span><span> </span><span>{</span><span> </span><span># start loop
</span><span> </span><span>out</span><span>[[</span><span>i</span><span>]]</span><span> </span><span>&lt;-</span><span> </span><span>fun</span><span>(</span><span>X</span><span>[[</span><span>i</span><span>]],</span><span> </span><span>...</span><span>)</span><span> </span><span># apply function on X
</span><span> </span><span>setpb</span><span>(</span><span>pb</span><span>,</span><span> </span><span>i</span><span>)</span><span> </span><span># update progress bar
</span><span> </span><span>}</span><span> </span><span># end loop
</span><span> </span><span>closepb</span><span>(</span><span>pb</span><span>)</span><span> </span><span># close progress bar on exit
</span><span> </span><span>out</span><span> </span><span># return output
</span><span>}</span><span>
</span></code></pre>
</div>
<p>The example demonstrates that the progress bar is updated at each iteration in the <code>for</code> loop, in this case it means 100 updates:</p>
<div>
<pre><code><span>B</span><span> </span><span>&lt;-</span><span> </span><span>100</span><span>
</span><span>fun</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>Sys.sleep</span><span>(</span><span>0.01</span><span>)</span><span>
</span><span>pbo</span><span> </span><span>&lt;-</span><span> </span><span>pboptions</span><span>(</span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;timer&quot;</span><span>)</span><span>
</span><span>tmp</span><span> </span><span>&lt;-</span><span> </span><span>pb_fun</span><span>(</span><span>1</span><span>:</span><span>B</span><span>,</span><span> </span><span>fun</span><span>)</span><span>
</span><span>## |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 01s
</span><span>pboptions</span><span>(</span><span>pbo</span><span>)</span><span>
</span></code></pre>
</div>
<p>If we want to put this updating process together with the parallel evaluation, we need to split the problem in a different way. Suppose we only send one job to each worker at a time. We can do that exactly <code>ceiling(B / ncl)</code> times. The <code>splitpb</code> function takes care of the indices. Using the previous example of 10 jobs and 2 workers, we get:</p>
<div>
<pre><code><span>splitpb</span><span>(</span><span>nx</span><span> </span><span>=</span><span> </span><span>10</span><span>,</span><span> </span><span>ncl</span><span> </span><span>=</span><span> </span><span>2</span><span>)</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## [[1]]
## [1] 1 2
##
## [[2]]
## [1] 3 4
##
## [[3]]
## [1] 5 6
##
## [[4]]
## [1] 7 8
##
## [[5]]
## [1]  9 10
</code></pre>
</div>
<p>This gives us 10/2 = 5 occasions for updating the progress bar. And it also means that we will increase communication overhead for our parallel jobs 5-fold (from 1 to 5 batches). The overhead increases linearly with problem size <code>B</code> as opposed to being constant.</p>
<h3>The cost of the progress bar</h3>
<p>To get a sense of the overhead cost associated with the progress bar, here is a prototype function that I used for the developmental version of the <strong>pbapply</strong> package. The <code>pbapply_test</code> function takes some of the arguments we have covered before (<code>X</code>, <code>FUN</code>, <code>cl</code>). The only new argument is <code>nout</code> which is the maximum number of splits we allow in <code>splitpb</code>. <code>nout = NULL</code> is the default which gives exactly <code>ceiling(nx / ncl)</code> splits. See what happens when we cap that at 4:</p>
<div>
<pre><code><span>splitpb</span><span>(</span><span>nx</span><span> </span><span>=</span><span> </span><span>10</span><span>,</span><span> </span><span>ncl</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>4</span><span>)</span><span>
</span></code></pre>
</div>
<div>
<pre><code>## [[1]]
## [1] 1 2 3 4
##
## [[2]]
## [1] 5 6 7 8
##
## [[3]]
## [1]  9 10
</code></pre>
</div>
<p><code>pbapply_test</code> exposes the <code>nout</code> argument so that we can see what happens when we cap the number of progress bar updates (use 100 and 1) vs. we keep the updates increasing with <code>B</code> (<code>NULL</code>).</p>
<div>
<pre><code><span>pblapply_test</span><span> </span><span>&lt;-</span><span>
</span><span>function</span><span> </span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>...</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>NULL</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>NULL</span><span>)</span><span>
</span><span>{</span><span>
</span><span>FUN</span><span> </span><span>&lt;-</span><span> </span><span>match.fun</span><span>(</span><span>FUN</span><span>)</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>is.vector</span><span>(</span><span>X</span><span>)</span><span> </span><span>||</span><span> </span><span>is.object</span><span>(</span><span>X</span><span>))</span><span>
</span><span>X</span><span> </span><span>&lt;-</span><span> </span><span>as.list</span><span>(</span><span>X</span><span>)</span><span>
</span><span>## catch single node requests and forking on Windows
</span><span> </span><span>if</span><span> </span><span>(</span><span>!</span><span>is.null</span><span>(</span><span>cl</span><span>))</span><span> </span><span>{</span><span>
</span><span>if</span><span> </span><span>(</span><span>inherits</span><span>(</span><span>cl</span><span>,</span><span> </span><span>&quot;cluster&quot;</span><span>)</span><span> </span><span>&amp;&amp;</span><span> </span><span>length</span><span>(</span><span>cl</span><span>)</span><span> </span><span>&lt;</span><span> </span><span>2L</span><span>)</span><span>
</span><span>cl</span><span> </span><span>&lt;-</span><span> </span><span>NULL</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>inherits</span><span>(</span><span>cl</span><span>,</span><span> </span><span>&quot;cluster&quot;</span><span>)</span><span> </span><span>&amp;&amp;</span><span> </span><span>cl</span><span> </span><span>&lt;</span><span> </span><span>2</span><span>)</span><span>
</span><span>cl</span><span> </span><span>&lt;-</span><span> </span><span>NULL</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>inherits</span><span>(</span><span>cl</span><span>,</span><span> </span><span>&quot;cluster&quot;</span><span>)</span><span> </span><span>&amp;&amp;</span><span> </span><span>.Platform</span><span>$</span><span>OS.type</span><span> </span><span>==</span><span> </span><span>&quot;windows&quot;</span><span>)</span><span>
</span><span>cl</span><span> </span><span>&lt;-</span><span> </span><span>NULL</span><span>
</span><span>}</span><span>
</span><span>do_pb</span><span> </span><span>&lt;-</span><span> </span><span>dopb</span><span>()</span><span>
</span><span>## sequential evaluation
</span><span> </span><span>if</span><span> </span><span>(</span><span>is.null</span><span>(</span><span>cl</span><span>))</span><span> </span><span>{</span><span>
</span><span>Split</span><span> </span><span>&lt;-</span><span> </span><span>splitpb</span><span>(</span><span>length</span><span>(</span><span>X</span><span>),</span><span> </span><span>1L</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>nout</span><span>)</span><span>
</span><span>B</span><span> </span><span>&lt;-</span><span> </span><span>length</span><span>(</span><span>Split</span><span>)</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span> </span><span>{</span><span>
</span><span>pb</span><span> </span><span>&lt;-</span><span> </span><span>startpb</span><span>(</span><span>0</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>on.exit</span><span>(</span><span>closepb</span><span>(</span><span>pb</span><span>),</span><span> </span><span>add</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>)</span><span>
</span><span>}</span><span>
</span><span>rval</span><span> </span><span>&lt;-</span><span> </span><span>vector</span><span>(</span><span>&quot;list&quot;</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>for</span><span> </span><span>(</span><span>i</span><span> </span><span>in</span><span> </span><span>seq_len</span><span>(</span><span>B</span><span>))</span><span> </span><span>{</span><span>
</span><span>rval</span><span>[</span><span>i</span><span>]</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>lapply</span><span>(</span><span>X</span><span>[</span><span>Split</span><span>[[</span><span>i</span><span>]]],</span><span> </span><span>FUN</span><span>,</span><span> </span><span>...</span><span>))</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span>
</span><span>setpb</span><span>(</span><span>pb</span><span>,</span><span> </span><span>i</span><span>)</span><span>
</span><span>}</span><span>
</span><span>## parallel evaluation
</span><span> </span><span>}</span><span> </span><span>else</span><span> </span><span>{</span><span>
</span><span>## snow type cluster
</span><span> </span><span>if</span><span> </span><span>(</span><span>inherits</span><span>(</span><span>cl</span><span>,</span><span> </span><span>&quot;cluster&quot;</span><span>))</span><span> </span><span>{</span><span>
</span><span>Split</span><span> </span><span>&lt;-</span><span> </span><span>splitpb</span><span>(</span><span>length</span><span>(</span><span>X</span><span>),</span><span> </span><span>length</span><span>(</span><span>cl</span><span>),</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>nout</span><span>)</span><span>
</span><span>B</span><span> </span><span>&lt;-</span><span> </span><span>length</span><span>(</span><span>Split</span><span>)</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span> </span><span>{</span><span>
</span><span>pb</span><span> </span><span>&lt;-</span><span> </span><span>startpb</span><span>(</span><span>0</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>on.exit</span><span>(</span><span>closepb</span><span>(</span><span>pb</span><span>),</span><span> </span><span>add</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>)</span><span>
</span><span>}</span><span>
</span><span>rval</span><span> </span><span>&lt;-</span><span> </span><span>vector</span><span>(</span><span>&quot;list&quot;</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>for</span><span> </span><span>(</span><span>i</span><span> </span><span>in</span><span> </span><span>seq_len</span><span>(</span><span>B</span><span>))</span><span> </span><span>{</span><span>
</span><span>rval</span><span>[</span><span>i</span><span>]</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>parallel</span><span>::</span><span>parLapply</span><span>(</span><span>cl</span><span>,</span><span> </span><span>X</span><span>[</span><span>Split</span><span>[[</span><span>i</span><span>]]],</span><span> </span><span>FUN</span><span>,</span><span> </span><span>...</span><span>))</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span>
</span><span>setpb</span><span>(</span><span>pb</span><span>,</span><span> </span><span>i</span><span>)</span><span>
</span><span>}</span><span>
</span><span>## multicore type forking
</span><span> </span><span>}</span><span> </span><span>else</span><span> </span><span>{</span><span>
</span><span>Split</span><span> </span><span>&lt;-</span><span> </span><span>splitpb</span><span>(</span><span>length</span><span>(</span><span>X</span><span>),</span><span> </span><span>as.integer</span><span>(</span><span>cl</span><span>),</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>nout</span><span>)</span><span>
</span><span>B</span><span> </span><span>&lt;-</span><span> </span><span>length</span><span>(</span><span>Split</span><span>)</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span> </span><span>{</span><span>
</span><span>pb</span><span> </span><span>&lt;-</span><span> </span><span>startpb</span><span>(</span><span>0</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>on.exit</span><span>(</span><span>closepb</span><span>(</span><span>pb</span><span>),</span><span> </span><span>add</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>)</span><span>
</span><span>}</span><span>
</span><span>rval</span><span> </span><span>&lt;-</span><span> </span><span>vector</span><span>(</span><span>&quot;list&quot;</span><span>,</span><span> </span><span>B</span><span>)</span><span>
</span><span>for</span><span> </span><span>(</span><span>i</span><span> </span><span>in</span><span> </span><span>seq_len</span><span>(</span><span>B</span><span>))</span><span> </span><span>{</span><span>
</span><span>rval</span><span>[</span><span>i</span><span>]</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>parallel</span><span>::</span><span>mclapply</span><span>(</span><span>X</span><span>[</span><span>Split</span><span>[[</span><span>i</span><span>]]],</span><span> </span><span>FUN</span><span>,</span><span> </span><span>...</span><span>,</span><span>
</span><span>mc.cores</span><span> </span><span>=</span><span> </span><span>as.integer</span><span>(</span><span>cl</span><span>)))</span><span>
</span><span>if</span><span> </span><span>(</span><span>do_pb</span><span>)</span><span>
</span><span>setpb</span><span>(</span><span>pb</span><span>,</span><span> </span><span>i</span><span>)</span><span>
</span><span>}</span><span>
</span><span>}</span><span>
</span><span>}</span><span>
</span><span>## assemble output list
</span><span> </span><span>rval</span><span> </span><span>&lt;-</span><span> </span><span>do.call</span><span>(</span><span>c</span><span>,</span><span> </span><span>rval</span><span>,</span><span> </span><span>quote</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>)</span><span>
</span><span>names</span><span>(</span><span>rval</span><span>)</span><span> </span><span>&lt;-</span><span> </span><span>names</span><span>(</span><span>X</span><span>)</span><span>
</span><span>rval</span><span>
</span><span>}</span><span>
</span></code></pre>
</div>
<p>The next function is <code>test_fun</code> which has a <code>type</code> argument that affects the type of progress bar. We will compare the following three types:</p>
<ul>
<li><code>&quot;none&quot;</code>: no progress bar updates, but the function does everything else the same way including splitting;</li>
<li><code>&quot;txt&quot;</code>: text progress bar showing percent of jobs completed;</li>
<li><code>&quot;timer&quot;</code>: timer progress bar showing percent of jobs completed with time estimates and final elapsed time.</li>
</ul>
<p>The no-progress bar case (<code>&quot;none&quot;</code>) with <code>nout = 1</code> will serve as baseline as in this case the function falls back to <code>lapply</code> and its parallel equivalents (<code>parLapply</code> and <code>mclapply</code>).</p>
<div>
<pre><code><span>timer_fun</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;timer&quot;</span><span>)</span><span> </span><span>{</span><span>
</span><span>pbo</span><span> </span><span>&lt;-</span><span> </span><span>pboptions</span><span>(</span><span>type</span><span> </span><span>=</span><span> </span><span>type</span><span>)</span><span>
</span><span>on.exit</span><span>(</span><span>pboptions</span><span>(</span><span>pbo</span><span>))</span><span>
</span><span>rbind</span><span>(</span><span>
</span><span>pb_NULL</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>NULL</span><span>)),</span><span>
</span><span>pb_100</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>100</span><span>)),</span><span>
</span><span>pb_1</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>1</span><span>)),</span><span>
</span><span>pb_cl_NULL</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>cl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>NULL</span><span>)),</span><span>
</span><span>pb_cl_100</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>cl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>100</span><span>)),</span><span>
</span><span>pb_cl_1</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>cl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>1</span><span>)),</span><span>
</span><span>pb_mc_NULL</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>ncl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>NULL</span><span>)),</span><span>
</span><span>pb_mc_100</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>ncl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>100</span><span>)),</span><span>
</span><span>pb_mc_1</span><span> </span><span>=</span><span> </span><span>system.time</span><span>(</span><span>pblapply_test</span><span>(</span><span>X</span><span>,</span><span> </span><span>FUN</span><span>,</span><span> </span><span>cl</span><span> </span><span>=</span><span> </span><span>ncl</span><span>,</span><span> </span><span>nout</span><span> </span><span>=</span><span> </span><span>1</span><span>))</span><span>
</span><span>)</span><span>
</span><span>}</span><span>
</span></code></pre>
</div>
<p>Here is the setup: 1000 iterations, 2 workers, and the function <code>fun2</code>. This function calls <code>Sys.sleep</code> and returns the squared value of the input. Once we open the cluster connection, run <code>timer_fun</code> with different settings and stop the cluster, we also call the <code>pbmclapply</code> function from the <strong>pbmcapply</strong> package for comparison and calculate a minimum expected time based on the fact that <code>0.01 * B</code> = 10 sec.</p>
<div>
<pre><code><span>ncl</span><span> </span><span>&lt;-</span><span> </span><span>2</span><span>
</span><span>B</span><span> </span><span>&lt;-</span><span> </span><span>1000</span><span>
</span><span>fun2</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>{</span><span>
</span><span>Sys.sleep</span><span>(</span><span>0.01</span><span>)</span><span>
</span><span>x</span><span>^</span><span>2</span><span>
</span><span>}</span><span>
</span><span>cl</span><span> </span><span>&lt;-</span><span> </span><span>makeCluster</span><span>(</span><span>ncl</span><span>)</span><span>
</span><span>timer_out</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>
</span><span>none</span><span> </span><span>=</span><span> </span><span>timer_fun</span><span>(</span><span>1</span><span>:</span><span>B</span><span>,</span><span> </span><span>fun2</span><span>,</span><span> </span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;none&quot;</span><span>),</span><span>
</span><span>txt</span><span> </span><span>=</span><span> </span><span>timer_fun</span><span>(</span><span>1</span><span>:</span><span>B</span><span>,</span><span> </span><span>fun2</span><span>,</span><span> </span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;txt&quot;</span><span>),</span><span>
</span><span>timer</span><span> </span><span>=</span><span> </span><span>timer_fun</span><span>(</span><span>1</span><span>:</span><span>B</span><span>,</span><span> </span><span>fun2</span><span>,</span><span> </span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;timer&quot;</span><span>))</span><span>
</span><span>stopCluster</span><span>(</span><span>cl</span><span>)</span><span>
</span><span>library</span><span>(</span><span>pbmcapply</span><span>)</span><span>
</span><span>tpbmc</span><span> </span><span>&lt;-</span><span> </span><span>system.time</span><span>(</span><span>pbmclapply</span><span>(</span><span>1</span><span>:</span><span>B</span><span>,</span><span> </span><span>fun</span><span>,</span><span> </span><span>mc.cores</span><span> </span><span>=</span><span> </span><span>ncl</span><span>))</span><span>
</span><span>tex</span><span> </span><span>&lt;-</span><span> </span><span>0.01</span><span> </span><span>*</span><span> </span><span>B</span><span>
</span><span>tpb</span><span> </span><span>&lt;-</span><span> </span><span>cbind</span><span>(</span><span>timer_out</span><span>[[</span><span>1</span><span>]][</span><span>1</span><span>:</span><span>3</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>2</span><span>]][</span><span>1</span><span>:</span><span>3</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>3</span><span>]][</span><span>1</span><span>:</span><span>3</span><span>,</span><span>3</span><span>])</span><span>
</span><span>tcl</span><span> </span><span>&lt;-</span><span> </span><span>cbind</span><span>(</span><span>timer_out</span><span>[[</span><span>1</span><span>]][</span><span>4</span><span>:</span><span>6</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>2</span><span>]][</span><span>4</span><span>:</span><span>6</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>3</span><span>]][</span><span>4</span><span>:</span><span>6</span><span>,</span><span>3</span><span>])</span><span>
</span><span>tmc</span><span> </span><span>&lt;-</span><span> </span><span>cbind</span><span>(</span><span>timer_out</span><span>[[</span><span>1</span><span>]][</span><span>7</span><span>:</span><span>9</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>2</span><span>]][</span><span>7</span><span>:</span><span>9</span><span>,</span><span>3</span><span>],</span><span>
</span><span>timer_out</span><span>[[</span><span>3</span><span>]][</span><span>7</span><span>:</span><span>9</span><span>,</span><span>3</span><span>])</span><span>
</span><span>colnames</span><span>(</span><span>tpb</span><span>)</span><span> </span><span>&lt;-</span><span> </span><span>colnames</span><span>(</span><span>tcl</span><span>)</span><span> </span><span>&lt;-</span><span> </span><span>colnames</span><span>(</span><span>tmc</span><span>)</span><span> </span><span>&lt;-</span><span> </span><span>names</span><span>(</span><span>timer_out</span><span>)</span><span>
</span><span>op</span><span> </span><span>&lt;-</span><span> </span><span>par</span><span>(</span><span>mfrow</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>1</span><span>,</span><span> </span><span>3</span><span>),</span><span> </span><span>las</span><span> </span><span>=</span><span> </span><span>1</span><span>,</span><span> </span><span>mar</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>5</span><span>,</span><span> </span><span>5</span><span>,</span><span> </span><span>2</span><span>,</span><span> </span><span>2</span><span>))</span><span>
</span><span>matplot</span><span>(</span><span>c</span><span>(</span><span>B</span><span>,</span><span> </span><span>100</span><span>,</span><span> </span><span>1</span><span>),</span><span> </span><span>tpb</span><span>,</span><span>
</span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;l&quot;</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>1</span><span>,</span><span> </span><span>ylim</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>0</span><span>,</span><span> </span><span>max</span><span>(</span><span>tpb</span><span>,</span><span> </span><span>tcl</span><span>,</span><span> </span><span>tmc</span><span>)),</span><span>
</span><span>xlab</span><span> </span><span>=</span><span> </span><span>&quot;# pb updates&quot;</span><span>,</span><span> </span><span>ylab</span><span> </span><span>=</span><span> </span><span>&quot;proc time (sec)&quot;</span><span>,</span><span> </span><span>main</span><span> </span><span>=</span><span> </span><span>&quot;sequential&quot;</span><span>)</span><span>
</span><span>abline</span><span>(</span><span>h</span><span> </span><span>=</span><span> </span><span>tex</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span> </span><span>col</span><span> </span><span>=</span><span> </span><span>1</span><span>)</span><span>
</span><span>matplot</span><span>(</span><span>c</span><span>(</span><span>B</span><span>/</span><span>ncl</span><span>,</span><span> </span><span>100</span><span>,</span><span> </span><span>1</span><span>),</span><span> </span><span>tcl</span><span>,</span><span>
</span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;l&quot;</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>1</span><span>,</span><span> </span><span>ylim</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>0</span><span>,</span><span> </span><span>max</span><span>(</span><span>tpb</span><span>,</span><span> </span><span>tcl</span><span>,</span><span> </span><span>tmc</span><span>)),</span><span>
</span><span>xlab</span><span> </span><span>=</span><span> </span><span>&quot;# pb updates&quot;</span><span>,</span><span> </span><span>ylab</span><span> </span><span>=</span><span> </span><span>&quot;proc time (sec)&quot;</span><span>,</span><span> </span><span>main</span><span> </span><span>=</span><span> </span><span>&quot;cluster&quot;</span><span>)</span><span>
</span><span>abline</span><span>(</span><span>h</span><span> </span><span>=</span><span> </span><span>tex</span><span> </span><span>/</span><span> </span><span>ncl</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span> </span><span>col</span><span> </span><span>=</span><span> </span><span>1</span><span>)</span><span>
</span><span>matplot</span><span>(</span><span>c</span><span>(</span><span>B</span><span>/</span><span>ncl</span><span>,</span><span> </span><span>100</span><span>,</span><span> </span><span>1</span><span>),</span><span> </span><span>tmc</span><span>,</span><span>
</span><span>type</span><span> </span><span>=</span><span> </span><span>&quot;l&quot;</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>1</span><span>,</span><span> </span><span>ylim</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>0</span><span>,</span><span> </span><span>max</span><span>(</span><span>tpb</span><span>,</span><span> </span><span>tcl</span><span>,</span><span> </span><span>tmc</span><span>)),</span><span>
</span><span>xlab</span><span> </span><span>=</span><span> </span><span>&quot;# pb updates&quot;</span><span>,</span><span> </span><span>ylab</span><span> </span><span>=</span><span> </span><span>&quot;proc time (sec)&quot;</span><span>,</span><span> </span><span>main</span><span> </span><span>=</span><span> </span><span>&quot;forking&quot;</span><span>)</span><span>
</span><span>abline</span><span>(</span><span>h</span><span> </span><span>=</span><span> </span><span>tex</span><span> </span><span>/</span><span> </span><span>ncl</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span> </span><span>col</span><span> </span><span>=</span><span> </span><span>1</span><span>)</span><span>
</span><span>abline</span><span>(</span><span>h</span><span> </span><span>=</span><span> </span><span>tpbmc</span><span>[</span><span>3</span><span>],</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span> </span><span>col</span><span> </span><span>=</span><span> </span><span>2</span><span>)</span><span>
</span><span>legend</span><span>(</span><span>&quot;bottomleft&quot;</span><span>,</span><span> </span><span>bty</span><span> </span><span>=</span><span> </span><span>&quot;n&quot;</span><span>,</span><span> </span><span>lty</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>1</span><span>,</span><span> </span><span>1</span><span>,</span><span> </span><span>1</span><span>,</span><span> </span><span>2</span><span>,</span><span> </span><span>2</span><span>),</span><span> </span><span>col</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>1</span><span>:</span><span>3</span><span>,</span><span> </span><span>1</span><span>,</span><span> </span><span>2</span><span>),</span><span>
</span><span>legend</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>names</span><span>(</span><span>timer_out</span><span>),</span><span> </span><span>&quot;minimum&quot;</span><span>,</span><span> </span><span>&quot;pbmclapply&quot;</span><span>))</span><span>
</span><span>par</span><span>(</span><span>op</span><span>)</span><span>
</span></code></pre>
</div>
<p><img alt="" src="https://i1.wp.com/peter.solymos.org/images/2016/09/11/pb-matplot.png?w=456" data-recalc-dims="1"></p>
<h3>Conclusions</h3>
<p>Here is the summary based on the plots (note: some variation exists from one run to another):</p>
<ul>
<li>the type of the progress bar did not affect timing results;</li>
<li>the sequential runs revealed no huge effect of number of updates on processing times (a minimal effect is visible though);</li>
<li>the parallel runs showed a linear relationship between number of updates and processing times;</li>
<li>impact on processing times was greater when using forking mechanism.</li>
</ul>
<p>It is clear from the results that the time required for updating/printing the progress bar is not the real bottleneck. If it was the bottleneck, we would see differences between the types. Moreover, event the <code>&quot;none&quot;</code> type led to worse performance when running in parallel. It is clearly the increased communication overhead that is driving the timings, i.e. how many times we split-pass-collect the jobs. The situation is worst when using forking. The <code>pbmclapply</code> function performs well because it uses <a target="_blank" href="https://cran.r-project.org/web/packages/future/index.html"><strong>future</strong></a> expectations without creating multiple forking processes (i.e. no unnecessary splits introduced).</p>
<p>Splitting the problem too many times severely impacts performance. It is thus a good idea to cap the number of progress bar updates when running in parallel. Another consideration in favor of this is that sometimes too many updates (i.e. tens of thousands) can clear the console output (similarly to <code>cat(&quot;\014&quot;)</code>, the equivalent of Ctrl+L). To prevent that, capping the number of updates could be helpful again. For example, updating 10 thousand times is pointless when results (percentage etc.) do not change much. It is like going beyond 24 frames per second for an animation: no one can recognize the difference but it makes production more expensive.</p>
<p>As a compromise, I came up with <code>nout = 100</code> as a default value being stored as part of the progress bar options (<code>pboptions</code>). If one wishes to play slow-motion with the progress bar use the following line:</p>
<div>
<pre><code><span>pboptions</span><span>(</span><span>nobs</span><span> </span><span>=</span><span> </span><span>NULL</span><span>)</span><span>
</span></code></pre>
</div>
<p>Those users who are desperate to see a progress bar while running parallel jobs, it is now available in the GitHub repository. More testing and fine tuning is required before the update finds its way to the master branch and ultimate to CRAN. In the meantime, check it out as:</p>
<div>
<pre><code><span>devtools</span><span>::</span><span>install_github</span><span>(</span><span>&quot;psolymos/pbapply&quot;</span><span>,</span><span> </span><span>ref</span><span> </span><span>=</span><span> </span><span>&quot;pb-parallel&quot;</span><span>)</span><span>
</span></code></pre>
</div>
<p>Please provide feedback either in the comments or on <a target="_blank" href="https://github.com/psolymos/pbapply/issues/9">GitHub</a>.</p>
<p><div><div><div><div data-width="55" data-show-faces="false" data-send="true" data-layout="box_count" data-height="62" data-href="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/"></div></div><div><a data-url="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/" data-counturl="https://www.r-bloggers.com/what-is-the-cost-of-a-progress-bar-in-r/" data-count="vertical" data-text="What is the cost of a progress bar in R?" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://peter.solymos.org/code/2016/09/11/what-is-the-cost-of-a-progress-bar-in-r.html"> Peter Solymos - R related posts</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" rel="nofollow" href="https://www.r-project.org/">R</a> news and <a title="R tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=tutorial">tutorials</a> on topics such as: <a title="Data science" rel="nofollow" href="https://www.r-bloggers.com/?s=data%20science">Data science</a>, <a title="Big Data" rel="nofollow" href="https://www.r-bloggers.com/?s=Big%20Data">Big Data, <a title="R jobs" rel="nofollow" href="https://www.r-users.com/">R jobs</a>, visualization (<a title="ggplot and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=ggplot2">ggplot2</a>, <a title="Boxplots using lattice and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=boxplot">Boxplots</a>, <a title="Maps and gis" rel="nofollow" href="https://www.r-bloggers.com/?s=map">maps</a>, <a title="Animation in R" rel="nofollow" href="https://www.r-bloggers.com/?s=animation">animation</a>), programming (<a title="RStudio IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=RStudio">RStudio</a>, <a title="Sweave and literate programming" rel="nofollow" href="https://www.r-bloggers.com/?s=sweave">Sweave</a>, <a title="LaTeX in R" rel="nofollow" href="https://www.r-bloggers.com/?s=LaTeX">LaTeX</a>, <a title="SQL and databases" rel="nofollow" href="https://www.r-bloggers.com/?s=SQL">SQL</a>, <a title="Eclipse IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=eclipse">Eclipse</a>, <a title="git and github, Version Control System" rel="nofollow" href="https://www.r-bloggers.com/?s=git">git</a>, <a title="Large data in R using Hadoop" rel="nofollow" href="https://www.r-bloggers.com/?s=hadoop">hadoop</a>, <a title="Web Scraping of google, facebook, yahoo, twitter and more using R" rel="nofollow" href="https://www.r-bloggers.com/?s=Web+Scraping">Web Scraping</a>) statistics (<a title="Regressions and ANOVA analysis tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=regression">regression</a>, <a title="principal component analysis tutorial" rel="nofollow" href="https://www.r-bloggers.com/?s=PCA">PCA</a>, <a title="Time series" rel="nofollow" href="https://www.r-bloggers.com/?s=time+series">time series</a>, <a title="finance trading" rel="nofollow" href="https://www.r-bloggers.com/?s=trading">trading</a>) and more...
<img height="1" alt="" width="1" src="http://feeds.feedburner.com/~r/RBloggers/~4/5fCKGdcuh4o"></a></div>