\documentstyle[twoside]{cscmenu}
\begin{document}

\paaotsikko{Parallel Virtual Machine -- PVM}

\two

\otsikko{Mik� on PVM ?}

PVM eli Parallel Virtual Machine on ohjelmisto, jolla voidaan
sovellusohjelma rinnakkaistaa usealle (UNIX-) tietokoneelle yhdeksi
samanaikaisesti toimivaksi laskentaresurssiksi. Rinnakkastaminen voidaan
suorittaa yhden tietokoneen sis�ll� tai ns. heterogeenisessa
tietokoneverkossa, jossa on useampi eri valmistajan laitteisto. My�s
joihinkin massiivisesti rinnakkaisiin tietokoneisiin on uusi PVM versio
saatavissa.

PVM:�� k�ytet��n siten, ett� aluksi k�ynnistet��n PVM-ymp�rist� ns.
PVM-demonin avulla, jolle kerrotaan mitk� tietokoneet aikovat osallistua
laskentaan. Sen j�lkeen kirjoitetaan C- (C++) tai Fortran-kielinen ohjelma,
jossa on kutsuja PVM-aliohjelmakirjastoon. N�m� hy�dynt�v�t PVM-demonin
palveluja ja k�yt�ss� olevaa tietoliikenneverkkoa tiedon siirt�miseksi eri
koneiden v�lill�. Syntynyt ohjelma saatetaan ajokelpoiseksi ja
k�ynnistet��n ns. is�nt�prosessiksi. Is�nt�prosessi puolestaan k�ynnist��
normaalisti aliprosesseja, jotka ryhtyv�t tiedonv�litykseen kesken��n.

Kaikki kommunikointi ja tiedon muuntaminen eri tietokoneiden v�lill� hoidetaan
l�pin�kyv�sti juuri PVM-aliohjelmakirjastojen v�lityksell�. PVM ohjelmointi
on siten er��nlaista 'k�yh�n miehen' massiivisesti rinnakkaisen laitteiston
ohjelmointia -- mutta suhteellisen helppoa sellaista.

PVM on julkinen ohjelmisto (public domain), jonka ovat kehitt�neet
A. L. Beguelin, J. J. Dongarra, G. A. Geist, R. J. Manchek, B.
K. Moore, ja V. S. Sunderam (University of Tennessee, Oak Ridge National
Laboratory, Emory University).  Jokainen voi asentaa halutessaan PVM:n itse
omalle tietokoneelleen. Ohjelman hankkimisesta saa tietoa
l�hett�m�ll� e-mail Netlib-osoitteeseen {\tt netlib@nac.no} ja laittamalla
{\tt Subject}-kentt��n {\tt send index from pvm3}.

CSC:ss� PVM on asennettuna Craylle, Convexille, Finsunille ja Azalealle (ja
samalla koko asiakastilan SGI-verkkoomme). Kulloinkin k�yt�ss� olevat
l�hdekieliset ohjelmat on sijoitettu aina hakemiston
{\tt{/usr/local/bin/pvm3}} alle. T�t� artikkelia kirjoitettaessa ohjelman
versio oli 3.1.

\otsikko{PVM esimerkkiohjelma}

Artikkelin lopussa on esimerkki PVM-aliohjelmakutsuja sis�lt�v�st�
ohjelmasta. Ohjelma on ns. SPMD-ohjelma (Single Program -- Multiple
Data), jossa is�nt�prosessi ja sen luomat aliprosessit kukin ajavat
samaa ongelmaa. Yleisess� tapauksessa n�in ei luonnollisesti tarvitse olla
vaan PVM-ohjelmat voivat k�ynnist�� mit� tahansa ohjelmia (ns. MIMD eli
Multiple Instruction -- Multiple Data -ohjelmointi). Ohjelmasta on esitetty
sek� C-kielinen, ett� Fortran-77 -kielinen versio.

