Boucle de parallélisation en python avec des tableaux numpy et de la mémoire partagée
je suis au courant de plusieurs questions et réponses sur ce sujet, mais n'ai pas trouvé une réponse satisfaisante à ce problème particulier:
Quelle est la façon la plus simple de faire une simple parallélisation en mémoire partagée d'une boucle python où les tableaux numpy sont manipulés par des fonctions numpy/scipy?
Je ne cherche pas le moyen le plus efficace, je voulais juste quelque chose de simple à mettre en œuvre qui ne nécessite pas une réécriture significative lorsque la boucle n'est pas exécutée en parallèle. Juste comme les outils OpenMP dans les langues de niveau inférieur.
la meilleure réponse que j'ai vu à cet égard est celui-ci, mais il s'agit d'une manière plutôt clunky qui nécessite d'exprimer la boucle dans une fonction qui prend un seul argument, plusieurs lignes de shared-array converting crud, semble exiger que la fonction parallèle soit appelée de __main__
, et cela ne semble pas bien fonctionner à partir de l'invite interactive (où je passe beaucoup de temps).
Avec tous La simplicité de Python est-ce vraiment la meilleure façon de parelléliser une boucle? Vraiment? C'est quelque chose de trivial à paralléliser en OpenMP mode.
j'ai lu minutieusement la documentation opaque du module multiprocessing, pour découvrir qu'elle est si générale qu'elle semble convenir à tout sauf à une simple parallélisation de boucle. Je ne suis pas intéressé à mettre en place des Gestionnaires, des Procurations, des Tuyaux, etc. J'ai juste une boucle simple, totalement parallèle qui n'a aucune communication. entre les tâches. Utiliser le MPI pour mettre en parallèle une situation aussi simple semble exagéré, sans mentionner qu'il serait inefficace de mémoire dans ce cas.
Je n'ai pas eu le temps d'en apprendre plus sur la multitude de paquets parallèles en mémoire partagée pour Python, mais je me demandais si quelqu'un avait plus d'expérience dans ce domaine et pouvait me montrer une façon plus simple. S'il vous plaît ne pas suggérer des techniques d'optimisation en série comme Cython (Je l'utilise déjà), ou en utilisant des fonctions parallèles de numpy/scipy comme BLAS (mon cas est plus général, et plus parallèle).
3 réponses
Avec Cython parallèle de soutien:
# asd.pyx
from cython.parallel cimport prange
import numpy as np
def foo():
cdef int i, j, n
x = np.zeros((200, 2000), float)
n = x.shape[0]
for i in prange(n, nogil=True):
with gil:
for j in range(100):
x[i,:] = np.cos(x[i,:])
return x
Sur un 2-noyau de la machine:
$ cython asd.pyx
$ gcc -fPIC -fopenmp -shared -o asd.so asd.c -I/usr/include/python2.7
$ export OMP_NUM_THREADS=1
$ time python -c 'import asd; asd.foo()'
real 0m1.548s
user 0m1.442s
sys 0m0.061s
$ export OMP_NUM_THREADS=2
$ time python -c 'import asd; asd.foo()'
real 0m0.602s
user 0m0.826s
sys 0m0.075s
cela fonctionne bien en parallèle, puisque np.cos
(comme les autres ufuncs) libère le GIL.
Si vous voulez l'utiliser de manière interactive:
# asd.pyxbdl
def make_ext(modname, pyxfilename):
from distutils.extension import Extension
return Extension(name=modname,
sources=[pyxfilename],
extra_link_args=['-fopenmp'],
extra_compile_args=['-fopenmp'])
et (supprimer asd.so
et asd.c
premier):
>>> import pyximport
>>> pyximport.install(reload_support=True)
>>> import asd
>>> q1 = asd.foo()
# Go to an editor and change asd.pyx
>>> reload(asd)
>>> q2 = asd.foo()
donc oui, dans certains cas, vous pouvez paralléliser simplement en utilisant des threads. OpenMP n'est qu'un emballage de fantaisie pour le filetage, et Cython est donc seulement nécessaire voici pour la syntaxe plus facile. Sans Cython, vous pouvez utiliser le threading
module - - - fonctionne de la même manière que le multiprocessing (et probablement plus robustement), mais vous n'avez pas besoin de faire quoi que ce soit de spécial pour déclarer des tableaux comme mémoire partagée.
cependant, toutes les opérations ne libèrent pas la GIL, donc YMMV pour la performance.
***
Et un autre peut-être utile lien en grattant les autres Stackoverflow réponses --- une autre interface de multitraitement: http://packages.python.org/joblib/parallel.html
en utilisant une opération de cartographie (dans ce cas multiprocessing.Pool.map()
) est plus ou moins le moyen canonique de paralyser une boucle sur une seule machine. À moins que et jusqu'à ce que le haut -map()
est toujours paralysé.
Un aperçu des différentes possibilités peuvent être trouvés ici.
Vous pouvez utiliser openmp avec python (ou plutôt cython, mais il n'a pas l'air facile.
IIRC, le point si seulement l'exécution multiprocessing stuff from __main__
est une nécessité en raison de la compatibilité avec Windows. Depuis windows manque fork()
, il démarre un nouvel interpréteur python et doit y importer le code.
Modifier
num Py peut paralyser certaines opérations comme dot()
,vdot()
et innerproduct()
, lorsqu'il est configuré avec une bonne bibliothèque de BLAS multithreading comme par exemple OpenBLAS. (Voir aussi cette question.)
puisque les opérations de numpy array sont principalement par élément, il semble possible pour paralléliser. Mais cela impliquerait la mise en place soit d'un segment de mémoire partagée pour les objets python, soit de diviser les tableaux en morceaux et de les alimenter aux différents processus, un peu comme ce que multiprocessing.Pool
ne. Quelle que soit l'approche adoptée, elle entraînerait des frais généraux de mémoire et de traitement pour gérer tout cela. Il faudrait effectuer des tests approfondis pour voir pour quelles tailles de tableaux cela vaudrait réellement la peine de l'effort. Le résultat de ces les tests varieraient probablement considérablement selon l'architecture matérielle, le système d'exploitation et la quantité de mémoire vive.
.map () méthode de la classe mathDict () dans ParallelRegression fait exactement ce que vous recherchez dans deux lignes de code qui devraient être très faciles à une invite interactive. Il utilise le vrai multiprocessing, de sorte que l'exigence que la fonction à exécuter en parallèle est pickle-able est inévitable, mais cela fournit un moyen facile de boucle sur une matrice dans la mémoire partagée de plusieurs processus.
dites que vous avez un cornichon fonction:
def sum_row( matrix, row ):
return( sum( matrix[row,:] ) )
alors vous avez juste besoin de créer un objet mathDict( ) qui le représente, et d'utiliser mathDict( ).map ():
matrix = np.array( [i for i in range( 24 )] ).reshape( (6, 4) )
RA, MD = mathDictMaker.fromMatrix( matrix, integer=True )
res = MD.map( [(i,) for i in range( 6 )], sum_row, ordered=True )
print( res )
# [6, 22, 38, 54, 70, 86]
la documentation (lien ci-dessus) explique comment passer une combinaison d'arguments de position et de mots-clés dans votre fonction, y compris la matrice elle-même à n'importe quelle position ou comme argument de mot-clé. Cela devrait vous permettre d'utiliser à peu près n'importe quelle fonction que vous avez déjà écrite sans la modifier.