2009-06-16 11 views
11

ci stiamo imbattendo in problemi di prestazioni, e un potenziale colpevole è un uso centralizzato di un singleton volatile. il codice specifico è della formaQual è il costo della parola chiave volatile in un sistema multiprocessore?

class foo { 
    static volatile instance; 
    static object l = new object(); 

    public static foo Instance { 
    if (instance == null) 
     lock(l) { 
     if (instance == null) 
      instance = new foo(); 
     } 

    return foo(); 
    } 
} 

questo è in esecuzione su una scatola a 8 vie, e stiamo vedendo cambio di contesto per la somma di 500.000 al secondo. le risorse di sistema tipiche vanno bene - 25% cpu util, 25% memory util, IO basso, no paging, ecc.

utilizza un campo volatile per indurre una barriera di memoria o qualsiasi tipo di cache della cpu ricaricare? o va solo dopo la memoria principale ogni volta, solo per quel campo?

+0

no, è un'istanza di foo. – kolosy

+0

Non sono sicuro del motivo per cui questo deve essere un volatile, poiché non stai cambiando il riferimento. –

+0

Io non sono né - questo è il codice di qualcuno che sto facendo il debug. la mia domanda principale, tuttavia, è che l'uso di volatile può causare problemi di contesa su una casella x86 multi-core – kolosy

risposta

4

lock induce una barriera di memoria, quindi se si accede sempre all'istanza in un blocco non è necessario il volatile.

Secondo this site:

Il C# attrezzi parola chiave volatile acquisiscono e rilasciano la semantica, che implica una barriera di memoria lettura su lettura e una barriera di memoria di scrittura su scrittura.

+0

no - dai un'occhiata al codice. in teoria, il blocco si verifica solo una volta, come dopo che la variabile volatile verrà impostata e il controllo esterno passerà. – kolosy

+0

questa modifica fa la differenza nel mondo :). Grazie. – kolosy

+1

Vance Morrison copre anche DCL in questo articolo MSDN - http://msdn.microsoft.com/en-us/magazine/cc163715.aspx –

1

Purtroppo, la Singleton prende una cattiva reputazione per quasi tutto :)

Questo non è il mio dominio di competenza, ma per quanto ne so non c'è nulla di speciale in volatili diverso dal compilatore/run -time NON riordinare lettura/scrittura (alla variabile) per scopi di ottimizzazione.

Modifica: I corretti. La volatilità non solo introduce barriere di memoria, ma ciò che accade (e incidentalmente, le prestazioni) dipende in gran parte dalla particolare CPU coinvolta. Vedi http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html

Questo è il motivo per cui è ancora necessario il blocco.

Domande che possono/potrebbero non hanno ricevuto risposta già:

  1. Qual è l'istanza Singleton effettivamente facendo? Forse il codice di istanza deve essere refactored ...
  2. Qual è il numero di thread del processo in esecuzione? Una casella a 8 direzioni non ti aiuterà se hai un numero eccessivo di thread.
  3. Se è più alto del previsto, perché?
  4. Che altro è in esecuzione sul sistema?
  5. Il problema di prestazioni è coerente?
+0

1. non molto. è un contenitore per alcuni dati che devono essere disponibili a livello globale 2. il numero di thread è adeguato. il cambio di contesto/secondo è incredibilmente alto. 500.000/s 3. buona domanda. 4. niente. 5. sì – kolosy

0

Nel tuo esempio qui, volatile non dovrebbe essere oggetto di alcun "rallentamento". Il lock(), tuttavia, può comportare enormi roundtrip al kernel, specialmente se c'è un sacco di contesa per il lock.

Non c'è davvero alcun bisogno di bloccare il Singleton in questo caso, si può solo fare

class Foo { 
    static Foo instance = new Foo(); 
    public static Foo FooInstance() { 
    return instance ; 
    } 
} 

Naturalmente, se viene utilizzato in un sacco di diversi thread 'esempio', saresti ancora devono blocca() tutto ciò che altera quel Foo, a meno che tutti i metodi/le proprietà di Foo siano in sola lettura. ad es.

class Foo { 
     static Foo instance = new Foo(); 
     object l = new object(); 
     int doesntChange = 42; 
     int canChange = 123; 
     public static Foo FooInstance() { 
     return instance ; 
     } 
     public void Update(int newVal) { 
     lock(l) { // you'll get a lot of trouble without this lock if several threads accesses the same FOO. Atleast if they later on read that variable 
      canChange = newVal; 
     } 

     public int GetFixedVal() { 
     return doesntChange; //no need for a lock. the doesntChange is effectivly read only 
     } 
    } 
0

Non c'è davvero bisogno dell'uso di volatile per un singleton, come lo si sta impostando esattamente una volta - e bloccando il codice lo imposta. Vedi Jon Skeet's article on singletons per maggiori informazioni.

+0

Un DCL senza una barriera di memoria sulla lettura è "Terza versione - tentativo (e fallito) di sicurezza del thread usando il blocco di doppio controllo .. * Senza alcuna barriera di memoria, è rotto anche nelle specifiche CLMA dell'ECMA. * ". Il codice OPs è strano in quanto può restituire * multiple * foo's (invece di restituire 'instance', che rappresenta un * diverso problema *) .. questo vecchio post (2006) non indica che un DCL * senza memory guard * sia sicuro per singleton (di nuovo, un bug nel codice ...?) – user2864740

+0

http://benbowen.blog/post/cmmics_iii/ - vedi nota su ". Modello di memoria 2.0" "garanzie". – user2864740

3

Una cosa che volatile non farà è causare un cambio di contesto. Se visualizzi 500.000 opzioni di contesto al secondo, significa che i tuoi thread stanno bloccando qualcosa e che volatile è non il colpevole.

0

La risposta breve è Sì, crea una barriera di memoria (svuota tutto e passa alla memoria principale, non solo quella singola variabile), ma No non sarà la causa del cambio di contesto.

Inoltre, come altri hanno già menzionato, non credo che qui sia necessario un volatile.

+0

'volatile' è necessario per * garantire * thread-safety (non è l'unica opzione, ma è quella richiesta nel pattern di base DCL di base). Impedisce il riordino (e garantisce una lettura "non aggiornata") quando il codice * non * acquisisce il blocco: se il codice * sempre * è entrato nel blocco, non ci sarebbe alcun beneficio di 'volatile'. – user2864740

Problemi correlati