it-swarm.com.de

Eingaben in Python Multiprocessing

    from multiprocessing import Pool
    with Pool(processes=5) as p:
        p.starmap(name_of_function, all_inputs)

Ich habe einen Code wie oben, der eine Funktion parallel ausführt. Angenommen, dass all_inputs 10.000 Elemente hat, würde ich gerne wissen, welche gerade ausgeführt wird, z. 100 von 10.000 ... Gibt es eine Möglichkeit, diesen Index zu erhalten?

14
user308827

Der Worker-Prozess in multiprocessing.Pool ist eine Instanz von Process. Er behält einen internen Zähler , um sich selbst zu identifizieren. Sie können diesen Zähler zusammen mit OS-Prozess-ID verwenden:

import os
from multiprocessing import current_process, Pool


def x(a):
    p = current_process()
    print('process counter:', p._identity[0], 'pid:', os.getpid())


if __== '__main__':
    with Pool(2) as p:
        r = p.map(x, range(4))
    p.join()

ergibt:

process counter: 1 pid: 29443
process counter: 2 pid: 29444
process counter: 2 pid: 29444
process counter: 1 pid: 29443
9
georgexsh

Sie können die current_process-Methode von Multiprocessing verwenden. Wenn dies nicht genau genug ist, können Sie den Prozessen eine name mit einer uuid übergeben.

from multiprocessing import current_process


def x(a):
    print(current_process(), a)
    return a*a

