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).

17
demandé sur Community 2012-10-25 16:37:24

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

16
répondu pv. 2012-10-26 22:12:37

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.

2
répondu Roland Smith 2017-05-23 12:33:41

.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.

0
répondu RichardB 2017-01-22 11:34:52