CONDOR példaprogramok

 Lapok készítése még nem fejezôdött be! A hibákért elnézést kérek!

A Grid rendszerek kutatásához kapcsolódóan több projekt keretében (ViSzSzKi, SzuperGrid, ClusterGrid, BMEGrid) vizsgáltuk ill. felhasználtuk (használjuk) a Condor rendszert. A vizsgálatokhoz néhány egyszerû mintaprogramot is kidolgoztunk, amely a Condor rendszer használatát mutatja be. A példák bemutatásához feltételeztük a Condor rendszer alapfogalmainak, és használatának alapvetõ ismeretét, melynek on-line dokumentációja több magyar szerveren is megtalálható.

script1 Egyszerû script a vanilla univerzum használatának bemutatása. Egy bemenõ adatsoron több számítást végzünk.
script2 Az elõzõnél kicsit kicsit összetettebb script. A módszer különösen alkalmas az ún. paraméter vizsgálat jellegû feladathoz.
prim1 Primek keresése.
prim2 Primek keresése módsított programmal.
elsopvm Egyszerû Condor-PVM program. Nem hibatûrõ

 


script1

Tipikus számitási feladat, amikor egy adott bemenõ adatsor különbözõ részletén kell elvégeni ugyanazt az idõigényes mûveletsorozatot. Az egyszerûség kedvéért tegyük fel, hogy állomány minden során el kell végezni egy idõigényes mûveletet, melynek eredményét szintén egy másik állományban akarjuk összegyûjteni. A mûvelet most csak az adott sor kiírása legyen. A feladat megoldásához egy olyan scriptet készítettünk, amley a feldolgozandó sor soszámát indítási paraméterként, a feldolgozandó adatokat pedig a standard bemeneten várja. Az eredmény a standard kimentene keletkezik. A script csak egyszerû UNIX segédprogramokat használ. (Az idõigényességet néhány másodperces várakozással helyettesítettük.)

#!/bin/sh

head -$1 | tail -1             # Standard input kiirasa a standard outputra
sleep 60                       # 60 masodpercet varakozik
exit 0                         # megallasi statusz

Ennek megfelelõen a Condor submit file a következõ:*

universe = vanilla             # vanilla univerzum
NAME = script1                 # a feladat neve (segedmakro)

executable = $(NAME).sh        # vegrehajtando program neve
arguments = $(Process)         # inditasi parameterek
input = $(NAME).inp            # ezt kapja a standard bemeneten
output = $(NAME)$(Process).out # kimeneti allomany
transfer_files = ALWAYS        # flock miatt fontos
log = $(NAME).log              # naplo allomany

queue 5                        # 5 peldanyban beteszuk a sorba

A condor_submit script1.cmd paranccsal elindított job eredményeként elõálló script10.out, script11.out, script12.out, script13.out és script14.out állományok rendre a bemeneti állomány 0., 1., 2., 3., és 4. sorát fogják tartalmazni, mivel az egyes futásoknál a $(Process) makró rendre 0,1,2,3,4 értékkel fog helyettesítõdni. Természetesen a 0. sor nem létezik, ezért a script10.out üres lesz. (A probléma megoldására a prim1 feladatnál látunk megoldást.)

* Megjegyezzûk, hogy a condor submit formátum nem negedi meg, hogy a parancssorban megjegyzés legyen, ezért a letölthetõ állományokban ezeket külön sorba, az aktuális parancs után írtuk.


script2

A tudományos számítások egy részében igen gyakori feladat, hogy egy adott programot, vagy egy programrendszert sokszor kell lefuttani különbözõ bemenõ adatokkal ill. paraméterekkel. Ez elõzõ példéhoz hasonlóan egy egyszerû scripttel helyettesítjük a valós feladat megoldását. Az elõzõ példához képest új elem, hogy a futtatandó program (script) mellett más állományokat is át kell vinnünk távoli gépre, ill. az eredményként keletkezõ állományt vissza kell onnan hozni. A valós fealdat megoldását helyettesítõ script néhány egyszerû UNIX segédprogramot használ, melyek erdményéül kírja a standard outputra a scriptet futató gép nevét és a standard bemenetén kapott állományt õsszefûzi egy megadott input állománnyal. Az így keletkezett eredményt egy olyan állományba írja, melynek nevét az indítási paraméterébõl generálja.

#!/bin/sh

