-Je souhaite traiter une grande quantité de données en parallèle. -Je veux utiliser efficacement le CPU car il a de nombreux cœurs. -J'ai essayé d'écrire le script pour le moment, mais je ne peux pas me permettre de le réécrire pour une exécution parallèle.
J'ai fait des recherches sur diverses choses et il me semblait facile d'utiliser subprocess.Popen (), donc j'ai expérimenté.
Article de référence https://qiita.com/HidKamiya/items/e192a55371a2961ca8a4
Windows 10 (64bit) Python 3.7.6
Le processeur est Ryzen 9 3950X et c'est l'environnement.
Expérimentez avec le code ci-dessous. (Afficher uniquement les deux derniers chiffres de la colonne de nombre de Fibonacci spécifiée par l'argument de ligne de commande.)
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))
Par exemple, si vous exécutez python fib_sample.py 10000
, il affichera n = 10000, résultat% 100 = 75
et quittera.
Tout d'abord, essayez de l'exécuter séquentiellement avec subprocess.run ().
Exécutez python avec un argument dans subprocess.run (['python', r". \ Fib_sample.py ", str (500000 + i)])
. Si vous modifiez l'argument de ligne de commande de 500000 à 500063 et que vous l'exécutez 64 fois,
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
(Omis)
n = 500061, result % 100 = 86
n = 500062, result % 100 = 31
n = 500063, result % 100 = 17
130.562213 sec
Cela a pris un peu plus de deux minutes. De toute évidence, le cœur du processeur n'est pas du tout utilisé.
Essayez d'exécuter le même processus en parallèle avec subprocess.Popen (). Contrairement à subprocess.run (), subprocess.Popen () n'attend pas la fin du processus généré. Le code suivant répète l'exécution du processus → attendez qu'ils se terminent tous → la prochaine exécution du processus → ... pour le nombre spécifié par max_process.
batch_parallel.py
from time import time
import subprocess
start=time()
#Nombre maximum d'exécutions de processus parallèles
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_Attendez la fin de tous les processus pour chaque processus
for subproc in proc_list:
subproc.wait()
proc_list = []
end=time()
print("%f sec" %(end-start))
Le résultat de l'exécution de 16 en parallèle selon le nombre de cœurs physiques du Ryzen 3950X.
> python .\batch_parallel.py
n = 500002, result % 100 = 51
n = 500004, result % 100 = 28
n = 500001, result % 100 = 26
(Omis)
n = 500049, result % 100 = 74
n = 500063, result % 100 = 17
n = 500062, result % 100 = 31
8.165289 sec
Comme ils sont exécutés en parallèle, l'ordre dans lequel le traitement se termine est dans le désordre. C'était presque 16 fois plus rapide de 130,562 secondes à 8,165 secondes.
Vous pouvez voir que tous les cœurs sont utilisés et qu'ils sont correctement exécutés en parallèle.
En passant, il n'est pas rapide d'exécuter 32 en parallèle en fonction du nombre de cœurs logiques au lieu du nombre de cœurs physiques. Parfois, il se fait tard. Le graphique ci-dessous montre le temps d'exécution moyen lorsque le nombre d'exécutions parallèles est modifié et exécuté trois fois chacune. J'ai utilisé diverses applications en arrière-plan, donc ce n'est pas très précis, mais je pense que la tendance est correcte.
C'était assez facile à accélérer. Avec le code ci-dessus, après le démarrage de l'exécution parallèle, il attend la fin du processus avec le temps de traitement le plus long, donc s'il y a un processus avec un temps de traitement long, la surcharge liée à l'attente de la fin de ce processus augmentera. À l'origine, je pense que le code devrait démarrer le processus suivant dès que chaque processus a fini de s'exécuter et créer le code de sorte que le nombre de parallèles soit toujours constant. Cependant, en réalité, le but était d'accélérer les applications telles que l'application d'un grand nombre de fichiers de données de même longueur au même traitement du signal, alors j'ai fermé les yeux en espérant que le temps d'exécution serait à peu près le même. Pour le moment, j'étais satisfait car j'ai pu facilement atteindre l'objectif d'accélération.
Recommended Posts