Esimerkkiohjelma luo aluksi yhteyden PVM-demoniin ja selvitt�� prosessinsa
PVM-tunnisteen ({\tt tid} eli task id). PVM-tunniste on kanava, jonka avulla
PVM-prosessit kommunikoivat kesken��n. Jos prosessi olisi is�nt�prosessi,
k�ynnist�isi se {\tt NPROC-1} eli 3 kappaletta aliprosesseja ja l�hett�isi
kaikille luoduille prosesseille muiden prosessien PVM-tunnisteet. Kukin
aliprosessi taas vastaanottaa muiden prosessien PVM-tunnisteet. Seuraavaksi
ohjelmat suorittava kukin {\tt dowork()} nimisen aliohjelman. Siin�
yksinkertainen viesti ('token') pakataan ja l�hetet��n seuraavalle
prosessille ringiss� ja vastaanotetaan edelliselt� uusi viesti. Viimeinen
prosessi (t�ss� 4. prosessi) l�hett�� viestin kuitenkin
ykk�sprosessille (eli is�nt�prosessille), jolloin ns. token ring on saatu
pystytetyksi.

PVM:n aliohjelmakutsut on selvitetty seuraavassa.  Aliohjelmien nimien
sekoittuminen PVM:n ulkopuolisiin rutiineihin estetty siten, ett�
C-kielisiss� ohjelmissa PVM-aliohjelmat alkavat aina {\tt pvm\_} ja
Fortran-kieliset {\tt PVMF} etuliitteell�. Lis�ksi on sovittu, ett�
Fortran-funktiot eiv�t palauta suoraan tietoa aliohjelmankutsun
onnistumisesta, vaan se palautetaan paluukoodina aliohjelman viimeisen�
parametrin�.

\otsikko{T�rkeit� PVM-aliohjelmakutsuja}

Seuraavassa taulukko yleisesti k�ytetyist� PVM aliohjelmakutsuista
t�rkeimmist� PVM aliohjelmakutsuista:

{\scriptsize
\begin{tabular}{|l|l|l|}
\hline
Aliohjelma & Aliohjelma   & Selitys \\
(C-kieli)  & (Fortran-77) & \\
\hline
{\tt pvm\_mytid} & {\tt PVMFmytid} & Palauttaa prosessin PVM-tunnisteen \\
{\tt pvm\_exit} & {\tt PVMFexit} & P��tt�� yhteyden PVM-demoniin \\
{\tt pvm\_spawn} & {\tt PVMFspawn} & Luo PVM-aliprosesseja \\
{\tt pvm\_kill} & {\tt PVMFkill} & Tappaa PVM-prosessin \\
\hline
{\tt pvm\_parent} & {\tt PVMFparent} & Palauttaa is�nt�prosessin tunnisteen \\
{\tt pvm\_config} & {\tt PVMFconfig} & Kertoo PVM-ajon tietokoneista \\
\hline
{\tt pvm\_initsend} & {\tt PVMFinitsend} & Alustaa l�hetyspuskurit \\
{\tt pvm\_pack} & {\tt PVMFpack} & Pakkaa tietoa l�hetyspuskureihin\\
{\tt pvm\_send} & {\tt PVMFsend} & L�hett�� tietoa PVM-prosessille \\
{\tt pvm\_mcast} & {\tt PVMFmcast} & L�hett�� tietoa kaikille PVM-prosesseille \\
\hline
{\tt pvm\_recv} & {\tt PVMFrecv} & Odottaa ja vastaanottaa viestin \\
{\tt pvm\_nrecv} & {\tt PVMFnrecv} & Vastaanottaa viestin, jos oli tulossa \\
{\tt pvm\_unpack} & {\tt PVMFunpack} & Purkaa tiedon vastaanottopuskureista \\
\hline
\end{tabular}
}

Tarkemmat tiedot PVM-aliohjelmista ja niiden k�ytt�mist� parametreist� saa
hakemistossa {\tt /usr/local/bin/pvm3/doc} olevista tiedostoista
{\tt{refcard.ps}} (Postscript) ja {\tt ug.ps.Z} ({\tt compress}oitu
k�ytt�j�n opas).

\otsikko{PVM-ohjelman tuottaminen}

