Esercizio 1 (20%) Si assuma di avere una macchina con 10 registri
Transcript
Esercizio 1 (20%) Si assuma di avere una macchina con 10 registri
Esercizio 1 (20%) Si assuma di avere una macchina con 10 registri ufficiali (da R0 a R9) e sufficienti registri segreti in grado di avviare 2 istruzioni per ciclo di clock. Tale macchina richiede 2 cicli di clock per completare somme e differenze e 4 cicli di clock per completare prodotti e divisioni. Si consideri la seguente sequenza di istruzioni (dove X è la cifra meno significativa non nulla del proprio numero di matricola): (1) RX = R1 - R2 (2) R3 = RX / R5 (3) R4 = R5 + R2 (4) R4 = R7 * R7 Indicare le istruzioni avviate e ritirate in ogni ciclo di clock (dall’inizio al termine dell’esecuzione del programma) secondo le seguenti strategie di esecuzione (l’uso dei registri segreti è consentito solo nei casi (B) e (C)): (A) avvio e ritiro in ordine; (B) avvio fuori ordine e ritiro in ordine; (C) avvio e ritiro fuori ordine. Fornire per ciascuno dei casi una breve spiegazione di ciò che succede in ogni ciclo con riferimento agli eventuali vincoli tra le istruzioni. Risposta Sia X=2 (per valori di X diversi la tabella risulta diversa) Clk 1 2 Decodifica (1) R2 = R1 - R2 (2) R3 = R2 / R5 (3) R4 = R5 + R2 (4) R4 = R7 * R7 3 4 Avv (1) - (A) In-In Rit Commenti (2) rinviata perché in RAW con (1) Avv (1) (4) (1) (2) (3) - 5 (B) Out-In Rit (2) rinviata perché in RAW con (1) (3) rinviata perché in RAW con (1), (4) avviata con uso di registro segreto S4 (1) (2) (3) (4) rinviata perché in WAW con (3) (3) e (4) ritirabili ma attendono il completamento di (2) (2) (3) 9 10 11 12 13 (C) Out-Out Rit (2) rinviata perché in RAW con (1) (3) rinviata perché in RAW con (1), (4) avviata con uso di registro segreto S4 (1) (2) (3) 6 7 8 Avv (1) (4) (2) (3) (4) (3) (4) (2) (4) (4) Esercizio 2 (20%) Si vuole progettare una cache unificata a mappatura diretta per una CPU con indirizzi a 32 bit e linee di cache di 32 byte. Supponendo di avere a disposizione una memoria di 4MB e 32 KB di spazio disponibile massimo sul chip della CPU determinare: A) la struttura di una possibile slot di cache che soddisfi questi requisiti e la relativa struttura dell’indirizzo di memoria; B) le dimensioni totali della cache progettata; C) se e come sia possibile modificare la struttura determinata al punto A per ridurre le collisioni sulle slot di cache; D) cosa può succedere se la CPU vuole leggere il byte 325 della memoria principale. Risposta A) Procedendo per tentativi si trova che una slot in grado si soddisfare i requisiti è la seguente: 18 bit TAG 1bit V 32 byte CACHE LINE a cui corrisponde un indirizzo con la struttura seguente: 18 bit TAG 9 bit LINE 5 bit BYTE Possibili alternative valide (che però sfruttavano meno lo spazio disponibile) hanno un campo TAG più grande e un campo LINE più piccolo. B) Una slot ha dimensione (18+1+32×8)= 275 bit. La cache ha 29 slots e quindi: dimensione della cache = 29×275 = 512×275 = 140.800 bit = 17.600 bytes ovvero circa 17KB < 32KB disponibili. Si osservi che scegliendo una slot con TAG di 17 bit si ottiene un campo LINE di 10 bit da cui: dimensione cache = 210×274 = 1024×274 = 280.576 bit = 35.072 bytes ovvero circa 34KB > 32KB disponibili che NON soddisfa i requisiti. La stessa cosa vale per valori di TAG inferiori. C) In generale per ridurre le collisioni o si aumenta il numero di slot (e conseguentemente si riduce il campo TAG) o si aumentano le “vie” (2-way, 4-way ecc.). Con il vincolo di 32KB di spazio disponibile però la prima soluzione non era praticabile (vedi punto B) e la seconda nemmeno (perché richiede sostanzialmente, a parità di numero di slot, almeno il raddoppio delle dimensioni della cache). D) Il byte 325 ha indirizzo binario (su 32 bit): 000000000000000000|000001010|00101. Si accede quindi alla undicesima slot (indirizzo 10) e, se il bit di validità è a 1, si verifica se il TAG della slot sia 000000000000000000. Se è così si ha un cache hit, si preleva la cache line dalla slot e si accede al sesto byte (indirizzo 5) che è quello cercato. Altrimenti si ha un cache miss, si preleva l’undicesima cache line dalla memoria, la si mette in cache nell’undicesima slot e si pone il TAG della slot a 000000000000000000 e il bit di validità a 1. Infine, si preleva il sesto byte (indirizzo 5) della cache line che è quello cercato. Tutto questo è realizzato dall’hardware. Esercizio 3 (20%) Sia dato un file di testo Regioni.txt le cui righe sono costituite da due stringhe e un intero che rappresentano rispettivamente una regione, una città (nella regione) e un numero di abitanti (della città). Scrivere in ANSI C un programma in grado di: (1) creare a partire dal file dato una lista costituita da record con un campo regione (stringa) e un campo abitanti (intero che specifica il numero totale di abitanti di quella regione), (2) salvare su di un file Regioni.dat gli elementi di questa lista, (3) dati in input una regione (stringa) e un numero (intero), sommare nel file Regioni.dat il numero passato da input al numero di abitanti della regione passata da input. Una possibile soluzione #include <stdio.h> #include <stdlib.h> #include <string.h> /* definiamo i tipi di dato che servono per implementare la lista */ typedef struct { char nome[20]; // il nome della regione int n; // il numero di abitanti } zona; typedef struct elem* plist; typedef struct elem { zona info; plist next; } elist; /* definiamo ora un metodo insert che data la lista e un elemento zona lo inserisce nella lista se la regione non è presente altrimenti aggiorna il numero di abitanti della regione passata da input; basta scorrere tutta lista fino a che non trovo una regione il cui nome è uguale a quella dell’input passato; se arrivo alla fine della lista, dovrò aggiungere il nuovo elemento in fondo */ void insert (plist p, zona z) { plist p1 = p; int test = 0; while ((p1->next != NULL) && (!test)) { if (strcmp((p1->info).nome,z.nome)) { test = 1; (p1->info).n += z.n; } else p1 = p1->next; } if (!test) { if (strcmp((p1->info).nome,z.nome)) (p1->info).n += z.n; else { plist p2 = (plist)malloc(sizeof(elist)); p2->info = z; p2->next = NULL; p1->next = p2; } } } /* ora implementiamo il metodo crealista() che accede al file Regioni.txt in modo sequenziale e costruisce la lista iterando il metodo insert */ plist crealista() { FILE* f = fopen("Regioni.txt", "r"); plist p = NULL; zona z; char citta[20]; while (!feof) { fprintf(f,"%s %s %d \n",z.nome,citta,&z.n); insert(p,z); } return p; } /* ora implementiamo il metodo che immagazzina nel file Regioni.dat gli elementi della lista così creata utilizzando un accesso casuale */ void creafile(plist p) { FILE* f = fopen("Regioni.dat", "wb"); plist p1=p; while (p!=NULL) { fwrite(&p1->info,sizeof(zona),1,f); p1=p1->next; } } /* ora implementiamo il metodo che data una regione abitanti di quella regione presente in Regioni.dat; sarebbe stato opportuno aggiungere in fondo al file metodo) ma la dicitura del testo non lo specificava versione in cui non aggiunge niente */ e un numero di abitanti aggiorna il numero di nel caso in cui non esista questa regione il nuovo elemento (e così sarà implementato il esplicitamente e quindi è corretta anche la void aggiorna(char nome[20], int n) { long pos; FILE* f = fopen("Regioni.dat", "rb+"); int test = 0; zona z; while ((!test) && (!feof)) { pos = ftell(f); fread(&z,sizeof(zona),1,f); if (strcmp(z.nome,nome)) { fseek(f,pos,SEEK_SET); z.n+=n; fwrite(&z,sizeof(zona),1,f); test = 1; } } if (!test) { fclose(f); f = fopen("Regioni.dat","ab"); strcpy(z.nome,nome); z.n=n; fwrite(&z,sizeof(zona),1,f); } } Esercizio 4 (40%) Indicare in fondo al foglio se le seguenti affermazioni sono vere o false. A) Si consideri un file di 15.000 record di 320 byte ciascuno, con un campo chiave di 68 byte, sul quale è stato costruito un indice ISAM. Supponendo di non frazionare mai un record su due blocchi e di disporre di blocchi di 1K byte con indirizzi di 4 byte, indicare se le seguenti affermazioni sono vere o false. @NO In un blocco del file dati entrano 4 record. @SI Sono necessari 5000 blocchi per memorizzare il file dati. @NO Ogni record del file indice è lungo 70 byte. @SI In un blocco del file indice entrano 14 record. @NO Sono necessari 357 blocchi per memorizzare il file indice. @SI In ogni blocco del file dati si sprecano 64 byte. @SI Lo spazio inutilizzato nell’ultimo blocco del file indice è maggiore dello spazio inutilizzato negli altri blocchi. @SI L’accesso ad un record di chiave data richiede un solo accesso al file dati. B) Con riferimento ai linguaggi macchina discussi a lezione: @SI Il linguaggio macchina della UltraSPARC vede solo 32 registri general-purpose sebbene i registri fisici siano di più. @NO Il linguaggio macchina dei Pentium ha a disposizione 16 registri general purpose a 32 bit. @NO Con il meccanismo di espansione dei codici, il numero di istruzioni a n operandi è indipendente dal numero di istruzioni a n-1 operandi. @SI Le istruzioni del linguaggio macchina IA-32 possono indirizzare direttamente una locazione di memoria principale. @SI Le uniche istruzioni del linguaggio macchina della UltraSPARC che indirizzano la memoria sono le LOAD e le STORE. @SI Nell’indirizzamento diretto si specifica nell’istruzione l’indirizzo di memoria dell’operando. @NO L’indirizzamento indiretto a registro richiede in generale più bit dell’indirizzamento diretto. @NO Una istruzione che adotta l’indirizzamento a stack contiene un solo indirizzo. C) Con riferimento alla memoria fisica, cache e virtuale, indicare se le seguenti affermazioni sono vere o false. @NO Nella memoria fisica dati ed istruzioni hanno spazi di indirizzamento separati. @NO La dimensione di una memoria cache di secondo livello (L2) è paragonabile a quella della memoria fisica. @SI La memoria cache di primo livello (L1) dei Pentium relativa alle istruzioni è accessibile in sola lettura. @NO Un blocco di memoria presente nella cache di primo livello (L1) è sempre allineato al relativo blocco in memoria centrale. @NO Nel Pentium ogni singolo processo vede uno spazio di indirizzamento virtuale pari a 4Gbyte. @NO Il tempo di accesso ai dati in memoria principale (DRAM) è dell’ordine dei millisecondi. @SI Utilizzando una politica write through i dati nella cache L1 sono sempre allineati con quelli della memoria. @NO Se un blocco è presente nella cache di secondo livello una copia del blocco è presente anche nella cache di primo livello. Con riferimento al seguente programma C e supponendo che vengano dati in input i dati “2 2.3 3.2”, indicare se le seguenti affermazioni sono vere o false: #include<stdio.h> #include<stdlib.h> float* GEN(int n) { float *b = (float*)calloc(n,sizeof(float*)); return b; } void main() { float *b; int i,n; scanf("%d",&n); b = GEN(n); for (i=0; i<n; i++) scanf("%f",&b[i]); for (i=0; i<n-1; i++) b[i+1]=b[i+1]+b[i]; printf("%f",b[i]); } @SI Nel main la funzione GEN alloca dinamicamente un vettore di 2 elementi. @NO Non è possibile utilizzare nel programma principale il vettore allocato nella funzione GEN. @SI Il programma stampa 5.5. @SI Se avessimo fornito in input la stringa “4 2.2 3.3” avremmo avuto lo stesso output. @NO Il programma dà un errore durante la compilazione. @SI L’istruzione (float*)calloc(n,sizeof(float*)); nella funzione GEN può essere sostituita dall’istruzione (float*)malloc(n*sizeof(float*)); @SI L’allocazione dinamica del vettore b consente di utilizzare solo la memoria strettamente sufficiente alla memorizzazione dei dati che deve contenere. @SI Non è possibile allocare staticamente un array la cui dimensione non è nota a priori. D) Con riferimento alle tecniche adottate nelle architetture con pipeline, indicare se le seguenti affermazioni sono vere o false. @SI La tavola di predizione dei salti contiene una linea per ogni istruzione di salto. @SI Nella micro architettura dei Pentium esiste un’unità dedicata al ritiro delle istruzioni. @SI Nel Pentium le istruzioni macchina vanno facilmente in conflitto a causa dei pochi registri ufficiali a disposizione. @NO Le predizioni di salto statiche non possono essere a carico del compilatore. @NO La ridenominazione dei registri si adotta per rimediare ai vincoli WAW e RAW tra istruzioni macchina. @SI Il ritiro in ordine delle istruzioni macchina garantisce interruzioni precise. @NO L’unità Dispatch/Execute del Pentium consente di eseguire fino a 10 microistruzioni in parallelo. @SI La decomposizione di istruzioni in microistruzioni nelle macchine Pentium è a carico dell’unità Fetch/Decode.