echo `hostname -d` ": $@"           # Gepnev es a parameterek kiirasa
cat > script2.o$1                   # a standard input masolasa
cat script2.in >> script2.o$1       # az allomany hozzafuzese
sleep $2                            # varakozas

exit 0                              # megallasi statusz

A script bemenete:

A script kimenete:

Ennek megfelelõen a submit file a következõ:*

universe = vanilla                         # vanilla univerzum
NAME = script2                             # a feladat neve (segedmakro)

executable = $(NAME).sh                    # vegrehajtando program neve
arguments = $(Process) 120                 # inditasi parameterek
input = $(NAME).inp                        # ezt kapja a standard bemeneten
output = $(NAME)$(Process).out             # kimeneti allomany
transfer_input_files = $(NAME).in          # at kell vinni a tavoli gepre
transfer_output_files = $(NAME).o$(Process)# vissza kell hozni
transfer_files = ALWAYS                    # flock miatt fontos
log = $(NAME).log                          # naplo allomany

queue 5                                    # 5 peldanyban beteszuk a sorba

A condor_submit script2.cmd paranccsal elindított job eredményeként elõálló script20.out, script21.out, script22.out, script23.out és script24.out állományok a scriptet futató gép nevét és a script paramétereit tartalmazzák. A script2.o0, script2.o1, script2.o2, script2.o3 és script2.o4 állományok pedig a script2.in és script2.inp állományokat tartalmazzák.

* A megjegyezést ld. a scrip1-nél.

prim1

Tételezzük fel, hogy van egy egyszerû program (prim1.c), amely primeket keres egy adott intervallumban. A program inditási paraméterként kapja az intervallum kezdetét és hosszát, amelyben prímeket keres. Condor rendszeben törtenõ használatához biztositani kell, hogy az egyes inditásoknál megfeleõ paramétereket kapjon. Mivel a condor csak az process és a cluster sorszámát tudja átadni, ezért vagy módosítani kell a programot (ld. prim2), vagy egy egyszerû scriptet kell köré készíteni, amely standard UNIX eszközöket használva a kapott process sorszámból elõállítja a megfelelõ indítási paramétereket. Ezt pl. így oldhatjuk meg:

#!/bin/sh

kezd=`expr $1 \* $2`            # intervallum kezdete
hossz=$2                        # intervallum hossza
chmod +x prim1                  # vegrahajthato legyen
exec ./prim1 $kezd $hossz       # program inditas

Mivel a condor adatként viszi át prímkeresõ programot, ezért azt futtahatóvá kell teni, ezért van szükség a scriptben a chmod parancsra. A script legutolsó parancsa úgy hajtja végre a prim1 programot, hogy script a végrehajtás elõtt megáll. Így a prim1 program exit státusza egybõl a condor rendszernek adódik át.

A submit file a következõ:*

universe = vanilla              # vanilla univerzum
NAME = prim1                    # a feladat neve (segedmakro)

executable = $(NAME).sh         # vegrehajtando program neve
arguments = $(Process) 1000     # inditasi parameterek
output = $(NAME)$(Process).out  # kimeneti allomany
transfer_input_files = $(NAME)  # at kell vinni programot
transfer_files = ALWAYS         # flock miatt fontos
log = $(NAME).log               # naplo allomany

queue 5                         # 5 peldanyban beteszuk a sorba

A condor_submit prim1.cmd paranccsal elindított jobA futás eredményeként elõálló prim10.out, prim11.out, prim12.out, prim13.out és prim14.out állományok rendre a [0, 999], [1000, 1999], [2000, 2999], [3000, 3999] és [4000, 4999] intervallumban talált primeket fogják tartalmazni.

Ha ezt a megoldást eltérõ architektúrájú gépeken alkalmazzuk, akkor nem csak azt kell biztosítani, hogy a megfelelõ architektórájú végrehajtható program kerüljön a távoli gepre, hanem annak architektúra függõ nevét is át kell adni a shell script számára. Egyszerûbb a helyzet, ha módunkban áll a problémát megoldó programot módosítani.

* A megjegyezést ld. a scrip1-nél.

prim2

Ebben a példában a prímkeresõ proramot úgy módósítottuk (prim2.c), hogy Condor környezetben is alkalmazható legyen. A program felismeri, hogy ha -m kapcsolóval indítják. Ekkor az intervallum kezdete helyett a példánysorszámot várja paraméterként. Ebbõl és az intervallum hosszából a program állítja elõ megfelelõ kezdeti értéket.