Ohjelman ajamiseksi PVM-ymp�rist�ss� on ohjelma ensin kirjoitettava
sellaiseksi, ett� se sis�lt�� PVM-aliohjelmakutsuja.  Aliohjelmakutsut
k�ytt�v�t hyv�kseen muutamia symboleja, jotka on m��ritelty erillisiss�
otsikkotiedostoissa. C-kielisiss� ohjelmissa symbolit saa mukaan
laittamalla l�hdekielisen ohjelman alkuun:

{\tt \#include "pvm3.h"}

Fortran-kielisiss� ohjelmissa laitetaan:

{\tt      INCLUDE 'fpvm3.h'}

N�m� otsikkotiedostot l�ytyv�t CSC:ss� hakemistosta
{\tt{/usr/local/include}}, josta ne on joko kopioitava samaan hakemistoon,
miss� omat l�hdekieliset ohjelmat sijaitsevat tai luotava ns. symboliset
linkit otsikkotiedostoihin:

{\tt \% ln -s /usr/local/include/pvm3.h .} \# C-ohjelmat \\
{\tt \% ln -s /usr/local/include/fpvm3.h .} \# Fortran

C-kielisille ohjelmille ei v�ltt�m�tt� tarvitse symbolista linkki� luoda,
jos k�ytet��n ohjelman k��nn�skomennossa valitsinta
{\tt{-I/usr/local/include}}. My�s jotkut Fortran-k��nt�j�t (esimerkiksi
Silicon Graphicsilla) tukevat t�t� samaa k��nt�j�n valitsinta.  Lis�ksi
koneissa, joissa on GNU:n C-k��nt�j�, voidaan 'cc'-komento korvata
'gcc':ll�.

Huomaa my�skin, ett� Convexilla ohjelmat tulee k��nt�� ja linkata
IEEE-aritmetiikan mukaanottavalla valitsimella {\tt -fi}. T�st� seuraa,
ett� IEEE-standardia oletuksena tukevien ty�asemien (esimerkiksi Silicon
Graphics, Sun ja HP) ja Convexin v�lisess� PVM-kommunikoinnissa ei tietoa
tarvitse muuntaa toisen koneen ymm�rt�m��n muotoon ja n�in s��stet��n hieman
tietokoneresursseja.

T�m�n j�lkeen ohjelma on k��nnett�v� ja ladattava bin��risen PVM-kirjaston
kanssa.  

\one 

%\begin{figure}
\begin{tabular}{|l|l|l|}
\hline
Tietokone  & C-ohjelmat & Fortran-ohjelmat \\
\hline
Cray      & cc -I/usr/local/include prog.c -lpvm3 -o prog & cf77 prog.f -lfpvm3 -lpvm3 -o prog \\
Convex    & cc -fi -I/usr/local/include prog.c -lpvm3 -o prog & fc -fi prog.f -lfpvm3 -lpvm3 -o prog \\
Azalea    & cc -I/usr/local/include prog.c -lpvm3 -lsun -o prog & f77 prog.f -lfpvm3 -lpvm3 -lsun -o prog \\
Finsun    & cc -I/usr/local/include prog.c -lpvm3 -o prog & f77 prog.f -lfpvm3 -lpvm3 -o prog \\
\hline
\end{tabular}
%\end{figure}

\two

CSC:n tietokoneiden PVM-arkkitehtuurityypit ovat CNVX, CRAY, SUN4 ja SGI
laitteille Convex, Cray, SunSparcstation ja Silicon Graphics, vastaavasti.
T�t� tietoa voidaan k�ytt�� hyv�ksi joissakin PVM:n aliohjelmakutsuissa.


\otsikko{PVM-ohjelman ajaminen}

Ennen oman ohjelman k�ynnist�mist� on ns. PVM-demoni (ohjelma {\tt pvmd3})
laitettava p��lle, jotta kommunikointi olisi mahdollista eri PVM-prosessien
v�lill�.  PVM-demoni ei kuitenkaan k�ynnisty kunnolla, koska versio 3.1
olettaa ep�suorasti, ett� k�ytt�j� on asentanut PVM-kirjastot itse oman
kotihakemistonsa alle, hakemistoon {\tt \$HOME/pvm3} . Vaikka en l�yt�nyt
todisteita 
t�st� virheest� PVM-dokumentaatiosta, sain PVM-demonin toimimaan kunnolla
vasta, kun olin laittanut jokaisen PVM-ajoon osallistuvan koneen
kotihakemistoon seuraavan symbolisen linkin:

{\tt \% ln -s /usr/local/bin/pvm3 \$HOME/pvm3}

T�m�n h�m�yksen j�lkeen PVM-demoni kuvittelee kirjastojen olevan asennettuina
kotihakemiston alle.

PVM-demonille voidaan kertoa my�s ne tietokoneet, joilla PVM-ohjelmaansa
aikoo ajaa. Demonille kerrotaan my�s muuta hy�dyllist� tietoa kuten
hakemistopolut, jonne PVM on asennettu sek� omien PVM:�� k�ytt�vien ohjelmien
sijoituspaikat. T�m� tieto annetaan ns. 'hostfile'-tiedoston v�lityksell�.
Seuraavassa esimerkki 'hostfile'-tiedostosta (tiedoston nimi saa olla mik�
vain): 

\begin{verbatim}
# PVM 3.1 hostfile for SPMD-test, 11-MAY-1993.
* dx=/usr/local/bin/pvmd3
finsun ep=/tmp/sbs/pvm3
& convex.csc.fi ep=/tmp/sbs
\end{verbatim}

Periaatteena on, ett� kullekin riville kirjoitetaan tiedot yhdest�
koneesta. Rivit, jotka alkavat '\#', '*' tai '\&' -merkeill� on lis�ksi
erikoismerkitys. '\#' tarkoittaa komenttirivi�. '*':ll� annetaan oletuksesta
poikkeavia vakioasetuksia, jotka ovat voimassa tiedoston loppuun tai
seuraavaan '*':een asti. '\&' -merkill� kerrotaan ne koneet, jotka aiotaan
ottaa mukaan my�hemmin PVM-koneiksi {\tt add} komennolla.

Edell� olleessa 'hostfile'-tiedostossa oli lis�ksi 'dx' ja 'ep' symbolit.
N�iden avulla m��ritell��n oletuksesta poikkeava PVM-demonin fyysinen
sijoituspaikka kulloisellakin koneella sek� hakemistopolku, josta k�ytt�j�n
PVM-sovellusohjelmia haetaan.

PVM-demoni k�ynnistet��n taustaprosessiksi komennolla:

{\tt \% pvmd3 hostfile \&}

Sitten pystytet��n erillinen PVM-ajon seurantaikkuna ohjelmien valvontaa
varten: 

{\tt \% xterm -e pvm \&}

T�m�n j�lkeen k�ynnistet��n oma PVM-sovellus:

{\tt \% SPMD}

Saadaan tulostus, joka on suunnilleen seuraavanlaista:

\begin{verbatim}
me = 0 mytid = 262182
[t80040000] [t40029] me = 1 mytid = 262185
[t80040000] [t40028] me = 2 mytid = 262184
[t80040000] [t40027] me = 3 mytid = 262183
token ring done
\end{verbatim}

Menem�ll� PVM-ajon seurantaikkunaan, voidaan katsoa onko jokin prosessi
viel� j��nyt ajoon, selvitt�� k�yt�ss� olevat koneet tai lis�t�/poistaa
PVM-ajoon osallistuvia koneita. Esimerkiksi:

{\scriptsize
\begin{tabular}{|l|l|}
\hline
Komento & Merkitys \\
\hline
{\tt help}    & Antaa listan seurantaikkunan komennoista \\
{\tt ps -a}   & Listaa kaikki viel� ajossa olevat prosessit \\
{\tt reset}   & Tappaa kaikki viel� ajossa olevat prosessit \\
{\tt kill} tid & Tappaa PVM-tunnisteella 'tid' olevan prosessin \\
{\tt halt}    & Sulkee seurantaikkunan ja lopettaa PVM-demonin \\
{\tt quit}    & Sulkee seurantaikkunan, mutta PVM-demoni jatkaa \\
{\tt conf}    & Listaa kaikki PVM-ajoon osallistuvat koneet \\
{\tt add} kone & Lis�� koneen 'kone' PVM-ajoon osallistuvaksi \\
{\tt delete} kone & Poistaa koneen 'kone' PVM-ajosta \\
\hline
\end{tabular}
}

Jos lis�t��n esimerkin vuoksi 'convex.csc.fi' PVM-ajoon osallistuvaksi, on
PVM:n seurantaikkunassa annettava: 

{\tt add convex.csc.fi}

Edellisen ajon tulostus n�ytt�� nyt seuraavalta:

\begin{verbatim}
me = 0 mytid = 262186
[t80040000] [t4002c] me = 1 mytid = 262188
[t80040000] [t4002b] me = 2 mytid = 262187
token ring done
[t80040000] [t1c0001] me = 3 mytid = 1835009
\end{verbatim}

Yhden PVM-istunnon aikana voi ajaa useita PVM-sovelluksia per�kk�in.
Jos sovellukset j��v�t jumiin (esimerkiksi ns. deadlock), voidaan
seurantaikkunassa antaa komento {\tt reset}, joka tappaa kaikki ajossa
silloin olleet prosessit -- kaikilta ajoon osallistuneilta koneilta.

PVM-istunto lopetetaan ja seurantaikkuna suljetaan komennolla {\tt halt}.
T�m� on syyt� muistaa, muutoin prosessit voivat j��d� ikuisesti
roikkumaan eri koneisiin.

\otsikko{Lis�tietoja}

Lis�tietoja saa luonnollisesti allekirjoittaneelta. My�s Usenet-news ryhm�
{\tt comp.parallel.pvm} tarjoaa mainion keskustelufoorumin.

PVM:st� kannattaa tulostaa ja lukea Postscript-ohjeet hakemistossa {\tt
/usr/local/bin/pvm3/doc} (tai {\tt \$DOC/pvm3}). Valmiita esimerkkej� on
hakemistossa {\tt /usr/local/bin/pvm3/examples}.

\one

{\scriptsize
\begin{figure}
\begin{twocol}{SPMD.c}{SPMD.f}
\begin{program}{90mm}
#include <stdio.h>
#include "pvm3.h"  /* PVM otsikkotiedosto */
#define NPROC 4    /* Prosessien enimmaismaara */

main()
\{
  int mytid;       /* Prosessin PVM-tunnistusnumero  */
  int tids[NPROC]; /* Kaikkien prosessien tunnisteet */
  int me;          /* Prosessin indeksi tids[]:hen   */
  int i;
  
  mytid = pvm_mytid();    /* Kirjoittaudu PVM:aan */
  tids[0] = pvm_parent(); /* Selvitetaan isantaprosessi */

  if( tids[0] < 0 ) \{     /* Tama on isantaprosessi */
    tids[0] = mytid;
    me = 0;
    /* Isantaprosessi kaynnistaa (tassa) samannimisia aliprosesseja */
    pvm_spawn("SPMD",     /* Kaynnistettavien ohjelmien nimi */
	      (char**)0,  /* Parametrilista */
	      0,          /* Erikoisoptio */
	      "",         /* Koneen arkkitehtuuri tai Internet-nimi */
	      NPROC-1,    /* Kaynnistettavien ohjelmien lukumaara */
	      &tids[1]);  /* Paluukoodina prosessien tunnisteet */
    
    /* Lahetetaan prosessien tunnisteet kaikille tiedoksi: */
    pvm_initsend( PvmDataDefault );   /* Alusta lahetyspuskurit */
    pvm_pkint(tids, NPROC, 1);        /* Pakkaa NPROC integeria */
    pvm_mcast(&tids[1], NPROC-1, 0);  /* Roiskaise menemaan !   */
  \} else \{   /* Tama on isantaprosessin kaynnistama aliprosessi */
    /* Vastaanotetaan prosessien tunnisteet: */
    pvm_recv(tids[0], 0);       /* Vastaanotto isantaprosessilta */
    pvm_upkint(tids, NPROC, 1); /* Vastaanota NPROC tunnistetta  */
    for( i=1; i<NPROC ; i++ )
      if( mytid == tids[i] )\{ me = i; break; \}
  \}
  /*----------------------------------------------------------------
   * Tassa vaiheessa kaikki NPROCia ovat samanarvoisia ja voivat
   * viitata toisiinsa tunnisteilla tids[0] ... tids[NPROC-1].
   * Jokaiselle prosessille muuttuja 'me' kuvautuu prosessi-
   * indeksiksi [0 ... (NPROC-1)].
   *-----------------------------------------------------------------
   */
  printf("me = %d mytid = %d{\bsl}n",me,mytid);
  dowork( me, tids, NPROC ); /* Tehdaan oma osamme laskentatyosta */
  pvm_exit();                /* Puretaan ohjelman PVM-yhteys */
  exit(1);
\}

dowork ( me,  tids, nproc )
     int me, *tids, nproc;
\{ /* Ohjelma lahettaa viestin 'token ring' -verkkoon */
  int token; int dest; int count  = 1;
  int stride = 1; int msgtag = 4;
  if( me == 0 ) \{  /* Isantaprosessi  tekee */
    token = tids[0];
    pvm_initsend( PvmDataDefault );     /* Alusta lahetys */
    pvm_pkint( &token, count, stride ); /* Pakkaa 'token' */
    pvm_send( tids[me+1], msgtag );     /* Laheta seuraavalle */
    pvm_recv( tids[nproc-1], msgtag );  /* Vastaanota viimeiselta */
    printf("token ring done{\bsl}n");
  \} else \{  /* Aliprosessi tekee */
    pvm_recv( tids[me-1], msgtag );     /* Vastaanota edelliselta */
    pvm_upkint( &token, count, stride );/* Pura viesti */ 
    pvm_initsend( PvmDataDefault );     /* Alusta lahetys */
    pvm_pkint( &token, count, stride ); /* Pakkaa 'token' */
    dest = (me == nproc-1)? tids[0] : tids[me+1] ;
    pvm_send( dest, msgtag );      /* Laheta seuraavalle ringissa */
  \}
\}
\end{program}
&
\begin{program}{82mm}
   program SPMD
   INCLUDE 'fpvm3.h'
   PARAMETER( NPROC=4 )
   integer mytid, me, i
   integer tids(0:NPROC)

   call PVMFmytid( mytid )
   call PVMFparent(tids(0))

   if( tids(0) .lt. 0 )  then
     tids(0) = mytid
     me = 0
     call PVMFspawn('SPMD',PVMDEFAULT,'*',NPROC-1,tids(1),info)
     call PVMFinitsend( PVMDEFAULT, info )
     call PVMFpack( INTEGER4, tids, NPROC, 1, info )
     call PVMFmcast( NPROC-1, tids(1), 0, info )
   else 
     call PVMFrecv( tids(0), 0, info )
     call PVMFunpack( INTEGER4, tids, NPROC, 1, info )
     do 30 i=1, NPROC-1
       if( mytid .eq. tids(i) ) me = i
30   continue 
   endif
   print*,'me =',me, '  mytid =',mytid
   call dowork( me, tids, NPROC )
   call PVMFexit(info)
   stop
   end

   subroutine dowork( me, tids, nproc )
   INCLUDE  'fpvm3.h'
   integer me, nproc
   integer tids( 0:nproc)
   integer token, dest, count, stride, msgtag 
   count  = 1
   stride = 1
   msgtag = 4
   if( me .eq. 0 ) then
      token = tids(0)
      call PVMFinitsend( PVMDEFAULT, info )
      call PVMFpack( INTEGER4, token, count, stride, info )
      call PVMFsend( tids(me+1), msgtag, info )
      call PVMFrecv( tids(nproc-1), msgtag, info )
      print*, 'token ring done'
   else
      call PVMFrecv( tids(me-1), msgtag, info )
      call PVMFunpack( INTEGER4, token, count, stride, info )
      call PVMFinitsend( PVMDEFAULT, info )
      call PVMFpack( INTEGER4, token, count, stride, info )
      dest = tids(me+1)
      if( me .eq. nproc-1 ) dest = tids(0)
      call PVMFsend( dest, msgtag, info )
   endif
   return
   end
\end{program}
\\
\end{twocol}
\caption{PVM esimerkkiohjelma C:ll� ja Fortranilla}
\end{figure}
}

\tekija{Sami Saarinen}

\end{document}
