La plupart des fonctions de Numpy activent le multithreading par défaut.
par exemple, je travaille sur un poste de travail cpu Intel à 8 cœurs, si je lance un script
import numpy as np
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
Linux top
affichera une utilisation de 800% du processeur pendant l'exécution,
ce qui signifie que numpy détecte automatiquement que mon poste de travail possède 8 cœurs et np.sqrt
utilise automatiquement les 8 cœurs pour accélérer le calcul.
Cependant, j'ai trouvé un bug bizarre. Si je lance un script
import numpy as np
import pandas as pd
df=pd.DataFrame(np.random.random((10,10)))
df+df
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
l'utilisation du processeur est de 100% !!. Cela signifie que si vous plus deux pandas DataFrame avant d'exécuter une fonction numpy, la fonction de multithreading automatique de numpy a disparu sans aucun avertissement! Ce n'est absolument pas raisonnable, pourquoi le calcul de Pandas dataFrame affecterait-il le paramètre de filetage Numpy? Est-ce un bug? Comment contourner cela?
PS:
Je creuse plus loin en utilisant l' perf
outil Linux .
exécution des premières émissions de script
Lors de l'exécution du deuxième script
Ainsi, les deux scripts impliquent libmkl_vml_avx2.so
, tandis que le premier script implique des éléments supplémentaires libiomp5.so
qui semblent être liés à openMP.
Et puisque vml signifie bibliothèque de mathématiques vectorielles Intel, donc selon le doc vml, je suppose qu'au moins les fonctions ci-dessous sont toutes automatiquement multithread
import numpy as np import pandas as pd import os os.environ["MKL_NUM_THREADS"] = '4' print(os.environ["MKL_NUM_THREADS"]) df=pd.DataFrame(np.random.random((10,10))) df+df print(os.environ["MKL_NUM_THREADS"]) a = np.random.random((20000000, 3)) b = np.random.random((3, 30)) for _ in range(10): c = np.dot(a, b)
Réponses:
Pandas utilise
numexpr
sous le capot pour calculer certaines opérations etnumexpr
définit le nombre maximal de threads pour vml à 1, lors de son importation :et il est importé par les pandas lorsqu'il
df+df
est évalué dans expressions.py :Cependant, la distribution Anaconda utilise également VML fonctionnalité pour des fonctions telles que
sqrt
,sin
,cos
et ainsi de suite - et une foisnumexpr
fixé le nombre de maximal de fils de VML à 1, les fonctions de numpy-plus parallélisation d'utilisation.Le problème peut être facilement vu dans gdb (en utilisant votre script lent):
c'est-à-dire que nous pouvons voir,
numexpr
définit le nombre de threads à 1. Qui est ensuite utilisé lorsque la fonction vml-sqrt est appelée:Nous pouvons donc voir que numpy utilise l'implémentation de vml
vdSqrt
qui permetmkl_vml_serv_threader_d_1i_1o
de décider si le calcul doit être effectué en parallèle et il regarde le nombre de threads:le registre
%rax
a le nombre maximal de threads et il est de 1.Maintenant, nous pouvons utiliser
numexpr
pour augmenter le nombre de threads vml , c'est -à- dire:Maintenant, plusieurs cœurs sont utilisés!
la source
numexpr
arrière-scène.En regardant numpy, il semble que sous le capot, il y ait eu des problèmes d'activation / désactivation avec le multithreading, et selon la version que vous utilisez, vous pouvez vous attendre à voir des plantages lorsque vous augmentez ne.set_vml_num_threads () ..
http://numpy-discussion.10968.n7.nabble.com/ANN-NumExpr-2-7-0-Release-td47414.html
J'ai besoin de comprendre comment cela est collé à l'interpréteur python, compte tenu de votre exemple de code où il semble permettre en quelque sorte à plusieurs appels apparemment synchrones / ordonnés à np.sqrt () de procéder en parallèle. Je suppose que l'interpréteur python retourne toujours juste une référence à un objet quand il fait apparaître la pile, et dans votre exemple, il suffit de lancer ces références et de ne pas les assigner ou les manipuler de quelque manière que ce soit. Mais si les itérations de boucle suivantes dépendent des précédentes, il semble alors moins clair comment elles pourraient être parallélisées en toute sécurité. Un échec / des résultats incorrects est sans doute pire que les plantages.
la source
Je pense que votre prémisse initiale peut être incorrecte -
Vous avez déclaré: ce qui signifie que numpy détecte automatiquement que mon poste de travail possède 8 cœurs et que np.sqrt utilise automatiquement les 8 cœurs pour accélérer le calcul.
Une seule fonction np.sqrt () ne peut pas deviner comment elle sera ensuite invoquée ou retourner avant qu'elle ne soit partiellement terminée. Il existe des mécanismes de parallélisme en python, mais aucun n'est automatique.
Maintenant, cela dit, l'interpréteur python peut être en mesure d'optimiser la boucle for pour le parallélisme, ce qui peut être ce que vous voyez, mais je soupçonne fortement que si vous regardez l'heure de l'horloge murale pour que cette boucle s'exécute, ce ne sera pas différent, peu importe si vous utilisez (apparemment) 8 cœurs ou 1 cœur.
MISE À JOUR: Après avoir lu un peu plus de commentaires, il semble que le comportement multicœur que vous voyez est lié à la distribution anaconda de l'interpréteur python. J'ai jeté un coup d'œil mais je n'ai pas pu trouver de code source pour cela, mais il semble que la licence python permette aux entités (comme anaconda.com) de compiler et de distribuer des dérivés de l'interpréteur sans exiger la publication de leurs modifications.
Je suppose que vous pouvez tendre la main aux gens d'anaconda - le comportement que vous voyez sera difficile à comprendre sans savoir quoi / si quelque chose a changé dans l'interpréteur.
Faites également une vérification rapide de l'heure de l'horloge murale avec / sans l'optimisation pour voir si elle est en effet 8 fois plus rapide - même si vous avez vraiment les 8 cœurs qui fonctionnent au lieu de 1, il serait bon de savoir si les résultats sont réellement 8 fois plus rapide ou s'il y a des verrous tournants en cours d'utilisation qui sérialisent toujours sur un seul mutex.
la source