पायथन मल्टीप्रोसेसिंग पूल imap_unordered कॉल की प्रगति दिखाएं?


95

मेरे पास एक स्क्रिप्ट है जो सफलतापूर्वक एक 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
मुझे कोड आउट (प्रत्येक पुनरावृत्ति नहीं) के बाद ही प्रिंट आउट दिखाई देता है। आपका कोई सुझाव है?
हनान शिंगिंगर्ट

@HananShteingart: यह पायथन 2 और 3 के साथ मेरे सिस्टम (उबंटू) पर ठीक काम करता है। 3. मैंने def do_word(*a): time.sleep(.1)एक उदाहरण के रूप में उपयोग किया है । यदि यह आपके लिए काम नहीं करता है, तो एक पूर्ण न्यूनतम कोड उदाहरण बनाएं जो आपके मुद्दे को प्रदर्शित करता है: शब्दों का उपयोग करके वर्णन करें कि आप क्या होने की उम्मीद करते हैं और इसके बजाय क्या होता है, उल्लेख करें कि आप अपनी पायथन स्क्रिप्ट कैसे चलाते हैं, आपका ओएस, पायथन संस्करण क्या है और इसे एक नए प्रश्न के रूप में पोस्ट करें
jfs

13
मुझे @HananShteingart के समान समस्या थी: यह इसलिए है क्योंकि मैं उपयोग करने की कोशिश कर रहा था Pool.map()। मुझे यह महसूस नहीं हुआ कि केवल imap() और imap_unordered()इस तरह से काम करते हैं - दस्तावेज़ीकरण केवल "मानचित्र का एक लेज़ियर संस्करण" () कहता है, लेकिन वास्तव में इसका अर्थ है "अंतर्निहित पुनरावृत्ति परिणाम के रूप में वे आते हैं"।
सिमोनकैमुल्लेन

@simonmacmullen: प्रश्न और मेरे उत्तर दोनों का उपयोग करें imap_unordered()। हानन का मुद्दा संभवतः sys.stderr.write('\r..')प्रगति को दिखाने के लिए एक ही पंक्ति को अधिलेखित करने के कारण है ।
JFS

2
यह भी संभव है! मैं मुख्य रूप से एक बेवकूफ धारणा का दस्तावेजीकरण करना चाहता था जिसे मैंने बनाया था - अगर कोई और इसे पढ़ता है तो उसने भी इसे बनाया है।
सिमोनमकुलन

94

मेरा व्यक्तिगत पसंदीदा - आपको एक अच्छा सा प्रगति बार और पूर्ण ईटीए देता है जबकि चीजें समानांतर में चलती हैं और प्रतिबद्ध हैं।

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
क्या होगा यदि पूल एक मूल्य लौटाता है?
निकपिक

11
मैंने लूप से पहले रिजल्ट नाम की एक खाली सूची बनाई, जिसके बाद लूप के अंदर सिर्फ result.append (x) होता है। मैंने इसे 2 प्रक्रियाओं के साथ आज़माया और नक्शे के बजाय imap का उपयोग किया और सब कुछ काम किया जैसा कि मैं इसे @nickpick
bs7280

2
इसलिए मेरी प्रगति पट्टी जगह-जगह प्रगति करने के बजाय नई लाइनों के लिए परेशान कर रही है, किसी भी विचार क्यों यह हो सकता है?
ऑस्टिन

2
pip install tqdm
श्री टी।

3
@ bs7280 by result.append (x) क्या आपका मतलब result.append (_) था? X क्या है?
जेसन

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
मुझे लगता है कि थ्रेड्स का एक गुच्छा बनाता है, और प्रत्येक धागा स्वतंत्र रूप से गिन रहा है
nern42

1
मेरे पास फ़ंक्शंस हैं जिनके परिणामस्वरूप अचार त्रुटि होती है।
ओजंक

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_unorderedलिए स्वैपिंग 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
मैंने इसे पायथन 2.7.6 के लिए परीक्षण किया और rs._number_left शेष भाग की संख्या प्रतीत होती है। इसलिए यदि rs._chunksize 1 नहीं है, तो rs._number_left शेष सूची मदों की संख्या नहीं होगी।
एलन

मुझे यह कोड कहां रखना चाहिए? मेरा मतलब है कि यह तब तक निष्पादित नहीं किया जाता है जब तक कि सामग्री ज्ञात न हो rsऔर थोड़ी देर हो या न हो?
वाकन टंका

@WakanTanka: अतिरिक्त थ्रेड को बंद करने के बाद यह मुख्य स्क्रिप्ट में जाता है। मेरे मूल उदाहरण में, यह "जबकि" लूप में जाता है, जहां rsपहले से ही अन्य थ्रेड लॉन्च किए गए हैं।
मिडनाइटलाइटिंग

1
क्या आप कृपया अपना प्रश्न और / या न्यूनतम कार्य उदाहरण दिखाने के लिए उत्तर दे सकते हैं। मैं rsकिसी भी लूप में नहीं दिख रहा हूं, मैं नौसिखिया को गुणा कर रहा हूं और इससे मदद मिलेगी। आपका बहुत बहुत धन्यवाद।
वाकन टंका

1
कम से कम python 3.5, उपयोग करने वाला समाधान _number_leftकाम नहीं करता है। _number_leftउन चक्रों का प्रतिनिधित्व करता है जो संसाधित होने के लिए बने रहते हैं। उदाहरण के लिए, यदि मैं समानांतर रूप से अपने कार्य के लिए 50 तत्वों को पास करना चाहता हूं, तो 3 प्रक्रियाओं के साथ थ्रेड पूल के लिए _map_async()प्रत्येक तत्व के साथ 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 का उपयोग करते हैं (इस मामले में, यह किसी सूची में दिए गए मान को जोड़ने के लिए है), इसलिए आपको कुछ और करने के लिए इंतजार नहीं करना होगा। फिर, थोड़ी देर के लूप के भीतर, आप कार्य की प्रगति की जांच करते हैं। इस मामले में, मैंने इसे अच्छे लगने के लिए एक विजेट जोड़ा।

उत्पादन:

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

यह सच नहीं है। यहां जेनरेटर ऑब्जेक्ट काम नहीं करेगा। जांच की गई।
18at पर स्वैगमत

9

टिम ने सुझाव दिया रूप में, आप उपयोग कर सकते हैं tqdmऔर imapइस समस्या को हल करने के लिए। मैंने अभी इस समस्या पर ठोकर खाई है और 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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.