Fondamenti di Informatica - Architettura degli elaboratori PDF

Summary

These lecture notes cover fundamental concepts in computer architecture, focusing on processors, memory, and peripherals. They explore the organization of computer systems and the different types of architectures, such as RISC and CISC, along with their characteristics, advantages and disadvantages.

Full Transcript

Fondamenti di Informatica Architettura degli elaboratori Carmine Dodaro, Università della Calabria Dal libro di testo1: “Un calcolatore digitale è un sistema in cui processori, memorie e dispositivi periferici sono connessi tra loro”. In generale, sono connessi tramite bus (architettura bus-oriente...

Fondamenti di Informatica Architettura degli elaboratori Carmine Dodaro, Università della Calabria Dal libro di testo1: “Un calcolatore digitale è un sistema in cui processori, memorie e dispositivi periferici sono connessi tra loro”. In generale, sono connessi tramite bus (architettura bus-oriented). Da questa frase si possono comprendere gli elementi principali di un calcolatore digitale: Processori Memorie Periferiche Bus 1A. Tanenbaum e T. Austin: Architettura dei calcolatori - Un approccio strutturale. Organizzazione dei sistemi di calcolo CPU Memoria centrale Disco Periferica 1 Periferica 2 Unità di Controllo Unità aritmetico-logica Registri Bus Il processore o CPU (Central Processing Unit) ha il ruolo di eseguire i programmi contenuti nella memoria centrale. Il processo di funzionamento è abbastanza semplice: le istruzioni dei programmi sono nella memoria centrale, la CPU le preleva e le esegue in sequenza. La CPU è composta da diversi elementi, tra cui: L’unità di controllo (CU), che si occupa di prelevare le istruzioni dalla memoria e di determinare il tipo dell’istruzione prelevata. L’unità aritmetico-logica (ALU), che si occupa di eseguire le operazioni aritmetico-logiche per eseguire le istruzioni. Una memoria molto veloce, composta da registri, che viene utilizzata principalmente per memorizzare i risultati temporanei e altre informazioni di controllo. Ogni registro ha una funzione e una dimensione. I due registri principali sono il Program Counter (PC), che contiene l’indirizzo in memoria dell’istruzione successiva da prelevare, e l’Instruction Register (IR), che contiene l’istruzione che si sta eseguendo. Processori La CPU esegue le istruzioni attraverso una serie di passi (detto ciclo macchina o ciclo fetch-decode-execute): 1)Preleva l’istruzione dalla memoria e la inserisce nell’IR. 2)Modifica il PC inserendo l’indirizzo dell’istruzione seguente. 3)Determina il tipo dell’istruzione prelevata al punto 1. 4)Se l’istruzione usa un blocco di dati dalla memoria, determina dove si trovano i dati. 5)Se necessario, copia il blocco di dati dalla memoria all’interno di uno dei registri. 6)Esegue l’istruzione. 7)Ritorna al punto 1. Esecuzione dell’istruzione Abbiamo visto che il processore esegue istruzioni, ma deve sapere che cosa fare e come farlo. Queste istruzioni devono essere standardizzate per permettere la comunicazione tra hardware e software. Il set di istruzioni è l’insieme di comandi che il processore può eseguire direttamente, quindi determina cosa può fare il processore e come interagisce con la memoria e le periferiche. Ci sono diversi tipologie di istruzioni: Aritmetiche: Addizione, sottrazione, moltiplicazione. Logiche: AND, OR, NOT. Controllo: Salti condizionali, chiamate di subroutine. L’ISA (Instruction Set Architecture) è il livello che definisce: Quali istruzioni può eseguire il processore. Come le istruzioni sono rappresentate in binario. Interfaccia tra software e hardware. L’ISA permette al software (programmi) di comunicare con l’hardware (CPU). Un’istruzione semplice come ADD R1, R2 (somma dei registri R1 e R2) viene tradotta in un comando binario che il processore esegue. Set di istruzioni RISC e CISC sono tipologie di architetture di processori. Queste architetture riguardano la progettazione hardware e la gestione del set di istruzioni del processore, ovvero il modo in cui le operazioni sono eseguite a livello fisico. RISC (Reduced Instruction Set Computer) impiega un set di istruzioni ridotto e più semplice, progettato per eseguire operazioni con istruzioni più rapide e semplici. CISC (Complex Instruction Set Computer) utilizza un set di istruzioni ampio e complesso, che consente al processore di eseguire operazioni complesse con una singola istruzione. Architetture dei processori: RISC vs CISC Caratteristiche: Set di istruzioni complesso e ampio. Ogni istruzione può eseguire operazioni complesse (es. caricamento di dati e calcolo in un’unica istruzione). Vantaggi: Minor numero di istruzioni per eseguire un compito. Riduzione della dimensione del programma. Svantaggi: Maggiore complessità del processore. Maggiore consumo di energia. Esempi: Famiglia x86 di Intel (Core™ i3/i5/i7/i9) e i processori AMD come Ryzen™ CISC Caratteristiche: Set di istruzioni semplice e limitato. Ogni istruzione esegue un’unica operazione semplice (es. caricamento di dati, calcolo). Progettato per eseguire più istruzioni per ciclo di clock. Vantaggi: Maggiore efficienza e velocità di esecuzione. Maggiore parallelismo. Svantaggi: Maggiore numero di istruzioni per compiti complessi. Potenziale aumento della dimensione del programma. Esempi: ARM, MIPS, RISC-V RISC Per migliorare le prestazioni di una CPU si può aumentare la velocità di clock (intuitivamente, il numero di operazioni che si possono eseguire ogni secondo), ma qualsiasi architettura hardware ha dei limiti fisici che non si possono superare. Per questo motivo, spesso, si ricorre più che altro al parallelismo, che può essere sfruttato: a livello di istruzione, dove ogni singola istruzione lo utilizza per fare in modo che la CPU ne possa elaborare molte di più al secondo; a livello di processore, dove più CPU lavorano insieme sullo stesso problema. Nel primo caso, si possono utilizzare delle tecniche per velocizzare il prelievo delle istruzioni dalla memoria. Una delle prime tecniche usate si chiama prefetching e consiste nel dividere l’esecuzione in due parti distinte: il prelievo dell’istruzione dalla memoria e l’esecuzione dell’istruzione stessa. Il concetto di pipeline estremizza questa strategia. Infatti, l’esecuzione non si divide più in due fasi, ma in un numero molto più alto di fasi (anche più di 10) che sono eseguite in parallelo. Ogni fase è gestita da un componente hardware dedicato La memoria principale (RAM, da Random Access Memory) è quel componente del calcolatore dove vengono memorizzati i programmi e i dati. Le memorie sono formate da celle che contengono delle informazioni. Ogni cella è identificata da un numero, cioè l’indirizzo, che può essere utilizzato dal programma per riferirsi alla cella. Gli indirizzi vanno da 0 a n-1, dove n è il numero di celle disponibili. Ogni cella è formata dallo stesso numero di bit. La RAM può essere byte-addressable, cioè con celle da 1 byte, oppure word-addressable, cioè con celle di dimensioni pari alla default word size, che è il numero massimo di bit su cui una CPU può lavorare: Un calcolatore a 32 bit ha registri a 32 bit e istruzioni in grado di gestire parole a 32 bit (word pari a 4 byte). Un calcolatore a 64 bit ha registri a 64 bit e istruzioni in grado di gestire parole a 64 bit (word pari a 8 byte). Quasi tutti i produttori usano celle a 8 bit (1 byte). Memoria principale Se una cella contiene k bit, può rappresentare 2k elementi. Se un indirizzo ha k bit, il massimo numero di celle indirizzabili è 2k. All’interno di una parola, in che ordine si memorizzano i dati? Si usa il termine endianness per riferirsi alla convenzione scelta per definire questo ordine e ci sono due ordinamenti più diffusi: Little endian: si memorizzano da destra verso sinistra. Big endian: si memorizzano da sinistra verso destra. Big endian: vantaggi e svantaggi Vantaggi Facilità nelle operazioni aritmetiche. Quando si leggono i dati di una parola (word) multi-byte, il byte meno significativo si trova all’indirizzo più basso. Questo corrisponde direttamente all’ordine delle operazioni aritmetiche nei registri. Compatibilità con i tipi variabili di dati: In molte architetture, il little-endian consente di trattare dati multi-byte come array di byte con un accesso semplice al valore più piccolo senza bisogno di ulteriori operazioni. Dominanza nell’industria: Utilizzato su piattaforme come x86/x86-64 e ARM (nella maggior parte dei casi). Favorisce l’interoperabilità con queste architetture, che rappresentano la maggioranza dei dispositivi consumer. Svantaggi Interoperabilità: Quando i dati vengono trasmessi o memorizzati su sistemi che utilizzano big-endian (es. alcuni protocolli di rete), è necessaria una conversione, che introduce complessità. Umanamente meno intuitivo: La rappresentazione in memoria non corrisponde all’ordine naturale dei numeri. Little-endian: vantaggi e svantaggi Vantaggi Interoperabilità con protocolli di rete: Molti protocolli di rete (come TCP/IP) e formati di dati standard usano big-endian, noto anche come network byte order. La corrispondenza diretta riduce la necessità di conversioni. Più leggibile per l’essere umano: l’ordine di memorizzazione dei byte in memoria corrisponde all’ordine naturale di lettura dei numeri, rendendolo più intuitivo per il debug e la visualizzazione. Svantaggi Operazioni aritmetiche più complesse: Le operazioni hardware come l’incremento di numeri multi-byte richiedono una gestione più complessa, poiché il byte meno significativo non è al primo indirizzo. Meno comune nell’hardware moderno: La maggior parte delle piattaforme moderne (x86, ARM) usa little-endian per impostazione predefinita. L’uso di big-endian può quindi richiedere adattamenti software o hardware. Big-endian: vantaggi e svantaggi Quando un programma C, C++ o Assembly viene eseguito, gli viene riservata una parte di memoria. Tale memoria è suddivisa in 6 sezioni (o segmenti) per memorizzare in modo organizzato le diverse informazioni che servono ad eseguire correttamente il programma. Questa organizzazione viene detta memory layout. Le sezioni sono organizzate in memoria con un ordine ben preciso: prima si trovano 4 sezioni che hanno dimensioni fissate, stabilite in fase di compilazione, e di seguito 2 sezioni la cui dimensione può variare nel corso dell’esecuzione del programma. Memoria dedicata a un programma Stack Attualmente non usata Heap Dati non inizializzati.bss Dati in sola lettura.rodata Dati inizializzati.data Codice del programma.text Le prime 4 sezioni sono:.text memorizza le istruzioni in linguaggio macchina del programma. È in sola lettura per evitare modifiche accidentali e prevenire problemi in fase di esecuzione o attacchi..data, sezione dei dati inizializzati. Memorizza le variabili globali che sono state inizializzate..rodata, sezione dei dati read-only. Memorizza le costanti globali (non possono cambiare valore nel corso dell’esecuzione del programma)..bss, sezione dei dati non inizializzati. Memorizza le variabili globali che NON sono state inizializzate. Queste variabili verranno automaticamente inizializzate a 0 all’avvio del programma. Il nome si può ricordare come Better to Save Space, ovvero variabili a cui è bene riservare spazio in memoria. Queste sezioni hanno una dimensione fissata, codificata nel file eseguibile. Il sistema operativo sa quanto deve dedicare a ciascuna di esse in base alle istruzioni che compongono il programma e alle variabili globali che sono dichiarate al suo interno. Memoria dedicata a un programma Heap è la sezione dedicata alle variabili dinamiche (es. le sequenze di elementi con dimensione variabile). In base a quanta memoria sarà necessaria in fase di esecuzione del programma, questa sezione crescerà o diminuirà. Memoria dedicata a un programma: heap Stack Attualmente non usata Heap Dati non inizializzati.bss Dati in sola lettura.rodata Dati inizializzati.data Codice del programma.text Stack è la sezione dove si memorizzano le variabili locali ovvero quelle dichiarate localmente all’interno di un blocco di codice, come ad esempio il corpo di una funzione, di un for, di un if, etc. Inoltre, lo stack viene usato per memorizzare i parametri passati alle funzioni e il return address ovvero l’indirizzo dell’istruzione successiva da cui riprendere l’esecuzione quando una funzione termina. Lo stack cresce verso il basso, dove è presente memoria non utilizzata e ha un funzionamento a pila: LIFO (Last In First Out), cioè l’ultimo elemento inserito è il primo ad essere estratto. L’operazione di inserimento (push) ed estrazione (pop) avviene sempre e solo dallo stesso verso, ovvero dal basso. Il lato in cui si inserisce ed estrae viene detto top (o cima) dello stack. Memoria dedicata a un programma: stack FFFFFFFFFFFFFFFF … Stack Attualmente non usata Heap Dati non inizializzati.bss Dati in sola lettura.rodata Dati inizializzati.data Codice del programma.text … 000000000000000 In generale, le CPU sono sempre state più veloci rispetto alle memorie, quindi nel momento in cui prelevano le istruzioni dalla memoria centrale devono aspettare un certo numero di cicli prima di ottenere il risultato. Per realizzare delle memorie veloci come le CPU queste devono essere collocate sul chip della CPU, poiché il bus per la memoria è troppo lento. Ma questo pone due limiti: i costi molto alti e la dimensione di un chip di CPU. Per questa ragione, spesso si combinano memorie centrali molto capienti ma lente con una piccola quantità di memoria molto veloce. Quest’ultima è chiamata cache. L’idea di funzionamento è molto intuitiva: la CPU cerca prima le parole che gli servono all’interno della memoria cache, se non le trova allora la richiede alla memoria centrale. Quando si preleva una parola dalla memoria centrale, oltre a prendere la parola stessa, si prendono anche un certo numero di parole vicine e si memorizzano nella cache. Questo meccanismo funziona perché spesso le istruzioni che si eseguono in sequenza sono memorizzate all’interno di indirizzi vicini in memoria centrale. Memoria cache Per calcolare le prestazioni complessive della cache si può utilizzare una semplice formula dove: c è il tempo di accesso alla cache. m è il tempo di accesso alla memoria centrale. h è la frazione di riferimenti che possono essere letti dalla cache invece che dalla memoria centrale. Il tempo medio di accesso si calcola come c + (1 - h) m. Se h fosse vicino a 1, cioè la maggior parte dei riferimenti sono letti dalla cache e non dalla memoria centrale, allora il tempo medio di accesso sarebbe pari a circa c (quindi la velocità della cache). Se h fosse vicino a 0, cioè la maggior parte dei riferimenti sono letti dalla memoria centrale, allora il tempo medio di accesso sarebbe pari a circa c + m (quindi la velocità della memoria centrale più il tempo speso a cercare inutilmente la parola nella cache). Memoria cache La progettazione della cache presenta diversi problemi: La grandezza della cache. Più è grande e migliori sono le prestazioni, ma anche i suoi costi, quindi non è semplice trovare un bilanciamento tra i due. La scelta della linea di cache. La linea di cache è il blocco minimo di dati che la cache può leggere o scrivere dalla memoria principale e ha influenza sulle prestazioni. Infatti, una linea più grande carica più dati vicini, quindi meno accessi alla memoria (più velocità), ma se quei dati vicini non vengono usati, si sprecano spazio e tempo. Una linea più piccola riduce lo spreco di spazio nella cache, caricando solo ciò che serve, ma potrebbe causare più accessi alla RAM se servono dati vicini. Tipicamente, si usano valori come 64 byte o 128 byte, che funzionano bene per la maggior parte dei programmi, ma dipende dai tipi di applicazioni. I programmi con dati vicini beneficiano di linee più grandi, mentre i programmi con dati non contigui beneficiano di linee più piccole. La scelta se mantenere un’unica cache per le istruzioni e i dati oppure avere due cache specializzate (architettura Harvard). La prima avrebbe il vantaggio di una progettazione più semplice, ma le CPU moderne usano il secondo approccio perché la pipeline permettono che l’accesso alla cache sia parallelo. La scelta sul numero di cache da avere (tipicamente tre livelli). Memoria cache I processori moderni hanno tre livelli principali di cache (L1, L2, L3) e, in alcuni casi, una cache di livello superiore (L4). Cache L1 (Livello 1) è la più vicina al core del processore ed è anche estremamente veloce. È molto piccola ed è specifica per ogni core del processore (quindi non condivisa). Cache L2 (Livello 2) è più grande della L1 ma leggermente più lenta (sebbene sia ancora molto veloce). In base all’architettura può essere dedicata per core o condivisa tra più core. Cache L3 (Livello 3) è una cache condivisa tra tutti i core del processore. In genere è più lenta di L1 e L2, ma più grande. Cache L4 (Livello 4) è presente in alcuni processori ad alte prestazioni ed è solitamente condivisa tra il processore e altre unità, come la GPU integrata. I livelli di cache lavorano in modo gerarchico: Il processore cerca prima il dato nella cache L1. Se non lo trova (cache miss), cerca nella L2, poi nella L3, e infine accede alla RAM. Nota: ogni livello è più grande ma più lento del precedente. Memoria cache

Use Quizgecko on...
Browser
Browser