2014-06-18 12 views
6

Ich lerne über BCASTing Datentypen in Fortran und habe einen Code, der zwei Werte vom Terminal nimmt und sie bei jedem Prozess anzeigt. Für die Kombination value1/value2 vom Typ integer/integer und integer/real funktioniert das, für die Kombination integer/real * 8 jedoch nicht.abgeleitete Datentypen mit MPI

Der Code ist:

use mpi 
implicit none 

integer :: ierror, pid, ncpu, root = 0 

integer :: counts, newtype, extent 
integer, dimension(2) :: oldtypes, blockcounts, offsets 

type value 
    integer :: value1 = 0 
    real*8 :: value2 
end type 

type (value) input 

call MPI_INIT(ierror) 
call MPI_COMM_RANK(MPI_COMM_WORLD, pid, ierror) 
call MPI_COMM_SIZE(MPI_COMM_WORLD, ncpu, ierror) 

! setup of 1 MPI_INTEGER field: value1 
offsets(1) = 0 
oldtypes(1) = MPI_INTEGER 
blockcounts(1) = 1 

! setup of 1 MPI_REAL8 field: value2 
call MPI_TYPE_EXTENT(MPI_INTEGER, extent, ierror) !determine offset of MPI_INTEGER 
offsets(2) = blockcounts(1)*extent     !offset is 1 MPI_INTEGER extents 
oldtypes(2) = MPI_REAL8 
blockcounts(2) = 1 

! define struct type and commit 
counts = 2 !for MPI_INTEGER + MPI_REAL8 
call MPI_TYPE_STRUCT(counts, blockcounts, offsets, & 
        oldtypes, newtype, ierror) 
call MPI_TYPE_COMMIT(newtype, ierror) 

do while (input%value1 >= 0) 
    if (pid == root) then 
     read(*,*) input 
     write(*,*) 'input was: ', input 
    end if 
    call MPI_BCAST(input, 1, newtype, & 
        root, MPI_COMM_WORLD, ierror) 
    write(*,*), 'process ', pid, 'received: ', input 
end do 

call MPI_TYPE_FREE(newtype, ierror) 
call MPI_FINALIZE(ierror) 

Es kann überprüft werden, dass integer/integer und integer/real funktionieren, indem Sie die entsprechende Erklärung zu ändern und oldtype. Die Kombination integer/real * 8 schlägt z.B. Eingänge -1 2.0 Erzeugungs:

input was:   -1 2.0000000000000000  
process   0 received:   -1 2.0000000000000000  
process   1 received:   -1 0.0000000000000000  
process   2 received:   -1 0.0000000000000000  
process   3 received:   -1 0.0000000000000000 

This Faden mit einem ähnlichen Problem legen nahe, dass MPI_TYPE_EXTENT Verwendung, da es nicht korrekt ist eine zusätzliche Polsterung sein könnte, die nicht berücksichtigt wird. Leider konnte ich das Problem nicht beheben und hoffe, dass mich jemand hier aufklären kann.

thx im Voraus

Antwort

7

Sie haben die Grundidee - Sie die Struktur erstellt hat, aber Sie gehen davon aus, dass der Wert mit doppelter Genauigkeit unmittelbar nach dem Integer-Wert gespeichert ist, und das ist in der Regel nicht korrekt . Hristo's Antwort, die Sie verknüpfen, gibt eine gute Antwort in C.

Das Problem ist, dass der Compiler normalerweise align Ihre Datenstruktur Felder für Sie ist. Die meisten Systeme können Werte, die im Speicher ausgerichtet sind, viel schneller lesen/schreiben, als sie nicht-ausgerichtete Zugriffe ausführen können, wenn sie diese überhaupt ausführen können. In der Regel besteht die Anforderung darin, dass die Ausrichtung auf Elementgrößen ausgerichtet ist. das bedeutet, dass eine 8-Byte-Zahl mit doppelter Genauigkeit auf 8-Byte-Grenzen ausgerichtet werden muss (das heißt, die Adresse des ersten Bytes ist Null-Modulo 8), während die Ganzzahl nur 4-Byte-ausgerichtet sein muss. Dies bedeutet fast sicher, dass zwischen dem Integer und dem Double 4 Bytes Padding liegen.

In vielen Fällen können Sie den Compiler beschwichtigen, um dieses Verhalten zu lockern - in Fortran können Sie auch das Schlüsselwort sequence verwenden, um zu verlangen, dass die Daten zusammenhängend gespeichert werden. So oder so, aus Performance-Sicht (weshalb Sie Fortran und MPI verwenden, man geht davon aus) ist dies fast nie das Richtige, aber es kann für Byte-zu-Byte-Kompatibilität mit anderen extern auferlegten nützlich sein Datentypen oder Formate.

Angesichts der wahrscheinlich auferlegten Auffüllung aus Leistungsgründen könnten Sie die Ausrichtung und den Hardcode in Ihrem Programm annehmen; aber das ist wahrscheinlich auch nicht das Richtige; Wenn Sie andere Felder hinzufügen oder die Art der reellen Zahl ändern, um eine 4-Byte-Genauigkeitszahl mit einfacher Genauigkeit usw. zu erhalten, wäre Ihr Code wieder falsch. Am besten ist es MPI_Get_address zu verwenden, um explizit die Orte zu finden und die richtigen Offsets selbst zu berechnen:

integer(kind=MPI_Address_kind) :: startloc, endloc  
integer :: counts, newtype 
integer, dimension(2) :: oldtypes, blockcounts, offsets 

type value 
    integer :: value1 = 0 
    double precision :: value2 
end type 

type (value) :: input 

!...  

! setup of 1 MPI_INTEGER field: value1 
call MPI_Get_address(input, startloc, ierror) 
oldtypes(1) = MPI_INTEGER 
blockcounts(1) = 1 
call MPI_Get_address(input%value1, endloc, ierror) 
offsets(1) = endloc - startloc 

oldtypes(2) = MPI_DOUBLE_PRECISION 
blockcounts(2) = 1 
call MPI_Get_address(input%value2, endloc, ierror) 
offsets(2) = endloc - startloc 

if (pid == 0) then 
    print *,'offsets are: ', offsets 
endif 

Beachten Sie, wenn Sie eine Reihe von solchen abgeleiteten Typen haben, den Fall der Polsterung zwischen dem letzten Elemente eines Elements zu decken und Am Anfang des nächsten möchten Sie dies explizit messen und die Gesamtgröße des Typs festlegen - den Offset zwischen dem Anfang eines Elements dieses Typs und dem Anfang des nächsten - mit MPI_Type_create_resized.

+0

+1 große Antwort und Beispiel. – casey

+0

+1 danke für die Klärung. Ich habe schon mit MPI_GET_ADDRESS herumgespielt, aber es hat noch nicht funktioniert. Dein Beispiel hat wunderbar funktioniert! – nluigi