Perché lo stato condiviso riduce le prestazioni?


19

Ho lavorato secondo il principio share-nothing della programmazione concorrente. In sostanza, tutti i miei thread di lavoro hanno copie di sola lettura immutabili dello stesso stato che non sono mai condivise tra loro ( anche per riferimento ). In generale, ha funzionato davvero bene.

Ora, qualcuno ha introdotto una cache singleton senza blocco ( ad esempio un dizionario statico ) a cui tutti i thread accedono contemporaneamente. Dal momento che il dizionario non viene mai modificato dopo l'avvio non ci sono blocchi. Non ci sono stati problemi di sicurezza del thread, ma ora c'è un peggioramento delle prestazioni.

La domanda è ... dal momento che non ci sono blocchi perché l'introduzione di questo singleton crea un successo nelle prestazioni? Cosa sta succedendo esattamente sotto le coperte che potrebbe spiegare questo?

Per confermare, l'accesso a questo nuovo singleton è l'unica modifica e posso ricrearlo in modo affidabile semplicemente commentando la chiamata nella cache.


8
Hai indicato un profiler al codice?
Timo Geusch,

2
È improbabile che la profilazione risponda a questa domanda a meno che non si stia profilando il CLR e possibilmente il kernel di Windows (non è un compito facile per il programmatore medio).
Igby Largeman,

1
@JoeGeeky Va bene allora, suppongo che l'unica cosa da fare per me qui sia +1 e favorito! Sembra strano dal momento che entrambi sono allo stesso livello di indiretta dopo tutto, e dovrebbero rientrare comunque nella cache del processore, ecc ...
Max

2
FWIT Ho generato un paio di thread e ho eseguito alcuni timer. Ho creato un'istanza di una classe, singleton, Locksingleton e dict <stringa, stringa>. Dopo la prima istanza di ciascuno, le esecuzioni consecutive hanno richiesto circa 2000 n per un determinato oggetto. Il dizionario ha funzionato 2 volte più lentamente, potrebbe essere causato dal codice del costruttore ... è più lento del blocco da solo. Considerando tutto il GC, la gestione del sistema operativo delle code dei thread e altri costi generali ... non sono sicuri che si possa veramente rispondere a questa domanda. Ma dai miei risultati non credo che il problema abbia a che fare con Singletons. Non se è implementato come su MSDN. Esclude le ottimizzazioni del compilatore.
P.Brian.Mackey,

1
@JoeGeeky - un altro pensiero: l'uso della cache aggiunge un livello di riferimento indiretto? Se vi si accede frequentemente, la ricerca di un puntatore extra deref (o MSIL equiv) potrebbe richiedere del tempo su una copia locale meno indiretta.
sabato

Risposte:


8

Potrebbe essere che lo stato immutabile condivida una linea di cache con qualcosa di mutabile. In questo caso, una modifica allo stato mutabile nelle vicinanze potrebbe avere l'effetto di forzare una risincronizzazione di questa linea di cache tra i core, con conseguente rallentamento delle prestazioni.


3
Sembra uno false sharingscenario che stai descrivendo. Per isolarlo dovrò profilare la cache L2. Sfortunatamente, si tratta di tipi di riferimento, quindi l'aggiunta di spazio buffer non sarà un'opzione se questo è effettivamente ciò che sta accadendo.
JoeGeeky,

3

Vorrei assicurarmi che i metodi Equals()e GetHashCode()degli oggetti che usi come chiavi del dizionario non abbiano effetti collaterali inaspettati e non threading. La profilazione sarebbe di grande aiuto qui.

Se per caso le tue chiavi sono stringhe, allora forse ce l'hai: si dice che le stringhe si comportino come oggetti immutabili ma per motivi di certe ottimizzazioni sono implementate internamente in modo mutevole, con tutto ciò che ciò comporta in relazione al multithreading .

Proverei a passare il dizionario ai thread che lo usano come riferimento regolare anziché come singleton per vedere se il problema risiede nella condivisione o nella singolarità del dizionario. (Eliminando le possibili cause.)

Vorrei anche provare con un ConcurrentDictionaryinvece di un normale Dictionarynel caso in cui il suo utilizzo produca risultati sorprendenti. Ci sono molte cose da speculare sul problema a portata di mano se ConcurrentDictionarysi rivela che funziona molto meglio o molto peggio del tuo normale Dictionary.

Se nessuno dei precedenti punti indica il problema, indovinerei che le prestazioni degradate sono causate da una strana sorta di contesa tra il thread di raccolta dei rifiuti e il resto dei tuoi thread, poiché il garbage collector sta cercando di capire se gli oggetti nel dizionario devono essere eliminati o meno, mentre ai thread accedono.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.