Ho esaminato l'articolo di Martin Thompson. Che è una spiegazione della condivisione falsa.Perché è falso condividere un problema se la variabile modificata da un thread è contrassegnata come volatile
http://mechanical-sympathy.blogspot.co.uk/2011/07/false-sharing.html
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
static
{
for (int i = 0; i < longs.length; i++)
{
longs[i] = new VolatileLong();
}
}
public FalseSharing(final int arrayIndex)
{
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception
{
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() -start));
}
private static void runTest() throws InterruptedException
{
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++)
{
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads)
{
t.start();
}
for (Thread t : threads)
{
t.join();
}
}
public void run()
{
long i = ITERATIONS + 1;
while (0 != --i)
{
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong
{
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6; // comment out
}
}
L'esempio dimostra il rallentamento sperimentato da più thread invalidanti la linea di cache di ogni altra anche se ogni aggiornamento solo una variabile esclusivamente.
BlockqFigure 1. in alto illustra il problema della condivisione errata. Un thread in esecuzione sul core 1 vuole aggiornare la variabile X mentre un thread sul core 2 vuole aggiornare la variabile Y. Purtroppo queste due variabili hot si trovano nella stessa riga della cache. Ogni thread correrà per la proprietà della linea cache in modo che possano aggiornarlo. Se il core 1 acquisisce la proprietà, il sottosistema della cache dovrà invalidare la riga della cache corrispondente per il core 2. Quando Core 2 acquisisce la proprietà e esegue l'aggiornamento, al core 1 verrà detto di invalidare la sua copia della linea della cache. Questo ping ping avanti e indietro attraverso la cache L3 influenzando notevolmente le prestazioni. Il problema verrebbe ulteriormente aggravato se i nuclei concorrenti si trovano su socket diversi e in aggiunta devono attraversare l'interconnessione socket.
La mia domanda è la seguente. Se tutte le variabili in fase di aggiornamento sono volatili, perché questo riempimento causa un aumento delle prestazioni? La mia comprensione è che una variabile volatile scrive e legge sempre nella memoria principale. Quindi suppongo che ogni scrittura e lettura su qualsiasi variabile in questo esempio comporterà un flush della linea corrente della cache dei core.
Quindi secondo la mia comprensione. Se il thread uno invalida la cache di thread due, questo non diventerà apparente al thread due fino a quando non andrà a leggere un valore dalla propria linea cache. Il valore che sta leggendo è un valore volatile, quindi questo rende efficacemente la cache sporca, con conseguente lettura dalla memoria principale.
Dove ho sbagliato nella mia comprensione?
Grazie
'volatile' è un suggerimento per il compilatore, facendo sapere che un valore può cambiare, anche se non appare così localmente. Mentre questo causa una ricarica, può essere letto dalla cache. –
@JohnnyCage Questo non è un problema. Volatile è una parola chiave importante per il multi-threading (non un "suggerimento" per il compilatore), proprio come la parola chiave sincronizzata e la [Specifica del modello di memoria Java] (http://docs.oracle.com/javase/specs/jls/ se8/html/jls-17.html # jls-17.4) descrive la sua operazione. Il modo in cui il modello di memoria viene implementato dipende dalla JVM, dalla CPU e dall'architettura della memoria, ecc. E l'implementazione reale probabilmente ha un impatto maggiore su questo rispetto al JMM. –
L'ipotesi che imponga una memoria/lettura sincrona non è corretta. Inserisce [memory barriers] (https://www.kernel.org/doc/Documentation/memory-barriers.txt), ma la CPU è in grado di ottimizzarlo purché mantenga la coerenza. –