prev up inhalt next


11 Programmiersprachen

Die algorithmische Idee eines All-to-All-Broadcast im Ring (Kapitel 4.3.1) wird durch den folgenden Pseudocode präzisiert. Hierbei bilden alle Prozessoren im Ring die Summe über alle Prozessorkennungen, indem sie diese Kennungen zyklisch weiterreichen:
/************************************************************************************/
/*                                                                                  */
/*                    Summe im Ring als Pseudocode                                  */
/*                                                                                  */
/************************************************************************************/
FOR p=0 TO n-1 DO IN PARALLEL

   id   = p;                   /* besorge die eigene Prozessorkennung               */
   anz  = n;                   /* besorge die Anzahl der Prozessoren                */
   odd  = id % 2;              /* lege fest, ob ungerade ID vorliegt                */

                               /* Topologie festlegen:                              */
   pre = LINK TO (id-1) % anz; /* Vorgaenger                                        */
   suc = LINK TO (id+1) % anz; /* Nachfolger                                        */

                               /* parallele Summe berechnen:                        */
   sum = id;                   /* vorlaeufige Summe                                 */
   out = id;                   /* naechster zu uebertragender Wert                  */
   FOR z = 1 TO anz-1 DO       /* anz-1 mal tue:                                    */
      IF (odd)                 /* falls ungerade ID                                 */
         RECV(pre, in );       /* erhalte vom Vorgaenger einen Wert fuer in         */
         SEND(suc, out);       /* schicke zum Nachfolger den Wert von out           */
      ELSE                     /* falls gerade ID                                   */
         SEND(suc, out);       /* schicke zum Nachfolger den Wert von out           */
         RECV(pre, in );       /* erhalte vom Vorgaenger den Wert fuer in           */
      sum += in;               /* Summe erhoehen                                    */
      out = in;                /* naechste Ausgabe vorbereiten                      */
   END
END
Auf den nächsten Seiten wird dieser Pseudocode in der Syntax von PARIX, MPI und PVM formuliert.

/*********************************************************************************************/
/*                                                                                           */
/*        Summe im Ring als Parix-Programm mit synchroner Kommunikation                      */
/*                                                                                           */
/*********************************************************************************************/
#include <sys/root.h>
#include <stdlib.h>

void main (int argc, char **argv)
{
   int anz, id, odd, sum, in, out, z;
   LinkCB_t *pre, *suc;                                /* Zeiger auf Link-Kontrollblocks     */
   int error;                                          /* Variable fuer Fehlermeldung        */
                                                       /* logische Topologie festlegen :     */

   anz = GET_ROOT()->ProcRoot->nProcs;                 /* Macro liefert Anzahl der Proz.     */
   id  = GET_ROOT()->ProcRoot->MyProcID;               /* Macro liefert Prozessor-ID         */
   odd = id % 2;                                       /* lege fest, ob ungerade ID vorliegt */		
                                                       /* die Kommunikationspartner muessen  */
                                                       /* sich gleichzeitig mit derselben ID */
                                                       /* auf ein Link verstaendigen:        */
   if (odd) {
      suc = ConnectLink((id+1+anz) % anz, 42, &error); /* definiere Link zum Nachfolger      */
      pre = ConnectLink((id-1+anz) % anz, 42, &error); /* definiere Link zum Vorgaenger      */
   } else {
      pre = ConnectLink((id-1+anz) % anz, 42, &error); /* definiere Link zum Vorgaenger      */
      suc = ConnectLink((id+1+anz) % anz, 42, &error); /* definiere Link zum Nachfolger      */
   }
                                                       /* Parallele Summe berechnen:         */

   sum = out = id;                                     /* initialisiere Variablen            */

   for (z = 1; z < anz; z++) {                         /* anz-1 mal tue:                     */
      if (odd) {
         RecvLink(pre, &in,  sizeof(int));             /* ueber Link pre empfangen nach in   */
         SendLink(suc, &out, sizeof(int));             /* ueber Link suc versenden von out   */
      } else {
         SendLink(suc, &out, sizeof(int));             /* ueber Link suc versenden von out   */
         RecvLink(pre, &in,  sizeof(int));             /* ueber Link pre empfangen nach in   */
      }
      sum += in;                                       /* Summe erhoehen                     */
      out  = in;                                       /* naechste Ausgabe vorbereiten       */
   }
   exit(0);                                            /* Programm beenden                   */
}

