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)