Ennek megfelelõen a submit file a következõ:*

universe = vanilla              # vanilla univerzum
NAME = prim2                    # a feladat neve (segedmakro)

executable = $(NAME).sh         # vegrehajtando program neve
arguments = -m $(Process) 1000  # inditasi parameterek
output = $(NAME)$(Process).out  # kimeneti allomany
transfer_files = ALWAYS         # flock miatt fontos
log = $(NAME).log               # naplo allomany

queue 5                         # 5 peldanyban beteszuk a sorba

A condor_submit prim2.cmd paranccsal elindított jobA futás eredményeként elõálló prim20.out, prim21.out, prim22.out, prim23.out és prim24.out állományok rendre a [0, 999], [1000, 1999], [2000, 2999], [3000, 3999] és [4000, 4999] intervallumban talált primeket fogják tartalmazni.

* A megjegyezést ld. a scrip1-nél.

elpsopvm

Egyszerû SPMD modell szerint felépített PVM program, ami semmi hasznosat nem csinál, csupán elküld egy üzenetet minden példánynak, amire minden példány visszaküldi a host nevét, amin fut. A program nem hibatûrõ, azaz nem kezeli, ha a virtuális gépbõl kiesik egy gép.

Példánkban szereplõ submit file a 2 különbözõ operaciós rendszeren indítja a programot. A második rendszerhez minimális pocesszorszámnak 0-t adtunk, így ha abból nem áll rendelkezésre, akkor is elindul. A példa szerint az aktuális katalógus LINUNX/elsopvm programja indul el a submit gépen. A távoli gépeken az operációs rendszertól függõen a LINUNX/elsopvm, vagy pedig a SOLARIS28/elsopvm program.

Megjegyezzük, hogy a Condor $OpSys makrója a PVM architekture nevekhez hasonló, de nem azonos neveket takar. A probléma forrása az, hogy a Condor megkülönbözteti az architektúrát és az operációs rendszert is a PVM csak az architektúrát. A különbözõ nevekbõl adódó konfliktusokat szimbolikus linkekkel célszerû megoldani. (Tegyük fel, hogy a példához mellékelt architektura független makefile [Maikefile.aimk] SUNMP néven hozza létre a programot tartalmazó katalógust, mely programot Condor teminológia szerint SUN4u architektúrájú SOLARIS28 rendszeren kivánunk futtatni. Ekkor ln -s SUNMP SOLARIS28 paranccsal a probléma egyszerûen megoldható).

A submit file így a következõ:*

universe = pvm                  # pvm univerzum
NAME = elsopvm                  # a feladat neve (segedmakro)
ARCH = LINUX
                                                # master program arch. (segedmakro)

executable = $(ARCH)/$(NAME).sh # vegrehajtando program neve
arguments = 200                 # inditasi parameterek
output = $(NAME)$(Process).out  # kimeneti allomany
errort = $(NAME)$(Process).err  # standard error allomany
log = $(NAME).log               # naplo allomany
machine_count = 5..30           
# minimum .. maximum proc.szam

queue                           # beteszuk a sorba

requirements = ( TARGET.OpSys == "SOLARIS28" )
machine_count = 0..5            
# minimum .. maximum proc.szam ezzel a req-val.

queue                           # beteszuk a sorba

* A megjegyezést ld. a scrip1-nél.

A PVM program a következõ:

#include <stdio.h>
#include <pvm3.h>
#define MAXPROC 80 /* maximalis processzorszam */

#define TAG_HNAME 10 /* Hostnev uzenet tipusa */
#define TAG_PNR 20 /* Sorszam uzenet tipusa */
/*
 
* startup: Processzek inditasat es a virtualis gepbe valo beleptetes
 
* segito rutin. Minden peldanyban meghivodik. Visszateresi erteke
 
* alapjan dontheto el, hogy melyik peldanyban vagyunk (SPMD modell)
 
*
 
* be: taskname - inditando taszkneve
 
*      waitsec - inditas elott varakozas
 *      *nprocs - inditando processzek szama
 
* ki:  *nprocs - tenylegesen elinditott processzek szama
 
*       tids[] - elinditott proceszek TID-je
 
*       tid[0] - indito (master) process TID-je
 
* fv.ertek:  0 - ez az indito (master) process
 
*         != 0 - adott slave tidje, csak tid[0] van kitoltve !
 
*                (*nprocs erteke azonos a bemenettel)
 
*/

