マルチプロセッシング:tqdmを使用して進行状況バーを表示する


97

私のコードをより「Pythonic」かつより高速にするために、「マルチプロセッシング」とマップ関数を使用して、a)関数とb)反復の範囲を送信します。

埋め込まれたソリューション(つまり、tqdm.tqdm(range(0、30)の範囲で直接tqdmを呼び出す)は、マルチプロセッシングでは機能しません(以下のコードで定式化)。

進行状況バーは0〜100%の範囲で表示されますが(Pythonがコードを読み取るときは?)、マップ関数の実際の進行状況は表示されません。

「マップ」機能がどのステップにあるかを示す進行状況バーを表示するにはどうすればよいですか?

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   p = Pool(2)
   r = p.map(_foo, tqdm.tqdm(range(0, 30)))
   p.close()
   p.join()

ヘルプや提案は大歓迎です...


進行状況バーのコードスニペットを投稿できますか?
Alex

1
解の探索の人々のために.starmap()ここでのパッチでPool追加すること.istarmap()もで動作しますこれは、tqdm
Darkonaut

回答:


127

処理された値の反復子を返すmapではなくimapを使用します。

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   with Pool(2) as p:
      r = list(tqdm.tqdm(p.imap(_foo, range(30)), total=30))

13
囲んでいるlist()ステートメントは、反復子が終了するのを待ちます。tqdmは反復の時間を知らないため、total =も必要です
hkyi

13
同様の解決策はありstarmap()ますか?
tarashypka

1
for i in tqdm.tqdm(...): pass もっと簡単かもしれません、それはlist(tqdm.tqdm)
savfod

1
これは機能しますが、他の誰かが、反復ごとに継続的にプログレスバーを改行に印刷しましたか?
Dennis Subachev

3
特定chunk_sizeの場合、動作は配線されp.imapます。tqdmすべてのチャンクではなく、すべての反復を更新できますか?
huangbiubiu

49

解決策が見つかりました:注意してください!マルチプロセッシングにより、推定時間(ループごとの反復、合計時間など)が不安定になる可能性がありますが、進行状況バーは完全に機能します。

注:プールのコンテキストマネージャーは、Pythonバージョン3.3からのみ使用できます。

from multiprocessing import Pool
import time
from tqdm import *

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
    with Pool(processes=2) as p:
        max_ = 30
        with tqdm(total=max_) as pbar:
            for i, _ in enumerate(p.imap_unordered(_foo, range(0, max_))):
                pbar.update()

2
pbar.close()必須ではありません。終了時に自動的に閉鎖されますwith
Sagar Kar

5
tqdmここで2回目の呼び出し/内部呼び出しが必要ですか?
シャドウトーカー2017

5
問題の「r」として返される_foo(my_number)の出力はどうですか?
Likak

3
同様の解決策はありstarmap()ますか?
tarashypka

2
@shadowtalker-;)なしで動作するようです。とにかく- imap_unorderedここで重要です、それは最高のパフォーマンスと最高の進行状況バーの見積もりを提供します。
Tomasz Gandor

19

p_tqdm代わりに使用できます。

https://github.com/swansonk14/p_tqdm

from p_tqdm import p_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = p_map(_foo, list(range(0, 30)))

1
これは非常にうまく機能し、非常に簡単pip installでした。これは私のニーズのほとんどのtqdmを交換している
crypdick

Merci Victor;)
ガブリエルロモン

p_tqdmに制限されてmultiprocessing.Poolおり、スレッドでは使用できません
pateheo

17

遅くなって申し訳ありませんが、必要なのが並行マップのみの場合、最新バージョン(tqdm>=4.42.0)には次の機能が組み込まれています。

from tqdm.contrib.concurrent import process_map  # or thread_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = process_map(_foo, range(0, 30), max_workers=2)

参照:https : //tqdm.github.io/docs/contrib.concurrent/およびhttps://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py


これをありがとう。簡単に機能し、私が試した他のどのソリューションよりもはるかに優れています。
user3340499

(1)クールが、スローHBox(children=(FloatProgress(value=0.0, max=30.0), HTML(value='')))Jupyterに
EBEアイザック


