2015-07-25 29 views
25

Sto provando a decidere tra due algoritmi. Uno scrive 8 byte (due parole a 4 byte allineati) in 2 linee di cache, l'altro scrive 3 intere linee di cache.In che modo le CPU Intel Xeon scrivono in memoria?

Se la CPU scrive solo gli 8 byte modificati in memoria, il primo algoritmo utilizza molto meno larghezza di banda di memoria: 8 byte contro 192 byte. Se la CPU scrive intere linee di cache, allora la differenza tra 128 e 192 byte è meno sorprendente.

Quindi, in che modo una CPU Intel Xeon riscrive in memoria? Saresti sorpreso di quanto sia difficile trovare una risposta in Google a qualcosa che dovrebbe essere ben noto.

A quanto ho capito, le scritture vanno nel buffer del negozio e quindi nella cache. Potrebbero essere scritti solo in memoria quando la riga della cache sporca è sfrattata dalla cache, ma Intel tiene traccia di quali parti della linea della cache sono sporche, o semplicemente scarica l'intera cosa? Dubito piuttosto che monitorino le cose al di sotto della granularità della cache line. Sarei anche molto sorpreso se qualcosa va in memoria prima che la linea della cache sia sfrattata.

+7

Perché i downvotes? Questa è una bella domanda! – inf

+4

@inf Un ​​problema con la domanda è l'uso del termine "CPU Intel Xeon" non fa una distinzione utile qui. Il marchio Xeon è stato applicato alle CPU Intel x86 dall'architettura Pentium II. Tecnicamente non denota in realtà un tipo diverso di processore, tanto più che denota un diverso tipo di cliente verso il quale i processori sono commercializzati. Limitando la domanda solo alle CPU di "classe enterprise", è meno utile di quella che si è appena posta sulle CPU Intel x86 in generale. La risposta sarà la stessa in entrambi i casi. –

+3

@RossRidge Bene, quindi, semplicemente chiedere chiarimenti a quale architettura si sta riferendo e non andare su una follia di downvote. – inf

risposta

16

La località è importante anche per la DRAM stessa, anche per il caching. Una scrittura burst di 64 byte contigui per una cache line sporca è molto più veloce di 16 scritture da 4B a 16 indirizzi diversi. O per dirla in un altro modo, scrivere di nuovo un'intera linea di cache non è molto più lento di scrivere solo pochi byte modificati in una linea di cache.

What Every Programmer Should Know About Memory, di Ulrich Drepper, spiega molte cose su come evitare i colli di bottiglia della memoria durante la programmazione. Include alcuni dettagli sull'indirizzamento DRAM. I controller DRAM devono selezionare una riga e quindi selezionare una colonna. L'accesso a un'altra pagina di memoria virtuale può anche causare una mancanza TLB.

DRAM dispone di un comando di trasferimento a raffica per il trasferimento di un blocco sequenziale di dati. (Ovviamente progettato per il beneficio delle CPU che scrivono le linee della cache). Il sistema di memoria nei computer moderni è ottimizzato per il modello di utilizzo della scrittura di intere linee di cache, perché è quello che succede quasi sempre.

Le linee della cache sono l'unità su cui le CPU tracciano tracce di sporco o meno. Sarebbe possibile tracciare lo sporco con una linea più piccola rispetto alle linee cache attuali o non, ma ciò richiederebbe transistor aggiuntivi e non ne vale la pena. I livelli multipli di cache sono impostati per trasferire intere linee di cache, in modo che possano essere il più veloci possibile quando è necessario leggere un'intera riga della cache.

Esistono letture/scritture non temporali (movnti/movntdqa) che ignorano la cache. Questi sono per l'uso con i dati che non saranno toccati di nuovo fino a quando non sarebbero stati sfrattati dalla cache comunque (da qui il non temporale).Sono una cattiva idea per i dati che potrebbero trarre vantaggio dalla memorizzazione nella cache, ma consentirebbero di scrivere 4 byte in memoria, anziché un'intera riga della cache. A seconda del MTRR per quell'intervallo di memoria, la scrittura potrebbe o non potrebbe essere soggetta a combinazioni di scrittura. (Questo è rilevante per le regioni di I/O mappate in memoria, dove due scritture 4B adiacenti non sono le stesse di una scrittura 8B.)

