Codice e soluzioni esercizi di programmazione
Tradurre in IA32 la seguente funzione C selection_sort definita in E1-selection-sort/e1.c che, dato un array di short v e la sua lunghezza n, ordina l’array usando l’algoritmo di ordinamento per selezione. La funzione richiama un’altra funzione swap già fornita in E1-selection-sort/e1_main.c.
e1.c
void selection_sort(short *v, int n) {
int i,j, min;
for (i=0; i<n-1; ++i) {
min = i;
for (j=i+1; j<n; ++j)
if (v[j]<v[min]) min = j;
swap(v+i, v+min);
}
}
Scrivere la soluzione nel file E1-selection-sort/e1.s. Usare il file E1-selection-sort/e1_eq.c per sviluppare la versione C equivalente e E1-selection-sort/e1_main.c come programma di prova.
Scrivere nel file E2-multi-proc/e2.c una funzione proc_gettime che prende come parametri il pathname di un eseguibile file, un puntatore a una funzione callback get_arg fornita dall’utente, un puntatore a dei dati data da fornire alla funzione callback, e un numero di ripetizioni n. La funzione proc_gettime esegue file per n volte, misurando il tempo in secondi (double) richiesto dall’esecuzione e restituisce la media dei tempi richiesti sulle n esecuzioni. Ad ogni esecuzione, la funzione chiama la callback per ottenere gli argomenti argv da passare al processo in modo da rendere possibile comportamenti diversi del processo ad ogni ripetizione. La callback deve ricevere il puntatore data preso come parametro da proc_gettime e il numero della ripetizione i con i compreso tra 0 e n-1.
double proc_gettime(const char* file, char** (*get_argv)(int i, void* data), void *data, int n);
Suggerimento: usare clock_gettime per misurare il tempo.
Compilare il programma con make ed eseguirlo con ./e2.
Si vuole scrivere una funzione che misura il tempo medio in millisecondi per accesso a disco in lettura e scrittura in modo casuale e in modo sequenziale. La funzione, che deve essere scritta in E3-files/e3.c, ha il seguente prototipo:
e3.c
void file_access_time(const char* file, unsigned trials, time_rec_t *t)
dove file è il file da accedere (sovrascrivendolo), trials è il numero di accessi casuali da effettuare sia per le letture che per le scritture, e t è una struttura definita come segue in E3-files/e3.h:
e3.h
typedef struct {
unsigned file_size;
double seq_read_t;
double seq_write_t;
double rnd_read_t;
double rnd_write_t;
} time_rec_t;
I campi della struttura vanno interpretati come segue:
file_size è la dimensione in byte del fileseq_read_t è il tempo medio in millisecondi per lettura sequenziale di 4 byteseq_write_t è il tempo medio in millisecondi per scrittura sequenziale di 4 byternd_read_t è il tempo medio in millisecondi per lettura casuale di 4 byternd_write_t è il tempo medio in millisecondi per scrittura casuale di 4 byteTutti i campi della struttura devono essere riempiti dalla funzione file_access_time. Le letture e le scritture sequenziali devono scorrere l’intero file. I valori che vengono scritti nel file non sono rilevanti e possono essere arbitrari. L’eseguibile può essere compilato con make ed eseguito con ./e3 file trials dove file e trials sono definiti come sopra. E’ possibile generare un file di prova di file.dat da 390 MB con make file. Per ripulire la directory alla fine dell’esperimento usare make clean.
Una sola delle seguenti affermazioni sui processi è falsa. Quale?
Si consideri la seguente sequenza di indirizzi acceduti da un processo: 980, 408, 984, 612, 410, 412, 500. Quali blocchi verrebbero a trovarsi alla fine della sequenza in una piccola cache completamente associativa con 3 linee da 32 byte ciascuna e politica di rimpiazzo LRU? (l’ordine degli indici di blocco non conta)
Si consideri la seguente sequenza di istruzioni:
movl $12, %eax
addl %ecx, %edx
movl %eax, %ebx
Quanti cicli di clock vengono richiesti da una semplice pipeline a 5 stadi (Fetch, Decode, Execute, Memory, Write-Back) per completare tutte le istruzioni assumendo che gli hazard vengano risolti con stalli?
Si consideri un sistema di memoria virtuale con pagine da 2 KB e spazio logico a 24 bit. Quanti byte occuperebbe la tabella delle pagine assumendo di avere indici dei frame a 32 bit?
Si consideri la seguente struct C:
struct A {
char a;
double b;
char c;
double d;
char e;
};
Come riordineresti la struttura per minimizzarne la dimensione assumendo che il compilatore inserisca padding per garantire l’allineamento dei campi e quale dimensione otterresti?
char, char, char, double, double => 19 bytechar, double, double, char, char => 32 bytechar, char, char, double, double => 24 bytechar, char, double, char, double => 32 bytechar, char, double, char, double => 24 byteSi consideri il seguente frammento di programma:
int x;
int main(int argc, char** argv) {
int* p = malloc(10*sizeof(int));
char* s1 = "hello";
char s2[] = "hello";
...
return 0;
}
Il un moderno sistema Linux, una sola fra le seguenti affermazioni è corretta. Quale?
x denota un oggetto allocato in bss, p denota un oggetto allocato in heap, s1 denota un oggetto allocato in rodata e *s2 denota un oggetto allocato in data.x denota un oggetto allocato in bss, *p denota un oggetto allocato in heap, *s1 denota un oggetto allocato in rodata e *s2 denota un oggetto allocato in stack.x denota un oggetto allocato in data, p denota un oggetto allocato in stack, s1 denota un oggetto allocato in stack e *s2 denota un oggetto allocato in heap.argc denota un oggetto allocato in stack, *argv denota un oggetto allocato in heapp denota un oggetto allocato in stack, argv denota un oggetto allocato in stack, **argv denota un oggetto allocato in heapUna sola fra le seguenti affermazioni è falsa. Quale?
Si consideri il seguente frammento di programma C:
int x = 0xDEADBEEF & ...
Cosa bisogna inserire al posto di ... affinché x prenda il valore 0xD0A0?
(252645135 << 16)(4042322160 >> 16)(3735928559 >> 8)(256736178 | 0xFF)(3462378762 >> 16)Si consideri questo frammento di programma:
int f(int x) {
return 2*x;
}
int g() {
return f(10) + 5;
}
e la sua versione ottimizzata:
int f(int x) {
return 2*x;
}
int g() {
return 25;
}
Quali ottimizzazioni sono state applicate? Una sola delle seguenti risposta è corretta.
Una sola delle seguenti affermazioni è falsa, quale?
intChe speedup otterremmo per un programma dimezzando il tempo di esecuzione di una sua porzione che richiede il 10% del tempo complessivo?
Una sola fra le seguenti affermazioni è falsa, quale?
NULL se non è necessario inviare alcun datoSupponiamo di essere in grado di dimezzare il tempo di esecuzione di una porzione di un programma mediante delle ottimizzazioni. Che percentuale del tempo complessivo deve prendere quella porzione per ottenere uno speedup di 1.5x?
Si consideri una cache associativa a 2 vie con 4 linee da 8 byte ciascuna e politica di rimpiazzo LRU. Potendo scegliere durante un cold miss, selezionare sempre la linea con indice più basso. Data la seguente sequenza di accessi a indirizzi di memoria, qual è il contenuto delle linee di cache alla fine della sequenza: 96, 98, 124, 86, 104, 120, 68, 138?
Si consideri un semplice allocatore di memoria malloc/free dove, potendo scegliere, la malloc riusa il blocco libero con l’indirizzo più basso. Assumere per semplicità che non vi sia alcuna header e che i blocchi allocati vengano arrotondati a una dimensione multiplo di 4 byte. Qual è il contenuto dell’heap alla fine della seguente sequenza di operazioni (dove X denota 4 byte allocati e . denota 4 byte liberi)?
p1=malloc(2)
p2=malloc(10)
p3=malloc(4)
free(p1)
p4=malloc(4)
p5=malloc(8)
free(p4)
free(p3)
p6=malloc(6)
X | XXX | . | XXXX | XXX | . | XX. | XXX | . | XX | XXXXX | . | XX | . | XXXX | . | XXX | . | XX.. | . | XXX | . | XXUna sola fra le seguenti affermazioni è vera. Quale?
Una sola fra le seguenti affermazioni è vera. Quale?
strlen finisce per invocare una system callprintf finisce per invocare una system callwrite richiama una porzione di codice che gira in spazio utente che a sua volta invoca l’istruzione int $80libc) è indispensabile per ottenere un eseguibile Linux usando gccmain è la prima ad essere invocata all’avvio di un processo.Una sola fra le seguenti affermazioni sul context switch è falsa. Quale?
Approssimativamente, ignorando il parallelismo introdotto dalla pipeline della CPU, quante istruzioni macchina che coinvolgono solo registri potrebbero essere eseguite nel tempo richiesto da un’istruzione che accede a una DRAM?
Una sola fra le seguenti affermazione sulle gerarchie di memoria è vera. Quale? Per livelli più alti si intende quelli più vicini alla CPU.
Si considerino le seguenti grandezze, nell’ordine: 1TB, 2KB, 8MB, 4GB, 16PB. A quali potenze di due corrispondono?
Si consideri un sistema di memoria virtuale con uno spazio di indirizzi a 16 bit, pagine da 4 KB, e la seguente tabella delle pagine: {0x1, 0x6, 0xB, 0x3, 0xA, 0x7, 0xC, 0x5, 0x9, 0x2, 0xD, 0xF, 0x0, 0x4, 0xE, 0x8 }. A quali indirizzi fisici corrispondono i seguenti indirizzi logici: 0xF1D6, 0x4F02, 0x6000?
0x90C1, 0xAF02, 0xE12B0xAFD4, 0xD071, 0xD0110x81D6, 0xAF02, 0xC0000xA0D1, 0xFF00, 0x91170x10CE, 0x1E94, 0xA7D9Dato un’indirizzo x a 32 bit, come definiresti il numero di pagina p(x) e l’offset o(x) assumendo pagine di 8 KB?
p(x) = x / 2^13, o(x) = x & 0xFFFFp(x) = x >> 13, o(x) = x & 0x1FFFp(x) = x / 13, o(x) = x & 0xFFFp(x) = x >> 2/13, o(x) = x & 0x8FFp(x) = x >> 13, o(x) = x & 0x18FFAssumendo che il registro eax contenga inizialmente il valore 0xDEADBEEF, quale sarebbe il suo contenuto dopo l’esecuzione dell’istruzione movsbw %al, %ax?
0xDEADFFEF0xFFFFFFEF0x0000FFEF0xDEAD00EF0xFFFF00EFSi consideri il seguente frammento di programma che si origina da un singolo processo:
fork();
fork();
fork();
Quanti processi sono attivi alla fine del frammento?
Una sola delle seguenti affermazioni è falsa. Quale?
L, scorrere sequenzialmente un array di N short genera sizeof(short)*N/L cache missN nodi genera nel caso peggiore N cache hitL, scorrere sequenzialmente un array di N short genera N-sizeof(short)*N/L cache hitUna sola delle seguenti affermazioni è vera. Quale?
movl %eax, %ecxUna sola delle seguenti affermazioni è falsa. Quale?
execve o execvpeenv permette di elencare le variabili di ambiente correntemente definite nella shellsetenvPATH contiene i percorsi delle directory dove vengono cercati i programmi eseguibili i cui nomi sono digitati sulla shell o invocati con execvp.main.Che permessi dovrebbe avere un file per essere accessibile in lettura e scrittura dall’utente proprietario, in sola lettura dal gruppo proprietario, e nessun permesso per tutti gli altri utenti?
07500530064206410640Una sola delle seguenti affermazione è vera. Quale?
Di quanto bisognerebbe velocizzare una funzione f che consuma il 10% del tempo di esecuzione di un programma P in modo da ottenere uno speedup complessivo per P pari a 2x?
Le risposte sono state commentate e discusse a lezione.
Indirizzi: 980, 408, 984, 612, 410, 412, 500
Blocchi (indirizzo/12): 30, 12, 30, 19, 12, 12, 15
Cache: (tra parentesi il tempo dall'ultimo accesso a un blocco in cache)
0 1 2
30 -> | 30(0) | | | (cold miss)
12 -> | 30(1) | 12(0) | | (cold miss)
30 -> | 30(0) | 12(1) | | (hit)
19 -> ! 30(1) ! 12(2) ! 19(0) ! (cold miss)
12 -> ! 30(2) ! 12(0) ! 19(1) ! (hit)
12 -> ! 30(3) ! 12(0) ! 19(2) ! (hit)
15 -> ! 15(0) ! 12(1) ! 19(3) ! (capacity miss, rimpiazzo LRU)
movl $12, %eax F D E M W
addl %ecx, %edx F D E M W
stallo * * * * *
stallo * * * * *
movl %eax, %ebx F D E M W
|<- 9 cicli ->|
2 KB = 2^11 byte per pagina
24-11=13 => 2^13 pagine
32 bit per frame = 4 byte => 2^2*2^13 = 2^15 byte = 32768 byte
char, char, char, double, double => 24 bytex denota un oggetto allocato in bss, *p denota un oggetto allocato in heap, *s1 denota un oggetto allocato in rodata e *s2 denota un oggetto allocato in stack.NULL se non è necessario inviare alcun datoPartiamo dalla legge di Amdahl: speedup = 1/(a/k+1-a). Vogliamo ottenere il valore di a tale che speedup = 1.5:
a/k-a = 1/speedup-1
a*(1/k-1) = 1/speedup-1
a=(1/speedup-1)/(1/k-1)
a=(1/1.5-1)/(1/2-1)=0.66667
Si noti che una in una cache associativa a 2 vie con 4 linee i blocchi di indice pari si mappano sulle linee 0, 1, mentre i blocchi con indice dispari si mappano sulle linee 2, 3.
Indirizzi: 96, 98, 124, 86, 104, 120, 68, 138
Blocchi (indirizzo/8): 12, 12, 15, 10, 13, 15, 08, 17
Cache: (tra parentesi il tempo dall'ultimo accesso a un blocco in cache)
0 1 2 3
12 -> | 12(0) | | | | (cold miss)
12 -> | 12(0) | | | | (hit)
15 -> | 12(1) | | 15(0) | | (cold miss)
10 -> | 12(2) | 10(0) | 15(1) | | (cold miss)
13 -> | 12(3) | 10(1) | 15(2) | 13(0) | (cold miss)
15 -> | 12(4) | 10(2) | 15(0) | 13(1) | (hit)
08 -> | 08(0) | 10(3) | 15(1) | 13(2) | (capacity miss)
17 -> | 08(1) | 10(4) | 15(3) | 17(0) | (capacity miss)
____________________
p1=malloc(2)
X
p1
____________________
p2=malloc(10)
X | XXX
p1 p2
____________________
p3=malloc(4)
X | XXX | X
p1 p2 p3
____________________
free(p1)
. | XXX | X
p2 p3
____________________
p4=malloc(4)
X | XXX | X
p4 p2 p3
____________________
p5=malloc(8)
X | XXX | X | XX
p4 p2 p3 p5
____________________
free(p4)
. | XXX | X | XX
p2 p3 p5
____________________
free(p3)
. | XXX | . | XX
p2 p3 p5
____________________
p6=malloc(6)
. | XXX | . | XX | XX
p2 p3 p5 p6
printf finisce per invocare una system call0x81D6, 0xAF02, 0xC000p(x) = x >> 13, o(x) = x & 0x1FFF0xDEADFFEF |
fork()--------------------+
| |
fork()--------+ fork()--------+
| | | |
fork()--+ fork()--+ fork()--+ fork()--+
| | | | | | | |
1 2 3 4 5 6 7 8
N nodi genera nel caso peggiore N cache hitsetenv0640