Quando si verifica un’interruzione, cosa succede alle istruzioni nella pipeline?

Assumiamo un’architettura a 5 fasi pipeline (IF = Istruzione Recupero, ID = Istruzione Decodifica, EX = Esegui, MEM = Accesso alla Memoria, WB = Registrazione riscritta). Ci sono 4 istruzioni che devono essere eseguite.

(Queste istruzioni di esempio non sono accurate, ma credo che il punto sarebbe compreso)

Nel quinto ciclo di clock, queste istruzioni saranno in pipeline come mostrato di seguito.

Aggiungi a, b, c [IF ID EX MEM WB]

Aggiungi a, b, d [IF ID EX MEM]

    Aggiungi a, b, e [IF ID EX]

    Aggiungi a, b, f [IF ID]

    Ora se si verificano interruzioni hardware, cosa succede a queste istruzioni. L’interruzione verrà gestita solo dopo aver eseguito tutte le istruzioni nella pipeline? Il software si interromperà e le eccezioni verranno gestite in un modo diverso ??

    In primo luogo, la terminologia:

    Di solito, almeno su Intel, un’interruzione è qualcosa che viene dal mondo esterno. Di solito non è sincronizzato con le istruzioni in esecuzione sul processore, cioè è un interrupt esterno asincrono.

    Nella terminologia Intel un’eccezione è causata dalle istruzioni in esecuzione sul processore. Ad esempio un errore di pagina o una trappola di istruzioni non definita.

    — + Interrompe lo scarico di tutte le istruzioni in volo

    Su tutte le macchine con cui ho familiarità – ad esempio tutti i processori Intel dal momento che il P5 (ho lavorato su P6), AMD x86, ARM, MIPS – quando viene ricevuto il segnale di interruzione, le istruzioni nella pipeline sono quasi sempre scaricate, gettate via.

    L’unica ragione per cui dico “quasi sempre” è che su alcune di queste macchine non si è sempre in un posto dove è permesso ricevere un’interruzione. Quindi, si procede al punto successivo in cui è consentito un interrupt – qualsiasi limite di istruzioni, in genere – e POI buttare via tutte le istruzioni nella pipeline.

    Del resto, gli interrupt potrebbero essere bloccati. Quindi procedi fino a quando gli interrupt non vengono sbloccati e POI li butti via.

    Ora, queste macchine non sono esattamente semplici condotte a 5 stadi. Tuttavia, questa osservazione – che la maggior parte delle macchine buttano via tutte le istruzioni nella pipeline, nelle condutture prima del pipestage dove vive la logica dell’interrupt – rimane quasi universalmente vera.

    Nelle macchine semplici la logica dell’interrupt vive tipicamente nell’ultima fase della pipeline, WB, corrispondente all’incirca alla pipest di commit delle macchine avanzate. A volte viene spostato fino a un pipestage poco prima, ad esempio MEM nel tuo esempio. Quindi, su tali macchine, tutte le istruzioni in IF ID EX, e in genere MEM, vengono gettate via.

    — ++ Perché mi interessa: evitare lo spreco di lavoro

    Questo argomento è vicino e caro al mio cuore perché ho proposto di NON farlo. Ad esempio, durante le visite dei clienti mentre stavamo progettando di build il P6, ho chiesto ai clienti quali preferivano: interruzioni di latenza più basse, istruzioni di flushing in volo o (leggermente) maggiore throughput, consentendo almeno alcune delle istruzioni in volo da completare, al costo di una latenza leggermente più lunga.

    Tuttavia, sebbene alcuni clienti preferissero quest’ultimo, abbiamo scelto di fare la cosa tradizionale, sciacquando immediatamente. A parte la bassa latenza, la ragione principale è la complessità:

    Es. Se prendi un interrupt, ma se una delle istruzioni già in volo prende anche un’eccezione, dopo aver fermato IF (recupero delle istruzioni) ma prima che qualsiasi istruzione nell’interrupt abbia commesso, che ha la priorità? A: dipende. E quel genere di cose è un dolore da affrontare.

    — +++ Folclore: batch di interrupt del mainframe OS

    Questo è un po ‘come il funzionamento di alcuni sistemi operativi mainframe IBM:

    • con tutti gli interrupt bloccati nel normale funzionamento tranne per l’interruzione del timer;
    • nell’interruzione del timer, si sbloccano gli interrupt e li si gestisce tutti;
    • e quindi tornare al normale funzionamento con la modalità bloccata degli interrupt

    È plausibile che possano utilizzare solo tale modalità di “interrupt batching” quando sono fortemente caricati; se caricati leggermente, potrebbero non bloccare gli interrupt.

    — +++ Deferred Machine Controlla le eccezioni

    Anche l’idea di posticipare le interruzioni per dare istruzioni già in fase di esecuzione è simile a quella che io chiamo “Deferred Machine Check Exception” – un concetto che ho incluso nella famiglia originale Intel Check Machine Architecture, circa 1991-1996, ma che sembra non essere stato rilasciato.

    Ecco il problema: errori di controllo della macchina come (un) errori ECC correggibili possono verificarsi DOPO che un’istruzione si è ritirata (cioè dopo che le istruzioni presumibilmente più giovani hanno commesso lo stato, ad esempio registri scritti), o PRIMA che l’istruzione sia andata in pensione.

    Il classico esempio di errori AFTER è un ECC non correggibile triggersto da un archivio che viene inserito in un buffer di scrittura alla laurea. Praticamente tutte le macchine moderne fanno questo, tutte le macchine con TSO, il che significa praticamente che c’è sempre la possibilità di un errore di controllo della macchina impreciso che avrebbe potuto essere preciso se ci tenesse abbastanza a non tamponare i negozi.

    Il classico esempio di errori BEFORE è … beh, ogni istruzione, su qualsiasi macchina con una pipeline. Ma, cosa più interessante, errori su istruzioni di percorso errato, nell’ombra di una errata predicazione di ramo.

    Quando un’istruzione di caricamento ottiene un errore ECC non correggibile, hai due possibilità:

    (1) si potrebbe tirare la catena immediatamente, uccidendo non solo le istruzioni YOUNGER rispetto alle istruzioni di caricamento, ma anche le istruzioni più vecchie

    (2) oppure potresti scrivere una sorta di codice di stato nella logica che controlla la speculazione e prendere l’eccezione al momento del pensionamento. Questo è praticamente ciò che devi fare per un errore di pagina, e rende tali errori precisi, aiutando il debugging.

    (3) Ma cosa succede se l’istruzione di caricamento che ha ottenuto l’errore ECC non correggibile era un’istruzione di percorso errata, e non si ritira mai perché un vecchio ramo in volo è stato male interpretato ed è andato in un altro modo?

    Bene, potresti scrivere lo stato per cercare di renderlo preciso. Dovresti avere contatori di errori precisi e errori imprecisi. Si potrebbe altrimenti ignorare un errore su tale istruzione di percorso errato – dopotutto, se si tratta di un errore grave, verrà nuovamente toccato, oppure potrebbe non esserlo./ Ad esempio è ansible che l’errore sia architettonicamente silenzioso – ad esempio una riga di cache non valida potrebbe essere sovrascritta da una buona linea di cache per lo stesso indirizzo.

    E, se lo volessi davvero, potresti impostare un po ‘in modo che se un ramo più vecchio predispone male, allora prendi l’eccezione di controllo della macchina in quel momento.

    Tale errore non si verificherebbe in un contatore di programma associato all’istruzione che ha causato l’errore, ma potrebbe ancora avere uno stato altrimenti preciso.

    Chiamo (2) rinvio di un’eccezione di controllo della macchina; (3) è proprio come potresti gestire il differimento.

    IIRC, tutte le eccezioni di controllo della macchina Intel P6 erano imprecise.

    — ++ Sulla mano che stringe: ancora più veloce

    Quindi, abbiamo discusso

    0) prendendo immediatamente l’interrupt o, se gli interrupt sono bloccati, eseguendo le istruzioni e le microistruzioni fino al raggiungimento di un punto di interruzione non bloccato. E poi il lavaggio di tutte le istruzioni in volo.

    1) cercando di eseguire le istruzioni nella pipeline, in modo da evitare lo spreco di lavoro.

    Ma c’è una terza possibilità:

    -1) se si dispone di checkpoint di stato di microarchitettura, interrompere immediatamente l’interruzione, senza mai attendere un punto di interruzione non bloccato. Che puoi fare solo se hai un punto di controllo di tutti gli stati rilevanti nel punto più recente “sicuro di prendere un interrupt”.

    Questo è ancora più veloce di 0), motivo per cui l’ho etichettato -1). Ma richiede i checkpoint, che utilizzano molte ma non tutte le CPU aggressive, ad esempio Intel P6 dod non usa i checkpoint. E questi punti di controllo post-pensionamento diventano funky in presenza di memoria condivisa – dopo tutto, puoi fare operazioni di memoria come carichi e negozi mentre gli interrupt sono bloccati. E puoi persino comunicare tra le CPU. Anche la memoria transazionale dell’hardware di solito non lo fa.

    — + Le eccezioni contrassegnano le istruzioni interessate

    Viceversa, eccezioni, cose come errori di pagina, segnano l’istruzione interessata.

    Quando quell’istruzione sta per eseguire il commit, a quel punto tutte le istruzioni successive dopo l’eccezione vengono scaricate e il recupero delle istruzioni viene reindirizzato.

    È plausibile che il recupero delle istruzioni possa essere anticipato prima, il modo in cui le previsioni errate vengono già gestite sulla maggior parte dei processori, nel punto in cui sappiamo che l’eccezione si verificherà. Non conosco nessuno che lo faccia. Sugli attuali carichi di lavoro, le eccezioni non sono così importanti.

    — + “Interrupt software”

    Gli “interrupt software” sono un’istruzione erroneamente associata di solito alle chiamate di sistema.

    Concepibilmente, tale istruzione potrebbe essere gestita senza interrompere la pipeline, prevista come un ramo.

    Tuttavia, tutte le macchine con cui ho familiarità con la serializzazione in qualche modo. Nel mio linguaggio, non rinominano il livello di privilegio.

    — + “Interruzioni precise”, EMON, PEBS

    Un altro poster menzionava interruzioni precise.

    Questo è un termine storico. Sulla maggior parte delle macchine moderne, gli interrupt sono definiti per essere precisi. Le macchine più vecchie con interruzioni imprecise non hanno avuto molto successo sul mercato.

    Tuttavia, c’è un significato alternativo, sono stato coinvolto nell’introduzione: quando ho ottenuto Intel per aggiungere la capacità di produrre un interrupt sull’overflow del contatore delle prestazioni, prima usando l’hardware esterno, e poi all’interno della CPU, lo era, nelle prime generazioni , completamente impreciso.

    Ad esempio, è ansible impostare il contatore per contare il numero di istruzioni ritirate. La logica di pensionamento (RL) vedrebbe ritirarsi le istruzioni e segnalare il circuito di monitoraggio degli eventi delle prestazioni (EMON). Potrebbero essere necessari due o tre cicli di clock per inviare questo segnale da RL a EMON. EMON incrementerebbe il contatore e poi vedrebbe che c’è stato un overflow. L’overflow attiverebbe una richiesta di interrupt all’APIC (Advanced Programmable Interrupt Controller). L’APIC potrebbe richiedere alcuni cicli per capire cosa stava succedendo e quindi segnalare la logica della pensione.

    Cioè l’interruzione EMON sarebbe segnalata in modo impreciso. Non al momento dell’evento, ma un po ‘di tempo dopo.

    Perché questa imprecisione? Bene, nel 1992-6, l’hardware di misurazione delle prestazioni non era una priorità elevata. Stavamo sfruttando l’hardware di interrupt esistente. I mendicanti non possono essere scelti.

    Inoltre, alcune prestazioni sono intrinsecamente imprecise. Ad esempio, quando segnali un interrupt per un errore di cache su un’istruzione speculativa che non si ritira mai? (Ho uno schema che ho chiamato eventi Deferred EMON, ma questo è ancora considerato troppo costoso.) Per quello che riguarda i problemi di cache nelle istruzioni del negozio, dove il negozio è collocato in un buffer del negozio e l’istruzione è già andata in pensione?

    Cioè a volte gli eventi di performance si verificano dopo che l’istruzione a cui sono associati si è impegnata (ritirata). Qualche volta prima. E spesso non esattamente alle istruzioni a cui sono associati.

    Ma in tutte le implementazioni finora, per quanto ne so, questi eventi di performance sono trattati come interrupt: le istruzioni esistenti nella pipe vengono scaricate.

    Ora, puoi rendere preciso un evento di performance trattandolo come una trappola. Ad esempio, se si tratta di un evento come le istruzioni ritirate, è ansible avere immediatamente la trappola della logica di pensionamento, invece di prendere il ciclo tortuoso descritto sopra. Se si verifica prima nella pipeline, è ansible che si sia verificato che è stato contrassegnato nello stato di errore istruzione nel ROB (Re-Order Buffer). Qualcosa di simile è ciò che Intel ha fatto con PEBS (Precise Event Based Sampling). http://software.intel.com/sites/products/collateral/hpc/vtune/performance_analysis_guide.pdf .

    Tuttavia, si noti che non tutti gli eventi possono essere campionati utilizzando PEBS. Ad esempio, PEBS nell’esempio precedente può contare i carichi che hanno rilevato un errore o una cache, ma non i negozi (poiché i negozi si verificano in seguito).

    Quindi questo è come le eccezioni: l’evento viene consegnato solo quando l’istruzione si ritira. Perché in un certo senso l’evento non si è verificato completamente – si tratta di un’istruzione di caricamento, che prende un errore di cache, e quindi si ritira. E le istruzioni dopo l’istruzione PEBS contrassegnata vengono scaricate dalla tubazione.

    Spero — + Late Addition sui primi computer

    Per interruzioni precise, le istruzioni in volo prima che la fase IF salti all’ISR si ritirino normalmente. Quando l’ISR ritorna, l’esecuzione riprende iniziando dall’istruzione successiva dopo l’ultima istruzione ritirata del processo originale. In altre parole, le interruzioni precise avvengono sempre tra le istruzioni.

    L’elaborazione per gli interrupt sincroni è leggermente diversa. Prendendo x86 come esempio, le eccezioni sincrone hanno tre gusti, trappole, errori e aborti.

    Una trappola, come INT3, fa sì che il core spinga l’istruzione dopo la trappola in pila, in modo tale che quando l’ISR ritorna, il nucleo non riesca inutilmente a rieseguire la stessa istruzione di trapping.

    Un errore, come un errore di pagina, fa sì che il core spinga l’istruzione di errore nello stack, in modo tale che quando l’ISR ritorna, il core rieseguirà l’istruzione di errore, presumibilmente ora in circostanze che evitano di nuovo lo stesso errore.

    Un abort, come un doppio errore, è un problema irreversibile fatale in cui il processore non può riprendere l’esecuzione da dove era stato interrotto.

    Il contenuto del frame dello stack di interrupt spinto dal core prima di inserire l’ISR varia a seconda del caso di cui si sta parlando.