Nach Übersetzen der Quelle lautet der Aufruf von der Shell-Ebene für ein 4 × 4 -Gitter:

$ run -c 0 4 4 summe_im_ring.px


/****************************************************************************************/
/*                                                                                      */
/*  Summe im Ring als Parix-Programm unter Verwendung einer virtuellen Topologie        */
/*                                                                                      */
/****************************************************************************************/
#include <sys/root.h>
#include <stdlib.h>
#include <virt_top.h>

void main (int argc, char **argv)
{
   int anz, id, odd, sum, in, out, z;
   int ring, pre, suc;
   RingData_t  *ring_data;                      /* Zeiger auf Topologiestruktur         */

                                                /* logische Topologie festlegen:        */

   anz = GET_ROOT()->ProcRoot->nProcs;          /* Macro liefert Anzahl der Prozessoren */ 

   ring = MakeRing(1, anz, MINSLICE, MAXSLICE,  /* bilde Ring in ein 3D-Gitter ab       */
                           MINSLICE, MAXSLICE,  /* dabei soll jeweils pro Dimension     */
                           MINSLICE, MAXSLICE); /* das gesamte Intervall genutzt werden */
   ring_data = GetRing_Data(ring);              /* besorge Topologieinformation         */

   id = ring_data->id;                          /* logische ID bzgl. des Rings          */
   odd = id % 2;                                /* lege fest ob ungerade                */

   suc = 1;                                     /* Name fuer Nachfolgerlink bzgl. Ring  */
   pre = 0;                                     /* Name fuer Vorgaengerlink bzgl. Ring  */

                                                /* Parallele Summe berechnen:           */


   sum = out = id;                              /* initialisiere Variablen              */

   for (z = 1; z < anz; z++) {                  /* anz-1 mal                            */

      if (odd) {
          Recv(ring, pre, &in,  sizeof(int));   /* ueber Link pre im Ring empfangen     */
          Send(ring, suc, &out, sizeof(int));   /* ueber Link suc im Ring verschicken   */
      } else {
          Send(ring, suc, &out, sizeof(int));   /* ueber Link suc im Ring verschicken   */
          Recv(ring, pre, &in,  sizeof(int));   /* ueber Link pre im Ring empfangen     */
      }
      sum += in;                                /* Summe erhoehen                       */
      out  = in;                                /* naechste Ausgabe vorbereiten         */
   }

   FreeTop(ring);                               /* Programm beenden                     */
   exit(0);
}



/**************************************************************************************/
/*                                                                                    */
/*    Summe im Ring als Parix-Programm unter Verwendung asynchroner Kommunikation     */
/*                                                                                    */
/**************************************************************************************/


#include <sys/root.h>
#include <stdlib.h>
#include <virt_top.h>

void main (int argc, char **argv)
{
   int anz, id, odd, sum, in, out, z;
   int ring, pre, suc;
   RingData_t  *ring_data;
   int result;


   anz = GET_ROOT()->ProcRoot->nProcs;				

   ring = MakeRing(1, anz, MINSLICE, MAXSLICE,
                           MINSLICE, MAXSLICE,
                           MINSLICE, MAXSLICE);  
   ring_data = GetRing_Data(ring);

   id = ring_data->id;
   odd = id % 2;

   suc = 1;
   pre = 0;

   AInit(ring, -1, -1);                             /* Vorbereitung fuer Threads      */
                                                    /* welche die Kommunikation       */
                                                    /* durchfuehren sollen            */
   sum = out = id;

   for (z = 1; z < anz; z++) {

      ASend(ring, suc, &out, sizeof(int), &result); /* asynchrones Verschicken        */
      ARecv(ring, pre, &in,  sizeof(int), &result); /* asynchrones Empfangen          */
      ASync(ring, -1);                              /* Warten auf Abschluss der       */
                                                    /* Kommunikation                  */

      sum += in;
      out  = in;
   }

   AExit(ring);
   exit(0);
}



