2016-04-28 3 views
1

J'utilise mpi4py pour paralléliser mon code. Je veux communiquer deux morceaux de données, un nombre entier et un nombre réel, entre les noeuds. Je voudrais également utiliser des tableaux et les fonctions Send et Recv qui sont plus rapides. En lisant quelques tutoriels, il semble que cela devrait être possible, mais je ne trouve aucun exemple. Voici une version simple de ce qui ne fonctionne pas:Comment créer une structure pour passer entre les nœuds en utilisant mpi4py

import numpy 
from mpi4py import MPI 
comm = MPI.COMM_WORLD 
size = comm.Get_size() 
rank = comm.Get_rank() 

dt = numpy.dtype('int,float') 
if rank == 0: 
    recvBuffr = numpy.zeros(1,dt) 
    comm.Recv(recvBuffr, source = MPI.ANY_SOURCE) 
    print recvBuffr 

else: 
    result = rank*1.5 
    sendBuffr = numpy.zeros(1,dt) 
    sendBuffr[0][0] = rank 
    sendBuffr[0][1] = result 
    comm.Send(sendBuffr, dest=0) 

Et l'erreur:

Traceback (most recent call last): 
    File "mpitest.py", line 10, in <module> 
Traceback (most recent call last): 
    File "mpitest.py", line 18, in <module> 
    comm.Send(sendBuffr, dest=0) 
    comm.Recv(recvBuffr, source = MPI.ANY_SOURCE) 
    File "MPI/Comm.pyx", line 248, in mpi4py.MPI.Comm.Recv (src/mpi4py.MPI.c:78963) 
    File "MPI/Comm.pyx", line 237, in mpi4py.MPI.Comm.Send (src/mpi4py.MPI.c:78765) 
    File "MPI/msgbuffer.pxi", line 380, in mpi4py.MPI.message_p2p_recv (src/mpi4py.MPI.c:26730) 
    File "MPI/msgbuffer.pxi", line 366, in mpi4py.MPI._p_msg_p2p.for_recv (src/mpi4py.MPI.c:26575) 
    File "MPI/msgbuffer.pxi", line 375, in mpi4py.MPI.message_p2p_send (src/mpi4py.MPI.c:26653) 
    File "MPI/msgbuffer.pxi", line 358, in mpi4py.MPI._p_msg_p2p.for_send (src/mpi4py.MPI.c:26515) 
    File "MPI/msgbuffer.pxi", line 114, in mpi4py.MPI.message_simple (src/mpi4py.MPI.c:23528) 
    File "MPI/msgbuffer.pxi", line 114, in mpi4py.MPI.message_simple (src/mpi4py.MPI.c:23528) 
    File "MPI/msgbuffer.pxi", line 59, in mpi4py.MPI.message_basic (src/mpi4py.MPI.c:22718) 
KeyError: 'T{l:f0:d:f1:}' 
    File "MPI/msgbuffer.pxi", line 59, in mpi4py.MPI.message_basic (src/mpi4py.MPI.c:22718) 
KeyError: 'T{l:f0:d:f1:}' 

Je pense que cela signifie qu'il ne suffit pas d'utiliser un tableau structuré de numpy, et je dois utiliser un type de données MPI. J'ai trouvé sur la documentation (https://mpi4py.scipy.org/docs/apiref/mpi4py.MPI.Datatype-class.html) qu'il y a une fonction mpi4py.MPI.Datatype.Create_struct, qui semble être ce que je veux, mais je ne comprends pas comment l'utiliser. La chaîne de doc dit:

Create_struct(...) 
    Datatype.Create_struct(type cls, blocklengths, displacements, datatypes) 

    Create an datatype from a general set of 
    block sizes, displacements and datatypes 

Merci pour toute aide!

Répondre

2

Ainsi, à partir du début:

Il est toujours possible de faire avancer les choses commencé à utiliser simplement les tuples de python, et les opérateurs de décapage très pratique de MPI4PY pour le faire en envoyant simplement un tuple:

from __future__ import print_function 
from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
size = comm.Get_size() 
rank = comm.Get_rank() 

assert size > 1 

if rank == 0: 
    result = comm.recv(source = MPI.ANY_SOURCE, tag = MPI.ANY_TAG) 
    print(result) 
elif rank == 1: 
    comm.send((1, 3.14), dest = 0) 

donne

course
$ mpirun -np 2 python send_tuple.py 
(1, 3.14) 

