2016-04-28 10 views
1

Ich verwende MPI4PY, um meinen Code zu parallelisieren. Ich möchte zwei Datenstücke, eine ganze Zahl und eine reelle Zahl, zwischen Knoten kommunizieren. Ich möchte auch Arrays und die Kapital Send und Recv Funktionen verwenden, die schneller sind. Wenn ich ein paar Tutorials lese, scheint es möglich zu sein, aber ich kann keine Beispiele finden. Hier ist eine einfache Version von dem, was nicht funktioniert hat:So erstellen Sie eine Struktur für die Weitergabe zwischen Knoten mit 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) 

Und der Fehler:

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:}' 

Ich denke, dass dies bedeutet, dass es nicht genug ist, eine numpy strukturierte Anordnung zu verwenden, und ich muß Verwenden Sie einen MPI-Datentyp. Ich fand in der Dokumentation(), dass es eine Funktion mpi4py.MPI.Datatype.Create_struct gibt, die aussieht, als ob es sein könnte, was ich will, aber ich verstehe nicht, wie man es benutzt. Die Doc-Zeichenfolge lautet:

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

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

Vielen Dank für jede Hilfe!

Antwort

2

So von Anfang an beginnen:

Es ist immer möglich, Dinge zu bekommen nur mit python Tupeln und MPI4PY ist sehr praktisch Beizen Betreiber dies zu tun, indem nur ein Tupel zu senden begonnen:

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) 

Lauf gibt

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

Aber dieses Beizen/Entpacken an jedem Ende der Nachricht braucht eine gewisse Zeit. Wenn die Dinge einmal funktionieren, ist es sicherlich ein mögliches Ziel für die Optimierung, dies in nativen MPI durch Definition eines Strukturtyps zu tun.

Damit dies funktioniert, müssen Sie den Speicherlayout der Struktur kennen, die Ihnen im Allgemeinen nicht mit (sagen wir) einem Tupel zur Verfügung steht; die Großbuchstaben-Nachrichtenoperatoren in MPI4PY verlassen sich auf numpy, was Garantien über das Speicherlayout gibt.

Für so etwas wie ein Array von Strukturen, können Sie numpy structured arrays verwenden:

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

So, jetzt haben wir eine Reihe von Strukturen, wobei das erste Feld ‚int‘ genannt wird und mit einem 4-Byte-Integer type, und der zweite mit dem Namen 'dbl' und einem 8-Byte-Fließkomma-Typ.

Sobald Sie diese haben, können Sie beginnen, die Daten-Layout Abfrage - die Größe einer einzelnen Struktur zu finden:

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

Das erste man das Ausmaß der Art erzählt - die Anzahl der Bytes zwischen dem Beginn der das erste Element und der Anfang der zweiten - und die zweite gibt Ihnen die Offsets in Bytes für jedes Element.Dass Sie benötigen für die Struktur:

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

Nun können Sie den MPI-Datentyp für die Struktur zu schaffen und es nur die gleiche Art und Weise mit Ihnen MPI.INT oder dergleichen würden. Der einzige verbleibende Trick besteht darin, dass Sie beim Aufruf von Create_struct aus numpy dtypes in MPI-Datentypen übersetzen müssen, aber das ist ziemlich einfach. Der folgende Code gibt Ihnen einen Start:

#!/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) 

Lauf gibt

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

Wenn eine der zu sendenden Daten eine Ganzzahl ist, wird sie als Tag gesendet. (Da jedoch diese Lösung auf eine ganze Zahl beschränkt ist, bin ich immer noch sehr interessiert an einer alternativen Antwort auf meine Frage.)

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)