/**********************************************************************************/
/*                                                                                */
/*           Summe im Ring als MPI-Programm                                       */
/*                                                                                */
/**********************************************************************************/
#include "mpi.h"
int main(int argc, char **argv)
{
   int id, anz, odd, pre, suc, sum, in, out, z;
   MPI_Status status;

   MPI_Init ( &argc, &argv );              /* Initialisiere MPI                   */

                                           /* logische Topologie festlegen:       */
   MPI_Comm_size ( MPI_COMM_WORLD, &anz ); /* besorge Anzahl der Prozessoren      */
   MPI_Comm_rank ( MPI_COMM_WORLD, &id  ); /* besorge Prozessor-ID                */

   odd = anz % 2;                          /* lege fest, ob ungerade              */
   pre = ( id - 1 + anz ) % anz;           /* ID des Vorgaengers                  */
   suc = ( id + 1       ) % anz;           /* ID des Nachfolgers                  */

                                           /* Parallele Summe berechnen:          */

   sum = out = id;                         /* initialisiere Variablen             */

   for (z=1; z < anz; z++) {               /* anz-1 mal                           */

      if (odd) {
         MPI_Recv (&in,                    /* lege ab bei Adresse von in          */
                   1,                      /* ein Datum                           */
                   MPI_INT,                /* nach Bauart MPI_INT                 */
                   pre,                    /* erhalten vom Vorgaenger             */
                   42,                     /* versehen mit dem Tag 42             */
                   MPI_COMM_WORLD,         /* bzgl. des allgemeinen Kommunikators */
                   &status );              /* Adresse fuer Fehlerstatus           */
         MPI_Send (&out,                   /* entnehme beginnend bei Adresse out  */
                   1,                      /* ein Datum                           */
                   MPI_INT,                /* nach Bauart MPI_INT                 */
                   suc,                    /* verschicke an Nachfolger            */
                   42,                     /* versehen mit Tag 42                 */
                   MPI_COMM_WORLD );       /* bzgl. des allgemeinen Kommunikators */
      } else {
          MPI_Send ( &out, 1, MPI_INT, suc, 42, MPI_COMM_WORLD );
          MPI_Recv ( &in,  1, MPI_INT, pre, 42, MPI_COMM_WORLD, &status );
      }
      sum += in;
      out  = in;
   }
      MPI_Finalize ();                     /* Programm beenden                    */
}
Nach Übersetzen der Quelle lautet der Aufruf von der Shell-Ebene für 16 Prozessoren:
$ mpirun -np 16 summe_im_ring

/************************************************************************************/
/*                                                                                  */
/*       Summe im Ring als MPI-Programm unter Verwendung von reduce                 */
/*                                                                                  */
/************************************************************************************/


#include "mpi.h"

int main(int argc, char **argv)
{
    int id, sum;

    MPI_Init ( &argc, &argv );              /* initialisiere MPI                    */
 
                                            /* logische Topologie festlegen:        */

    MPI_Comm_rank ( MPI_COMM_WORLD, &id );  /* bestimme Prozessor-ID                */

                                            /* Parallele Summe berechnen:           */

    MPI_Allreduce (   &id,                  /* Eingabeparameter: id                 */
                      &sum,                 /* Ausgabeparameter: sum                */
                      1,                    /* 1 Datum                              */
                      MPI_INT,              /* von der Bauart MPI_INT               */
                      MPI_SUM,              /* zu bestimmen ist die Summe           */
                      MPI_COMM_WORLD );     /* innerhalb des globalen Kommunikators */


    MPI_Finalize ();                        /* Programm beenden                     */

}


/****************************************************************************************/
/*                                                                                      */
/*           Summe im Ring als PVM-Programm: Master                                     */
/*                                                                                      */
/****************************************************************************************/


#include "pvm3.h"