tqdm_notebookをハックするためのディスカッションに問題があるようですが、tqdm.contrib.concurrentを解決するための解決策を見つけることができません。
EBEアイザック

8

XaviMartínezの回答に基づいて、関数を記述しましたimap_unordered_barimap_unordered処理バーが表示されることだけが異なるのと同じ方法で使用できます。

from multiprocessing import Pool
import time
from tqdm import *

def imap_unordered_bar(func, args, n_processes = 2):
    p = Pool(n_processes)
    res_list = []
    with tqdm(total = len(args)) as pbar:
        for i, res in tqdm(enumerate(p.imap_unordered(func, args))):
            pbar.update()
            res_list.append(res)
    pbar.close()
    p.close()
    p.join()
    return res_list

def _foo(my_number):
    square = my_number * my_number
    time.sleep(1)
    return square 

if __name__ == '__main__':
    result = imap_unordered_bar(_foo, range(5))

3
これにより、新しい行の各ステップでバーが再描画されます。同じ行を更新する方法は?
misantroop

私の場合の解決策(Windows / Powershell):コロラマ。
misantroop

「pbar.close()は必要ありません。@ scipyの回答にSagarが投稿したコメントのように、withの終了時に自動的に閉じられます
Tejas Shetty

0
import multiprocessing as mp
import tqdm


some_iterable = ...

def some_func():
    # your logic
    ...


if __name__ == '__main__':
    with mp.Pool(mp.cpu_count()-2) as p:
        list(tqdm.tqdm(p.imap(some_func, iterable), total=len(iterable)))

0

これは、並列実行関数から結果を取得する必要がある場合の私の見解です。この関数はいくつかのことを行います(詳細については別の投稿があります)が、重要な点は、タスク保留キューとタスク完了キューがあることです。ワーカーは保留中のキューの各タスクを完了すると、タスク完了キューに結果を追加します。tqdmプログレスバーを使用して、チェックをタスク完了キューにラップできます。ここでのメッセージはタスク完了キューを監視し、結果が表示されるたびに進行状況バーを更新するためのものなので、ここではdo_work()関数の実装は使用していません。

def par_proc(job_list, num_cpus=None, verbose=False):

# Get the number of cores
if not num_cpus:
    num_cpus = psutil.cpu_count(logical=False)

print('* Parallel processing')
print('* Running on {} cores'.format(num_cpus))

# Set-up the queues for sending and receiving data to/from the workers
tasks_pending = mp.Queue()
tasks_completed = mp.Queue()

# Gather processes and results here
processes = []
results = []

# Count tasks
num_tasks = 0

# Add the tasks to the queue
for job in job_list:
    for task in job['tasks']:
        expanded_job = {}
        num_tasks = num_tasks + 1
        expanded_job.update({'func': pickle.dumps(job['func'])})
        expanded_job.update({'task': task})
        tasks_pending.put(expanded_job)

# Set the number of workers here
num_workers = min(num_cpus, num_tasks)

# We need as many sentinels as there are worker processes so that ALL processes exit when there is no more
# work left to be done.
for c in range(num_workers):
    tasks_pending.put(SENTINEL)

print('* Number of tasks: {}'.format(num_tasks))

# Set-up and start the workers
for c in range(num_workers):
    p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed, verbose))
    p.name = 'worker' + str(c)
    processes.append(p)
    p.start()

# Gather the results
completed_tasks_counter = 0

with tqdm(total=num_tasks) as bar:
    while completed_tasks_counter < num_tasks:
        results.append(tasks_completed.get())
        completed_tasks_counter = completed_tasks_counter + 1
        bar.update(completed_tasks_counter)

for p in processes:
    p.join()

return results

-2

このアプローチはシンプルで機能します。

from multiprocessing.pool import ThreadPool
import time
from tqdm import tqdm

def job():
    time.sleep(1)
    pbar.update()

pool = ThreadPool(5)
with tqdm(total=100) as pbar:
    for i in range(100):
        pool.apply_async(job)
    pool.close()
    pool.join()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.