Python 다중 처리 풀 imap_unordered 호출의 진행 상황을 표시합니까?


96

imap_unordered()호출 을 통해 다중 처리 풀 작업 집합을 성공적으로 수행하는 스크립트가 있습니다 .

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
p.join() # Wait for completion

그러나 내 num_tasks약은 약 250,000이므로 join()10 초 정도 주 스레드를 잠그고 주 프로세스가 잠기지 않았 음을 보여주기 위해 명령 줄에 점진적으로 에코 아웃 할 수 있기를 바랍니다. 다음과 같은 것 :

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  remaining = rs.tasks_remaining() # How many of the map call haven't been done yet?
  if (remaining == 0): break # Jump out of while loop
  print "Waiting for", remaining, "tasks to complete..."
  time.sleep(2)

남은 작업 수를 나타내는 결과 개체 또는 풀 자체에 대한 메서드가 있습니까? multiprocessing.Value개체를 카운터로 사용하려고 시도했지만 ( 작업을 수행 한 후 작업을 do_work호출 함 counter.value += 1) 카운터는 증가를 중지하기 전에 총 값의 ~ 85 % 만 얻습니다.

답변:


80

결과 세트의 개인 속성에 액세스 할 필요가 없습니다.

from __future__ import division
import sys

for i, _ in enumerate(p.imap_unordered(do_work, xrange(num_tasks)), 1):
    sys.stderr.write('\rdone {0:%}'.format(i/num_tasks))

7
코드 종료 후에 만 ​​인쇄물이 표시됩니다 (모든 반복이 아님). 제안이 있습니까?
Hanan Shteingart 2014

@HananShteingart : Python 2와 3이 모두있는 내 시스템 (Ubuntu)에서 잘 작동합니다. 저는 def do_word(*a): time.sleep(.1)예제로 사용 했습니다. 작동하지 않는 경우 문제를 보여주는 완전한 최소 코드 예제 를 생성 하십시오. 예상되는 일과 그 대신 발생하는 일을 단어를 사용하여 설명하고, Python 스크립트를 실행하는 방법, OS, Python 버전을 언급하십시오. 그리고 새로운 질문으로 게시 .
jfs

14
나는 @HananShteingart 같은 문제가 있었다 : 내가 사용하려고했기 때문에 그것의 Pool.map(). 나는 몰랐어요 imap() 하고 imap_unordered()이런 식으로 일을 - 문서 그냥하지만 정말 "그들이에 와서 반복자가 결과를 반환 기본"을 의미 말한다 "()은지도의 lazier 버전".
simonmacmullen

@simonmacmullen : 질문과 내 대답 모두 imap_unordered(). Hanan의 문제는 아마도 sys.stderr.write('\r..')(진행 상황을 보여주기 위해 같은 줄을 덮어 씀 ) 때문일 것입니다 .
jfs 2015 년

2
또한 가능합니다! 나는 주로 내가 만든 어리석은 가정을 문서화하고 싶었습니다.
simonmacmullen 2015 년

94

개인적으로 가장 좋아하는 것은 일이 병렬로 실행되고 커밋되는 동안 멋진 작은 진행률 표시 줄과 완료 ETA를 제공합니다.

from multiprocessing import Pool
import tqdm

pool = Pool(processes=8)
for _ in tqdm.tqdm(pool.imap_unordered(do_work, tasks), total=len(tasks)):
    pass

64
풀이 값을 반환하면 어떨까요?
Nickpick 2017

11
루프 전에 result라는 빈 목록을 만든 다음 루프 내부에서 result.append (x)를 수행하십시오. 나는이 개 과정 대신지도의 사용 IMAP와 함께이 시도 내가 @nickpick 그것을 원하는대로 모든 일
bs7280

2
내 진행률 표시 줄이 제자리에서 진행되는 대신 새 줄로 반복됩니다. 왜 그런지 알 수 있습니까?
오스틴

2
잊지 마세요pip install tqdm
Mr. T

3
@ bs7280 result.append (x)에 의해 result.append (_)를 의미 했습니까? x는 무엇입니까?
jason

27

