J'ai de l'expérience dans le codage d'OpenMP pour les machines à mémoire partagée (en C et en FORTRAN) afin d'effectuer des tâches simples comme l'ajout de matrice, la multiplication, etc. Je connais assez OpenMP pour effectuer des tâches simples sans avoir à consulter la documentation.
Récemment, je suis passé à Python pour mes projets et je n’ai aucune expérience de Python au-delà des bases absolues.
Ma question est :
Quel est le le plus simple _ moyen d’utiliser OpenMP en Python? Par plus simple, je veux dire celui qui demande le moins d’efforts du côté programmeur (même si cela se fait au détriment du temps système)?
J'utilise OpenMP parce qu'un code série peut être converti en un code parallèle fonctionnel avec quelques !$OMP
s dispersés. Le temps requis pour obtenir une parallélisation brute est étonnamment court. Est-il possible de répliquer cette fonctionnalité en Python?
En parcourant SO, je peux trouver:
Y a-t-il plus? Lequel s'aligne le mieux avec ma question?
En raison de GIL, il n’est pas utile d’utiliser des threads pour les tâches gourmandes en temps processeur de CPython. Vous avez besoin du multitraitement ( exemple ) ou d’utiliser les extensions C qui libèrent GIL lors des calculs, par exemple certaines fonctions numpy, exemple .
Vous pouvez facilement écrire des extensions C utilisant plusieurs threads en Cython, exemple .
À ma connaissance, il n'y a pas de paquet OpenMP pour Python (et je ne sais pas ce qu'il ferait s'il en existait un). Si vous souhaitez que les threads soient directement sous votre contrôle, vous devrez utiliser l'une des bibliothèques de threads. Cependant, comme l'ont souligné d'autres personnes, le GIL (Global Interpreter Lock) rend le multi-threading en Python plus performant, mais inutile, *. Le GIL signifie qu'un seul thread peut accéder à l'interpréteur à la fois.
Je suggérerais plutôt de regarder NumPy/SciPy. NumPy vous permet d'écrire du code Matlab-esque lorsque vous utilisez des tableaux et des matrices avec des opérations uniques. Il a également des capacités de traitement en parallèle, voir SciPy Wiki .
Autres endroits pour commencer à chercher:
* Ok, ce n'est pas inutile, mais à moins que le temps ne soit consommé en dehors du code Python (comme par un processus externe appelé via popen
ou quelque chose du genre), les threads ne vous achèteront rien d'autre que la commodité.
Cython a le support OpenMP: avec Cython, OpenMP peut être ajouté à l'aide de l'opérateur prange
(plage parallèle) et en ajoutant la directive de compilation -fopenmp
à setup.py.
Lorsque vous travaillez dans une strophe prange, l'exécution est effectuée en parallèle car nous désactivons le verrou d'interpréteur global (GIL) en utilisant le with nogil:
pour spécifier le bloc où GIL est désactivé.
Pour compiler _cython_np.pyx_, nous devons modifier le script setup.py comme indiqué ci-dessous. Nous lui disons d'informer le compilateur C d'utiliser -fopenmp
comme argument lors de la compilation - pour activer OpenMP et pour établir un lien avec les bibliothèques OpenMP .
Avec prange,
de Cython, nous pouvons choisir différentes approches de planification. Avec static,, la charge de travail est répartie uniformément sur les processeurs disponibles. Toutefois, comme certaines de vos régions de calcul coûtent cher dans le temps et d’autres, si vous demandez à Cython de planifier les blocs de travail de manière égale en utilisant static sur tous les processeurs, les résultats de certaines régions seront plus rapides que d’autres et ces threads resteront ensuite inactifs . Les options de planification dynamic et Guidée tenteront d’atténuer ce problème en allouant de manière dynamique le travail en fragments plus petits au moment de l’exécution, de manière à ce que les CPU soient mieux répartis le temps de calcul est variable. Ainsi, pour votre code, le choix correct variera en fonction de la nature de votre charge de travail.
La version premium de Numba, NumbaPro, prend en charge de manière expérimentale un opérateur de parallélisation prange
pour travailler avec OpenMP.
Pythran (un compilateur de Python à C++ pour un sous-ensemble de Python) peut tirer parti des possibilités de vectorisation et des possibilités de parallélisation basée sur OpenMP, bien qu’il utilise uniquement Python 2.7. Vous spécifiez des sections parallèles à l’aide des directives pragma omp
(très semblable au support OpenMP de Cython décrit ci-dessus), par exemple:
Le compilateur JIT Python PyPy prend en charge le module de multitraitement (voir ci-après) et comporte un projet appelé PyPy-STM " une version spéciale en développement de PyPy pouvant exécuter plusieurs threads indépendants du processeur dans le même processus en parallèle " .
OpenMP est une interface de bas niveau pour plusieurs cœurs. Vous voudrez peut-être consulter multiprocessing.
Le module multiprocessing
fonctionne à un niveau supérieur, partageant les structures de données Python, tandis qu'OpenMP fonctionne avec des objets primitifs C (par exemple, des entiers et des flottants) une fois que vous avez compilé en C. Cela n'a de sens que d'utiliser OpenMP si vous compilez votre code; Si vous ne compilez pas (par exemple, si vous utilisez du code efficace numpy et que vous voulez exécuter plusieurs cœurs), rester avec multiprocessing
est probablement la bonne approche.
Si vous voulez libérer GIL et utiliser OpenMP, vous pouvez jeter un coup d’œil sur Cython. Il offre un parallélisme simple pour certaines tâches courantes. Vous pouvez en lire plus dans Cython documentation .
Peut-être que votre réponse est en Cython:
"Cython prend en charge le parallélisme natif via le module cython.parallel. Pour utiliser ce type de parallélisme, la GIL doit être libérée (voir Libération de la GIL). Elle prend actuellement en charge OpenMP, mais plus tard, d’autres backends pourraient être pris en charge." Documentation Cython
Il existe un package appelé pymp , que l'auteur a décrit comme un package qui apporte une fonctionnalité de type OpenMP à Python. J'ai essayé de l'utiliser, mais avec un cas d'utilisation différent: le traitement de fichier. Ça a marché. Je pense que c'est assez simple à utiliser. Ci-dessous un exemple tiré de la page GitHub:
import pymp
ex_array = pymp.shared.array((100,), dtype='uint8')
with pymp.Parallel(4) as p:
for index in p.range(0, 100):
ex_array[index] = 1
# The parallel print function takes care of asynchronous output.
p.print('Yay! {} done!'.format(index))
http://archive.euroscipy.org/talk/6857 "présente les capacités OpenMP de Cython en se concentrant sur les boucles parallèles sur des tableaux NumPy. Des exemples de code source montrent comment utiliser OpenMP à partir de Python. Des améliorations peuvent être obtenues pour différentes tailles de données par rapport à d'autres stratégies de parallélisation. "
import numpy
import cython
from cython cimport parallel
@cython.boundscheck(False)
@cython.wraparound(False)
def func(object[double, ndim=2] buf1 not None,
object[double, ndim=2] buf2 not None,
object[double, ndim=2] output=None,
int num_threads=2):
cdef unsigned int x, y, inner, outer
if buf1.shape != buf2.shape:
raise TypeError('Arrays have different shapes: %s, %s' % (buf1.shape,
buf2.shape))
if output is None:
output = numpy.empty_like(buf1)
outer = buf1.shape[0]
inner = buf1.shape[1]
with nogil, cython.boundscheck(False), cython.wraparound(False):
for x in parallel.prange(outer, schedule='static',
num_threads=num_threads):
for y in xrange(inner):
output[x, y] = ((buf1[x, y] + buf2[x, y]) * 2 +
buf1[x, y] * buf2[x, y])
return output