int startup(char *taskname, int waitsec, int *nprocs, int tids[])
{
  int my_tid, parent_tid;
  int i, proc;
  int nhost, narch;
  struct pvmhostinfo *hostinfo;

  my_tid = pvm_mytid();
  parent_tid = pvm_parent();
  if (parent_tid == PvmNoParent) {
    tids[0] = my_tid; /* sajat TID */
   
if (waitsec) {
      
printf("Waiting %d sec for setting up the virt. machine\n", waitsec);
     
sleep(waitsec); /* var a virt.gep felepulesere */
    
}
    pvm_config(&nhost, &narch, &hostinfo); /* virt. gep konfiguracioja */
   
printf("Nhost=%d, Narch=%d\n", nhost, narch); fflush(stdout);
#ifdef NO_CACH_OUT_BUG
   
pvm_catchout(stderr); /* gyerek taszkok stdout-ja es stderr */
#endif
    
for (proc = i = 0; i < nhost; i++) {
      printf("Starting %s on arch: %s", taskname, hostinfo[i].hi_arch);
   
  fflush(stdout);
   
  if (pvm_spawn(taskname, NULL, PvmTaskArch, hostinfo[i].hi_arch, 1, &tids[proc+1]) == 1) {
   
    proc++;
       
printf(" -- success\n");
       
if (proc >= *nprocs)
         
break; /* van eleg processz */
     
 } else {
        printf(" -- fail\n");
     
}
    
}
   
printf ("%d/%d additional processes were initiated\n", proc, *nprocs);
   
fflush(stdout);
         *nprocs = proc;
   
return(0);
  
} else {
   
tids[0] = parent_tid;
   
return(my_tid);
  
}
}

/*
 
* Ha argc > 1, akkor Condor rendszert tetelezunk fel, es
 
* argv[1]-nek megfeleo ideig varunk a virt. gep felepulesere.
*/
int main(int argc, char *argv[])
{
  
int tids[MAXPROC+1];
 
char hname[80];
 
char *taskname;
 
int tid;
 
int nr;
 
int i;
 
int nprocs = MAXPROC;
 
int waitsec = 0;
 
if (argc > 1) {
   
waitsec = atoi(argv[1]);
   
taskname = "$(OpSys)/elsopvm"; /* program neve Condor-PVM-ben */
 
} else {
   
taskname = "elsopvm"; /* $HOME/pvm3/bin/$PVM_ARCH/name */
 
}
 
if ((tid = startup(taskname, waitsec, &nprocs, tids)) == 0) {
 
  for (i = 1; i <= nprocs; i++) { /* kuld egy uzenetet mindenkinek */
     
pvm_initsend(PvmDataDefault); /* Uzenetbuffer */
     
pvm_pkint(&i, 1, 1); /* egy sorszam */
     
pvm_send(tids[i], TAG_PNR);
      }
    
for (i = 0; i < nprocs; i++) { /* Begyujti a slavek adatait */
     
pvm_recv(-1, TAG_HNAME); /* uzenet barhonnan */
     
pvm_upkint(&nr, 1, 1); /* sorszam */
     
pvm_upkint(&tid, 1, 1); /* kuldo TID */
     
pvm_upkstr(hname); /* hostnev */
     
printf("Message from task%d (%06X): %s\n", nr, tid, hname);
     
fflush(stdout);
 
  }
  
} else {
    
pvm_recv(tids[0], TAG_PNR);
    
pvm_upkint(&nr, 1, 1); /* sorszam */
    
gethostname(hname, sizeof(hname)); /* Hostnev */
    
pvm_initsend(PvmDataDefault); /* Uzenetbuffer */
    
pvm_pkint(&nr, 1, 1); /* sorszam */
    
pvm_pkint(&tid, 1, 1); /* sajat TID */
            pvm_pkstr(hname); /* sajat hostname */
    
printf("Task%d (%06X) to %06X (%d)\n", nr, tid, tids[0], pvm_send(tids[0], TAG_HNAME));
    
fflush(stdout);
   
}
  
pvm_exit();
  
return(0);
}


Szeberényi Imre
© BME  Irányítástechnika és Informatika Tanszék
Utolsó módosítás:2003-06-05