2016-06-18 30 views
7

Sui processori x86 esiste un modo per caricare i dati dalla normale memoria di scrittura nei registri senza passare attraverso la gerarchia della cache?Come evitare l'inquinamento della cache durante il caricamento di un flusso di numeri

Il mio caso d'uso è che ho una struttura di grande ingrandimento (Hash map o B-Tree). Sto lavorando su un grande flusso di numeri (molto più grande del mio L3 ma adatto alla memoria). Quello che sto cercando di fare è molto semplice:

int result = 0; 
for (num : stream_numbers) { 
    int lookup_result = lookup_using_b_tree(num); 
    result += do_some_math_that_touches_registers_only(lookup_result); 
} 
return result; 

Dato che io sto visitando ogni numero una sola volta e la somma di tutti i numeri è più che la dimensione L3 Immagino che finiranno per sfrattare alcune linee di cache che contengono parti del mio albero B. Preferirei invece non avere alcun numero da questa cache di esecuzione del flusso poiché non hanno alcuna localizzazione temporale (solo letti una volta). In questo modo posso massimizzare le possibilità che il mio albero B rimanga nella cache e le ricerche siano più veloci.

Ho esaminato le istruzioni (v)movntdqa disponibili in SSE 4.1 per i carichi temporali. Non sembra essere una buona idea, perché sembra funzionare solo per la scrittura non combinabile che combina la memoria. Questo antico article da Intel sostiene che:

Le future generazioni di processori Intel possono contenere ottimizzazioni e miglioramenti per carichi di streaming, come ad esempio un maggiore utilizzo dei buffer di carico di streaming e il supporto per i tipi di memoria aggiuntive, creando ancora più opportunità per il software sviluppatori per aumentare le prestazioni e l'efficienza energetica delle loro applicazioni.

Tuttavia, non sono a conoscenza di alcun processore di oggi. Ho letto elsewhere che un processore può semplicemente scegliere di ignorare questo suggerimento per la memoria di scrittura e utilizzare invece uno movdqa. Quindi c'è un modo per ottenere carichi dalla normale memoria di scrittura senza passare attraverso la gerarchia della cache sui processori x86, anche se è possibile solo su Haswell e sui modelli successivi? Gradirei anche qualche informazione su se questo sarà possibile in futuro?

+1

Una domanda simile è stata posta di recente, potreste essere interessati anche a [questo] (http://stackoverflow.com/q/28684812/417501). – fuz

+0

@FUZxxl: Questo è stato chiarito come motivo di benchmarking, che è piuttosto diverso. Questo è più simile a http://stackoverflow.com/questions/37889896/intel-instructions-for-access-to-memory-which-skips-cache, che in realtà non è un duplicato della domanda di benchmarking. –

+1

AFAIK non c'è un modo affidabile/garantito per farlo. 'prefetchnta' potrebbe essere utile, ma, ancora una volta, non è chiaro se può fare qualcosa di utile, dal momento che non sovrascrive la semantica della coerenza della cache di ordine forte dei tipi di memoria WB. Penso che il meglio che puoi sperare sia prefetchnta o movntdqa da caricare nella cache e impostare i dati LRU per quella linea per indicare che sarebbe un buon obiettivo di sfratto. Quindi, se l'hardware funziona effettivamente in questo modo, si spera che i dati di questo stream eliminino semplicemente le righe precedenti dallo stesso flusso una volta che ha una voce in ogni set. –

risposta

0

Sì, è possibile utilizzare MOVNTI per memorizzare i valori direttamente in memoria senza che questi tocchino la cache.

La latenza di un MOVNTI è di circa 400 cicli (su Skylake).
Tuttavia, se si memorizzano solo i valori, si preoccupa poco della latenza e molto di più sul rendimento reciproco, che è 1 ciclo per MOVNTI.

Nota che è necessario eseguire un SFENCE o un MFENCE dopo aver finito con i negozi.

Secondo la mia sperimentazione con MOVNTI (nel contesto di una routine ZeroMem) vale la pena se si scrivono più di 512 KB.
I valori esatti dipenderanno essenzialmente dalla dimensione della cache ecc

La non-temporalness si applica solo ai scrive, non legge!
In effetti non conosco alcuna variante di NT-mov che funzioni in modo non temporale durante la lettura dei dati.

Tuttavia, se si sta eseguendo un ciclo di lettura-modifica-scrittura, ha poco senso utilizzare le mosse non temporali.
È inoltre necessario prendere in considerazione la località della struttura del nodo.
E 'probabile che si presenta così:

left, right: pointer_to_node (8 bytes aligned on 32 byte boundary). 
data: integer;    (4 bytes) 
.... 

Se è così voi leggendo il puntatore del nodo left/right succhierà il data lungo all'interno nella linea di cache di 32 byte (*).
Solo fare un NT-mov sui dati non aiuta qui, è già stato risucchiato durante la lettura degli altri dati del nodo e quindi è già nella cache.

Il fatto che i compilatori allineano la struttura dei dati sui limiti di cache friendly assicura che la quantità massima di dati del nodo venga aspirata nella cache con ogni accesso al puntatore del nodo.

(*) La dimensione della linea della cache dipende dal processore.

+1

movnti non funziona come carico. L'unica istruzione di caricamento NT è [MOVNTDQA] (http://www.felixcloutier.com/x86/MOVNTDQA.html). Non sovrascrive la semantica degli ordini, quindi non penso che possa saltare completamente cache. http://stackoverflow.com/a/37891933/224132 –

+0

@PeterCordes, premo save early e spesso, così puoi tranquillamente ignorare le prime bozze :-). Per quanto ne so, MOVNTDQA (durante la lettura) è più un proofing futuro piuttosto che una funzione 'performs as advertised'. Grazie per il testa a testa. – Johan

+1

@PeterCordes Non penso sia possibile saltare completamente la cache durante la lettura della memoria normale. Ma dal lato teorico, ci sono due cose che * potrebbero * essere utili. 'prefetchnta' e' clflushopt'. Se 'prefetchnta' fa quello che dovrebbe, non inquinerà le cache L2 e L3. 'clflushopt' è una novità di Skylake ed è una versione veloce (rilassata e ordinata) del vecchio' clflush'. Quindi tu 'prefetchnta' in L1. Carica l'intera riga della cache, quindi "clflushopt".In teoria, questo non dovrebbe toccare L2/L3 e minimizzare l'impatto su L1. – Mysticial

Problemi correlati