Sto scrivendo un oggetto thread-safe che fondamentalmente rappresenta una doppia e utilizza una serratura per garantire una lettura e una scrittura sicure. Uso molti di questi oggetti (20-30) in un pezzo di codice che sta leggendo e scrivendoli tutti 100 volte al secondo e sto misurando il tempo di calcolo medio di ciascuna di queste fasi temporali. Ho iniziato a esaminare alcune opzioni per implementazioni del mio getter e dopo aver eseguito molti test e raccolto molti campioni per calcolare la mia misurazione del tempo di calcolo, ho riscontrato che alcune implementazioni sono sempre migliori di altre, ma non le implementazioni che mi aspetterei.C# differenze prestazionali thread-safe getter
Attuazione 1) Calcolo tempo medio = 0.607ms:
protected override double GetValue()
{
lock(_sync)
{
return _value;
}
}
attuazione 2) tempo di calcolo della media = 0.615ms:
protected override double GetValue()
{
double result;
lock(_sync)
{
result = _value;
}
return result;
}
Attuazione 3) tempo di calcolo media = 0.560ms:
protected override double GetValue()
{
double result = 0;
lock(_sync)
{
result = _value;
}
return result;
}
Cosa mi aspettavo: Mi aspettavo che l'implementazione 3 fosse la peggiore del 3 (questo era in realtà il mio codice originale, quindi era la possibilità o la codifica pigra che l'avevo scritto in questo modo), ma sorprendentemente è costantemente il migliore in termini di prestazioni. Mi aspetto che l'implementazione 1 sia la più veloce. Mi aspettavo anche che l'implementazione 2 fosse almeno altrettanto veloce, se non addirittura più veloce dell'implementazione 3, dal momento che sto semplicemente rimuovendo un assegnamento al doppio risultato che viene comunque sovrascritto, quindi non è necessario.
La mia domanda è: qualcuno può spiegare perché queste 3 implementazioni hanno le prestazioni relative che ho misurato? Mi sembra controintuitivo e mi piacerebbe davvero sapere perché.
Mi rendo conto che queste differenze non sono importanti, ma la loro misura relativa è coerente ogni volta che eseguo il test, raccogliendo migliaia di campioni ogni test per calcolare la media del tempo di calcolo. Inoltre, tieni presente che sto facendo questi test perché la mia applicazione richiede prestazioni molto elevate, o almeno quanto posso ragionevolmente ottenerla. Il mio test case è solo un piccolo test case e le prestazioni del mio codice saranno importanti durante il rilascio.
MODIFICA: si noti che sto utilizzando MonoTouch e sto eseguendo il codice su un dispositivo Mini iPad, quindi forse non è correlato a C# e altro ancora relativo al cross-compilatore di MonoTouch.
Considerando quanto siano quasi identici (anche da un punto di vista dell'IL non mi aspetto che siano computazionalmente _che_ molto diverso) e quanto siano vicini i tempi di riferimento, sospetto che il grande colpevole sia il vostro metodo di prova. È compilato per la modalità 'release'? Come stai benchmark? Gli altri processi sul computer possono interferire con le risorse/tempo di elaborazione? Stai simulando la contesa della serratura, e se sì, come? EDIT: Inoltre, sospetto che la tua applicazione nel mondo reale abbia un lavoro più complicato nel blocco 'lock'? Perché così com'è, il lucchetto mi sembra superfluo. –
1) Senza contesa per il blocco, i test non hanno senso. –
per favore mostra il codice che stai usando per testare –