Çoklu işlem: bir ilerleme çubuğu görüntülemek için tqdm kullanın


103

Kodumu daha "pitonik" ve daha hızlı yapmak için, "çoklu işlem" ve ona a) işlevi ve b) yineleme aralığını göndermek için bir harita işlevi kullanıyorum.

İmplante çözüm (yani, tqdm.tqdm aralığında doğrudan tqdm çağrısı yapın (aralık (0, 30)) çoklu işlemle çalışmaz (aşağıdaki kodda formüle edildiği gibi).

İlerleme çubuğu% 0 ile% 100 arasında görüntülenir (python kodu okuduğunda?) Ancak harita işlevinin gerçek ilerlemesini göstermez.

'Harita' işlevinin hangi adımda olduğunu gösteren bir ilerleme çubuğu nasıl görüntülenir?

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()

Herhangi bir yardım veya öneriye açığız ...


İlerleme çubuğunun kod parçasını gönderebilir misiniz?
Alex

2
Aşağıdakilerle bir çözüm arayan kişiler için .starmap(): İştePool eklemek için bir yama var ve .istarmap()birlikte çalışacak tqdm.
Darkonaut

Yanıtlar:


136

İşlenmiş değerlerin bir yineleyicisini döndüren harita yerine imap kullanın.

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))

14
Çevreleyen bir liste () ifadesi yineleyicinin bitmesini bekler. tqdm yinelemenin ne kadar süreceğini bilmediğinden toplam = da gereklidir,
hkyi

16
İçin benzer bir çözüm var mı starmap()?
tarashypka

2
for i in tqdm.tqdm(...): pass daha dolaysız olabilir, bulist(tqdm.tqdm)
savfod

1
Bu işe yarıyor, ancak başka biri, her yineleme için ilerleme çubuğunu sürekli olarak yeni satıra yazdırdı mı?
Dennis Subachev

3
Davranış yaparken belirli kablolu chunk_sizearasında p.imap. Can tqdmher yinelemesini yerine her yığın güncelleme?
huangbiubiu

56

Çözüm Bulundu: Dikkatli olun! Çoklu işlem nedeniyle, tahmin süresi (döngü başına yineleme, toplam süre, vb.) Kararsız olabilir, ancak ilerleme çubuğu mükemmel şekilde çalışır.

Not: Havuz için bağlam yöneticisi yalnızca Python sürüm 3.3'te mevcuttur.

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()gerekli değil,with
Sagar Kar

5
Burada ikinci / iç tqdmçağrı gerekli mi?
shadowtalker

7
söz konusu "r" olarak döndürülen _foo (my_number) çıktısı ne olacak?
Likak

4
İçin benzer bir çözüm var mı starmap()?
tarashypka

3
@shadowtalker - olmadan çalışıyor gibi görünüyor;). Her neyse - imap_unorderedburada anahtardır, en iyi performansı ve en iyi ilerleme çubuğu tahminlerini verir.
Tomasz Gandor

26

Geç kaldığım için üzgünüm ama tek ihtiyacınız olan eşzamanlı bir harita ise, bu işlevi şuraya ekledim 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)

Referanslar: https://tqdm.github.io/docs/contrib.concurrent/ ve https://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py

Bu destekler max_workersve chunksizede kolayca geçiş yapabilirsiniz process_mapiçin thread_map.


1
Bunun için teşekkürler. Kolayca çalışıyor, denediğim diğer çözümlerden çok daha iyi.
user3340499

Havalı (+1), ancak HBox(children=(FloatProgress(value=0.0, max=30.0), HTML(value='')))Jupyter'de fırlatılıyor
Ébe Isaac


Tartışmada tqdm_notebook'u hacklemek için bir sorun görüyorum, ancak tqdm.contrib.concurrent için çözmek için bir çözüm bulamıyorum.
Ébe Isaac

Bu harika. Kutudan çıkar çıkmaz çalışır.
Lars Larsson

21

Bunun p_tqdmyerine kullanabilirsiniz .

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
Bu son derece iyi çalışıyor ve çok kolaydı pip install. Bu, çoğu ihtiyacım için tqdm'nin yerini alıyor
crypdick

Merci Victor;)
Gabriel Romon

p_tqdmsınırlı multiprocessing.Pool, konular için mevcut değil
pateheo

8

Xavi Martínez'in cevabına dayanarak fonksiyonu yazdım imap_unordered_bar. imap_unorderedBir işleme çubuğunun gösterildiği tek farkla aynı şekilde kullanılabilir .

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
Bu, yeni bir satırdaki her adımda çubuğu yeniden çizecektir. Aynı satır nasıl güncellenir?
misantroop

Benim durumumdaki çözüm (Windows / Powershell): Colorama.
misantroop

'pbar.close () gerekli değil, Sagar'ın @ scipy'nin cevabında yaptığı yorum gibi
Tejas Shetty

1

Paralel çalıştırma işlevlerinizden sonuçları geri almanız gerektiğinde burada benim alacağım. Bu işlev birkaç şey yapar (bunu daha ayrıntılı açıklayan başka bir gönderi var) ancak kilit nokta bekleyen bir görev ve tamamlanmış bir görev sırası olmasıdır. Çalışanlar bekleme sırasındaki her görevle işlerini bitirirken, sonuçları tamamlanan görevler kuyruğuna eklerler. Tqdm ilerleme çubuğu ile denetimi tamamlanan görevler kuyruğuna kaydırabilirsiniz. Buraya do_work () işlevinin uygulamasını koymuyorum, bununla ilgili değil, çünkü buradaki mesaj tamamlanan görevleri sıraya koymak ve her sonuç geldiğinde ilerleme çubuğunu güncellemek.

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

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)))

-2

Bu yaklaşım basit ve işe yarıyor.

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.