Multiprocessing immer gleich langsam

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
CptK
User
Beiträge: 7
Registriert: Sonntag 16. Januar 2022, 12:04
Wohnort: Darmstadt

Hallo zusammen, ich versuche mich gerade mal an multiprocessing und habe dafür folgendes (nutzloses) Beispiel, das einfach die Summe einer Liste berechnet:

Code: Alles auswählen

import time
import multiprocessing as mp
import sys

def split(data, n):
    k, m = divmod(len(data), n)
    return list(data[i*k+min(i, m):(i+1)*k+min(i+1, m)] for i in range(n))

def my_func(x):
    print(len(x))
    for i in range(len(x)): # nur da, damit die Funktion auch Zeit verbraucht
        a = i * i
    return sum(x)

def main(num_procs):
    pool = mp.Pool(num_procs)
    result = pool.map(my_func, split(list(range(10**8)), num_procs))

    print("num_procs:", num_procs, "result:", sum(result))

if __name__ == "__main__":
    start_time = time.process_time()
    main(int(sys.argv[1]))
    print(f'time needed: {(time.process_time() - start_time)} s')
Jetzt teste ich das Ganze mit 1,2,4 und 8 Prozessen und bekomme dafür folgende Ausgabe:

Code: Alles auswählen

num_procs: 1 result: 4999999950000000
time needed: 6.375935067 s
num_procs: 2 result: 4999999950000000
time needed: 6.353192033 s
50000000
50000000
num_procs: 4 result: 4999999950000000
time needed: 6.453357544999999 s
25000000
25000000
25000000
25000000
num_procs: 8 result: 4999999950000000
time needed: 6.496723103 s
12500000
12500000
12500000
12500000
12500000
12500000
12500000
12500000
Drei Sachen verstehe ich nicht:
1. Für num_procs = 1 fehlt die Ausgabe von print(len(x)) (erste Aktion in my_func), wieso?
2. Wie kann es sein, dass die Ausgaben von print(len(x)) erst kommen, nachdem das Ergebnis und die Zeit geprintet werden?
3. Was mache ich falsch, dass mehr Prozesse länger brauchen?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe damit ein bisschen rumgespielt, und so hier bekomme ich die gewuenschten Ergebnisse:

Code: Alles auswählen

import time
import multiprocessing as mp
import sys


def my_func(wait):
    time.sleep(wait)
    return wait


def main(num_procs):
    ranges = [10.0 / num_procs] * num_procs
    pool = mp.Pool(num_procs)
    start_time = time.monotonic()
    result = pool.map(my_func, ranges)
    print(f'time needed: {(time.monotonic() - start_time)} s')

if __name__ == "__main__":
    main(int(sys.argv[1]))
Ich vermute das Original-Problem liegt darin, dass deine Datenmenge, die du transferierst, das Geschehen dominiert. Zumindest ich habe keinen signifikanten Unterschied beim auskommentieren von der for-Schleife gesehen. Und die Datenmenge, die da durch die Gegend geschaufelt wird, durch Pipes oder was auch, ist eben immer gleich - dadurch braucht das auch gleichlang.

Erst wenn du etwas berechnest, das wirklich davon profitiert, eine teure Rechnung weniger lang zu taetigen, wird sich da was tun.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@CptK: `process_time()` misst die Zeit von dem Hauptprozess und zwar nicht die „wall time“ sondern nur die Zeit, die dieser Prozess aktiv war. Und das verteilen und sammeln der Aufgabe/Ergebnisse dauert halt immer ungefähr gleich, beziehungsweise steigt sogar leicht wenn die Zahl der Prozesse steigt. Was dort nicht gemessen wird, ist schlafen/warten auf andere Prozesse, denn zu der Zeit ist der Hauptprozess ja nicht aktiv. Der erste Fehler ist also, dass Du falsch misst.

Das zweite Problem dürfte werden/sein, dass `map()` per Default nicht jede Teilaufgabe einzeln an einen Prozess übergibt, sondern die zu ”chunks” zusammenfasst. Wie viele, hängt von der Grösse des Pools ab. Da müsste man also explizit die Anzahl 1 für die ”chunks” an `map()` übergeben, wenn man jede Teilaufgabe einem eigenen Prozess zuordnen möchte.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten