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 heap
p
denota un oggetto allocato in stack
, argv
denota un oggetto allocato in stack
, **argv
denota un oggetto allocato in heap
Una 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?
int
Che 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 | . | XX
XX | XXX | . | XX
. | XXX | . | XX | XX
XXX | . | XX | . | XX
XX | . | XXX | . | XX
.. | . | XXX | . | XX
Una 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 $80
libc
) è indispensabile per ottenere un eseguibile Linux usando gcc
main
è 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
, 0xE12B
0xAFD4
, 0xD071
, 0xD011
0x81D6
, 0xAF02
, 0xC000
0xA0D1
, 0xFF00
, 0x9117
0x10CE
, 0x1E94
, 0xA7D9
Dato 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 & 0xFFFF
p(x) = x >> 13
, o(x) = x & 0x1FFF
p(x) = x / 13
, o(x) = x & 0xFFF
p(x) = x >> 2/13
, o(x) = x & 0x8FF
p(x) = x >> 13
, o(x) = x & 0x18FF
Assumendo che il registro eax
contenga inizialmente il valore 0xDEADBEEF
, quale sarebbe il suo contenuto dopo l’esecuzione dell’istruzione movsbw %al, %ax
?
0xDEADFFEF
0xFFFFFFEF
0x0000FFEF
0xDEAD00EF
0xFFFF00EF
Si 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, %ecx
Una sola delle seguenti affermazioni è falsa. Quale?
execve
o execvpe
env
permette di elencare le variabili di ambiente correntemente definite nella shellsetenv
PATH
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?
0750
0530
0642
0641
0640
Una 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
, 0xC000
p(x) = x >> 13
, o(x) = x & 0x1FFF
0xDEADFFEF
|
fork()--------------------+
| |
fork()--------+ fork()--------+
| | | |
fork()--+ fork()--+ fork()--+ fork()--+
| | | | | | | |
1 2 3 4 5 6 7 8
N
nodi genera nel caso peggiore N
cache hitsetenv
0640