void  main ( int  argc, char  **argv )
{
   int  anz, z;
   int  *tids;

   anz = atoi ( argv[1] );                 /* besorge Anzahl der Prozessoren            */
   tids = (int*) malloc (anz*sizeof(int)); /* besorge Speicherplatz fuer Task-Id-Vektor */
  
   pvm_spawn (  "slave",                   /* Starte das Programm slave                 */
                (char **) NULL,            /* ohne Argumente                            */
                PvmTaskArch,               /* eingeschraenkt auf eine Architektur       */
                "SUN4",                    /* vom Typ SUN4                              */
                anz,                       /* anz mal                                   */
                tids );                    /* erhalte einen Vektor von Task-IDs zurueck */

                                           /* globale Task-Informationen verteilen      */

   for ( z = 0; z < anz; z++ ) {           /* anz mal                                   */
           pvm_initsend ( PvmDataRaw );    /* Sende-Puffer vorbereiten                  */
           pvm_pkint ( &z, 1, 1 );         /* den Wert von z verpacken                  */
           pvm_pkint ( &anz, 1, 1 );       /* den Wert von anz verpacken                */
           pvm_pkint ( tids, anz, 1 );     /* den Task-ID-Vektor verpacken              */
           pvm_send ( tids[z], 0 );        /* an den z-ten Prozessor verschicken        */
   }


   pvm_exit ( );                           /* Task beenden                              */
}

1.
Virtuelle Maschine zusammenstellen durch Start des PVM-Dämons auf jedem Host (laufen unabhängig im Hintergrund)
2.
Programme übersetzen:
   $ gcc -o summe_im_ring master.c -lpvm3
   $ gcc -o slave slave.c -lpvm3
3.
Aufruf für Ring mit 16 Tasks:
   $ summe_im_ring 16


/**************************************************************************************/
/*                                                                                    */
/*          Summe im Ring als PVM-Programm: Slave                                     */
/*                                                                                    */
/**************************************************************************************/
#include "pvm3.h"

void  main ( int  argc, char  **argv )
{
   int  id, anz, odd, in, sum, out, pre, suc, z;
   int  *tids;

                                           /* Logische Topologie festlegen:           */

   pvm_recv   ( pvm_parent ( ), -1 );      /* erhalte vom aufspannenden Vater         */
   pvm_upkint ( &id, 1, 1 );               /* entpacke id                             */
   pvm_upkint ( &anz, 1, 1 );              /* entpacke anz                            */
   tids = (int*) malloc (anz*sizeof(int)); /* besorge Platz fuer Task-ID-Vektor       */
   pvm_upkint ( tids, anz, 1 );

   odd = id % 2;                           /* lege fest, ob ungerade id vorliegt      */
   pre = tids[(id+anz-1)%anz];             /* Task-ID des Vorgaengers                 */
   suc = tids[(id+1)%anz];                 /* Task-ID des Nachfolgers                 */

                                           /* Parallele Summe berechnen:              */
   sum = out = id;
   for ( z = 1; z < anz; z++ ) {           /* anz-1 mal                               */
      if ( odd ) {
              pvm_recv ( pre, -1 );        /* erhalte vom Vorgaenger                  */
              pvm_upkint ( &in, 1, 1 );    /* entpacke nach in                        */

              pvm_initsend ( PvmDataRaw ); /* initialisiere Ausgabepuffer             */
              pvm_psend ( suc,             /* versende zum Nachfolger                 */
                          0,               /* mit dem Tag 0                           */
                          &out,            /* beginnend bei Adresse von out           */
                          1,               /* ein Datum                               */
                          PVM_INT);        /* nach Bauart PVM_INT                     */
      } else {
              pvm_initsend ( PvmDataRaw );
              pvm_psend ( suc, 0, &out, 1, PVM_INT );
              pvm_recv ( pre, -1 );
              pvm_upkint ( &in, 1, 1 );
      }

      sum += in;                           /* Summe erhoehen                          */
      out = in;                            /* naechsten Ausgabewert vorbereiten       */
   }

   pvm_exit ( );                           /* Programm beenden                        */
}


prev up inhalt next