2009-06-23 9 views
20

Interlocked.Increment(ref x) è più veloce o più lento di x++ per ints e long su varie piattaforme?Prestazioni di Interlocked.Increment

+0

Come altri sottolineano, non è la stessa cosa. Detto questo, secondo http://msdn.microsoft.com/en-us/magazine/cc163726.aspx un Interlocked.Increment richiede circa 14nS (o circa 71'000'000 al secondo), quindi non mi preoccuperei di molto sulle prestazioni – smirkingman

+0

Interlocked.Increment deve essere utilizzato negli ambienti thread – EProgrammerNotFound

risposta

37

È più lento poiché forza l'azione a verificarsi in modo atomico e funge da barriera di memoria, eliminando la capacità del processore di riordinare la memoria accede a tutte le istruzioni.

Si dovrebbe usare Interlocked.Increment quando si desidera che l'azione sia atomica su stato che può essere condivisa tra i thread, non è intesa come sostituzione completa per x ++.

3

Sarà sempre più lento perché deve eseguire un blocco del bus CPU rispetto all'aggiornamento di un registro. Tuttavia, le moderne CPU raggiungono quasi le prestazioni del registro, quindi è trascurabile anche nell'elaborazione in tempo reale.

+3

Mentre la CPU X86 esegue un blocco durante le operazioni Interlock, non è richiesto un blocco di bus da parte di tutte le CPU che forniscono operazioni interbloccate. Alcune CPU sono in grado di significare che hanno riservato una singola linea di cache e possono eseguire operazioni interbloccate su quella cacheline senza un buslock. – Adisak

4

È più lento. Tuttavia, è il modo generale più performante che io conosca per raggiungere la sicurezza del thread su variabili scalari.

+1

'volatile' è più performante su scalari, ma ha lo svantaggio di richiedere buone pratiche di codifica per essere usato bene. – Abel

+2

Attenzione con volatile; alcune architetture di processore (x86/x64) hanno la capacità di riordinare gli accessi alla memoria, indipendentemente dal fatto che tale memoria sia stata contrassegnata come volatile per il compilatore. –

6

Pensaci per un momento e ti renderai conto che una chiamata Increment non può essere più veloce di una semplice applicazione dell'operatore di incremento. Se lo fosse, allora l'implementazione del compilatore dell'operatore di incremento chiamerebbe internamente Increment, e avrebbero eseguito lo stesso.

Ma, come puoi vedere provandolo per te, non hanno lo stesso rendimento.

Le due opzioni hanno scopi diversi. Utilizzare generalmente l'operatore di incremento. Utilizzare Increment quando è necessario che l'operazione sia atomica e si è sicuri che tutti gli altri utenti di tale variabile stiano utilizzando anche operazioni interbloccate. (Se non collaborano tutti, non è di grande aiuto.)

+0

No, non sarebbe - Interlocked.Increment non può essere chiamato su una proprietà, mentre l'operatore ++ può. Pertanto, ++ non sarebbe in grado di chiamarlo. – SLaks

+0

Per essere più precisi, Increment prende un ref int (o long); ++ prende un non-ref int (o long) – SLaks

+3

Il compilatore potrebbe certamente implementare ++ tramite Increment. Non sarebbe implementato con una semplice istruzione "call", ma potrebbe essere fatto usando un temporaneo introdotto dal compilatore. Il punto è che il compilatore usa il metodo a digiuno disponibile per incrementare un numero; se ci fosse qualcosa di più veloce, il compilatore lo avrebbe invece usato. –

12

Nella nostra esperienza, InterlockedIncrement() e altri su Windows hanno impatti piuttosto significativi. In un caso campione siamo stati in grado di eliminare l'interlock e usare ++/- invece. Questo da solo ha ridotto il tempo di esecuzione da 140 a 110 secondi. La mia analisi è che l'interlock forza un roundtrip di memoria (altrimenti come potrebbero vederlo gli altri core?). Una cache L1 lettura/scrittura è di circa 10 cicli di clock, ma una memoria di lettura/scrittura più simile 100.

In questo caso di esempio, ho stimato il numero di operazioni di incremento/decremento a circa 1 miliardo. Quindi su una CPU da 2 GHz questo è qualcosa come 5 secondi per il ++/- e 50 secondi per l'interblocco. Diffondi la differenza su più thread e il suo vicino a 30 secondi.

+5

Micrsoft dice: InterlockedIncrement è stato misurato come prendendo 36-90 cicli in http://msdn.microsoft.com/en-us/library/windows/desktop/ee418650(v=vs.85).aspx – Lothar

+1

36 suoni giusti per non contestati operazione, sto misurando circa 120 volte per un'operazione pesantemente contestata su un Core i7, ma forse l'ho rovinato? In ogni caso, "interlock forza un roundtrip di memoria (altrimenti come potrebbero vederlo gli altri core?). Una lettura/scrittura cache L1 è di circa 10 cicli di clock ..." - è sufficiente marcare quella pagina come cambiata e svuotare solo da L1 alla memoria se un altro core ha bisogno di vederlo, l'operazione non contestata può essere più vicina all'estremità 10 dello spettro (a 36) anziché a 100 + .... –

4

La mia prova di perfomance:

volatile: 65.174.400

serratura: 62.428.600

asservita: 113.248.900

TimeSpan span = TimeSpan.FromSeconds(5); 

object syncRoot = new object(); 
long test = long.MinValue; 

Do(span, "volatile",() => { 

    long r = Thread.VolatileRead(ref test); 

    r++; 

    Thread.VolatileWrite(ref test, r); 
}); 

Do(span, "lock",() => 
{ 
    lock (syncRoot) 
    { 
     test++; 
    } 
}); 

Do(span, "interlocked",() => 
{ 
    Interlocked.Increment(ref test); 
}); 
+2

Cosa c'è "Do'?" – SLaks

+0

Esegue il metodo n volte fino al momento in cui è stato raggiunto il tempo – MastsrOfDesaster

+3

Quindi, aspettare, in questo caso è meglio? Potresti specificare quali sono le tue metriche? –