L'algoritmo che tocca solo due linee di cache ha certamente il vantaggio su quel punteggio, a meno che ci vuole molto più calcolo, o soprattutto ramificazione, per capire quale memoria scrivere. Forse fai una domanda diversa se vuoi aiutare a decidere. (vedi i link a https://stackoverflow.com/tags/x86/info, esp Agner Fog's guides, per informazioni che ti aiuteranno a decidere da solo.)

Vedere la risposta di Cornstalks per gli avvertimenti sui pericoli di avere più thread su diverse CPU che toccano la stessa memoria. Questo può portare a rallentamenti maggiori rispetto alle scritture extra per un programma a thread singolo.

+0

Ho dimenticato le scritture non temporali. Con quelli posso davvero fare in modo che l'algoritmo scriva solo 8 byte, e non è probabile che venga presto riletto, quindi potrebbe funzionare. Ora devo considerare seriamente quale algoritmo usare. – Eloff

+0

La cache sarà una grande accelerazione se sarà * scritta * di nuovo presto, non solo la lettura. Ma vale la pena provare con 'movnti' di un' int32_t' o 'int64_t'. Se il modello di accesso è sequenziale (anche con un passo moderato), il prefetching di HW porterà i dati dalla DRAM alla cache in modo che le scritture siano veloci. ('movnti' sconfiggerà questo).Penso che sia piuttosto raro che "movnt" sia una vittoria, ma questo potrebbe essere uno di quei casi. Idealmente, puoi testare entrambi i modi nel contesto del tuo programma completo, non di un microbench. –

+1

@Eloff, non temporali non sono utili in questo scenario, se non possono combinarsi con linee complete a 64B, non scriveranno ancora i byte parziali (immagina di avere la linea modificata localmente in qualche altra cache del core - chi sta per fare l'unione?). Penso che solo alcuni tipi di memo non memorizzabili lo consentiranno, e la penalità è enorme (probabilmente snoop la linea da tutti gli altri core prima per evitare la perdita di coerenza), non è usata per le prestazioni, solo per scopi funzionali come dispositivi/IO – Leeor

7

Affinché la CPU possa scrivere solo i byte sporchi in memoria, è necessario memorizzare un bit dirty per ogni byte nella cache. Questo è irrealizzabile e non viene fatto su CPU moderne (per quanto ne so). Le CPU hanno solo un bit sporco per una linea cache. Scrivendo su qualsiasi byte nella riga della cache, l'intera riga viene contrassegnata come sporca.

Quando arriva il momento di svuotare la riga della cache sporca, l'intera riga deve essere scritta, poiché la CPU non ha idea di quale/i byte/i sia cambiato/i.

Questo può essere visto nelle politiche di invalidazione della cache in cui la scrittura su una riga della cache in un core può invalidare la linea della cache in un core differente (poiché le due linee della cache si associano allo stesso indirizzo), anche se il primo nucleo è utilizzando la metà della linea della cache e il secondo core utilizza la metà superiore della linea della cache. Cioè, se il core 1 scrive sul byte N e il core 2 sta usando il byte N + 1, il core 2 deve ancora aggiornare la linea della cache anche se io e te sappiamo che non è necessario.

+0

Penso che Xeon's scriva 64 bit alla volta sul bus della memoria? Quindi si potrebbe immaginare di avere 8 bit sporchi per riga di cache per tracciare quale parola di 8 byte è sporca. Non penso che sia effettivamente fatto. A proposito, hai appena descritto il motivo per cui la falsa condivisione è una cosa con il tuo esempio di invalidazione della cache. I core possono finire a ping-pong una linea cache avanti e indietro anche se non hanno realmente bisogno di entrare in conflitto l'uno con l'altro. – Eloff

+0

@Eloff: Si potrebbe immaginare di avere 8 bit sporchi per una linea della cache, ma non penso che abbia senso. L'intera ragione per avere una linea di cache è di avere un'unità fondamentale di localizzazione spaziale su cui la CPU opera. Se la linea della cache può essere ulteriormente suddivisa in singoli blocchi dalla CPU, allora non è più una linea di cache, vero? – Cornstalks

+0

concordato, non ha senso né per me – Eloff