Mais ce décapage/décompactage à chaque extrémité du message prend un certain temps, donc une fois que les choses fonctionnent, c'est certainement une cible possible pour l'optimisation de le faire en MPI natif en définissant un type de structure. Pour que cela fonctionne, vous devez connaître la mémoire mise en place de la structure, qui en général n'est pas disponible pour vous avec (disons) un tuple; les opérateurs de message en majuscule dans MPI4PY s'appuient sur numpy, ce qui donne des garanties sur la disposition de la mémoire.

Pour quelque chose comme un tableau de structures, vous pouvez utiliser numpy structured arrays:

>>> a = numpy.zeros(2, dtype=([('int',numpy.int32),('dbl',numpy.float64)])) 
>>> a 
array([(0, 0.0), (0, 0.0)], 
     dtype=[('int', '<i4'), ('dbl', '<f8')]) 

Alors maintenant, nous avons un éventail de structures, avec le premier champ étant nommé « int » et ayant un nombre entier de 4 octets type, et le second étant nommé 'dbl' et ayant un type à virgule flottante de 8 octets.

Une fois que vous avez cela, vous pouvez commencer à interroger la mise en page de données - trouver la taille d'une structure individuelle:

>>> print(a.nbytes/2) 
12 
>>> print(a.dtype.fields) 
mappingproxy({'dbl': (dtype('float64'), 4), 'int': (dtype('int32'), 0)}) 

Cette première vous indique la mesure du type - le nombre d'octets entre le début de le premier élément et le début de la seconde - et la seconde vous donne les décalages en octets pour chaque élément.Ce dont vous avez besoin pour la structure:

>>> displacements = [a.dtype.fields[field][1] for field in ['int','dbl']] 
>>> print(displacements) 
[0, 4] 

Maintenant, vous pouvez commencer à créer le type de données MPI pour la structure et de l'utiliser de la même façon que vous le feriez avec MPI.INT ou similaires. Le seul truc restant est que dans l'appel à Create_struct, vous devez traduire des dtypes numpy en types de données MPI, mais c'est assez simple. Le code suivant vous donne un début:

#!/usr/bin/env python 
from __future__ import print_function 
from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
size = comm.Get_size() 
rank = comm.Get_rank() 

assert size > 1 

def definetype(field_names, field_dtypes): 
    num = 2 
    dtypes = list(zip(field_names, field_dtypes)) 
    a = np.zeros(num, dtype=dtypes) 

    struct_size = a.nbytes // num 
    offsets = [ a.dtype.fields[field][1] for field in field_names ] 

    mpitype_dict = {np.int32:MPI.INT, np.float64:MPI.DOUBLE} #etc 
    field_mpitypes = [mpitype_dict[dtype] for dtype in field_dtypes] 

    structtype = MPI.Datatype.Create_struct([1]*len(field_names), offsets, field_mpitypes) 
    structtype = structtype.Create_resized(0, struct_size) 
    structtype.Commit() 
    return structtype 


if __name__ == "__main__": 
    struct_field_names = ['int', 'dbl'] 
    struct_field_types = [np.int32, np.float64] 
    mytype = definetype(struct_field_names, struct_field_types) 
    data = np.zeros(1, dtype=(list(zip(struct_field_names, struct_field_types)))) 

    if rank == 0: 
     comm.Recv([data, mytype], source=1, tag=0) 
     print(data) 
    elif rank == 1: 
     data[0]['int'] = 2 
     data[0]['dbl'] = 3.14 
     comm.Send([data, mytype], dest=0, tag=0) 

lorsqu'il fonctionne

$ mpirun -np 2 python send_struct.py 
[(2, 3.14)] 
0

Si l'une des données envoyées est un nombre entier, l'envoyer en tant que variable fonctionne. (Cependant, étant donné que cette solution est limitée à un nombre entier, je suis toujours très intéressé par une réponse alternative à ma question.)

import numpy 
from mpi4py import MPI 
comm = MPI.COMM_WORLD 
size = comm.Get_size() 
rank = comm.Get_rank() 

if rank == 0: 
    result = numpy.zeros(1,float) 
    status=MPI.Status() 
    comm.Recv(result, source = MPI.ANY_SOURCE, status = status, tag = MPI.ANY_TAG) 
    print status.Get_tag(), result 

else: 
    result = numpy.array([rank*1.5,]) 
    i = 5 
    comm.Send(result, dest=0, tag=i)