Perché i motori devono essere ottimizzati per i nuovi processori della stessa architettura?


39

Quando viene rilasciata una nuova generazione di processori, la maggior parte dei siti Web segnala che i motori di gioco e i programmi devono essere ottimizzati per il nuovo hardware. Non capisco bene perché. Un processore di solito ha un'architettura che definisce il tipo di set di istruzioni che utilizza. Quello che tutti noi usiamo al giorno d'oggi è amd_x86_64. Perché un programma o un compilatore dovrebbe essere aggiornato se tutti i processori utilizzano questa stessa architettura? Sicuramente ci sono funzionalità ENTRO la pipeline del nuovo processore che ottimizza l'esecuzione del codice macchina, ma perché il codice macchina stesso dovrebbe essere modificato se l'architettura no?


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Josh

14
"Need" è una formulazione errata, e più marketing che verità, più o meno allo stesso modo in cui Windows deve supportare una certa nuova generazione di CPU (o non funziona, come nel caso di Windows 7, che in linea di principio funzionerebbe perfettamente bene con es. Ryzen, tranne per l'utilizzo del 3-4% di potenza in più del necessario). Questa messa a punto riguarda solo il tentativo di spremere un po 'di più dalla CPU, avvicinandosi al massimo. Realisticamente, potresti essere in grado di ottenere un totale dell'1-2% in esempi non controversi a causa della diversa pianificazione e utilizzando un paio di nuove istruzioni.
Damon,

2
Solo perché due processori possono eseguire le stesse operazioni, il che non significa che le operazioni abbiano le stesse prestazioni su entrambi i processori ...
Mehrdad,

Vedi una mia domanda correlata su StackTranslate.it
Marc.2377,

Risposte:


54

Perché generazioni diverse della stessa architettura possono avere set di istruzioni diversi .

Ad esempio, Streaming di estensioni SIMD è probabilmente il set di istruzioni x86 più noto, ma ancora, e nonostante ci sia solo un'architettura x86, esistono SSE, SSE2, SSE3 e SSE4.

Ognuna di queste generazioni può includere nuove istruzioni che forniscono metodi più rapidi per eseguire determinate operazioni. Un esempio rilevante per i giochi potrebbe essere rappresentato dalle istruzioni del prodotto.

Quindi, se un motore di gioco viene compilato per una generazione precedente di un'architettura, non avrà supporto per queste nuove istruzioni. Allo stesso modo, potrebbe essere necessario ottimizzare il motore per istruzioni più recenti; SSE4 , ad esempio, supporta le istruzioni del prodotto dot che funzionano su dati array-of-struct. Un'ottimizzazione che potrebbe trarre vantaggio da queste nuove istruzioni sarebbe quella di cambiare il layout dei dati in array di strutture.


1
@Panzercrisis - grazie per il suggerimento di modifica. Per essere chiari: la domanda originale non riguardava il tuo codice, si trattava del codice del motore, quindi "ottimizzare il tuo codice" non è un buon suggerimento di modifica. Tuttavia, ha messo in evidenza che avevo bisogno di chiarire che quando ho detto "ottimizza" intendevo "ottimizzare il codice del motore", quindi ho modificato per riprenderlo.
Maximus Minimus,

37

La risposta di Massimo è corretta, voglio solo dare un altro pezzo della storia:

L'hardware stesso cambia nel modo in cui è necessario modificare il modo in cui si codifica, indipendentemente dalle istruzioni appena introdotte.

  • L'aumento o la diminuzione della quantità di cache significa che devi preoccuparti di meno o di più sull'ottimizzazione della cache / invalidazione della cache in caso di problemi. Più cache significa che con dati di piccole dimensioni è possibile concentrarsi meno sull'assicurarsi che i dati siano contigui e che non si verifichino problemi di prestazioni. Meno cache significa che questo potrebbe essere un problema e pochissima cache significa che con alcune grandi strutture dati non importa in alcun modo.

  • Nuovi livelli di cache significano che devi pensare di più a come organizzare set di dati ancora più grandi (L1, vs L2, vs L3 vs L4).

  • Più core significa che devi pensare a come farai meglio le applicazioni multi-thread e come la tua applicazione si ridimensiona in un ambiente multi-processo.

  • Orologi più veloci significano che è necessario iniziare a pensare alla latenza della memoria più di quanto sia necessario pensare alla velocità di calcolo della CPU come un collo di bottiglia del sistema.

  • Il numero di FPU su un sistema potrebbe non corrispondere più al numero di ALU interi per core (AMD aveva / ha architetture come questa).

  • Il numero di cicli di clock necessari per calcolare un'operazione è diminuito o aumentato.

  • Il numero di registri disponibili è cambiato.

Tutti questi hanno un impatto molto reale sulle prestazioni dei programmi che hanno fatto ipotesi sull'architettura sottostante nell'hardware precedente con lo stesso ISA, positivo o negativo.


"Livelli aumentati o diminuiti di cache significano che devi preoccuparti meno della coerenza della cache." - praticamente ogni CPU è coerente con la cache. Vuoi dire falsa condivisione? Anche praticamente qualsiasi linea $ CPU è quasi sempre 64 B ...
Maciej Piechotka il

1
Maciej stava solo prendendo le tue dichiarazioni sulla coerenza della cache :) Probabilmente volevi dire "ottimizzazione della cache" o qualcosa del genere. La coerenza della cache è la capacità di un sistema di mantenere una visione coerente della memoria in modo trasparente al software anche se in presenza di N cache indipendenti . Questo è completamente ortogonale alla dimensione. TBH l'affermazione non è realmente pertinente ma la tua risposta (in particolare i punti 5 e 6) affronta la domanda meglio di quella accettata IMO :) Forse sottolineando la differenza tra architettura e u-architecture, la farai risaltare di più.
Margaret Bloom,

4
"come la moltiplicazione richiede più tempo dell'aggiunta, dove come oggi su Intel moderna e su CPUS ci vuole la stessa quantità di tempo" Non è tutto vero. Nelle architetture pipeline è necessario distinguere tra latenza (quando il risultato è pronto) e velocità effettiva (quante è possibile fare per ciclo). L'aggiunta Int sui moderni processori Intel ha un throughput di 4 e una latenza di 1. Moltiplica ha un throughput 1 e una latenza 3 (o 4). Queste sono le cose che cambiano con ogni architettura e necessitano di ottimizzazione. Ad esempio, pdepprende 1 ciclo su Intel ma 6 su Ryzen, quindi potrebbe non voler usarlo su Ryzen.
Christoph,

2
@Clearer So che stiamo parlando di CPU qui, ma non hai mai programmato per le GPU, vero? Lo stesso codice produce risultati così incredibilmente diversi nelle prestazioni che spesso sei costretto a prendere in considerazione le capacità hardware in CUDA. Ecco da dove provengo questo, le dimensioni della cache (memoria condivisa, cache L1 gestita) devono in realtà essere prese in considerazione nel modo in cui codifichi qualcosa in CUDA.
quando il

2
@Christoph è corretto. Il benchmark che colleghi è per un loop su un array c[i] = a[i] OP b[i](ovvero 2 carichi e 1 archivio per operazione), quindi i tempi sono dominati dalla larghezza di banda della memoria a causa dell'intensità computazionale molto bassa. La dimensione dell'array non viene visualizzata in modo IDK se si adatta a L1D. ( gcc4.9 -Ofastmolto probabilmente auto-vettorializza quei loop, quindi non stai nemmeno misurando il costo delle normali operazioni scalari come parte di un codice intero complesso). La prima riga di quella pagina è IMPORTANTE: un feedback utile ha rivelato che alcune di queste misure sono gravemente difettose. Un importante aggiornamento è in arrivo .
Peter Cordes,

2

Anche al di là di cambiamenti grossolani come il supporto di nuove istruzioni, i produttori di microprocessori modificano costantemente i loro progetti per migliorare le prestazioni e ogni nuovo progetto può avere prestazioni relative diverse per ciascuna istruzione o tecnica. Forse hai scritto un codice branchless attentamente ottimizzato per il Modello X, ma il Modello Y ha un predittore di ramo migliorato che riduce la penalità di errore per la versione non branchless del codice (che libera anche un registro da utilizzare altrove) . Forse il Modello Y supporta un maggiore parallelismo di una determinata istruzione ad alta latenza, in modo che ora un ciclo srotolato di tale istruzione ti dia un rendimento migliore, mentre sul Modello X una sequenza più breve era migliore.

Qualsiasi problema può essere risolto in molti modi e ogni programma è una raccolta ad incastro di compromessi e allocazioni di risorse, dal punto di ottimizzazione. Anche piccoli cambiamenti nella disponibilità di tali risorse o nel costo di un determinato pezzo di codice in termini di tali risorse, possono avere un effetto a cascata che offre un sostanziale vantaggio prestazionale a un pezzo di codice o all'altro. Anche se un chip aggiornato ha "più di tutto", come molto di più di ogni cosa può oscillare la bilancia.

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.