Dagens tema Parallellitet Eksempelprogrammet Litt om X En enkelt pakke for tegning på skjerm Et program for å tegne fraktaler Kommunikasjon mellom prosesser Rør Felles lager Ark 1 av 24 Forelesning 28.4.1998
Vindussystemet X I de fleste operativsystemer er vindussystemet en integrert del. Slik er det ikke når det gjelder UNIX og X: Xserver Xlib xclock Brukerens maskin En annen maskin Dette gir følgende fordeler: Brukeren kan velge mellom ulike vindussystemer. Det er mulig å spre kjøringen. Forelesning 28.4.1998 Ark 2 av 24
En enkel X-pakke X er et stort lavnivåsystem, og tungt å programmere. Derfor er det laget pakker for dette: WindowTools, Motif, o a. x147 er en meget enkel pakke som kun tar hensyn til dette kursets behov: #include <X11/Xlib.h> extern void x147open(int w, int h); /* Open an X window */ extern void x147plot(int x, int y, /* Draw a dot in the given colour */ int col); extern void x147stat(void); /* Print statistics on colours */ extern void x147sync(void); /* Check for interrupts */ extern void x147pause(void); /* Wait until finished */ extern unsigned long x147black, /* The two standard colours */ x147white; extern unsigned int x147ncolbits; /* Log2(number of colours) */ Kildekoden ligger på ~in147. Forelesning 28.4.1998 Ark 3 av 24
x147open lager et nytt vindu med angitt størrelse. x147plot tegner ett punkt i vinduet med angitt farge. (Definisjonen av farger er tilfeldig.) x147stat gir litt informasjon om bildet (som fordelingen av farger) x147sync bør kalles jevnlig for å sjekke om brukeren har klikket i vinduet eller det er løftet frem. x147pause venter inntil brukeren har klikket i vinduet. x147black er den svarte fargen (for kall på x147plot). x147white er den hvite fargen. x147ncolbits er antall bit som brukes til å representere farger på den aktuelle skjermen. Typiske verdier er 1, 8 og 24. Forelesning 28.4.1998 Ark 4 av 24
Eksempel Følgende lille program tegner en sinus-kurve: #include <math.h> #include "x147.h" #define PI 3.14159265 #define NX 400 #define NY 200 void main(void) { float x, dx = 2*PI/(NX-1), y; int ix, iy; x147open(nx, NY); for (ix = 0; ix < NX; ++ix) { x = ix*dx; y = sin(x); iy = y/2*(ny-1)+100; x147plot(ix,iy,x147black); x147pause(); Forelesning 28.4.1998 Ark 5 av 24
Programmet kompileres med kommandoen cc -o sinus sinus.c x147.c -lx11 -lm Det gir følgende vindu på skjermen: Når man klikker på det med musen, forsvinner det. Forelesning 28.4.1998 Ark 6 av 24
Sekvensiell eksekvering En sekvensiell versjon av fraktalprogrammet kan se slik ut: #include <stdio.h> #include <time.h> #include "fraktal.h" #include "x147.h" #define NX 1000 #define NY 800 #define MAX 180 float x1 = -0.01, x2 = 0.01, y1 = -0.01, y2 = 0.01; void plot_col(int ix) { float x, y; int iy, fx; x = x1 + ix*(x2-x1)/(nx-1); for (iy = 0; iy < NY; ++iy) { y = y1 + iy*(y2-y1)/(ny-1); fx = fraktal(x, y, 255.0, MAX); x147plot(ix, iy, fx); x147sync(); void plot_area(int xstart, int xend) { int ix; for (ix = xstart; ix <= xend; ++ix) plot_col(ix); void main(int argc, char *argv[]) { time_t start_tid = time(null); x147open(nx, NY); plot_area(0, NX-1); printf("det hele tok %d sekunder.\n", time(null)-start_tid); x147pause(); Forelesning 28.4.1998 Ark 7 av 24
Dette programmet gir det kjente bildet av Julia-kurven: Dette bildet tok det 51 sekunder å lage på spesialmaskinen modsognir og 89 sekunder på dain som er en vanlig SGI-maskin, så det hadde vært fint å kunne parallellisere arbeidet så det kunne gå raskere... Forelesning 28.4.1998 Ark 8 av 24
Parallellisering En parallell versjon av fraktalprogrammet består av én eller flere prosesser som beregner punkter i fraktalbildet, og én prosess som tegner opp punktene. De evige spørsmål Når det er snakk om parallellprogrammering, er det alltid to spørsmål som må besvares: Hvordan skal data overføres? Hvordan skal arbeidet synkroniseres? Begrepet kommunikasjon omfatter disse to tingene. Forelesning 28.4.1998 Ark 9 av 24
Rør i UNIX UNIX-rør brukes når barneprosesser vil kommunisere med søsken eller sitt opphav. Rør opprettes med systemkallet pipe: int pip[2];. pipe(pip); Resultatet er et rør med to filnumre: pip[0] benyttes til lesing. pip[1] benyttes til skriving. Forelesning 28.4.1998 Ark 10 av 24
Å skrive til et rør Man kan sende n byte med data (lagret i buffer) til et rør med systemkallet w_bytes = write(pip[1], buffer, n); Returverdien w bytes forteller hvor mange byte som faktisk ble skrevet (og det vil vanligvis være n). Å lese fra et rør Man kan lese fra et rør med kommandoen r_bytes = read(pip[0], buf2, n); Data skal plasseres i buf2 hvor det er plass til n byte. Returverdien r bytes forteller hvor mange byte som faktisk ble mottatt. Synkronisering Man oppnår synkronisering ved at read om nødvendig blir blokkert til det er mer data å lese. Forelesning 28.4.1998 Ark 11 av 24
Eksempel Bruk av rør $ cat pip0.c #include <stdio.h> #include <unistd.h> int main(void) { int pip[2]; char *melding = "Hei på deg!"; char buffer[20]; int n; pipe(pip); if (safefork()) { /* Foreldre */ write(pip[1], melding, strlen(melding)+1); else { /* Barn */ n = read(pip[0], buffer, 20); printf("mottatt %d tegn: %s \n", n, buffer); $ pip0 Mottatt 12 tegn: Hei på deg! $ Dette fungerer fordi selve røret eksisterer «utenfor» prosessen; følgelig blir ikke røret kopiert av safefork, bare referansene i pip (som er heltall). Forelesning 28.4.1998 Ark 12 av 24
Kommunikasjon med rør Siden alle prosessene som lager fraktalbildet, utgår fra samme urprosess, kan UNIX-rør benyttes. En versjon med to produsenter og hvert sitt rør kan se slik ut: #include <stdio.h> #include <string.h> #include <time.h> #include <unistd.h> #include "fraktal.h" #include "x147.h" extern pid_t safefork(void); #define NX 1000 #define NY 800 #define MAX 180 float x1 = -0.01, x2 = 0.01, y1 = -0.01, y2 = 0.01; typedef struct { int xpos; unsigned char ycol[ny]; col_data; void gen_area(int pip[], int xstart, int xend) { float x, y; int ix, iy; col_data buf; for (ix = xstart; ix <= xend; ++ix) { buf.xpos = ix; x = x1 + ix*(x2-x1)/(nx-1); for (iy = 0; iy < NY; ++iy) { y = y1 + iy*(y2-y1)/(ny-1); buf.ycol[iy] = fraktal(x, y, 255.0, MAX); write(pip[1], &buf, sizeof(buf)); buf.xpos = -1; write(pip[1], &buf, sizeof(buf)); Forelesning 28.4.1998 Ark 13 av 24
void main(int argc, char *argv[]) { int pipe1[2], pipe2[2]; int done1=0, done2=0, n, npl=0, iy; time_t start_tid = time(null); col_data b; pipe(pipe1); pipe(pipe2); x147open(nx, NY); if (safefork()==0) { gen_area(pipe1,0,nx/2-1); if (safefork()==0) { gen_area(pipe2,nx/2,nx-1); exit(0); exit(0); while (!done1!done2) { n = read(pipe1[0], &b, sizeof(b)); if (b.xpos >= 0) { for (iy = 0; iy < NY; ++iy) x147plot(b.xpos, iy, b.ycol[iy]); else done1 = 1; n = read(pipe2[0], &b, sizeof(b)); if (b.xpos >= 0) { for (iy = 0; iy < NY; ++iy) x147plot(b.xpos, iy, b.ycol[iy]); else done2 = 1; if (++npl%100) x147sync(); printf("det hele tok %d sekunder.\n", time(null)-start_tid); x147pause(); Forelesning 28.4.1998 Ark 14 av 24
Nå genereres bildet av to parallelle prosesser: Kjøretid dain modsognir Sekvensiell 89 52 Rør, 2 produsenter 81 27 Forelesning 28.4.1998 Ark 15 av 24
Vurdering Dataoverføring skjer gjennom rørene, en col data hver gang. Synkronisering skjer ved at tegneprogrammet leser vekselvis fra de to rørene. + Enkel programmering. + Sikker synkronisering. + Dobbelt så rask eksekvering på en parallellmaskin. Forusetter at alle prosessene startes av samme urprosess (siden det brukes rør). Dårlig utnyttelse av prosessene hvis de har ulik hastighet. Vanskelig å skalere, dvs øke antallet prosesser. Forelesning 28.4.1998 Ark 16 av 24
Utvidete mekanismer I «klassisk UNIX» er filer og rør de eneste kommunikasjonsmulighetene. Dette er ofte for lite (som vi nettopp har sett). Utviklerne av System V-varianten av UNIX laget derfor en utvidelse kalt SVIPC («System V IPC»). Denne utvidelsen omfatter felles lager (omtales i dag) semaforer (omtales neste uke) meldinger og meldingskøer (omtales av Øystein) Felles lager Et felles lager kan brukes av flere prosesser fordi det ligger utenfor de vanlige prosessene. Det fjernes heller ikke automatisk. Forelesning 28.4.1998 Ark 17 av 24
Opprettelse av felles lager Følgende operasjoner må til for å opprette et felles lager med plass til 2 int-verdier: #include <sys/ipc.h> #include <sys/shm.h> int sh_mem_id, *sh_mem; sh_mem_id = shmget(ipc_private, 2*sizeof(int), 0700); sh_mem = shmat(sh_mem_id,0,0); Verdiene er nå tilgjengelige som sh mem[0] og sh mem[1]. Fjerning av et felles lager Et felles lager fjernes ikke automatisk når en prosess dør. Følgende kall må til: shmctl(sh_mem_id, IPC_RMID, 0); Forelesning 28.4.1998 Ark 18 av 24
Husk å fjerne felles lager Hvis et program feilet, kan man sjekke om det finnes et felles lager som ikke ble fjernet: % ipcs -m Shared Memory: T ID KEY MODE OWNER GROUP m 6901 0x00000000 --rw------ dag dag Da kan lageret fjernes med kommandoen % ipcrm -m 6901 Husk: Felles lager er en meget begrenset ressurs! Forelesning 28.4.1998 Ark 19 av 24
En parallell løsning med felles lager #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <string.h> #include <time.h> #include <unistd.h> #include "fraktal.h" #include "x147.h" extern pid_t safefork(void); #define NX 1000 #define NY 800 #define MAX 180 float x1 = -0.01, x2 = 0.01, y1 = -0.01, y2 = 0.01; typedef struct { int xpos; /* -1="finished", -2="no data" */ unsigned char ycol[ny]; col_data; int sh_mem_id; col_data *sh_mem; void gen_area(int xstart, int xend) { float x, y; int ix, iy; col_data buf; for (ix = xstart; ix <= xend; ++ix) { buf.xpos = ix; x = x1 + ix*(x2-x1)/(nx-1); for (iy = 0; iy < NY; ++iy) { y = y1 + iy*(y2-y1)/(ny-1); buf.ycol[iy] = fraktal(x, y, 255.0, MAX); *sh_mem = buf; buf.xpos = -1; *sh_mem = buf; Forelesning 28.4.1998 Ark 20 av 24
void main(int argc, char *argv[]) { int n_proc, nc, iy, npl = 0; time_t start_tid = time(null); n_proc= argc <= 1? 1 : atoi(argv[1]); sh_mem_id = shmget(ipc_private,sizeof(col_data),0700); sh_mem = shmat(sh_mem_id,0,0); sh_mem->xpos = -2; x147open(nx, NY); for (nc = 1; nc <= n_proc; ++nc) { if (safefork()==0) { gen_area(nx*(nc-1)/n_proc, NX*nc/n_proc-1); exit(0); while (sh_mem->xpos!= -1) { if (sh_mem->xpos >= 0) { for (iy = 0; iy < NY; ++iy) x147plot(sh_mem->xpos, iy, sh_mem->ycol[iy]); sh_mem->xpos = -2; if (++npl%100 == 0) x147sync(); printf("det hele tok %d sekunder.\n", time(null)-start_tid); x147pause(); shmctl(sh_mem_id, IPC_RMID, 0); Forelesning 28.4.1998 Ark 21 av 24
IN 147 Program og maskinvare Resultatet blir imidlertid ikke pent, og verre jo flere prosesser vi har: 1 2 4 8 Dette skyldes manglende synkronisering mellom prosessene. Forelesning 28.4.1998 Ark 22 av 24
Kjøretid dain modsognir Sekvensiell 89 52 Rør, 2 produsenter 81 27 Felles lager, 1 produsent 130 44 Felles lager, 2 produsent 90 24 Felles lager, 3 produsent 73 19 Felles lager, 4 produsent 66 16 Felles lager, 8 produsent 52 11 Disse tidene er vanskelige å tolke når det gjelder felles lager: Jo flere produsenter, desto mer spares pga mindre aktiv venting. Jo flere prosesser, desto mindre tegnes pga dårlig synkronisering. Forelesning 28.4.1998 Ark 23 av 24
Vurdering Dataoverføring over felles lager fungerer fint. Synkronisering er ved hjelp av en variabel (sh mem->xpos) og det er for primitivt. Data går tapt! + Rask dataoverføring. Produsentene av data er ikke synkronisert med konsumenten! Konsumenten benytter aktiv venting! Mye tid kastes bort. Kan gi gale data: x147 error: Illegal coordinates (-1,2). Felles lager er fint til dataoverføringen, men vi trenger en synkroniseringsmekanisme. Den kommer neste uke! Forelesning 28.4.1998 Ark 24 av 24