2015-07-02 6 views
6

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

+0

'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. –

+0

@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. –

+2

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. –

risposta

4

Se tutte le variabili in fase di aggiornamento sono volatili, perché questo imbottitura provoca un aumento delle prestazioni?

Quindi ci sono due cose che accadono qui:

  1. abbiamo a che fare con una serie di oggetti VolatileLong con ogni thread di lavoro per conto proprio VolatileLong. (Vedere private final int arrayIndex).
  2. Ciascuno dell'oggetto VolatileLong ha un singolo campo volatile.

Il volatile accesso significa che i fili devono sia invalidare la "linea" cache che trattengono il volatile long value e devono bloccare questa linea di cache di aggiornarlo. Come afferma l'articolo, una linea di cache è in genere ~ 64 byte o giù di lì.

L'articolo sta dicendo che aggiungendo padding all'oggetto VolatileLong, sposta l'oggetto che ognuno dei thread sta bloccando in diverse righe di cache.Quindi, anche se i diversi thread stanno ancora attraversando le barriere della memoria mentre assegnano il loro volatile long value, si trovano in una linea cache diversa e quindi non causeranno un'eccessiva larghezza di banda della cache L2.

In breve, l'aumento delle prestazioni avviene perché anche se i thread stanno ancora bloccando la linea cache per aggiornare il campo volatile, questi blocchi ora sono su blocchi di memoria diversi e quindi non si scontrano con i blocchi degli altri thread e causano invalidazioni della cache.

+0

+1 Mi è piaciuta la tua spiegazione! Ma non ho alcuna idea delle linee cache (a livello hardware) e L2..L (N) cache. Potete aiutarmi con qualche link per comprendere questi termini a livello hardware/software? –

+1

Ho appena fatto una ricerca per: cpu cache line. La pagina di Wikipedia sembra un buon punto di partenza per @VishalK: https://en.wikipedia.org/wiki/CPU_cache – Gray

Problemi correlati