ProgressMeter.jl icon indicating copy to clipboard operation
ProgressMeter.jl copied to clipboard

[RFC] add support for multiple simultaneous progressbars

Open MarcMush opened this issue 5 years ago • 27 comments

following my answer in #156 , this adds two types of progressbars that mimic Progress:

ParallelProgress is pretty much the example in the readme and works like that :

using Distributed
addprocs()
@everywhere using ProgressMeter
prog = ParallelProgress(10; desc="test ")
pmap(1:10) do i
    sleep(rand())
    next!(prog)
    return myid()
end

MultipleProgress spawns multiple progress bars using offset

using Distributed
addprocs(2)
@everywhere using ProgressMeter
p = MultipleProgress([Progress(10; desc="task $i ") for i in 1:5], Progress(50; desc="global "))
pmap(1:5) do i
    for _ in 1:10
        sleep(rand())
        next!(p[i])
    end
    sleep(0.01)
    myid()
end
global 100%|██████████████████████████████████████████████████████████| Time: 0:00:24
task 4 100%|██████████████████████████████████████████████████████████| Time: 0:00:05
task 5 100%|██████████████████████████████████████████████████████████| Time: 0:00:05

It is also possible to add progressbars from another worker

p = MultipleProgress(Progress(10, "tasks done "); count_finishes=true)
sleep(0.1)
pmap(1:10) do i
    L = rand(20:50)
    p[i] = Progress(L, desc=" task $i ")
    for _ in 1:L
        sleep(0.05)
        next!(p[i])
    end
end

the main progress can be a Progress, either counting all individual steps, or counting finished tasks. It can also be a ProgressUnknown

the other progresses can be Progress, UnknownProgress or ProgressTresh

feel free to try this PR with ]add ProgressMeter#5c21f5d and give feedback and suggestions Everything should be explained in ?MultipleProgress

closes #9, closes #96, closes #97, closes #125, closes #156, closes #253

MarcMush avatar May 25 '20 14:05 MarcMush

This is great! It's exactly what I was just looking for, and seems stable on a few trials across remote distributed workers.

One minor thought, I noticed that the color field of the added update!() method isn't used. It might be nice to be able to change that on the fly. I couldn't see an easy way to implement that with this approach. I've also in the past updated the progress bar description during runs by changing prog.desc directly. It might be nice to add that functionality, but that could definitely wait for a future PR.

IanButterworth avatar May 30 '20 01:05 IanButterworth

Master now has the option to update the desc from update!() via https://github.com/timholy/ProgressMeter.jl/pull/159. It would be good to think about whether it could be incorporated here. I did try a few attempts, but couldn't figure out a simple modification.

IanButterworth avatar Jun 03 '20 17:06 IanButterworth

I added tests and fixed some bugs due to overshooting

@ianshmean it wouldn't be difficult to change it, by passing NamedTuples instead of Ints in the RemoteChannel

MarcMush avatar Jun 25 '20 13:06 MarcMush

i'm working on it here https://github.com/MarcMush/ProgressMeter.jl/tree/parallel_update

MarcMush avatar Jun 25 '20 15:06 MarcMush

This looks really nice. Does it work for tasks distributed over threads as well?

jtrakk avatar Dec 05 '20 05:12 jtrakk

yes, it should work, you can even use it on a single core I didn't work on this much since I did this PR, but I can continue working on it if there is demand for it (or if someone wants to continue it, feel free to do it)

MarcMush avatar Dec 07 '20 12:12 MarcMush

I think it would be really useful for parallel long-running tasks. For example, it would be a nice improvement in the UX of parallel algorithms in Turing.jl.

jtrakk avatar Dec 22 '20 03:12 jtrakk

It would be nicer if you could encapsulate any other progress bars, e.g. ProgressUnknown etcetera, in this way. That is, if you could just pass an array of progress meters:

p = MultipleProgress([Progress(...), ProgressUnknown(...), ProgressThresh(...)])

stevengj avatar Jun 01 '21 12:06 stevengj

Whats the status of this?

bdeonovic avatar Aug 09 '21 14:08 bdeonovic

I did some work on this PR I implemented @IanButterworth's suggestion https://github.com/timholy/ProgressMeter.jl/pull/157#issuecomment-638330463

MarcMush avatar Aug 12 '21 15:08 MarcMush

It would be nicer if you could encapsulate any other progress bars, e.g. ProgressUnknown etcetera, in this way. That is, if you could just pass an array of progress meters:

p = MultipleProgress([Progress(...), ProgressUnknown(...), ProgressThresh(...)])

I really like the idea, and I enabled it for ParallelProgress, but for MultipleProgress, there will be a problem for tasks that are not started immediatly, since the start time will report when the whole object was created There is several solutions to this, I don't know which one is better:

  • just don't care, the timings will be wrong in some cases
  • pass functions that will be executed at first call of the progressmeter eg p = MultipleProgress([()->Progress(...), ()->ProgressUnknown(...), ()->ProgressThresh(...)])
  • reset the p.tinit at first call

