Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica

Home | Avvisi | Diario Lezioni | Esercitazioni | Esami | Materiale Didattico | Valutazioni Studenti | Lezioni di Camil Demetrescu |

[T11] Esercitazione del 24 maggio 2019

Istruzioni per l’esercitazione:

Per maggiori informazioni fate riferimento al regolamento delle esercitazioni.

Esercizio 1 (sottosequenza di array)

Tradurre in IA32 la seguente funzione C sub_array definita in E1-sub-array/e1.c che, dati due array di short a e b di lunghezza rispettivamente na e nb, verifica se a è una sottosequenza consecutiva di b. Ad esempio {2,3,4} è sottosequenza di {1,2,3,4,5}, mentre {1,3,4} non lo è. La funzione richiama un’altra funzione is_prefix già fornita in E1-sub-array/e1_main.c.

e1.c

int sub_array(const short *a, unsigned na, const short *b, unsigned nb) {
    int i;
    for (i=0; i+na <= nb; ++i) {
        int res;
        is_prefix(a, b+i, na, &res);
        if (res) return 1;
    }
    return 0;
}

Scrivere la soluzione nel file E1-sub-array/e1.s. Usare il file E1-sub-array/e1_eq.c per sviluppare la versione C equivalente e E1-sub-array/e1_main.c come programma di prova. Attenzione all’aritmetica dei puntatori e a tenere res in stack.

Esercizio 2 (ottimizzazioni per la cache)

Si consideri la seguente funzione in E2-cache/e2.c:

#define STRIDE 64

long f(const short *v, unsigned n){
    long x = 0;
    unsigned i, j;
    for (i=0; i<STRIDE; ++i)
        for (j=0; j<n; j+=STRIDE) x += v[i+j];
    return x;
}

Scrivere nel file E2-cache/e2_opt.c una versione della funzione semanticamente equivalente che faccia un migliore uso delle cache. Compilare il programma con make ed eseguirlo con ./e2. Il programma riporterà il tempo di esecuzione in millisecondi della versione non ottimizzata e di quella ottimizzata insieme allo speedup ottenuto. Verificare che la versione ottimizzata calcoli lo stesso valore di quella non ottimizzata.

Esercizio 3 (Domande)

Domanda 1 Si consideri una cache completamente associativa con 4 linee da 16 byte ciascuna e politica di rimpiazzo LRU. Potendo scegliere durante un cold cache 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: 116, 71, 142, 162, 70, 244, 273, 132, 112 ? (l’ordine è importante)

Domanda 2 Si consideri una cache associativa a 2 vie con 4 linee da 16 byte ciascuna e politica di rimpiazzo LRU. Potendo scegliere durante un cold cache 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: 72, 199, 131, 77, 138, 115, 40, 250, 208?

Domanda 3 Quale delle seguenti affermazioni è VERA?

Domanda 4 Quale delle seguenti affermazioni è FALSA?

Domanda 5 Data la seguente porzione di codice. 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?

movl $1, %eax
addl %eax, %ecx
addl $2, %edi
incl %esi
addl %esi, %edx
movl $3, %ebx

Domanda 6 Con riferimento alla domanda 5, è possibile ottimizzare il codice riordinando le istruzioni (senza cambiare la semantica) in modo da ridurre il numero di cicli di clock richiesti?

Domanda 7 Quale delle seguenti affermazioni è VERA?

Domanda 8 Quale delle seguenti affermazioni è FALSA?

Soluzioni

Esercizio 1 (sottosequenza di array)

e1_eq.c

#include "e1.h"

int sub_array(const short *a, unsigned na, const short *b, unsigned nb) {
    int i=0, tmp, res=0;
    const short* tmp2;
L:  tmp = i;
    tmp += na;
    if (tmp > nb)
        goto E;
    tmp2 = b + i;
    is_prefix(a, tmp2, na, &res);
    if (res)
        goto E;
    i++;
    goto L;
E:  return res;
}

e1.s

.globl sub_array

sub_array: # int sub_array(const short *a, unsigned na, const short *b, unsigned nb)
    pushl %ebx               # i
    pushl %edi               # na
    pushl %esi               # nb
    pushl %ebp               # b
    subl $20, %esp           # 16 byte per i 4 parametri + 4 byte per res
    movl 44(%esp), %edi
    movl 48(%esp), %ebp
    movl 52(%esp), %esi
    xorl %ebx, %ebx          # i=0;
    movl $0, 16(%esp)        # res=0
L:  movl %ebx, %eax          # eax = i;
    addl %edi, %eax          # eax += na;
    cmpl %esi, %eax          # if (eax > nb)
    jg E                     #     goto E;
    movl 40(%esp),%eax       # eax = a
    movl %eax, (%esp)        # primo parametro
    leal (%ebp,%ebx,2), %eax # eax = b + i;
    movl %eax, 4(%esp)       # secondo parametro
    movl %edi, 8(%esp)       # terzo parametro
    leal 16(%esp), %eax      # eax = &res
    movl %eax, 12(%esp)      # quarto parametro
    call is_prefix           # is_prefix(a, tmp2, na, &res);
    cmpl $0, 16(%esp)        # if (res)
    jne E                    #     goto E;
    incl %ebx                # i++;
    jmp L                    # goto L;