진행 상황을 확인하려고 할 때 이미 작업이 완료되었음을 알았습니다. 이것은 tqdm을 사용하여 나를 위해 일한 것입니다 .

pip install tqdm

from multiprocessing import Pool
from tqdm import tqdm

tasks = range(5)
pool = Pool()
pbar = tqdm(total=len(tasks))

def do_work(x):
    # do something with x
    pbar.update(1)

pool.imap_unordered(do_work, tasks)
pool.close()
pool.join()
pbar.close()

이것은 차단 여부에 관계없이 모든 종류의 다중 처리에서 작동합니다.


4
나는 스레드의 무리를 생성 생각하고 각 스레드는 독립적으로 계산됩니다
nburn42

1
함수 내에 산세 오류가 발생하는 함수가 있습니다.
ojunk

21

좀 더 자세히 살펴보면서 직접 답을 찾았 __dict__습니다. imap_unordered결과 객체를 살펴보면 _index작업이 완료 될 때마다 증가 하는 속성이 있다는 것을 알았습니다 . 따라서 이것은 로깅을 위해 작동하며 while루프에 래핑됩니다 .

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  completed = rs._index
  if (completed == num_tasks): break
  print "Waiting for", num_tasks-completed, "tasks to complete..."
  time.sleep(2)

그러나 결과 개체는 약간 다르지만를 imap_unorderedfor a map_async로 바꾸면 실행 속도가 훨씬 빨라지는 것을 알았습니다. 대신의 결과 개체 map_async에는 _number_left속성과 ready()메서드가 있습니다.

