Piszę obiekt bezpieczny wątku, który w zasadzie reprezentuje podwójne i używa blokady, aby zapewnić bezpieczne czytanie i pisanie. Używam wielu z tych obiektów (20-30) w kawałku kodu, który czyta i zapisuje je wszystkie 100 razy na sekundę, i mierzę średni czas obliczania każdego z tych kroków czasowych. Zacząłem przyglądać się kilku opcjom implementacji mojego gettera i po przeprowadzeniu wielu testów i zebraniu wielu próbek w celu uśrednienia mojego pomiaru czasu obliczeń uważam, że pewne implementacje działają konsekwentnie lepiej niż inne, ale nie implementacje, których oczekiwałbym.C# wątek bezpieczne różnice wydajności getter
Wykonanie 1) Obliczenie średni czas = 0.607ms:
protected override double GetValue()
{
lock(_sync)
{
return _value;
}
}
wykonawcze 2) czas obliczeń średniej = 0.615ms:
protected override double GetValue()
{
double result;
lock(_sync)
{
result = _value;
}
return result;
}
Realizacja 3) czas obliczeń średniej = 0.560ms:
protected override double GetValue()
{
double result = 0;
lock(_sync)
{
result = _value;
}
return result;
}
Czego się spodziewałem: Spodziewałem się, że implementacja 3 będzie najgorsza z 3 (to był właściwie mój oryginalny kod, więc było to przypadkowe lub leniwe kodowanie, które napisałem w ten sposób), ale zaskakująco jest to konsekwentnie najlepsze pod względem wydajności. Spodziewałbym się, że wdrożenie 1 będzie najszybsze. Spodziewałem się również, że implementacja 2 będzie co najmniej tak szybka, jeśli nie szybsza niż implementacja 3, ponieważ właśnie usuwam przypisanie do podwójnego wyniku, który i tak jest nadpisywany, więc nie jest to konieczne.
Moje pytanie brzmi: Czy ktoś może wyjaśnić, dlaczego te 3 wdrożenia mają względną wydajność, którą zmierzyłem? Wydaje mi się to sprzeczne z intuicją i naprawdę chciałbym wiedzieć dlaczego.
Zdaję sobie sprawę, że różnice te nie są duże, ale ich względna miara jest stała za każdym razem, gdy wykonuję test, zbierając tysiące próbek w każdym teście, aby uśrednić czas obliczeń. Pamiętaj też, że wykonuję te testy, ponieważ moja aplikacja wymaga bardzo wysokiej wydajności lub przynajmniej tak dobrej jak to tylko możliwe. Mój przypadek testowy to tylko mały przypadek testowy, a wydajność mojego kodu będzie ważna przy uruchomieniu w wersji.
EDYCJA: Zwróć uwagę, że używam MonoTouch i uruchamiam kod na urządzeniu iPad Mini, więc być może nie ma to nic wspólnego z C# i czymś więcej związanym z kompilatorem krzyża MonoTouch.
Biorąc pod uwagę ich prawie identyczne (nawet z perspektywy IL nie oczekuję, że będą obliczeniowo - to znacznie różni) i jak bliskie są czasy testów, podejrzewam, że wielkim winowajcą jest twoja metoda testowania. Czy jest skompilowany dla trybu "release"? Jak się masz benchmarking? Czy inne procesy na komputerze mogą zakłócać przetwarzanie zasobów/czasu? Czy w ogóle symulujesz rywalizację o zamek, a jeśli tak, to w jaki sposób? EDYCJA: Ponadto, podejrzewam, że Twoja aplikacja w świecie rzeczywistym ma bardziej skomplikowaną pracę w bloku 'blokady'? Bo jak jest, zamek wydaje mi się zbędny. –
1) Bez rywalizacji o zamek testy są bez znaczenia. –
pokaż kod, którego używasz do przetestowania –