note that right now, and with one these solutions (except the first one) the first step won't be included in the timing except if initialized with update!(p,0) for example

MarcMush avatar Aug 12 '21 15:08 MarcMush

Codecov Report

Attention: Patch coverage is 98.19277% with 3 lines in your changes missing coverage. Please review.

Project coverage is 97.21%. Comparing base (807496a) to head (5c21f5d). Report is 15 commits behind head on master.

Files Patch % Lines
src/parallel_progress.jl 98.19% 3 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #157      +/-   ##
==========================================
+ Coverage   93.48%   97.21%   +3.72%     
==========================================
  Files           1        2       +1     
  Lines         399      717     +318     
==========================================
+ Hits          373      697     +324     
+ Misses         26       20       -6     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar Aug 12 '21 20:08 codecov[bot]

but for MultipleProgress, there will be a problem for tasks that are not started immediately, since the start time will report when the whole object was created

How about have a Channel object that you can put! progress bars to asynchronously? That way you don't need to know in advance how many progress bars you need (e.g. if you are recursively processing a directory tree).

stevengj avatar Aug 14 '21 22:08 stevengj

It would be cool to have a full tree of hierarchical progress bars for hierarchical task trees, matching up with the move toward structured concurrency in https://github.com/JuliaLang/julia/issues/33248.

jtrakk avatar Aug 14 '21 23:08 jtrakk

again, a lot of changes this includes #215, which is of course necessary for this

Now, there are 2 main ways to use a MultipleProgress, with a few options

MultipleProgress([Progress(10), ProgressUnknown(), ProgressTresh(0.1)...], [mainprogressbar])

p = MultipleProgress(ProgressUnknown())
addprogress!(p[1], Progress, 10, desc="task 1")

I tried to detail the different possibilities in the docstring and in the tests

MarcMush avatar Aug 16 '21 14:08 MarcMush

I apologize if I am resurrecting this thread inappropriately. What is the current status of this feature?

HiramTheHero avatar Feb 10 '22 18:02 HiramTheHero

this is basically working but there are some decisions to make regarding syntax and functionnalities

MarcMush avatar Feb 15 '22 09:02 MarcMush

This PR is about to celebrate its third anniversary, and I would love to merge this one day. Here is an attempt to list what is currently blocking to do so:

  • [ ] are we satisfied with the current addprogress!(mp[i], Progress, args...)? This can be confusing and the index i needs to be known and managed correctly. See discussion https://github.com/timholy/ProgressMeter.jl/pull/157#pullrequestreview-758944656
  • [ ] mp[0] is currently the main progressmeter, and mp[1:end] all the other ones. It is not an AbstractArray, no firstindex. Should this be changed?
  • [ ] is everything ok with the approach? in particular the Channels
  • [ ] documentation/readme

I encourage everyone interested in having multiple progressbars to try this PR with ]add ProgressMeter#3156d04 and give feedback and suggestions. Everything should be explained in ?MultipleProgress.

MarcMush avatar May 21 '23 19:05 MarcMush

Sorry I've not come around to reviewing this impressive PR. My main thought is that if progress bars might be added or removed at various times, integer-indexing seems like a bad idea. I'd rather use a Dict-style of indexing, where each progress bar gets a unique key (and trying to create a new progress bar using an existing key throws an error). Keys should live for the entire lifetime of the main progress bar, even if they've finished.

timholy avatar Aug 04 '23 10:08 timholy

I've changed to a Dict for storing the progressbars and throws an error when trying to re-add a progess with the same id. It is possible to use Ints or any other type for indexing. The main progress can be specified with the kwarg main and recalled with p.main

MarcMush avatar Sep 03 '23 17:09 MarcMush

this seems actually ready? gentle bump for a nod from main devs and we can do documentation etc. and ship it?

Moelf avatar Nov 07 '23 10:11 Moelf

have you tested it? I'd appreciate other people trying it and giving feedback, see if it is actually what is wanted you can use it with ]add ProgressMeter#3156d04

MarcMush avatar Nov 07 '23 12:11 MarcMush

I read the examples here and I'm exactly using Distributed pmap so I think yes?

Moelf avatar Nov 07 '23 12:11 Moelf

@distributed pmap() was already implemented before

this PR adds next!() accross workers with ParallelProgress, as well as multiple progressbars at once with MultipleProgress

MarcMush avatar Nov 07 '23 14:11 MarcMush

@distributed pmap() was already implemented before

I have multiple sub tasks and also pmap() is not good enough because workers are unstable

Moelf avatar Nov 07 '23 14:11 Moelf

great, you can use this PR with ]add ProgressMeter#3156d04 and see if it works as expected for your usecase

MarcMush avatar Nov 07 '23 15:11 MarcMush

I changed the syntax for adding new progressbars, now it simply works with setindex!:

mp = MultipleProgress()
mp[1] = Progress(10)
next!(p[1])

MarcMush avatar Jul 13 '24 09:07 MarcMush