E:  movl 16(%esp), %eax      # return res;
    addl $20, %esp
    popl %ebp
    popl %esi
    popl %edi
    popl %ebx
    ret
Esercizio 2 (ottimizzazioni per la cache)

e2_opt.c

#include "e2.h"

long f_opt(const short *v, unsigned n){
    long x = 0;
    unsigned i;
    for (i=0; i<n; i+=1) x += v[i];
    return x;
}
Esercizio 3 (Domande)
  1. E. Nessuno dei precedenti

    Spiegazione:

    Indirizzi:              116, 71, 142, 162, 70, 244, 273, 132, 112
    Blocchi (indirizzo/12):   7,  4,   8,  10,  4,  15,  17,   8,   7
    Cache: (tra parentesi il tempo dall'ultimo accesso a un blocco in cache)
            0       1       2      3
     7 -> |  7(0) |       |       |       | (cold miss)
     4 -> |  7(1) |  4(0) |       |       | (cold miss)
     8 -> |  7(2) |  4(1) |  8(0) |       | (cold miss)
    10 -> |  7(3) |  4(2) |  8(1) | 10(0) | (cold miss)
     4 -> |  7(4) |  4(0) |  8(2) | 10(1) | (hit)
    15 -> | 15(0) |  4(1) |  8(3) | 10(2) | (capacity miss, rimpiazzo LRU)
    17 -> | 15(1) |  4(2) | 17(0) | 10(3) | (capacity miss, rimpiazzo LRU)
     8 -> | 15(2) |  4(3) | 17(1) |  8(0) | (capacity miss, rimpiazzo LRU)
     7 -> | 15(3) |  7(0) | 17(2) |  8(1) | (capacity miss, rimpiazzo LRU)
    
  2. B. 8, 2, 13, 15

    Spiegazione:

    Indirizzi:               72, 199, 131, 77, 138, 115, 40, 250, 208
    Blocchi (indirizzo/12):   4,  12,   8,  4,   8,   7,  2,  15,  13
    Cache: (tra parentesi il tempo dall'ultimo accesso a un blocco in cache)
            0       1       2      3
      4 -> |  4(0) |       |       |       | (cold miss)
     12 -> |  4(1) | 12(0) |       |       | (cold miss)
      8 -> |  8(0) | 12(1) |       |       | (conflict miss)
      4 -> |  8(1) |  4(0) |       |       | (conflict miss)
      8 -> |  8(0) |  4(1) |       |       | (hit)
      7 -> |  8(1) |  4(2) |  7(0) |       | (cold miss)
      2 -> |  8(2) |  2(0) |  7(1) |       | (conflict miss)
     15 -> |  8(3) |  2(1) |  7(2) | 15(0) | (cold miss)
     13 -> |  8(4) |  2(2) | 13(0) | 15(1) | (capacity miss)
        
    
  3. D. Con una cache ad accesso diretto non è possibile usare la politica di rimpiazzo LRU

    Spiegazione:

    Con una cache ad accesso diretta ogni blocco può essere caricato soltanto in una specifica linea di cache. Per tanto ogni volta che avviene un cache miss non è possibile scegliere quale linea di cache rimpiazzare, poiché questa sarà necessariamente l’unica in cui il blocco può essere caricato.

  4. E. La dimensione di una linea di cache è pari alla dimensione di una pagina della memoria virtuale

    Spiegazione:

    Non c’è alcuna relazione tra la dimensione delle linee di cache e la dimensione delle pagine della memoria virtuale.

  5. A. 16

    Spiegazione:

    movl $1, %eax     F D E M W
    stallo              * * * * *
    stallo                * * * * *
    stallo                  * * * * *
    addl %eax, %ecx           F D E M W
    addl $2, %edi               F D E M W
    incl %esi                     F D E M W
    stallo                          * * * * *
    stallo                            * * * * *
    stallo                              * * * * *
    addl %esi, %edx                       F D E M W
    movl $3, %ebx                           F D E M W
                      |<---      16 cicli       --->|
    
  6. C. Sì è possibile ridurre il numero di cicli di clock a 10, ma non a meno

    Spiegazione:

    movl $1, %eax     F D E M W
    incl %esi           F D E M W
    addl $2, %edi         F D E M W
    movl $3, %ebx           F D E M W
    addl %eax, %ecx           F D E M W
    addl %esi, %edx             F D E M W
                      |<-  10 cicli   ->|
    
  7. E. Nessuna delle precedenti

    Spiegazione:

    L’affermazione A è falsa perché la tecnica dell’instruction scheduling viene applicata anche dai compilatori. L’affermazione B è falsa perché il piplining permette di aumentare il throughput. L’affermazione C è falsa perché il pipelining non riduce il tempo di esecuzione delle singole istruzioni. L’affermazione D è falsa perché il pipelining viene applicato all’interno dei core e dunque può essere presente tanto sulle architetture single-core quanto su quelle multi-core. Dunque l’affermazione E è vera.

  8. D. La tecnica del pipelining permette di eseguire più processi in parallelo

    Spiegazione:

    La tecnica del pipelining permette di sovrapporre temporalmente l’esecuzione di istruzioni successive di un processo. Non viene usata per eseguire più processi in parallelo.