Løsningsforslag til eksamen i IN 147(A) Dag Langmyhr (oppgave 1, 4 og 6) ØysteinGranLarsen (oppgave 2, 3 og 5) 31. mai 1999 1 Oversettelse Funksjonen strxxx går gjennom en tekst og finner adresessen til siste forekomst av et tegn. Hvis tegnet ikke finnes i teksten, returneres 0. Dette er faktisk akkurat det standardfunksjonen strrchr gjør. 1 char *strxxx(char *s, char c) 2 { 3 char *res = 0; 4 5 while (*s) { 6 if (*s == c) res = s; 7 ++s; 8 } 9 return res; 10 } 2 Implementasjon av D flip-flop Det er de fire midterste linjene i sannhetsverditabellen som er interessante for D-flip-flop-funksjonaliteten: J K Q(t) Q(t + 1) 0 1 0 0 0 1 1 0 1 0 0 1 1 0 1 1 Tabell 1: Utdrag fra sannhetsverditabellen for en JK-flip-flop Det man ser er at hvis D kobles til J og D kobles til K oppnår man den ønskede funksjonen. Ligningene blir altså: J = D, K = D I skjemaet trengs bare en ekstra inverterer: 3 Tilpasning av kode Som nevnt i oppgaven er den enkleste måten å få koden i oppgaven til å kjøre på en MIPS-prosessor, å legge inn nop-er. De skal settes etter «load» og hopp: 1
Figur 1: En D-flip-flop implementert med en JK-flip-flop 1 entry: 2 move v0, zero 3 move t0, zero 4 loop: 5 lw t1, 0(a0) 6 nop 7 add v0, v0, t1 8 addi a0, a0, 4 9 addi t0, t0, 1 10 bne t0, a1, loop 11 nop 12 add v0, v0, v0 13 andi t0, v0, 0x2 14 bne t0, zero, adjust 15 nop 16 srl v0, v0, 1 17 b exit 18 nop 19 adjust: 20 srl v0, v0, 2 21 exit: 22 sub v0, v0, a2 23 jr ra 24 nop I neste deloppgave skal man vise en bedre løsning ved å flytte litt på noen instruksjoner: 1 entry: 2 move v0, zero 3 move t0, zero 4 loop: 5 lw t1, 0(a0) 6 addi a0, a0, 4 7 addi t0, t0, 1 8 bne t0, a1, loop 9 add v0, v0, t1 10 add v0, v0, v0 11 andi t0, v0, 0x2 12 bne t0, zero, adjust 13 nop 14 b exit 15 srl v0, v0, 1 16 adjust: 17 srl v0, v0, 2 18 exit: 19 jr ra 20 sub v0, v0, a2 De som gjør anda mer avanserte ting (som dette), fortjener en ekstra bonus: 1 entry: 2 move v0, zero 2
3 move t0, zero 4 loop: 5 lw t1, 0(a0) 6 addi a0, a0, 4 7 addi t0, t0, 1 8 bne t0, a1, loop 9 add v0, v0, t1 10 add v0, v0, v0 11 andi t0, v0, 0x2 12 beq t0, zero, exit # endret test 13 srl v0, v0, 1 14 srl v0, v0, 1 # endret shift amount 15 exit: 16 jr ra 17 sub v0, v0, a2 4 Forbedret tekstbehandling i C Funksjonen textlen kan skrives slik: 5 int textlen(text *t) 6 { 7 text *tp = t; 8 int n = 0; 9 10 while (tp!= NULL) { 11 n += tp->nchars; tp = tp->next; 12 } 13 return n; 14 } Enda enklere blir den om den skrives rekursivt, men den sekvensielle løsningen er en like god besvarelse. 16 int textlen2(text *t) 17 { 18 if (t == NULL) return 0; 19 return t->nchars + textlen2(t->next); 20 } Funksjonen textadd må først finne siste element i listen. Så må den skjøte på et element om det siste er fullt. Til sist kan tegnet settes inn. 22 void textadd(text *t, char c) 23 { 24 while (t->next) t = t->next; 25 26 if (t->nchars == TEXTDATASIZE) { 27 t->next = (text*)malloc(sizeof(text)); 28 t = t->next; 29 t->next = NULL; t->nchars = 0; 30 } 31 t->data[t->nchars++] = c; 32 } Funksjonen textget må først lete seg frem til riktig element; så kan tegnet hentes frem. Hvis man kommer utenfor listen, er posisjonen feil, og det er på tide å skrive en feilmelding. (Jeg valgte å skrive feilmeldingen på stderr, men det er intet krav.) 3
34 char textget(text *t, unsigned int pos) 35 { 36 int px = pos; 37 38 while (t!= NULL) { 39 if (px < t->nchars) return t->data[px]; 40 px -= t->nchars; t = t->next; 41 } 42 fprintf(stderr, "\ntextget: Illegal position: %d\n", pos); 43 exit(8); 44 } Funksjonen textdel må også først lete seg frem til riktig element; så må tegnet fjernes ved at alle de etterfølgende elementene flyttes én posisjon, og nchars senkes med 1. Ved ulovlig posisjon skrives en feilmelding på samme måte som for strget. 46 void textdel(text *t, unsigned int pos) 47 { 48 int px = pos, i; 49 50 while (t!= NULL) { 51 if (px < t->nchars) { 52 for (i = px+1; i < t->nchars; ++i) 53 t->data[i-1] = t->data[i]; 54 --t->nchars; 55 return; 56 } 57 px -= t->nchars; t = t->next; 58 } 59 fprintf(stderr, "\ntextdel: Illegal position: %d\n", pos); 60 exit(9); 61 } 5 TLB med plass til 2 oversettinger (ikke for de som tar IN147A) Oppgaven sier ingen ting om hvor mange instruksjoner det er i de to prosedyrene. Hvis det er nødvendig å konkretisere dette, er følgende en enkel variant: 1 P1 gjør load av data 2 dataaksess 3 P1 behandler data 4 P1 hopper til P2 5 P2 gjør load av data 6 dataaksess 7 P2 behandler data 8 P2 returnerer til P1 Dette gjentas 10 ganger. Hit/miss opptrer når prosesoren gjør aksesser som trenger oversetting fra virtuell til fysisk adresse. (Noen vil kanskje komme med betraktninger om forskjellige ordninger av TLB og cache.) Hovedpoenget med deloppgave 2 er at TLB-en ikke kan ta vare på lokalitet i tid. Med referanse til varianten over: 1 P1 gjør load av data # TLB miss element 1 2 dataaksess # TLB miss element 2 3 P1 behandler data # TLB hit element 1 4
4 P1 hopper til P2 # TLB hit element 1 5 P2 gjør load av data # TLB miss, ta ibruk element 2 6 dataaksess # TLB miss, ta ibruk element 1 7 P2 behandler data # TLB hit element 2 8 P2 returnerer til P1 # TLB hit element 2 Forholdet hit/miss blir 1 til 1. Med plass til både proc1 og proc2 på samme side («page») i hukommelsen forandrer dette seg dramatisk: 1 første gjennomløp: 2 P1 gjør load av data # TLB miss element 1 3 dataaksess # TLB miss element 2 4 P1 behandler data # TLB hit element 1 5 P1 hopper til P2 # TLB hit element 1 6 P2 gjør load av data # TLB hit element 1 7 dataaksess # TLB hit element 2 8 P2 behandler data # TLB hit element 1 9 P2 returnerer til P1 # TLB hit element 1 10 de neste 9 gjennomløp: 11 P1 gjør load av data # TLB hit element 1 12 dataaksess # TLB hit element 2 13 P1 behandler data # TLB hit element 1 14 P1 hopper til P2 # TLB hit element 1 15 P2 gjør load av data # TLB hit element 1 16 dataaksess # TLB hit element 2 17 P2 behandler data # TLB hit element 1 18 P2 returnerer til P1 # TLB hit element 1 Altså blir forholdet hit/miss 78 til 2. 6 Parallell sortering (ikke IN147A) I denne oppgaven er det to dataområder som må beskyttes uavhengig av hverandre: work må beskyttes. Bruken av work blir en variant av produsent/konsument-problemet hvor P-ene vekselvis er produsenter og konsumenter. Som vanlig for P/K-problemet trengs tre semaforer: work-full, work-empty og work-mutex. counter må også beskyttes; se nedenfor. Her trengs det bare én semafor: cnt-mutex. Vektoren a trengs altså ikke beskyttes; se nedenfor. (Det bør imidlertid ikke trekkes noe om man har vært forsiktig og allikevel beskyttet den.) Mitt forslag til en skisse for P ser slik ut: 1 void P(void) 2 { 3 while (1) { 4 down(cnt-mutex); n = *counter; up(cnt-mutex); 5 if (n == N) exit(0); 6 7 down(work-full); down(work-mutex); 8 (i1,i2) = hent oppdrag fra `work'; 9 up(work-mutex); up(work-empty); 10 11 if (i1 == i2) { 12 down(cnt-mutex); (*counter)++; up(cnt-mutex); 13 } else { 5
14 splitt a[i1]...a[i2] i a[i1]...a[ix] og a[ix+1]...a[i2] 15 16 down(work-empty); down(work-mutex); 17 legg nytt oppdrag (i1,ix) i `work'; 18 up(work-mutex); up(work-full); 19 20 down(work-empty); down(work-mutex); 21 legg nytt oppdrag (ix+1,i2) i `work'; 22 up(work-mutex); up(work-full); 23 } 24 } 25 } (En kvikk student så at dette kan føre til vranglås om ikke work er stor nok. Vedkommende bør få bonus, men de øvrige bør ikke trekkes for ikke å ha oppdaget dette.) Når arbeidet er ferdig (dvs n=n), vil ikke alle prosesser oppdage det og avslutte; noen vil forbli blokkert av work_full.siden jobben er gjort,er ikke dette noe å bry seg om. Når det gjelder a, trenger den faktisk ingen beskyttelse! Hvert vektorelement vil bare finnes i ett oppdrag av gangen, så derfor vil kun én prosess aksessere det. Derimot trenger counter beskyttelse fordi ++ ikke er noen atomisk operasjon. En C-kompilator vil typisk lage følgende MIPS-kode for setningen «(*counter)++»: 1 la $t0,counter # Hent adressen til counter. 2 lw $t1,0($t0) # Hent counter. 3 lw $t2,0($t1) # Hent *counter. 4 addi $t2,$t2,1 # Øk med 1 og 5 sw $t2,0($t1) # skriv den nye verdien tilbake. Vi kan da få et prosessbytte rett før eller rett etter addi, og da kan oppdateringen av *counter gå galt. (Det finnes andre prosessorer for eksempel Motorola 680x0-serien hvor øking med 1 kan gjøres med én instruksjon, og da trenger man ikke beskyttelse. Forutsetningen er imidlertid at kompilatoren bruker denne instruksjonen; dette kan man imidlertid aldri være sikker på, så beskyttelse bør derfor alltid være med.) 6