2017-08-18 1 views
1

(comme vous le voyez, je ne suis pas très familier avec les concepts comme le Python GIL et multithreading PYTHON (ou cython))En utilisant openMP avec Cython: paralléliser une boucle intérieure

J'ai écrit une fonction Cython qui consiste d'un fragment de code avec un double pour boucle où une fonction f est appelée plusieurs fois.

for i in range(I): 
    for j in range(J): 
    res=f(A[i],B[j]) 

J'ai une machine avec 4 cœurs de processeurs et je veux paralléliser pas la première, mais la deuxième boucle. J'ai trouvé this wonderful website mais il ne traite pas le cas de la boucle interne et ne va pas dans les détails. Donc, à mon avis, je peux écrire:

for i in range(I): 
    #In what case can I release the GIL safely ? Is that necessary at all ? 
    with nogil, parallel(num_threads=4): 
    for j in prange(J,shedule="dynamic"): 
     res=f(A[i],B[j]) 

Est-ce que ça marche? Est-ce que je dois mettre le nogil à l'extérieur des deux boucles de façon à ce qu'il ne se répète pas à l'intérieur et ne «capture» pas ce truc de GIL? Quelqu'un pourrait-il m'expliquer comment et quelle est la logique derrière l'écriture de telles déclarations afin que je puisse généraliser à des problèmes insaisissables.

Répondre

2

Il y a un coût en temps pour libérer et recapturer le GIL et également un coût en temps pour la mise en place d'une boucle parallèle. Pour cette raison, il est généralement préférable de faire de la boucle la plus externe la parallèle. Cependant, si vous avez une bonne raison de vouloir paralléliser la boucle interne, alors cela fonctionnera et nous espérons que le coût devrait être faible par rapport au vrai travail contenu dans f. La libération du GIL vous empêche d'accéder aux variables Python et d'appeler des fonctions Python.

Les variables Cython typées, les fonctions cdef et les vues de mémoire Cython fonctionnent correctement. Vous obtiendrez une petite accélération de mettre le with nogil: aussi loin que possible. Par conséquent, mettez-le autour de la boucle externe si possible, mais si ce n'est pas possible alors c'est OK où vous l'avez montré.

Il est nécessaire de libérer vers GIL pour une boucle prange. Si nécessaire, vous pouvez ensuite le récupérer dans la boucle (with gil) mais essayez de ne le faire que pour de petites fractions de la boucle et seulement si nécessaire (le code qui nécessite le GIL ne peut pas fonctionner parallèlement à d'autres codes nécessitant le GIL).

La ligne res=f(A[i],B[j]) est légèrement bizarre pour le code parallèle puisque seul le res de la dernière boucle serait sauvegardé. Généralement, vous écrivez dans des éléments d'un tableau (par exemple res[i,j]=f(A[i],B[j])). Cependant, il peut y avoir de bonnes raisons de le faire comme vous l'avez montré ...

Cython vous avertira (généralement) si vous essayez de faire quelque chose qui nécessite le GIL, alors une bonne idée est de l'essayer et de le voir.

+0

Merci @DavidW réponse définitivement intéressante! Évidemment, il y a une bonne raison pour que je ne parallélise pas la boucle extérieure. Je n'ai pas écrit les détails de mon code et les trucs res sont stupides. Je vais améliorer ma question un peu. J'ai upvoted et l'accepterai si ça marche! – jean