This chapter serves as an introduction to the package ProgressBar and includes many examples.
The main purpose of this package is to display the progression of a process in the terminal via a progress bar that refreshes in the screen after each step of the process.
Instead of writing new lines in the terminal screen after each step of a process, the terminal screen gets refreshed in space. This method of displaying information avoids spamming in the terminal, and provides an intuitive and clean visualization of the progress.
In the first example we show the simplest use case of the package, namely displaying the progression of a loop over a fixed list.
For this we simply wrap the list into the function ProcessIterator
.
gap> LoadPackage("ProgressBar");; gap> doLoop := function(n, nSleep) > local i; > for i in ProcessIterator([1 .. n]) do > MicroSleep(nSleep); > od; > end;; gap> doLoop(7, 10^6/4);;
In the above example we might encounter the following outputs during the execution of the iteration. All these outputs are visible in the same lines of the terminal for roughly 1/4 of a second.
| Total Time ~ 00:00:00 [========================================-------------------------------] | 4/7 | Total Time ~ 00:00:01 [=======================================================================] | 7/7
The package can also handle very fast updates, but keep in mind that it adds some overhead to the computation. In this example, the overhead acumulates over the 5000 iterations to 2 extra seconds for a computation that would otherwise only take about 5 seconds.
gap> LoadPackage("ProgressBar");; gap> doLoop := function(n, nSleep) > local i; > for i in ProcessIterator([1 .. n]) do > MicroSleep(nSleep); > od; > end;; gap> doLoop(5000, 1000);
We can also display the progression of an indefinite loop.
For this we simply wrap an iterator into the function ProcessIterator
.
gap> LoadPackage("ProgressBar");; gap> doLoop := function(n, nSleep) > local i; > for i in ProcessIterator(IteratorOfCombinations([1 .. n])) do > MicroSleep(nSleep); > od; > end;; gap> doLoop(8, 10^4);;
In the above example we might encounter the following outputs during the execution of the iteration.
| Total Time ~ 00:00:01 [--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--=] | 123/oo | Total Time ~ 00:00:02 [==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-] | 193/oo | Total Time ~ 00:00:03 [===================================================================] | 256/oo
Sometimes we can actually compute the number of steps needed for the process to terminate beforehand. This will change the display and provide more detailed information about the progress, see 1.1.
gap> LoadPackage("ProgressBar");; gap> doLoop := function(n, nSleep) > local iter, i; > iter := ProcessIterator(rec( > iter := IteratorOfCombinations([1 .. n]), > totalSteps := 2^n > )); > for i in iter do > MicroSleep(nSleep); > od; > end;; gap> doLoop(8, 10^4);;
If we set the totalSteps
wrong, the display will change to an indefinite loop once we exceed them in the iteration.
gap> LoadPackage("ProgressBar");; gap> doLoop := function(n, nSleep) > local iter, i; > iter := ProcessIterator(rec( > iter := IteratorOfCombinations([1 .. n]), > totalSteps := 2^(n-1) > )); > for i in iter do > MicroSleep(nSleep); > od; > end;; gap> doLoop(8, 10^4);;
If we want to print the value of the current iteration, this can be done via an optional content record.
gap> LoadPackage("ProgressBar");; gap> doLoopWithValue := function(n, nSleep) > local i, j; > for i in ProcessIterator([1 .. n], rec( > title := JoinStringsWithSeparator([ > "---------------", > "Main iteration:", > " j = {{value + 100}}", > ], "\n"))) do > j := i + 100; > MicroSleep(nSleep); > od; > end;; gap> doLoopWithValue(35, 10^6/10);;
The package also supports displaying nested loops.
gap> LoadPackage("ProgressBar");; gap> doNestedLoop := function(n, nSleep, m, mSleep) > local i, j; > for i in ProcessIterator([1 .. n]) do > MicroSleep(nSleep); > for j in ProcessIterator([1 .. m]) do > MicroSleep(mSleep); > od; > od; > end;; gap> doNestedLoop(3, 10^6/2, 50, 10^4);;
An example output might look like this
| Total Time ~ 00:00:01 [==============================================-----------------------] | 2/ 3 [------------------------------------------------------------------] | 0/50 | Total Time ~ 00:00:02 [==============================================-----------------------] | 2/ 3 [===============================================-------------------] | 36/50 | Total Time ~ 00:00:02 [=====================================================================] | 3/ 3 [==================================================================] | 50/50
We can also display much more intertwined processes than nested loops. This package can handle any process tree.
gap> LoadPackage("ProgressBar");; gap> doTree := function(n, nSleep, l, lSleep, m, mSleep, r, rSleep) > local i, j, k; > for i in ProcessIterator([1 .. n]) do > MicroSleep(nSleep); > for j in ProcessIterator([1 .. l]) do > MicroSleep(lSleep); > for k in ProcessIterator([1 .. m]) do > MicroSleep(mSleep); > od; > od; > for j in ProcessIterator([1 .. r]) do > MicroSleep(rSleep); > od; > od; > end;; gap> doTree(3, 10^6/10, 5, 10^6/2, 4, 10^6/10, 12, 10^6/10);;
Example output during the execution of the function:
| Total Time ~ 00:00:15 [==============================================-----------------------] | 2/ 3 [=======================================---------------------------] | 3/ 5 | [===============================--------------------------------] | 2/ 4 [------------------------------------------------------------------] | 0/12
In the above example we create the processes on the fly, so for example the "Right" process appears in the terminal only after we create this process for the first time. When this happens, the layout of the display changes slightly, because the total number of iterations for "Right" has 2 digits, whereas for each of the ones before that it had 1 digit. Example output before process "Right" gets created for the first time:
| Total Time ~ 00:00:03 [-----------------------------------------------------------------------] | 0/3 [======================================================--------------] | 4/5 [-----------------------------------------------------------------] | 0/4
Example output after process "Right" gets created for the first time:
| Total Time ~ 00:00:11 [=======================----------------------------------------------] | 1/ 3 [==================================================================] | 5/ 5 | [===============================================================] | 4/ 4 [======================================----------------------------] | 7/12
If we want to see all processes from the beginning, we need to set the processes before the most outer loop starts.
gap> LoadPackage("ProgressBar");; gap> doTree := function(n, nSleep, l, lSleep, m, mSleep, r, rSleep) > local iter, i, j, k; > iter := ProcessIterator([1 .. n], "Root", fail); > SetProcess(l, "Left", "Root"); > SetProcess(m, "Inner", "Left"); > SetProcess(r, "Right", "Root"); > for i in iter do > MicroSleep(nSleep); > for j in ProcessIterator([1 .. l], "Left", "Root") do > MicroSleep(lSleep); > for k in ProcessIterator([1 .. m], "Inner", "Left") do > MicroSleep(mSleep); > od; > od; > for j in ProcessIterator([1 .. r], "Right", "Root") do > MicroSleep(rSleep); > od; > od; > end;; gap> doTree(3, 10^6/10, 5, 10^6/2, 4, 10^6/10, 12, 10^6/10);;
Then the output looks like this from the start:
| Total Time ~ 00:00:03 [---------------------------------------------------------------------] | 0/ 3 [=======================================---------------------------] | 3/ 5 | [---------------------------------------------------------------] | 0/ 4 [------------------------------------------------------------------] | 0/12
If for example we want to compare the performance of different processes, it might be more intuitive to switch to the table layout.
For this we need to define a dummy process, that we terminate manually in the end. All processes we want to track have to be defined as children of this dummy.
TODO
If there is code that we do not want to track in our process, we can use StopProcess
and StartProcess
.
gap> LoadPackage("ProgressBar");; gap> doLoopWithStop := function(n, nSleep, mSleep) > local i; > for i in ProcessIterator([1 .. n], "proc") do > MicroSleep(nSleep); > StopProcess("proc"); > MicroSleep(mSleep); > StartProcess("proc"); > od; > end;; gap> doLoopWithStop(50, 10^6/10, 10^6/10);;
In the above example the code runs for roughly 10 seconds, but the tracked total time that contributes to the process shows roughly 5 seconds.
generated by GAPDoc2HTML