-Ich möchte eine große Datenmenge parallel verarbeiten. -Ich möchte die CPU effektiv nutzen, da sie viele Kerne hat. -Ich habe vorerst versucht, das Skript zu schreiben, aber ich kann es mir nicht leisten, es für die parallele Ausführung neu zu schreiben.
Ich habe verschiedene Dinge recherchiert und es schien einfach zu sein, subprocess.Popen () zu verwenden, also habe ich experimentiert.
Referenzartikel https://qiita.com/HidKamiya/items/e192a55371a2961ca8a4
Windows 10 (64bit) Python 3.7.6
Die CPU ist Ryzen 9 3950X und dies ist die Umgebung.
Experimentieren Sie mit dem folgenden Code. (Zeigen Sie nur die letzten beiden Ziffern der durch das Befehlszeilenargument angegebenen Fibonacci-Zahlenspalte an.)
fib_sample.py
import sys
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
if __name__ == "__main__":
num = int(sys.argv[1])
result = fib(num)
print("n = {0}, result % 100 = {1}".format(num, result % 100))
Wenn Sie beispielsweise "python fib_sample.py 10000" ausführen, wird "n = 10000", das Ergebnis% 100 = 75 "angezeigt und beendet.
Versuchen Sie zunächst, es nacheinander mit subprocess.run () auszuführen. Führen Sie Python mit einem Argument in "subprocess.run ([" Python ", r". \ Fib_sample.py ", str (500000 + i)])" aus. Wenn Sie das Befehlszeilenargument von 500000 in 500063 ändern und 64 Mal ausführen,
batch_sequential.py
from time import time
import subprocess
start=time()
loop_num = 64
for i in range(loop_num):
subprocess.run(['python', r".\fib_sample.py", str(500000 + i)])
end=time()
print("%f sec" %(end-start))
> python .\batch_sequential.py
n = 500000, result % 100 = 25
n = 500001, result % 100 = 26
n = 500002, result % 100 = 51
(Weggelassen)
n = 500061, result % 100 = 86
n = 500062, result % 100 = 31
n = 500063, result % 100 = 17
130.562213 sec
Es dauerte etwas mehr als zwei Minuten. Offensichtlich wird der CPU-Kern überhaupt nicht verwendet.
Versuchen Sie, denselben Prozess parallel zu subprocess.Popen () auszuführen. Im Gegensatz zu subprocess.run () wartet subprocess.Popen () nicht auf den Abschluss des erzeugten Prozesses. Der folgende Code wiederholt die Prozessausführung → Warten Sie, bis alle abgeschlossen sind → Die nächste Prozessausführung → ... für die von max_process angegebene Nummer.
batch_parallel.py
from time import time
import subprocess
start=time()
#Maximale Anzahl paralleler Prozessausführungen
max_process = 16
proc_list = []
loop_num = 64
for i in range(loop_num):
proc = subprocess.Popen(['python', r".\fib_sample.py", str(500000 + i)])
proc_list.append(proc)
if (i + 1) % max_process == 0 or (i + 1) == loop_num:
#max_Warten Sie für jeden Prozess auf das Ende aller Prozesse
for subproc in proc_list:
subproc.wait()
proc_list = []
end=time()
print("%f sec" %(end-start))
Das Ergebnis der parallelen Ausführung von 16 entsprechend der Anzahl der physischen Kerne von Ryzen 3950X.
> python .\batch_parallel.py
n = 500002, result % 100 = 51
n = 500004, result % 100 = 28
n = 500001, result % 100 = 26
(Weggelassen)
n = 500049, result % 100 = 74
n = 500063, result % 100 = 17
n = 500062, result % 100 = 31
8.165289 sec
Da sie parallel ausgeführt werden, ist die Reihenfolge, in der die Verarbeitung endet, nicht in der richtigen Reihenfolge. Es war fast 16-mal schneller von 130,562 Sekunden auf 8,165 Sekunden.
Sie können sehen, dass alle Kerne verwendet werden und parallel parallel korrekt ausgeführt werden.
Übrigens ist es nicht schnell, 32 parallel nach der Anzahl der logischen Kerne anstelle der Anzahl der physischen Kerne auszuführen. Eher manchmal wird es spät. Die folgende Grafik zeigt die durchschnittliche Ausführungszeit, wenn die Anzahl der parallelen Ausführungen jeweils dreimal geändert und ausgeführt wird. Ich habe verschiedene Anwendungen im Hintergrund ausgeführt, daher ist dies nicht sehr genau, aber ich denke, der Trend ist korrekt.
Es war ziemlich einfach zu beschleunigen. Mit dem obigen Code wartet er nach dem Start der parallelen Ausführung auf das Ende des Prozesses mit der längsten Verarbeitungszeit. Wenn es also einen Prozess mit einer langen Verarbeitungszeit gibt, erhöht sich der Aufwand für das Warten auf das Ende dieses Prozesses. Ursprünglich denke ich, dass der Code den nächsten Prozess starten sollte, sobald jeder Prozess die Ausführung beendet hat, und den Code so gestalten sollte, dass die Anzahl der Parallelen immer konstant ist. In der Realität bestand der Zweck jedoch darin, Anwendungen wie das Anwenden einer großen Anzahl von Datendateien derselben Länge auf dieselbe Signalverarbeitung zu beschleunigen. Daher schloss ich meine Augen mit der Erwartung, dass die Ausführungszeit ungefähr gleich sein würde. Vorerst war ich zufrieden, weil ich den Zweck der Beschleunigung leicht erreichen konnte.
Recommended Posts