Come vengono eseguite le righe di codice dalla CPU?


11

Sto cercando di capire davvero come esattamente un linguaggio di alto livello viene convertito in codice macchina e quindi eseguito dalla CPU.

Comprendo che il codice viene compilato nel codice macchina, che è il codice di basso livello che una CPU può utilizzare. Se ho una dichiarazione di incarico dire:

x = x + 5;
y = x - 3;

La CPU esegue ciascuna riga una alla volta? Quindi eseguirà prima x = x + 5; l'istruzione e quindi l'istruzione successiva che verrà eseguita dalla CPU è y = x- 3; Sto davvero cercando di capire il processo di esecuzione e come il codice che scrivo viene effettivamente eseguito dalla CPU.


Potresti voler provare a capire il design di una delle CPU open source, ci sono alcune implementazioni davvero semplici basate su stack come excamera.com/sphinx/fpga-j1.html - sono molto più semplici delle architetture a 3 indirizzi come nel tuo esempio.
Logica SK

3
Quando sono entrato in questo business, questo avrebbe avuto risposte semplici e ben definite. Al giorno d'oggi, le CPU sono estremamente complicate e fanno di tutto per aumentare la potenza di elaborazione.
David Thornley,

Risposte:


12

Le righe di codice non hanno nulla a che fare con il modo in cui la CPU lo esegue. Consiglierei di leggere su assemblatore, perché questo ti insegnerà molto su come l'hardware fa effettivamente le cose. È inoltre possibile ottenere l'output dell'assemblatore da molti compilatori.

Tale codice potrebbe essere compilato in qualcosa del genere (in un linguaggio assembly assemblato):

load R1, [x] ; meaning load the data stored at memory location x into register 1
add R1, 5
store [x], R1 ; store the modified value into the memory location x
sub R1, 3
store R1, [y]

Tuttavia, se il compilatore sa che una variabile non viene utilizzata di nuovo, l'operazione di archiviazione potrebbe non essere emessa.

Ora affinché il debugger sappia quale codice macchina corrisponde a una riga della sorgente del programma, le compilazioni vengono aggiunte dal compilatore per mostrare quale riga corrisponde a dove si trova il codice macchina.


Perchè no? Un'architettura a 3 indirizzi avrà istruzioni simili ADD Rx, Rx, $5e SUB Ry, Rx, $3(supponendo che le variabili xey siano state mappate in registri). Stai descrivendo un approccio RISC di caricamento / archiviazione.
Logica SK

1
@ SK-logic: mentre ciò può accadere per linee di codice molto semplici in linguaggi di programmazione molto semplici con tipi di dati e operazioni che la CPU supporta abbastanza bene, in nessun caso il caso generale. È conveniente per gli esperti, ma prima di tutto è importante capire che le istruzioni sul codice della macchina generalmente hanno scarsa risonanza alle righe di codice in un linguaggio di alto livello.

@ SK-Logic: funziona solo per questo esempio particolare. In generale, tuttavia, maxpolun ha ragione. Le dichiarazioni linguistiche di alto livello devono essere tradotte in un linguaggio di livello inferiore, con più "burocrazia" necessarie per fare cose concettualmente semplici. Immagino che l'OP chiedesse un esempio di questa trasformazione.
Andres F.

1
@ SK-Logic: l'OP ha iniziato la sua domanda con "Sto cercando di capire davvero come esattamente un linguaggio di alto livello [...]"
Andres F.

1
@ SK-logic Il contesto è "Se ho un'istruzione di assegnazione dire: [snippet di codice] La CPU esegue ciascuna riga una alla volta?" - Mi sembra che sia inteso come codice sorgente in un linguaggio non assemblatore. Più in generale, non vedo alcun indicatore di una comprensione di quanto sia basso il codice macchina e alcune frasi (come parlare di linee) indicano alcune idee sbagliate. Non è impossibile come si suppone, non tutti hanno avuto il piacere di essere scagliati per primi su alcuni semplici microcontrollori (come me e apparentemente altri). Forse Frankie dovrebbe chiarire.

2

Dipende.

All'inizio di macchine davvero semplici, sì, il codice veniva eseguito una riga alla volta. Man mano che le macchine diventavano più grandi, più veloci e più complesse, hai iniziato a vedere sia la capacità di eseguire più istruzioni contemporaneamente che le letture e le scritture della memoria impiegano molto più tempo delle operazioni sui registri.

L'ottimizzazione dei compilatori doveva tenerne conto e le linee fornite potevano essere eseguite "più o meno" in parallelo, con una parte del processore che lavorava sul calcolo di y, mentre un'altra parte stava memorizzando il nuovo valore precedentemente calcolato di x (e il calcolo di y stava usando quel nuovo valore dal registro).

