Đa xử lý: sử dụng tqdm để hiển thị thanh tiến trình


97

Để làm cho mã của tôi "pythonic" hơn và nhanh hơn, tôi sử dụng "đa xử lý" và một hàm bản đồ để gửi nó a) hàm và b) phạm vi lặp lại.

Giải pháp được cấy ghép (tức là gọi tqdm trực tiếp trên phạm vi tqdm.tqdm (phạm vi (0, 30)) không hoạt động với đa xử lý (như được xây dựng trong mã bên dưới).

Thanh tiến trình được hiển thị từ 0 đến 100% (khi python đọc mã?) Nhưng nó không cho biết tiến trình thực tế của chức năng bản đồ.

Làm cách nào để hiển thị thanh tiến trình cho biết chức năng 'bản đồ' đang ở bước nào?

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

Mọi trợ giúp hoặc đề xuất đều được hoan nghênh ...


Bạn có thể đăng đoạn mã của thanh tiến trình không?
Alex

1
Đối với những người đang tìm kiếm giải pháp với .starmap(): Đây là một bản vá để Poolthêm .istarmap(), cũng sẽ hoạt động với tqdm.
Darkonaut

Câu trả lời:


127

Sử dụng imap thay vì bản đồ, trả về một trình lặp các giá trị đã xử lý.

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
Một câu lệnh enclosing list () đợi trình vòng lặp kết thúc. tổng = cũng được yêu cầu từ tqdm không biết bao lâu lặp sẽ được,
hkyi

13
Có một giải pháp tương tự cho starmap()?
tarashypka

1
for i in tqdm.tqdm(...): pass có thể dễ dàng hơn, điều đólist(tqdm.tqdm)
savfod

1
Điều này hoạt động nhưng có ai khác đã liên tục in thanh tiến trình trên một dòng mới cho mỗi lần lặp không?
Dennis Subachev

3
Hành vi có dây khi cụ thể chunk_sizecủa p.imap. Có thể tqdmcập nhật mọi lần lặp thay vì mọi đoạn không?
huangbiubiu

49

Giải pháp được tìm thấy: Hãy cẩn thận! Do đa xử lý, thời gian ước tính (số lần lặp trên mỗi vòng lặp, tổng thời gian, v.v.) có thể không ổn định, nhưng thanh tiến trình hoạt động hoàn hảo.

Lưu ý: Trình quản lý ngữ cảnh cho Pool chỉ có sẵn từ phiên bản 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()không cần thiết, nó sẽ tự động bị đóng về việc chấm dứtwith
Sagar Kar

5
tqdmCuộc gọi thứ hai / bên trong có cần thiết ở đây không?
shadowtalker

5
còn đầu ra của _foo (my_number) được trả về là "r" trong câu hỏi thì sao?
Likak

3
Có một giải pháp tương tự cho starmap()?
tarashypka

2
@shadowtalker - nó dường như hoạt động mà không có;). Dù sao - imap_unorderedlà chìa khóa ở đây, nó mang lại hiệu suất tốt nhất và ước tính thanh tiến trình tốt nhất.
Tomasz Gandor

19

Bạn có thể sử dụng p_tqdmthay thế.

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
Điều này hoạt động cực kỳ hiệu quả và rất dễ dàng pip install. Này được thay thế tqdm cho hầu hết các nhu cầu của tôi
crypdick

Merci Victor;)
Gabriel Romon

p_tqdmđược giới hạn cho multiprocessing.Pool, không có sẵn cho các chủ đề
pateheo

16

Xin lỗi vì đã đến muộn nhưng nếu tất cả những gì bạn cần là một bản đồ đồng thời, phiên bản mới nhất ( tqdm>=4.42.0) hiện có tích hợp này:

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)

Tài liệu tham khảo: https://tqdm.github.io/docs/contrib.concurrent/https://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py


Cảm ơn vì điều đó. Hoạt động dễ dàng, tốt hơn nhiều so với bất kỳ giải pháp nào khác mà tôi đã thử.
user3340499 Ngày

Tuyệt vời (+1), nhưng ném HBox(children=(FloatProgress(value=0.0, max=30.0), HTML(value='')))trong Jupyter
Ébe Isaac


Tôi thấy có vấn đề với cuộc thảo luận để hack tqdm_notebook, tuy nhiên, không thể tìm ra giải pháp để giải quyết cho tqdm.contrib.concurrent.
Ébe Isaac

8

dựa trên câu trả lời của Xavi Martínez tôi đã viết hàm imap_unordered_bar. Nó có thể được sử dụng theo cách giống như imap_unorderedvới sự khác biệt duy nhất là thanh xử lý được hiển thị.

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
Thao tác này sẽ vẽ lại thanh ở mỗi bước trên một dòng mới. Làm thế nào để cập nhật cùng một dòng?
misantroop

Giải pháp trong trường hợp của tôi (Windows / Powershell): Colorama.
misantroop

'pbar.close () không cần thiết, nó sẽ tự động bị đóng về việc chấm dứt với' như nhận xét Sagar thực hiện trên câu trả lời @ scipy của
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

Đây là lý do của tôi khi bạn cần lấy lại kết quả từ các hàm thực thi song song của mình. Hàm này thực hiện một số điều (có một bài viết khác của tôi giải thích thêm) nhưng điểm mấu chốt là có một hàng đợi nhiệm vụ đang chờ xử lý và một hàng đợi nhiệm vụ đã hoàn thành. Khi người lao động hoàn thành từng nhiệm vụ trong hàng đợi đang chờ xử lý, họ thêm kết quả vào hàng đợi nhiệm vụ đã hoàn thành. Bạn có thể đưa séc vào hàng đợi nhiệm vụ đã hoàn thành bằng thanh tiến trình tqdm. Tôi không đặt việc triển khai hàm do_work () ở đây, nó không liên quan, vì thông điệp ở đây là theo dõi hàng đợi đã hoàn thành nhiệm vụ và cập nhật thanh tiến trình mỗi khi có kết quả.

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

Cách tiếp cận này đơn giản và nó hoạt động.

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.