Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect progress updates when using thread_map #1565

Open
5 of 6 tasks
joelostblom opened this issue Apr 13, 2024 · 1 comment
Open
5 of 6 tasks

Incorrect progress updates when using thread_map #1565

joelostblom opened this issue Apr 13, 2024 · 1 comment

Comments

@joelostblom
Copy link

joelostblom commented Apr 13, 2024

I'm running a thread_map with five inner loops, each with it's own progress bar. However, instead of updating 20% per completed bar, the outer loop updates 20% for the first three together (should be 60%) and then another 80% (should be 40%) when the last two completes:

recording-2024-04-13_10.47.03.mp4

(the reason for sleep in the beginning is to make sure the outer bar is on top, I think due to this issue #1496). process_map behaves similarly but I don't see the inner bars.

from tqdm.auto import tqdm

def inner_pbar(step):
    sleep(0.2)
    with tqdm(total=100) as pbar:
        for i in range(20):
            pbar.update(5)
            sleep(0.2)

thread_map(inner_pbar, range(5), max_workers=3, desc='Outer loop')
  • I have marked all applicable categories:
    • exception-raising bug
    • visual output bug
  • I have visited the source website, and in particular
    read the known issues
  • I have searched through the issue tracker for duplicates
  • I have mentioned version numbers, operating system and
    environment, where applicable:
import tqdm, sys
print(tqdm.__version__, sys.version, sys.platform)
# 4.66.2 3.10.14 | packaged by conda-forge | (main, Mar 20 2024, 12:45:18) [GCC 12.3.0] linux
@NyaMisty
Copy link

I improved thread_map's behaviour to made it more like imap_unordered's progress output:

from concurrent.futures import ThreadPoolExecutor

def _executor_map(PoolExecutor, fn, *iterables, **tqdm_kwargs):
    """
    Implementation of `thread_map` and `process_map`.

    Parameters
    ----------
    tqdm_class  : [default: tqdm.auto.tqdm].
    max_workers  : [default: min(32, cpu_count() + 4)].
    chunksize  : [default: 1].
    lock_name  : [default: "":str].
    """
    from tqdm.contrib.concurrent import ensure_lock, length_hint, tqdm_auto, cpu_count
    kwargs = tqdm_kwargs.copy()
    if "total" not in kwargs:
        kwargs["total"] = length_hint(iterables[0])
    tqdm_class = kwargs.pop("tqdm_class", tqdm_auto)
    max_workers = kwargs.pop("max_workers", min(32, cpu_count() + 4))
    chunksize = kwargs.pop("chunksize", 1)
    lock_name = kwargs.pop("lock_name", "")
    with ensure_lock(tqdm_class, lock_name=lock_name) as lk:
        # share lock in case workers are already using `tqdm`
        with PoolExecutor(max_workers=max_workers, initializer=tqdm_class.set_lock,
                          initargs=(lk,)) as ex:
            with tqdm_class(**kwargs) as pbar:
                orisubmit = ex.submit
                def patchsubmit(*args, **kwargs):
                    fut = orisubmit(*args, **kwargs)
                    fut.add_done_callback(lambda _: pbar.update())
                    return fut
                ex.submit = patchsubmit
                return list(ex.map(fn, *iterables, chunksize=chunksize))

thread_map = lambda fn, *iterables, **tqdm_kwargs: _executor_map(ThreadPoolExecutor, fn, *iterables, **tqdm_kwargs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants