Cosa è importante quando si ottimizza per la cache della CPU (in C)?


13

Leggendo queste due domande , vedo che comprendere il comportamento del caching della CPU può essere importante quando si ha a che fare con grandi quantità di dati in memoria. Vorrei capire come funziona la memorizzazione nella cache per aggiungere un altro strumento al mio toolbox di ottimizzazione.

Quali sono i punti fondamentali su come funziona la cache della CPU in modo che io possa scrivere il codice che lo utilizza in modo ragionevole? Allo stesso modo, c'è un modo per profilare il codice per vedere se un uso scadente della cache rallenta le cose?


Le cache non sono le stesse ovunque; ovviamente, hanno dimensioni variabili. Non aspettarti di apprendere segreti profondi, solo buone pratiche (come il consiglio di Michael Borgwardt).
David Thornley,

Risposte:


17
  • Mantieni i tuoi dati piccoli se possibile
  • Tieni le cose a cui accederai insieme (o subito dopo l'altro) uno accanto all'altro in memoria
  • Scopri i parametri di ottimizzazione del tuo compilatore
  • Leggi ciò che ogni programmatore dovrebbe sapere sulla memoria per maggiori dettagli di quanti tu possa desiderare

+1 per "Mantieni le cose a cui si accederà l'una accanto all'altra"; è quello che è facile dimenticare.
Donal Fellows,

E dì al compilatore di ottimizzare.
destra del

@WTP: a destra - aggiunto.
Michael Borgwardt,

Inoltre, mantieni i mutex ben separati. La modifica di un mutex (dovrebbe) svuotare tutte le righe della cache in cui si trova, su tutte le CPU. Questo può essere un grande successo se sei riuscito a ottenere 2-3 mutex in una singola riga della cache.
Vatine,

12

La complessità di questo problema è stata al di là della comprensione umana in questi giorni. (È stato così dagli ultimi 5 anni.) Combinalo con il parallelismo a vettore corto (SIMD) e hai la sensazione disperata che l'ottimizzazione del codice a mano non sia più economicamente fattibile - non che non sia possibile, ma lo farebbe non essere più conveniente.

L'approccio attuale è quello di fare affidamento sull'insegnamento ai computer su come ottimizzare, apportando variazioni di codice che calcolano le stesse risposte con strutture diverse (loop, struttura dati, algoritmi) e valutano automaticamente le prestazioni. Le regole per la trasformazione del codice sono specificate con un modello matematico molto rigoroso, in modo che sia qualcosa che entrambi gli informatici possano capire e che i computer possano eseguire.

Quello che segue è un link pubblicato da Larry OBrien in una delle sue risposte .

http://onward-conference.org/2011/images/Pueschel_2011_AutomaticPerformanceProgramming_Onward11.pdf


2
l'implementazione BLAS più veloce (GotoBLAS) utilizza un codice ottimizzato a mano per garantire il massimo utilizzo della cache per la moltiplicazione della matrice
quant_dev

2

È del tutto possibile comprendere e ottimizzare le cache. Inizia con la comprensione dell'hardware e continua con il controllo del sistema. Meno controllo hai sul sistema, meno probabilità avrai di riuscire. Linux o Windows che eseguono un sacco di applicazioni / thread che non sono inattivi.

La maggior parte delle cache sono in qualche modo simili nelle loro proprietà, usano alcune parti del campo dell'indirizzo per cercare hit, hanno una profondità (vie) e una larghezza (linea di cache). Alcuni hanno buffer di scrittura, alcuni possono essere configurati per scrivere o bypassare la cache in scrittura, ecc.

È necessario essere consapevoli di tutte le transazioni di memoria in corso che colpiscono quella cache (alcuni sistemi dispongono di istruzioni indipendenti e cache di dati che semplificano l'attività).

Puoi facilmente rendere inutile una cache non gestendo con cura la tua memoria. Ad esempio, se hai più blocchi di dati che stai elaborando, sperando di tenerli nella cache, ma sono in memoria in indirizzi che sono anche multipli rispetto al controllo hit / miss della cache, diciamo 0x10000 0x20000 0x30000 e ne hai più di questi modi diversi nella cache, potresti finire molto rapidamente facendo qualcosa che funziona abbastanza lentamente con la cache attiva, più lenta di quanto farebbe con la cache spenta. Ma cambiarlo in forse 0x10000, 0x21000, 0x32000 e questo potrebbe essere sufficiente per sfruttare appieno la cache, riducendo gli sfratti.

In conclusione, la chiave per l'ottimizzazione di una cache (beh, oltre a conoscere il sistema abbastanza bene) è quella di mantenere tutte le cose per le quali hai bisogno di prestazioni nella cache allo stesso tempo, organizzando quei dati in modo che sia possibile avere tutto nella cache in una volta. E prevenire cose come l'esecuzione di codice, interruzioni e altri eventi regolari o casuali dallo sfratto porzioni significative di questi dati che si stanno utilizzando.

Lo stesso vale per il codice. È un po 'più difficile, poiché è necessario controllare le posizioni in cui si trova il codice per evitare collisioni con altro codice che si desidera conservare nella cache. Durante il test / profiling di qualsiasi codice che passa attraverso una cache che aggiunge una singola riga di codice qua e là o anche una singola nop, tutto ciò che sposta o cambia gli indirizzi in cui il codice vive da una compilazione all'altra per lo stesso codice, cambia dove le righe della cache rientrano in quel codice e cambiano ciò che viene sfrattato e cosa no per le sezioni critiche.


1

Entrambi di nwong e di Michael Borgwardt risposte dare buoni consigli.

Inoltre, affidati innanzitutto alle ottimizzazioni del compilatore su questi problemi.

Se si utilizza un compilatore GCC recente, è possibile utilizzare (con parsimonia) la sua __builtin_prefetchfunzione. Vedi questa risposta su StackOverflow.

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.