Control Data 6600 è stata la prima macchina che conosco a fare questo tipo di cose. L'aggiunta di interi ha richiesto 300 nsec, il riferimento di memoria (lettura o scrittura) ha richiesto 1000 nsec, moltiplica e le divisioni hanno impiegato MOLTO più tempo. Possono essere eseguite in parallelo fino a una decina di istruzioni, a seconda delle unità funzionali richieste. I compilatori CDC 6600 FORTRAN sono stati MOLTO bravi a programmare tutto questo.


In questo caso l'input dell'istruzione successiva dipende dal risultato della prima istruzione, quindi deve essere eseguito in sequenza.
SK-logica

@ SK-logic: non proprio. L'input della seconda riga dipende dal risultato del lato destro della prima riga, ma, basato esclusivamente su ciò che possiamo vedere nel codice di esempio originale, potrebbe NON dipendere dall'archivio in memoria del risultato di la prima riga. Se x fosse stato dichiarato volatile (in C / C ++), il compilatore avrebbe dovuto memorizzare prima il risultato, quindi ricaricarlo dalla memoria, prima di iniziare a calcolare il nuovo valore di y, poiché "volatile" significa che qualcosa (un gestore di interrupt, diciamo) potrebbe entrare e zapare x tra le due linee.
John R. Strohm,

Presumo che xey siano registri (e il codice sia in un linguaggio pseudoassembly a 3 indirizzi piuttosto che qualcosa come C). In questo caso entrambe le istruzioni sono inevitabilmente sequenziali. In caso contrario, OP ha dovuto porre due o più domande diverse anziché questa.
Logica SK

Mi chiedo se i processori proverebbero a "speculare" quale sia il valore di x? In questo modo ha già eseguito il codice e lo ha archiviato nella cache.
Kolob Canyon,

Anche se sono registri, A SECONDA DELLA MACCHINA, non si può presumere che le istruzioni vengano eseguite completamente in sequenza. Il 6600 aveva una logica di programmazione (il "quadro di valutazione") che avrebbe forzato la semantica sequenziale, basata sul presupposto che il programmatore voleva fare l'ovvio. Le macchine successive hanno omesso quell'hardware, basandosi invece sui compilatori per pianificare attentamente le istruzioni. I programmatori umani che stavano programmando il linguaggio assembly in quelle bestie erano PROPRIE.
John R. Strohm,

1

No, non esiste una mappatura individuale tra righe di codice / istruzioni in linguaggi di livello superiore e inferiore. In effetti, entrambe le righe sopra sono tradotte in più istruzioni del codice macchina , come

  1. caricare un valore da un determinato indirizzo di memoria in un registro
  2. modifica il valore
  3. riscrivilo in memoria

I dettagli effettivi di queste istruzioni variano tra le piattaforme.

Questa è la visione di base delle cose. Tuttavia, per complicare ulteriormente i problemi, le moderne CPU applicano tecniche come pipeline di esecuzione , esecuzione fuori ordine e più core , tra gli altri. Ciò comporta che la CPU esegua più operazioni contemporaneamente, ad esempio le condutture elaborano diverse fasi delle successive istruzioni in parallelo all'interno della stessa unità di elaborazione, mentre più core possono elaborare in parallelo istruzioni indipendenti.


0

Dovresti guardare in dettaglio in un libro per trovare maggiori dettagli su come funziona, possibilmente anche una classe di compilatore.

Fondamentalmente, la tua domanda si concentra su 2 diversi aspetti.

1) Come viene tradotto il codice in codice macchina?

2) Quando / come viene calcolato il codice usando la parallelizzazione?

La risposta a 1) dipende dalla lingua che usi (anche se per il tuo esempio è banale quindi l'output sarebbe lo stesso). Il modo in cui il compilatore esegue la traduzione in codice macchina è una delle forze del linguaggio. Inoltre, ci sono diverse preoccupazioni che devono essere prese in considerazione nel tuo esempio, il codice dovrebbe caricare i dati in memoria, archiviarli, ecc.

Infine, la parallelizzazione è una funzionalità che puoi forzare dal punto di vista della programmazione, ma in poche parole, alcuni processori potrebbero provare a pensare che una parte del codice possa essere eseguita contemporaneamente, perché sono indipendenti. Nel tuo caso, chiaramente, non è il caso, poiché è necessario eseguire le istruzioni in sequenza, quindi no, non funzionerà contemporaneamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.