Multitraitement.Piscine fait Numpy multiplication de matrice plus lent
Donc, je joue avec multiprocessing.Pool
et Numpy
, mais il semble que j'ai manqué quelque point. Pourquoi le pool
version beaucoup plus lent? J'ai regardé htop
et je peux voir à plusieurs procédés d'être créé, mais ils partagent tous un des Processeurs d'ajouter jusqu'à ~100%.
$ cat test_multi.py
import numpy as np
from timeit import timeit
from multiprocessing import Pool
def mmul(matrix):
for i in range(100):
matrix = matrix * matrix
return matrix
if __name__ == '__main__':
matrices = []
for i in range(4):
matrices.append(np.random.random_integers(100, size=(1000, 1000)))
pool = Pool(8)
print timeit(lambda: map(mmul, matrices), number=20)
print timeit(lambda: pool.map(mmul, matrices), number=20)
$ python test_multi.py
16.0265390873
19.097837925
[mise à jour]
- changé à
timeit
pour l'analyse comparative des processus de - init de la Piscine avec un certain nombre de mes cœurs
- changé de calcul de sorte qu'il n'y a plus de calcul et de moins de transfert de mémoire (je l'espère)
Toujours pas de changement. pool
version est encore plus lent et je peux voir dans htop
qu'un seul core est utilisé également plusieurs processus sont générés.
[update2]
En ce moment je lis à propos de @Jan-Philip Gehrcke la suggestion d'utiliser multiprocessing.Process()
et Queue
. Mais en attendant, je voudrais savoir:
- Pourquoi mon exemple, le travail pour tiago? Ce qui pourrait être la raison pour laquelle il ne fonctionne pas sur ma machineUn?
- Est dans mon exemple de code toute copie entre les processus? J'ai prévu mon code afin de donner à chaque thread une matrice de la matrice, liste.
- Est mon code est un mauvais exemple, parce que je utiliser
Numpy
?
J'ai appris que, souvent, l'on obtient une meilleure réponse, quand les autres savent mon objectif final est donc: j'ai beaucoup de fichiers, qui sont atm chargés et traités dans une série de la mode. Le traitement du CPU est intense, donc je suppose que beaucoup pourrait être acquise par la parallélisation. Mon but est d'appeler la fonction python qui analyse un fichier en parallèle. De plus, cette fonction est juste une interface pour le code en C, je suppose, cela fait une différence.
Un Ubuntu 12.04, Python 2.7.3, i7 860 @ 2.80 - s'il vous Plaît laissez un commentaire si vous avez besoin de plus d'infos.
[update3]
Voici les résultats de Stefano exemple de code. Pour une raison quelconque il n'y a pas de vitesse. :/
testing with 16 matrices
base 4.27
1 5.07
2 4.76
4 4.71
8 4.78
16 4.79
testing with 32 matrices
base 8.82
1 10.39
2 10.58
4 10.73
8 9.46
16 9.54
testing with 64 matrices
base 17.38
1 19.34
2 19.62
4 19.59
8 19.39
16 19.34
[jour 4] réponse à Jan-Philip Gehrcke commentaire
Désolé que je n'ai pas fait moi-même plus clair. Comme je l'ai écrit dans la mise à Jour 2 mon principal but est de paralléliser plusieurs série d'appels d'un 3ème partie Python fonction de la bibliothèque. Cette fonction est une interface pour du code C. J'ai été recommandé d'utiliser Pool
, mais cela n'a pas fonctionné, j'ai donc essayé quelque chose de simple, le montre l'exemple ci-dessus avec numpy
. Mais là aussi, je ne pouvais pas obtenir une amélioration de la performance, même s'il semble pour moi", emberassing parallélisables`. Donc je suppose que je dois avoir manqué quelque chose d'important. Cette information est ce que je suis à la recherche de cette question et la générosité.
[jour 5]
Merci à tous pour votre formidable d'entrée. Mais à lire vos réponses ne crée plus de questions pour moi. Pour cette raison, je vais la lire sur le notions de base et en créer de nouveaux AFIN de questions quand j'ai une compréhension plus claire de ce que je ne sais pas.
timeit
module ou au moins déplacer le pool = Pool()
fonction de la chronologie de la routine.J'ai peut-être tort, mais je soupçonne la plupart du temps est consacré à l'envoi de la matrices d'avant en arrière entre vos processus.
Mais on ne devrait pas tous les processus/threads de travail sur leur propre de la matrice? Comme chaque processus en prenant une matrice à partir de la liste de travail et avec qui?
Mais vous devez passer entre les différents processus (c'est à dire la copie de la mémoire). La multiplication de matrice est plutôt rapide (il faut environ 6 ms selon vos timings) de telle sorte que cette surcharge est importante.
J'ai changé l'exemple, afin qu'il n'y a plus de calcul et de moins de transfert de mémoire.
OriginalL'auteur Framester | 2013-03-14
Vous devez vous connecter pour publier un commentaire.
Concernant le fait que l'ensemble de vos processus en cours d'exécution sur le même PROCESSEUR, voir ma réponse ici.
Lors de l'importation,
numpy
modifie l'affinité CPU du processus parent, de sorte que lorsque vous utilisez ensuitePool
tous les processus de travail qu'elle engendre sera à la fin en lice pour le même cœur, plutôt que d'utiliser tous les cœurs disponibles sur votre machine.Vous pouvez appeler
taskset
après l'importationnumpy
pour réinitialiser l'affinité CPU de sorte que tous les cœurs sont utilisés:De sortie:
Si vous regardez CPU useage à l'aide de
top
pendant que vous exécutez ce script, vous devriez le voir à l'aide de l'ensemble de vos cœurs lorsqu'il exécute le "parallèle". Comme d'autres l'ont souligné, dans votre exemple d'origine les frais généraux impliqués dans le décapage de données, de processus de création, etc. probablement l'emportent sur tout avantage possible de la parallélisation.Edit: je soupçonne qu'une partie de la raison pour laquelle le processus unique semble être toujours plus rapide, c'est que
numpy
peut avoir quelques astuces pour accélérer cet élément-sage de la multiplication de matrice qu'il ne peut pas utiliser lorsque les tâches sont réparties entre plusieurs cœurs.Par exemple, si je viens d'utiliser Python ordinaire des listes de calculer la suite de Fibonacci, je peux obtenir une énorme accélération de la parallélisation. De même, si je ne l'élément de sage multiplication d'une manière qui ne prend pas avantage de la vectorisation, je reçois un semblable speedup pour la version parallèle:
De sortie:
En fait, je crois toujours que c'est plus probablement liée à des bizarreries de
numpy
plutôt que de simplement le faire avec l'utilisation du CPU. Même quand je paralléliser Framester le code original de sorte qu'il est en fait l'utilisation de l'ensemble de mon Cpu j' trouve que c'est légèrement plus lente qu'une exécution en série. C'est seulement quand j'ai délibérément éviter de faire des choses quinumpy
est particulièrement bon à ce que je vois aucun gain de performance de la parallélisation.Vous avez raison; désolé, je n'ai pas lu assez loin, j'ai juste commencé à tester sur mon propre trivial/stupide exemple de code. Jamais l'esprit. 🙂
Pour la comparaison, vous avez à montrer ce qui se passe lorsque vous quittez
os.system("taskset -p 0xff %d" % os.getpid())
.Pourquoi? Si je quitte cette ligne alors (au moins sur ma machine) en un seul noyau qui sera utilisé, alors, évidemment, je ne vois pas d'accélération à partir de la version parallèle.
OriginalL'auteur
L'imprévisible de la concurrence entre la communication de la surcharge et de calcul de l'accélération est certainement la question ici. Ce que vous observez est parfaitement bien. Si vous obtenez une vitesse nette dépend de nombreux facteurs et est quelque chose qui doit être quantifié correctement (comme vous l'avez fait).
Alors pourquoi est -
multiprocessing
si "lent de façon inattendue" dans votre cas?multiprocessing
'smap
etmap_async
fonctions réellement pickle des objets Python en arrière à travers les tuyaux qui relient le parent avec l'enfant des processus. Cela peut prendre un temps considérable. Pendant ce temps, le processus enfants n'ont presque rien à faire, c'est ce qui est à voir danshtop
. Entre les différents systèmes, il y a peut être une grande pipe de transport différence de performance, qui est aussi pourquoi pour certaines personnes de votre piscine code est plus rapide que votre CPU seul code, même si pour vous il n'est pas (d'autres facteurs peuvent entrer en jeu ici, c'est juste un exemple pour expliquer l'effet).Que pouvez-vous faire pour le rendre plus rapide?
Ne pas cornichons à l'entrée sur POSIX systèmes.
Si vous êtes sous Unix, vous pouvez obtenir autour de la relation parent->enfant de la communication frais généraux par l'intermédiaire de prendre avantage de POSIX' processus de fourche comportement (copie de la mémoire lors de l'écriture):
Créez votre tâche d'entrée (par exemple une liste de matrices de grande taille), de travailler sur dans le processus parent dans un accessible dans le monde entier variable. Puis de créer des processus de travail en appelant
multiprocessing.Process()
vous-même. Chez les enfants, prenez le travail de saisie à partir de la variable globale. Simplement exprimé, ce qui rend l'enfant à accéder à la mémoire de la mère sans aucune communication, les frais généraux (*, l'explication ci-dessous). Envoyer le résultat à la maison mère, par exemple, au moyen d'unmultiprocessing.Queue
. Cela permettra d'économiser beaucoup de la communication-dessus, surtout si la sortie est faible par rapport à l'entrée. Cette méthode ne fonctionnera pas sur, par exemple, Windows, parce quemultiprocessing.Process()
il crée une toute nouvelle Python processus qui n'hérite pas de l'état de la société mère.Utiliser numpy multithreading.
Selon votre tâche de calcul, il peut arriver que la participation de
multiprocessing
ne va pas aider du tout. Si vous compilez numpy vous-même et de permettre aux directives OpenMP, puis des opérations sur des grandes matrices pourrait devenir très efficacement multithread (et distribué sur de nombreux cœurs de PROCESSEUR; le GIL est pas un facteur limitant ici) par eux-mêmes. Fondamentalement, c'est le plus efficace l'utilisation de plusieurs cœurs de PROCESSEUR que vous pouvez obtenir dans le contexte de numpy/scipy.*L'enfant ne peut pas accéder directement au parent de la mémoire en général. Cependant, après
fork()
, le parent et l'enfant sont dans un équivalent de l'état. Il serait stupide de copier l'ensemble de la mémoire de la mère à une autre place dans la RAM. C'est pourquoi la copie sur écriture principe de sauts. Tant que l'enfant n'a pas changement son état de la mémoire, en effet, il accède à la mère de la mémoire. Uniquement en cas de modification, les morceaux sont copiés dans l'espace mémoire de l'enfant.Majeur edit:
Permettez-moi d'ajouter un bout de code qui croque une grande quantité de données d'entrée avec plusieurs processus de travail et suit les conseils "1. Ne pas cornichons à l'entrée sur POSIX systèmes.". En outre, la quantité d'information transférée au travailleur manager (le processus père) est assez faible. La lourde calcul le cadre de cet exemple est une valeur unique de la décomposition. Il peut faire un usage intensif de OpenMP. J'ai exécuté l'exemple à plusieurs reprises:
OMP_NUM_THREADS=1
, de sorte que chaque processus de travail crée une charge maximale de 100 %. Là, le nombre de travailleurs-calculer les temps de mise à l'échelle de comportement est presque linéaire et la nette accélération facteur correspond au nombre de travailleurs concernés.OMP_NUM_THREADS=4
, de sorte que chaque processus crée une charge maximale de 400 % (par l'intermédiaire de frai 4 threads OpenMP). Ma machine a 16 de véritables noyaux, donc 4 processus avec max 400 % de la charge à chaque va et presque obtenir le maximum de performances de la machine. La mise à l'échelle n'est pas parfaitement linéaire plus et l'accélération facteur n'est pas le nombre de travailleurs impliqués, mais la valeur de temps de calcul devient significativement réduite par rapport àOMP_NUM_THREADS=1
et le temps encore diminue significativement avec le nombre de processus de travail.OMP_NUM_THREADS=4
. Il en résulte une moyenne de charge du système de 1253 %.OMP_NUM_THREADS=5
. Il en résulte une moyenne de charge du système de 1598 %, ce qui suggère que nous avons tout de de 16 de base de la machine. Cependant, le calcul de la paroi temps ne s'améliore pas par rapport à ce dernier cas.Le code:
La sortie:
+1: plus d'explication plausible. Permettez-moi seulement d'ajouter qu'en plus de permettre OpenMP dans numpy, on doit aussi utiliser des fournisseurs de bibliothèques blas, si disponible.
Je me demande si la raison pour laquelle vous ne pas voir beaucoup de notable gain de performance lors de la parallélisation de la matrice des manipulations dans
numpy
pourrait en fait être parce quenumpy
externes BLAS et LAPACK bibliothèques, qui sont souvent compilées à l'utilisation de plusieurs cœurs simultanément. Si vous essayez d'exécuter quelque chose commesvd
en parallèle (qui utilise LAPACK) peut-être que chaque travailleur se comporte comme si elle est en cours d'exécution sur plusieurs cœurs, et ne "sous-optimale" des choses comme écrire des uns et des autres caches etc.Dans le premier exemple, nous voyons idéal de mise à l'échelle (1-enfants speedup: 1.00, 2-les enfants de l'accélération: 2.02, 4-les enfants de l'accélération: 3.81). Je suppose que ce que vous êtes en train de parler: Calcul de la durée dans le cas où 4 enfants /
OMP_NUM_THREADS=1
: 4.37 s vs 2.95 s avecOMP_NUM_THREADS=4
. Oui, c'est de loin pas un changement de facteur 4 (comme l'aurait été l'idéal). Cependant, ce qui est attendu. Comme SVD sur de grandes matrices consiste à déplacer autour des tonnes de données entre la mémoire RAM, la mémoire cache, et les registres, les pipelines (esp. entre le CPU et la RAM, c'est à dire Hypertransport/Quickpath/FSB) sont le goulot d'étranglement. Très simple.Merci pour l'exemple de code. Malheureusement, parfois, le code s'arrête après " Croquer entrée avec 1 enfant(s)' et y reste à jamais. Mais je n'ai pas vérifié l'OMP soutien de mon numpy version.
OriginalL'auteur
Votre code est correct. J'ai juste couru mon système (avec 2 cœurs, l'hyperthreading) et a obtenu les résultats suivants:
J'ai regardé les processus et, comme prévu, le parallèle de la partie montrant plusieurs processus de travail de près de 100%. Ce doit être quelque chose dans votre système ou de l'installation de python.
Pas sûr de ce que pourrait être mauvais. Quel système utilisez-vous? Je voudrais essayer d'autres
multiprocessing
méthodes de côté dePool
de départ, ou mêmePool
avec les différents processus de travail sur les parties d'un tableau partagé.OriginalL'auteur
La mesure de l'arithmétique débit est une tâche très difficile: sur le fond, votre cas de test est trop simple, et je vois beaucoup de problèmes.
Première à tester l'arithmétique entière: est-il une raison particulière? Avec virgule flottante vous obtenez des résultats qui sont comparables sur plusieurs architectures différentes.
Deuxièmematrix = matrix*matrix
remplace le paramètre d'entrée (matrices sont passés par référence et non par valeur), et chaque échantillon a travailler sur des données différentes...Derniers tests devraient être réalisés sur un large éventail de la taille du problème et le nombre de travailleurs, afin de saisir les tendances générales.
Voici donc modifié mon script de test
et mes résultats:
[Mise à JOUR] - je exécuter cet exemple à la maison sur un ordinateur différent, l'obtention d'un uniforme ralentissement:
Je dois avouer que je ne sais pas qui est à blâmer (numpy, python, compilateur, noyau)...
s'il vous plaît ajouter
pool.join()
aprèspool.close()
; si l'exécution est court, vous pouvez augmenter le nombre d'itérations danstimeit
.Merci, j'ai essayé le code et je reçois aussi pas speedup?
Personne n'est à blâmer, sauf pour le code! 🙂 Je l'ai essayé sur un moderne 16 core E5-2650 système. J'observe une vitesse pour un député de la piscine de taille 2 et 4. Au-delà, le temps d'exécution devient de pire encore. La parallélisation de la méthode de ce code est loin d'être efficace. Stefano: la vitesse que vous avez observé sur un ordinateur n'est pas du tout linéaire au nombre de noyaux impliqués. Raisonnable théorie pour expliquer les différences entre vos deux ordinateurs: dans le premier exemple, le ratio entre la vitesse de base et du transport des tuyaux de performance est plus faible que dans le deuxième exemple.
OriginalL'auteur
Par défaut,
Pool
utilise uniquement les processus n, où n est le nombre de Processeurs sur votre machine. Vous devez spécifier le nombre de processus que vous souhaitez utiliser, commePool(5)
.Voir ici pour plus d'infos
OriginalL'auteur
Puisque vous mentionnez que vous avez beaucoup de fichiers, je dirais que la solution suivante;
Pool.map()
appliquer la fonction à la liste de fichiers.Puisque chaque instance se charge désormais de son propre fichier, les données transmises sont des noms de fichiers, et non pas (potentiellement important) les tableaux numpy.
OriginalL'auteur
J'ai aussi remarqué que quand j'ai couru numpy multiplication de matrice à l'intérieur de la Piscine.map() de la fonction, il a couru beaucoup plus lent sur certaines machines. Mon but était de paralléliser mon travail sur la Piscine.map(), et de lancer un processus sur chaque cœur de ma machine. Quand les choses ont été en cours d'exécution rapide, le numpy multiplication de matrice était seulement une petite partie de l'ensemble du travail effectué en parallèle. Quand j'ai regardé à l'utilisation du PROCESSEUR du processus, j'ai pu voir que chaque processus peut utiliser, par exemple, plus de 400% de CPU sur les machines où il s'lent, mais toujours <=100% sur les machines où il a couru vite. Pour moi, la solution a été de arrêter de numpy de multithreading. Il s'avère que numpy a été mis en place pour multithread exactement sur les machines où ma Piscine.map() a été lent. Evidemment, si vous êtes déjà de la parallélisation de l'aide à la Piscine.map(), puis d'avoir numpy aussi paralléliser crée des interférences. Je viens d'appeler
export MKL_NUM_THREADS=1
avant l'exécution de mon code Python et il a travaillé rapide partout.OriginalL'auteur