multiprocessing dans une def!
Bonjours, le script suivant fonctionne presque normalement lorsqu'il est dans un objet mais alors à partir du moment que je l'ai mis dans une définition, les choses se sont compliquées!
Au début, les deux premiers passages se passaient à merveille mais dès la création du troisième objet, le calcul s’arrêtait en plein milieu, il se mettait en pause et les processeurs n'avait plus rien à se mettre sous la dent! (Je ne comprend pas pourquoi d’ailleurs!). Pour tenter d'y remédier, j'ai tout mis dans une définitions de façon à vraiment repartir à 0 à chaque fois que je la rappelle. Seulement, au lieu de faire une pause au troisième passage, Python exprime directement son mécontentement à travers le message suivant:
File "fractale_objet.py", line 2707, in calculage_rapide
matrice.extend([resultat.get() for resultat in liste_res])#Récupération des lignes qui viennent d'être calculées
File "fractale_objet.py", line 2707, in <listcomp>
matrice.extend([resultat.get() for resultat in liste_res])#Récupération des lignes qui viennent d'être calculées
File "/usr/lib/python3.4/multiprocessing/pool.py", line 599, in get
raise self._value
File "/usr/lib/python3.4/multiprocessing/pool.py", line 383, in _handle_tasks
put(task)
File "/usr/lib/python3.4/multiprocessing/connection.py", line 206, in send
self._send_bytes(ForkingPickler.dumps(obj))
File "/usr/lib/python3.4/multiprocessing/reduction.py", line 50, in dumps
cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function fractale.<locals>.calculage_rapide.<locals>.calcul at 0x7ff331942a60>: attribute lookup calcul on __main__ failed
Bon, voila, je ne comprend pas vraiment le message mais il se trouve que là, il semble vraiment furax!
Voici le code qui le met dans cet état:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
def calculage_rapide(identifiant, lecture):
"""
Rempli peu a peu la variable 'matrice'
iters_max = nbr maximum d'itérations
mod_carre = module maximum du nbr complex
largeur = largeur de la matrice
hauteur = hauteur de la marice
z0 = premier terme de la suite
suite = formule de récurence de la suite
couleur = information que l'on enregistre dans la matrice
xmin, xmax, ymin et ymax = dimension du plan complex
iteration = True quand les itérations comptent et pas seulement le module
nbr_couleur = Nombre totale de couleur différentes que peut avoir la fractale
"""
def calcul(ligne, hauteur):
"""
Remplissage d'une partie de la matrice
Retourne la ligne dans une liste
"""
print(" Ligne n°"+str(ligne+1)+"----"+str(int(100*ligne/hauteur))+"%")
liste = [] #On initialise la liste que l'on va remplir
I = complex(0,1) #Ainsi, I est considéré comme une variable
y = (ymax-ymin)*ligne/(hauteur-1)+ymin #La partie imaginaire du plan complex
for colone in range(0,largeur): #Pour chaque case de chaque ligne
x = (xmax-xmin)*colone/(largeur-1)+xmin #La partie réel du plan complex
z = eval(z0) #Création du premier rang de la suite
c = 0 #Initialisation du compteur
while (c<iters_max) and (((z.real**2)+(z.imag**2))<mod_carre): #Tant qu'il n'y a aucune raison d'arrêter le calcul de la suite
c+=1 #Incrémentation du compteur
z = eval(suite) #Calcul du rang suivant
if iteration: #Si la divergence de la suite compte
c = c/iters_max #On prend en compte le nombre d'itérations
else: #Si le module de la suite compte
c = abs(z)/math.sqrt(mod_carre) #Sinon, c'est le module de z qui est pris en compte
liste.append(int(c*nbr_couleurs)/nbr_couleurs) #Ce pxl est ajouté à la ligne
return liste #La ligne est la valeur de retour
global matrice,fin #On fait en sorte que la matrice soit accessible dans tous le programme
if __name__ == "__main__": #Si le programme en cours dexécution est le programme principal
tuple1, tuple2 = lecture.lire_tout(identifiant) #On récupère les données qui permettent de créer l'image
iters_max = tuple1[5]
mod_carre = tuple1[6]
largeur = tuple2[6]
hauteur = tuple2[7]
z0 = str(tuple1[7])
suite = str(tuple1[8])
xmin = tuple1[9]
xmax = tuple1[10]
ymin = tuple1[11]
ymax = tuple1[12]
iteration = tuple1[13]
nbr_couleurs = tuple1[4]
import multiprocessing #On importe le module
pool = multiprocessing.Pool() #Il est possible de mettre en argument: processes=nbr_de_coeurs
liste_res = [] #Tous les résultats seront enregistrés ici
compteur = 0 #On met le compteur à 0
for ligne in range(0,hauteur): #Pour chacune des lignes à traiter
if fin: return None #Si il faut en finir, on s'arrète de suite
compteur += 1 #On incrémente le compteur
liste_res.append(pool.apply_async(calcul, args=(ligne, hauteur)))#On s'en charge
compteur = compteur%(hauteur//10) #C'est le nombre de boucles pandant lesquelles la matrice n'est pas actualisée
if compteur == 0: #Si on en est à la 16ème boucle
pool.close() #On arrète momentanément de charger la liste d'attente
pool.join() #On lance les calculs en liste d'attente
matrice.extend([resultat.get() for resultat in liste_res])#Récupération des lignes qui viennent d'être calculées
pool = multiprocessing.Pool() #On réinitialise le pooleur
liste_res = [] #On vide la liste des lignes qui ont déjà étés calculées
pool.close() #Validation des dernières lignes
pool.join() #Lancement des derniers calculs
matrice.extend([resultat.get() for resultat in liste_res]) #Récupération des derniers résultats
fin = True
print(" Fin du calcul")
return None |
En plus, ce qui complique le programme, c'est qu'il est impératif que la 'matrice' ait une portée global car un thread qui tourne en parallèle, se charge d'enregistrer chaque ligne sur le disque et de supprimer chaque ligne calculée affin de faire de la place en mémoire et de ne pas perdre de temps a la fin du calcul pour enregistrer l'image sur le disque.
Encore une fois, je suis preneur de toutes les pistes qui permettraient de corriger ce plantage, ou bien d'une autre idée de conception qui aurait le même effet final.