with Pool(5) as p:
    p.map(x, [1,2,3,4,5]
5
user1767754

IUC, Sie können auch die Indizes übergeben. (Die Installation von @ user1767754 stehlen) (Bitte lassen Sie mich wissen, ob dies nicht das ist, wonach Sie suchen.)

from multiprocessing import Pool

arr = [1,2,3,4,5]
arr_with_idx = Zip(arr, range(len(arr)))

def x(a, idx):
    print(idx)
    return a*a

with Pool(5) as p:
    p.starmap(x, arr_with_idx)

Oder genauer, verwenden Sie enumerate

from multiprocessing import Pool

arr = [1,2,3,4,5]

def x(idx, a):  # different here
    print(idx)
    return a*a

with Pool(5) as p:
    p.starmap(x, enumerate(arr))

starmap entpackt jeden Tuple und Sie können den Indexteil ausdrucken.

3
Tai

Ich würde vorschlagen, den Index zusammen mit den anderen Argumenten zu übergeben. Sie könnten enumerate verwenden, möglicherweise kombiniert mit einem Generatorausdruck, und fügen Sie den Wert zu Ihren vorhandenen Argumenten hinzu. Der folgende Code setzt voraus, dass all_inputs eine Wiederholung von Tupeln ist:

with Pool(processes=5) as p:
    p.starmap(name_of_function, ((i,) + args for i, args in enumerate(all_inputs)))

Sie können aus einer Reihe von Variationen zu diesem allgemeinen Thema wählen. Beispielsweise könnten Sie den Index am Ende der Argumente und nicht am Anfang platzieren (einfach (i,) + args gegen args + (i,) austauschen).

2
Blckknght

Der einfachste Weg, um herauszufinden, welcher Prozess den Wert 100 verarbeitet, besteht in Teilen Sie das Array in gleiche Teile, bevor der Pool startet. Dadurch können Sie steuern, welche Prozesse im Pool mit welchem ​​Teil des Arrays umgehen. (Da Sie vordefinierte Werte übergeben Anfang, Ende Indizes für jeden, an dem gearbeitet werden soll.

Wenn all_inputs beispielsweise 50 Elemente mit Werten zwischen 80 und 130 hätte, würde eine einfache Jobabteilung für eine 8-Kern-CPU die folgenden Indexpaare zurückgeben:

Process #0 will work on indexes [0:5] with values: 80 - 85
Process #1 will work on indexes [6:11] with values: 86 - 91
Process #2 will work on indexes [12:17] with values: 92 - 97
Process #3 will work on indexes [18:23] with values: 98 - 103
Process #4 will work on indexes [24:29] with values: 104 - 109
Process #5 will work on indexes [30:35] with values: 110 - 115
Process #6 will work on indexes [36:41] with values: 116 - 121
Process #7 will work on indexes [42:50] with values: 122 - 130

Wenn der Pool startet, wissen Sie bereits, welcher für die Verarbeitung des Werts 100 verantwortlich ist.

Der Erfolg dieses Ansatzes hängt von jobDiv() ab, um seine Magie zu erreichen und die Daten für die Prozesse basierend auf der Größe des Eingangsarrays und der Anzahl verfügbarer CPU-Kerne zu unterteilen.

Quellcode:

import multiprocessing as mp    

# processArray(): a parallel function that process an array based on start and 
#   end index positions.
def processArray(procId, array, indexes):
    startIdx, endIdx = indexes
    print("  Process #" + str(procId) + " startIdx=" + str(startIdx), " endIdx=" + str(endIdx))

    # Do some work:
    for i in range(startIdx, endIdx+1):
        print("    Process #" + str(procId) + " is computing index " + str(i), " with value " + str(array[i]))


# jobDiv(): performs a simple job division between available CPU cores
def jobDiv(inputArray, numCPUs):
    jobs = []
    arrayLength = len(inputArray)

    jobRange = int(arrayLength / numCPUs)
    extra = arrayLength - (jobRange * numCPUs)

    prevEnd = 0
    for c in range(numCPUs):
        endIdx = (c * jobRange) + jobRange - 1
        if (c == (numCPUs-1)):
            endIdx += extra

        startIdx = prevEnd
        if ( (c > 0) and (startIdx+1 < arrayLength) ):
            startIdx += 1

        jobs.append( (startIdx, endIdx) )
        prevEnd = endIdx

    return jobs


if __== '__main__':
    # Initialize dataset for multiprocessing with 50 numbers, with values from 80 to 131
    nums = range(80, 131)

    # How many CPU cores can be used for this dataset
    numCPUs = mp.cpu_count()
    if (numCPUs > len(nums)):
        numCPUs = len(nums)

    # This function returns a list of tuples containing array indexes for
    # each process to work on. When nums has 100 elements and numCPUs is 8,
    # it returns the following list:
    #   (0, 11), (12, 23), (24, 35), (36, 47), (48, 59), (60, 71), (72, 83), (84, 99)
    indexes = jobDiv(nums, numCPUs)

    # Prepare parameters for every process in the pool, where each process gets one Tuple of:
    #   (cpu_id, array, array_indexes)
    jobArgs = []
    for id, arg in enumerate(indexes):
        start, end = arg
        print("Process #" + str(id) + " will work on indexes [" + str(start) + ":" + str(end) +
              "] with values: " + str(nums[start]) + " - " + str(nums[end]))
        jobArgs.append( (id, nums, arg) )

    print("* Starting Pool")

    # For every process, send the data for processing along with it's respective Tuple of parameters
    with mp.Pool(processes=numCPUs) as p:
        sums = p.starmap(processArray, jobArgs)

    print("* Finished")

Ausgabe:

* Starting Pool
  Process #0 startIdx=0  endIdx=5
    Process #0 is computing index 0  with value 80
    Process #0 is computing index 1  with value 81
    Process #0 is computing index 2  with value 82
    Process #0 is computing index 3  with value 83
    Process #0 is computing index 4  with value 84
    Process #0 is computing index 5  with value 85
  Process #1 startIdx=6  endIdx=11
    Process #1 is computing index 6  with value 86
    Process #1 is computing index 7  with value 87
    Process #1 is computing index 8  with value 88
    Process #1 is computing index 9  with value 89
    Process #1 is computing index 10  with value 90
    Process #1 is computing index 11  with value 91
  Process #2 startIdx=12  endIdx=17
    Process #2 is computing index 12  with value 92
    Process #2 is computing index 13  with value 93
    Process #2 is computing index 14  with value 94
    Process #2 is computing index 15  with value 95
    Process #2 is computing index 16  with value 96
    Process #2 is computing index 17  with value 97
  Process #3 startIdx=18  endIdx=23
    Process #3 is computing index 18  with value 98
    Process #3 is computing index 19  with value 99
    Process #3 is computing index 20  with value 100
    Process #3 is computing index 21  with value 101
    Process #3 is computing index 22  with value 102
  Process #4 startIdx=24  endIdx=29
    Process #3 is computing index 23  with value 103
    Process #4 is computing index 24  with value 104
    Process #4 is computing index 25  with value 105
    Process #4 is computing index 26  with value 106
    Process #4 is computing index 27  with value 107
    Process #4 is computing index 28  with value 108
    Process #4 is computing index 29  with value 109
  Process #5 startIdx=30  endIdx=35
    Process #5 is computing index 30  with value 110
    Process #5 is computing index 31  with value 111
    Process #5 is computing index 32  with value 112
    Process #5 is computing index 33  with value 113
    Process #5 is computing index 34  with value 114
    Process #5 is computing index 35  with value 115
  Process #6 startIdx=36  endIdx=41
    Process #6 is computing index 36  with value 116
    Process #6 is computing index 37  with value 117
    Process #6 is computing index 38  with value 118
  Process #7 startIdx=42  endIdx=50
    Process #6 is computing index 39  with value 119
    Process #6 is computing index 40  with value 120
    Process #7 is computing index 42  with value 122
    Process #6 is computing index 41  with value 121
    Process #7 is computing index 43  with value 123
    Process #7 is computing index 44  with value 124
    Process #7 is computing index 45  with value 125
    Process #7 is computing index 46  with value 126
    Process #7 is computing index 47  with value 127
    Process #7 is computing index 48  with value 128
    Process #7 is computing index 49  with value 129
    Process #7 is computing index 50  with value 130
* Finished

Es lohnt sich, auf das Offensichtliche hinzuweisen und zu sagen, dass jeder Prozess weiß, welcher Wert gerade verarbeitet wird, jedoch main() nicht

main() kennt nur den Indexbereich, mit dem jeder Prozess arbeiten wird, weiß jedoch nicht (in Echtzeit), welche Werte gerade von diesen Prozessen verarbeitet werden. 

Wenn Sie main() benötigen, um auf diese Informationen zugreifen zu können, während die Prozesse laufen, ist es wahrscheinlich am besten, eine Queue in main() einzurichten und sie vor dem Start des Pools auf einer separaten Thread auszuführen. Stellen Sie dann sicher, dass das Queue-Objekt als Teil der Parameter gesendet wird, die an jeden Prozess übergeben werden, damit alle dasselbe Objekt gemeinsam nutzen und die Daten speichern können, die sie gerade verarbeiten.

1
karlphillip

Wenn Sie Ihren Code zu einem bestimmten Zeitpunkt auf einer modernen Multi-Core-CPU ausführen, werden Ihre Worker-Prozesse mehrere Aufgaben parallel ausführen. Sie können eine Warteschlange verwenden, um ein benutzerdefiniertes Protokoll zu implementieren, mit dem Ihre Mitarbeiter den Hauptprozess wissen lassen, mit welcher Aufgabe (Index der Aufgabe) sie beginnen und die Arbeit beenden.

import os
import time
import queue
import random
import multiprocessing

def fn(st_queue, i):
    st_queue.put((multiprocessing.current_process().name, i))
    time.sleep(random.random())  # your long calculation
    st_queue.put((multiprocessing.current_process().name, None))
    return i ** 2

def main():
    status = {}
    st_queue = multiprocessing.Manager().Queue()

    result = []
    pool = multiprocessing.Pool(4)
    args = Zip([st_queue] * 20, range(20))
    async_res = pool.starmap_async(
        fn, args, callback = lambda r: result.append(r))

    while not async_res.ready():
        try:
            msg = st_queue.get(True, 0.1)
        except queue.Empty:
            pass
        else:
            status.update([msg])
            print(status)      
    print(result.pop())
    pool.close()

if __== '__main__':
    main()

Statuswörterbuch sieht wie folgt aus:

{
    'ForkPoolWorker-4': None, 
    'ForkPoolWorker-5': 16, 
    'ForkPoolWorker-2': 18, 
    'ForkPoolWorker-3': 15
}
0
saaj

Wenn Sie wissen möchten, wann jedes Element fertig ist (anstatt gestartet zu werden), können Sie apply_async mit einem Rückruf verwenden, wie folgt:

# dummy data and function
all_inputs = list(Zip(range(10), range(20,30)))
def name_of_function(a, b):
    return a+b

# main code
from multiprocessing import Pool

num_items = len(all_inputs)
num_done = 0
def handle_result(res):
    global num_done
    num_done += 1
    print('finished item {} of {}.'.format(num_done, num_items))

p = Pool(5)
for args in all_inputs:
    p.apply_async(name_of_function, args, callback=handle_result)
p.close()
p.join() # wait for tasks to finish

ergebnis:

finished item 1 of 10.
finished item 2 of 10.
finished item 3 of 10.
finished item 4 of 10.
finished item 5 of 10.
finished item 6 of 10.
finished item 7 of 10.
finished item 8 of 10.
finished item 9 of 10.
finished item 10 of 10.

Beachten Sie, dass die Ergebnisse nicht notwendigerweise in derselben Reihenfolge wie all_inputs stehen. Wenn Sie genau wissen möchten, welches Element verarbeitet wurde, können Sie die Argumente wie folgt auflisten:

from multiprocessing import Pool

num_items = len(all_inputs)
num_done = 0
def handle_result(idx):
    global num_done
    num_done += 1
    print('finished index {} ({}/{}).'.format(idx, num_done, num_items))

def wrapper(idx, args):
    name_of_function(*args)
    return idx

p = Pool(5)
for args in enumerate(all_inputs):
    p.apply_async(wrapper, args, callback=handle_result)
p.close()
p.join() # wait for tasks to finish

ergebnis:

finished index 0 (1/10).
finished index 1 (2/10).
finished index 2 (3/10).
finished index 3 (4/10).
finished index 4 (5/10).
finished index 6 (6/10).
finished index 8 (7/10).
finished index 7 (8/10).
finished index 9 (9/10).
finished index 5 (10/10).
0
Matthias Fripp

Wenn Sie glücklich sind, den Index aus den Arbeitsprozessen zu melden, und es Ihnen nichts ausmacht, sie außerhalb der Reihenfolge zu melden, können Sie die von anderen vorgeschlagenen Aufzählungsansätze verwenden. Wenn Sie die Arbeit vom Hauptprozess aus verfolgen möchten (z. B. um eine Statusleiste zu verwalten) und/oder Sie die Gesamtzahl der Elemente wissen müssen, die gestartet wurden, müssen Sie sich an jeden Mitarbeiter melden das Elternteil, wie es beginnt Dies kann mit einem Rohr wie unten gezeigt durchgeführt werden. 

Ich bin mir nicht sicher, ob Sie den Index des Elements oder die Gesamtzahl der Elemente, die gestartet wurden (sie könnten außerhalb der Reihenfolge beginnen) melden, also beide.

# dummy data and function
all_inputs = list(Zip(range(10), range(20,30)))
def name_of_function(a, b):
    return a+b

# main code
from multiprocessing import Pool, Pipe, Lock

parent_conn, child_conn = Pipe()
lock = Lock()

def wrapper(idx, args):
    with lock:
        child_conn.send(idx)
    return name_of_function(*args)

with Pool(processes=5) as p:
    p.starmap_async(wrapper, enumerate(all_inputs))
    # receive status updates
    num_items = len(all_inputs)
    for i in range(num_items):
        idx = parent_conn.recv()
        print("processing index {} ({}/{})".format(idx, i+1, num_items))

child_conn.close()
parent_conn.close()

# output (note that items may be started out of sequence):
# processing index 0 (1/10)
# processing index 1 (2/10)
# processing index 2 (3/10)
# processing index 3 (4/10)
# processing index 5 (5/10)
# processing index 6 (6/10)
# processing index 4 (7/10)
# processing index 7 (8/10)
# processing index 8 (9/10)
# processing index 9 (10/10)

Beachten Sie, dass dies starmap_async anstelle von starmap verwendet, um die Ausführung des Hauptthreads fortzusetzen, während die Unterprozesse ausgeführt werden. Alternativ können Sie starmap verwenden und einen separaten Thread starten, um den Fortschritt zu melden:

from threading import Thread
from multiprocessing import Pool, Pipe, Lock
parent_conn, child_conn = Pipe()
lock = Lock()

def receive_updates(num_items):
    for i in range(num_items):
        idx = parent_conn.recv()
        print("processing index {} ({}/{})".format(idx, i+1, num_items))

def wrapper(idx, args):
    with lock:
        child_conn.send(idx)
    return name_of_function(*args)

# launch another thread to receive the results, since the main thread
# will wait for starmap
result_thread = Thread(target=receive_updates, args=(len(all_inputs),))
result_thread.Daemon = True  # stop if main thread is killed
result_thread.start()

# your original code
with Pool(processes=5) as p:
    p.starmap(wrapper, all_inputs)

child_conn.close()
parent_conn.close()
0
Matthias Fripp