MEDCoupling, multiprocessing ---------------------------- Cet exercice fait la supposition de Numpy Scipy sont correctement maîtrisés, sinon voir :ref:`medcouplingnumpyptr`. On va faire simuler un traitement un peu long (ici l'interpolation d'un maillage de 64000 cells avec un autre de 64000 cells). On va faire le traitement d'abord en séquentiel puis en parallèle pour exploiter les coeurs de notre CPU. Nous allons utiliser le module ``multiprocessing`` pour cela. Début de l'implémentation ~~~~~~~~~~~~~~~~~~~~~~~~~ Pour commencer l'exercice importer le module Python ``MEDCoupling``, ``MEDCouplingRemapper``, ``numpy``, ``scipy``, ``multiprocessing`` et ``datetime`` pour chronométrer : :: import MEDCoupling as mc import MEDCouplingRemapper as mr import multiprocessing as mp from datetime import datetime from scipy.sparse import csr_matrix Créer un maillage cartésien régulier 3D avec 40 cells en X, Y et Z ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Créons un maillage cartésien 3D m de pas régulier entre 0. et 1. en X, Y et Z : :: nbCells=40 arr=mc.DataArrayDouble(nbCells+1) ; arr.iota() ; arr/=nbCells m=mc.MEDCouplingCMesh() ; m.setCoords(arr,arr,arr) Traduisons m en non structuré pour que le calcul soit plus long : :: m=m.buildUnstructured() Créer une copie m2 de m translatée de la moitié du pas en X, Y et Z :: m2=m.deepCopy() t=mc.DataArrayDouble(3) t[:]=1/(2*float(nbCells)) m2.translate(t.getValues()) Calculer séquentiellement la matrice d'interpolation M de la projection entre m et m2 en P0P0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ m sera considéré comme le maillage source et m2 sera considéré comme le maillage cible. Profitons en pour chronométrer le temps necessaire pour le traitement séquentiel. Utilisons ``MEDCouplingRemapper`` pour cela. :: remap=mr.MEDCouplingRemapper() strt=datetime.now() assert(remap.prepare(m,m2,"P0P0")==1) print "time in sequential : %s"%(str(datetime.now()-strt)) Stockons la sparse matrix scipy dans ``matSeq``. :: matSeq=remap.getCrudeCSRMatrix() Calculer cette même matrice M en parallèle avec multiprocessing. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Commencons par récupérer le nombre de coeur de notre machine. :: nbProc=mp.cpu_count() L'idée est de faire une méthode ``work`` prenant un tuple de longueur 2. Le premier élément du tuple est la partie du maillage ``m2`` considérée. Le 2eme élément donne la correspondance entre les cells id de ``m2Part`` les cells id de ``m2``. L'idée est d'interpoler ``m`` avec ``m2Part``. On récupèrera ensuite la matrice sparse ``myMat`` issue de ``m`` avec ``m2Part``. Ensuite l'idée et de générer une matrice sparse ``mat2`` à partir de ``myMat`` avec les ids globaux de ``m2``. :: def work(inp): m2Part,partToGlob=inp myRemap=mr.MEDCouplingRemapper() assert(myRemap.prepare(m,m2Part,"P0P0")==1) myMat=myRemap.getCrudeCSRMatrix() a=mc.DataArrayInt.Range(s.start,s.stop,s.step) indptrnew=mc.DataArrayInt(m2.getNumberOfCells()) indptrnew.fillWithZero() d=mc.DataArrayInt(myMat.indptr).deltaShiftIndex() indptrnew[partToGlob]=d indptrnew.computeOffsetsFull() mat2=csr_matrix( (myMat.data,myMat.indices,indptrnew.toNumPyArray()), shape=(m2.getNumberOfCells(),m.getNumberOfCells())) return mat2 Il s'agit désormais de faire la liste des inputs à donner aux ``nbProc`` instances de ``work`` qui seront exécutés en parallèle. Appelons cette liste python ``workToDo`` qui sera de longueur ``nbProc``. On peut se faire aider de ``mc.DataArray.GetSlice``. :: workToDo=[] for i in xrange(nbProc): s=mc.DataArray.GetSlice(slice(0,m2.getNumberOfCells(),1),i,nbProc) part=m2[s] partToGlob=mc.DataArrayInt.Range(s.start,s.stop,s.step) workToDo.append((part,partToGlob)) pass On est maintenant prêt pour faire travailler chacun des coeurs indépendamment. Pour ce faire, on crée un ``mp.Pool`` et on assigne à chaque worker le travail ``work`` avec autant de worker que de coeurs. Et chronométrons le tout ! :: strt=datetime.now() pool = mp.Pool() asyncResult = pool.map_async(work,workToDo) subMatrices = asyncResult.get() print "time in parallel (x%d) : %s"%(nbProc,str(datetime.now()-strt)) .. note:: A noter la magie ! On a transféré entre le process maitre et les process esclave sans même s'en rendre compte les maillages et les DataArrayInt contenus dans ``workToDo`` ! Merci à la pickelisation des objets MEDCoupling :) Vérfication ~~~~~~~~~~~ Vérifions que les matrices sont les mêmes ! Sommons ``subMatrices`` (``matPar``) et regardons le nombre de non zéros de la différence entre la ``matPar`` et ``matSeq``. :: matPar = sum(subMatrices) matDelta=matSeq-matPar assert(matDelta.nnz==0)