p = multiprocessing.Pool()
rs = p.map_async(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  if (rs.ready()): break
  remaining = rs._number_left
  print "Waiting for", remaining, "tasks to complete..."
  time.sleep(0.5)

3
나는 이것을 Python 2.7.6에서 테스트했으며 rs._number_left는 남은 청크의 수인 것으로 보입니다. 따라서 rs._chunksize가 1이 아니면 rs._number_left는 남은 목록 항목의 수가 아닙니다.
Allen

이 코드를 어디에 넣어야합니까? 내용 rs이 알려질 때까지 실행되지 않고 조금 늦었습니까?
Wakan Tanka 2015 년

@WakanTanka : 추가 스레드를 스핀 오프 한 후 기본 스크립트에 들어갑니다. 내 원래 예제에서는 rs이미 다른 스레드를 시작한 "while"루프에 들어갑니다 .
MidnightLightning

1
최소한의 작업 예를 보여주기 위해 질문 및 / 또는 답변을 편집 해 주시겠습니까? 나는 rs어떤 루프에서도 볼 수 없으며 다중 처리 초보자이며 이것이 도움이 될 것입니다. 대단히 감사합니다.
Wakan Tanka 2015 년

1
적어도에서는 python 3.5사용하는 솔루션 _number_left이 작동하지 않습니다. _number_left처리해야하는 청크를 나타냅니다. 예를 들어, 50 개의 요소를 병렬로 내 함수에 전달하려면 3 개의 프로세스 _map_async()가 있는 스레드 풀에 대해 각각 5 개의 요소가있는 10 개의 청크를 생성합니다. _number_left그런 다음 이러한 청크 중 몇 개가 완료되었는지 나타냅니다.
mSSM

9

나는 이것이 다소 오래된 질문이라는 것을 알고 있지만 파이썬에서 작업 풀의 진행 상황을 추적하고 싶을 때 수행하는 작업은 다음과 같습니다.

from progressbar import ProgressBar, SimpleProgress
import multiprocessing as mp
from time import sleep

def my_function(letter):
    sleep(2)
    return letter+letter

dummy_args = ["A", "B", "C", "D"]
pool = mp.Pool(processes=2)

results = []

pbar = ProgressBar(widgets=[SimpleProgress()], maxval=len(dummy_args)).start()

r = [pool.apply_async(my_function, (x,), callback=results.append) for x in dummy_args]

while len(results) != len(dummy_args):
    pbar.update(len(results))
    sleep(0.5)
pbar.finish()

print results

기본적으로 callbak와 함께 apply_async를 사용하므로 (이 경우 반환 된 값을 목록에 추가) 다른 작업을 수행하기 위해 기다릴 필요가 없습니다. 그런 다음 while 루프 내에서 작업의 진행 상황을 확인합니다. 이번에는 더 멋지게 보이도록 위젯을 추가했습니다.

출력 :

4 of 4                                                                         
['AA', 'BB', 'CC', 'DD']

도움이되기를 바랍니다.


꼭 변경 : [pool.apply_async(my_function, (x,), callback=results.append) for x in dummy_args]대한(pool.apply_async(my_function, (x,), callback=results.append) for x in dummy_args)
데이비드 Przybilla

그건 사실이 아니야. 생성기 개체는 여기서 작동하지 않습니다. 확인했습니다.
swagatam

9

Tim이 제안한대로 tqdmimap을 사용 하여이 문제를 해결할 수 있습니다 . 방금이 문제를 발견 imap_unordered하고 매핑 결과에 액세스 할 수 있도록 솔루션을 조정했습니다 . 작동 방식은 다음과 같습니다.

from multiprocessing import Pool
import tqdm

pool = multiprocessing.Pool(processes=4)
mapped_values = list(tqdm.tqdm(pool.imap_unordered(do_work, range(num_tasks)), total=len(values)))

작업에서 반환 된 값에 신경 쓰지 않는 경우 목록을 변수에 할당 할 필요가 없습니다.


4

다음과 함께 작업하는 간단한 솔루션을 찾고있는 사람 Pool.apply_async():

from multiprocessing import Pool
from tqdm import tqdm
from time import sleep


def work(x):
    sleep(0.5)
    return x**2

n = 10

p = Pool(4)
pbar = tqdm(total=n)
res = [p.apply_async(work, args=(
    i,), callback=lambda _: pbar.update(1)) for i in range(n)]
results = [p.get() for p in res]

3

진행률 인쇄물을 만들기 위해 사용자 지정 클래스를 만들었습니다. Maby는 다음을 도와줍니다.

from multiprocessing import Pool, cpu_count


class ParallelSim(object):
    def __init__(self, processes=cpu_count()):
        self.pool = Pool(processes=processes)
        self.total_processes = 0
        self.completed_processes = 0
        self.results = []

    def add(self, func, args):
        self.pool.apply_async(func=func, args=args, callback=self.complete)
        self.total_processes += 1

    def complete(self, result):
        self.results.extend(result)
        self.completed_processes += 1
        print('Progress: {:.2f}%'.format((self.completed_processes/self.total_processes)*100))

    def run(self):
        self.pool.close()
        self.pool.join()

    def get_results(self):
        return self.results

1

풀링에도 사용할 수있는이 간단한 큐 기반 접근 방식을 시도해보십시오. 진행률 표시 줄이 시작된 후에 인쇄하면 적어도이 특정 진행률 표시 줄에 대해 이동됩니다. (PyPI의 진행률 1.5)

import time
from progress.bar import Bar

def status_bar( queue_stat, n_groups, n ):

    bar = Bar('progress', max = n)  

    finished = 0
    while finished < n_groups:

        while queue_stat.empty():
            time.sleep(0.01)

        gotten = queue_stat.get()
        if gotten == 'finished':
            finished += 1
        else:
            bar.next()
    bar.finish()


def process_data( queue_data, queue_stat, group):

    for i in group:

        ... do stuff resulting in new_data

        queue_stat.put(1)

    queue_stat.put('finished')  
    queue_data.put(new_data)

def multiprocess():

    new_data = []

    groups = [[1,2,3],[4,5,6],[7,8,9]]
    combined = sum(groups,[])

    queue_data = multiprocessing.Queue()
    queue_stat = multiprocessing.Queue()

    for i, group in enumerate(groups): 

        if i == 0:

            p = multiprocessing.Process(target = status_bar,
                args=(queue_stat,len(groups),len(combined)))
                processes.append(p)
                p.start()

        p = multiprocessing.Process(target = process_data,
        args=(queue_data, queue_stat, group))
        processes.append(p)
        p.start()

    for i in range(len(groups)):
        data = queue_data.get() 
        new_data += data

    for p in processes:
        p.join()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.