Podczas przeglądu kodu z pracownikiem Microsoft natrafiliśmy na dużą część kodu wewnątrz try{}
bloku. Ona i przedstawiciel IT zasugerowali, że może to mieć wpływ na wydajność kodu. W rzeczywistości zasugerowali, że większość kodu powinna znajdować się poza blokami try / catch i że należy sprawdzać tylko ważne sekcje. Pracownik Microsoft dodał i powiedział, że nadchodząca biała księga ostrzega przed nieprawidłowymi blokami try / catch.
Rozejrzałem się i odkryłem, że może to wpływać na optymalizacje , ale wydaje się, że ma to zastosowanie tylko wtedy, gdy zmienna jest współużytkowana między zakresami.
Nie pytam o łatwość konserwacji kodu, ani nawet o obsługę odpowiednich wyjątków (kod, o którym mowa, wymaga ponownej faktoryzacji, bez wątpienia). Nie odnoszę się również do używania wyjątków do kontroli przepływu, co w większości przypadków jest oczywiście błędne. Są to ważne kwestie (niektóre są ważniejsze), ale nie w tym miejscu.
Jak bloki try / catch wpływają na wydajność, gdy wyjątki nie są zgłaszane?
źródło
Odpowiedzi:
Sprawdź to.
Wynik:
W milisekundach:
Nowy kod:
Nowe wyniki:
źródło
Po zobaczeniu wszystkich statystyk dla try / catch i bez try / catch, ciekawość zmusiła mnie do obejrzenia się za siebie, aby zobaczyć, co jest generowane dla obu przypadków. Oto kod:
DO#:
MSIL:
DO#:
MSIL:
Nie jestem ekspertem od IL, ale widzimy, że lokalny obiekt wyjątku jest tworzony w czwartej linii,
.locals init ([0] class [mscorlib]System.Exception ex)
po czym rzeczy są takie same jak w przypadku metody bez próbowania / przechwytywania do linii siedemnastejIL_0021: leave.s IL_002f
. Jeśli wystąpi wyjątek, sterowanie przeskakuje do linii, wIL_0025: ldloc.0
przeciwnym razie przeskakujemy do etykietyIL_002d: leave.s IL_002f
i funkcja wraca.Mogę spokojnie założyć, że jeśli nie wystąpią wyjątki, to narzutami tworzenia lokalnych zmiennych są
tylkoobiekty wyjątków i instrukcja skoku.źródło
Nie. Jeśli trywialne optymalizacje, które wyklucza blok try / wreszcie, rzeczywiście mają wymierny wpływ na twój program, prawdopodobnie nie powinieneś używać .NET.
źródło
Całkiem wyczerpujące wyjaśnienie modelu wyjątku .NET.
Ciekawostki o wydajności Rico Mariani: Koszt wyjątku: kiedy rzucać, a kiedy nie
Dmitrij Zasławski:
źródło
Struktura ta różni się od przykładu Ben M . Zostanie on rozciągnięty nad głową w wewnętrznej
for
pętli, co spowoduje, że nie będzie to dobre porównanie między dwoma przypadkami.Poniższe informacje są bardziej dokładne do porównania, gdy cały kod do sprawdzenia (w tym deklaracja zmiennej) znajduje się w bloku Try / Catch:
Kiedy uruchomiłem oryginalny kod testowy z Ben M , zauważyłem różnicę zarówno w konfiguracji Debugowania, jak i Releas.
W tej wersji zauważyłem różnicę w wersji debugowania (właściwie więcej niż w innej wersji), ale nie było różnicy w wersji Release.
Conclution :
W oparciu o te badania, myślę, że możemy powiedzieć, że try / catch nie mają niewielki wpływ na wydajność.
EDYCJA:
Próbowałem zwiększyć wartość pętli z 10000000 do 1000000000, i uruchomiłem ponownie w wersji, aby uzyskać pewne różnice w wersji, a wynik był następujący:
Widzisz, że wynik jest niekonsekwentny. W niektórych przypadkach wersja wykorzystująca Try / Catch jest w rzeczywistości szybsza!
źródło
try/catch
. Mierzysz czas 12 prób / złap wchodzenie w sekcję krytyczną przeciwko 10M pętlom. Szum pętli wyeliminuje wszelkie wpływy, jakie ma try / catch. jeśli zamiast tego umieścisz try / catch w ciasnej pętli i porównasz z / bez, skończyłbyś z kosztem try / catch. (bez wątpienia takie kodowanie nie jest ogólnie dobrą praktyką, ale jeśli chcesz zmierzyć czas narzutu konstrukcji, tak to robisz). Obecnie BenchmarkDotNet to narzędzie zapewniające rzetelne terminy realizacji.Przetestowałem rzeczywisty wpływ
try..catch
ciasnej pętli i sam w sobie jest zbyt mały, aby stanowić zagrożenie dla wydajności w każdej normalnej sytuacji.Jeśli pętla działa bardzo mało (w moim teście zrobiłem to
x++
), możesz zmierzyć wpływ obsługi wyjątków. Uruchomienie pętli z obsługą wyjątków trwało około dziesięć razy dłużej.Jeśli pętla rzeczywiście działa (w moim teście wywołałem metodę Int32.Parse), obsługa wyjątków ma zbyt mały wpływ, aby można ją było zmierzyć. Dostałem znacznie większą różnicę, zmieniając kolejność pętli ...
źródło
spróbuj złapać bloki mają znikomy wpływ na wydajność, ale wyjątek Rzucanie może być dość spore, prawdopodobnie w tym miejscu twój współpracownik był zdezorientowany.
źródło
Wpływ try / catch na wydajność.
Ale to nie jest ogromny wpływ. złożoność try / catch to na ogół O (1), podobnie jak proste przypisanie, z wyjątkiem sytuacji, gdy są one umieszczone w pętli. Musisz więc mądrze z nich korzystać.
Oto odniesienie do wydajności try / catch (nie wyjaśnia to jednak złożoności, ale jest implikowane). Zobacz sekcję Rzuć mniej wyjątków
źródło
Teoretycznie blok try / catch nie będzie miał wpływu na zachowanie kodu, chyba że rzeczywiście wystąpi wyjątek. Istnieją jednak pewne rzadkie okoliczności, w których istnienie bloku try / catch może mieć znaczący wpływ, a niektóre rzadkie, ale prawie niezrozumiałe, w których efekt może być zauważalny. Powodem tego jest podany kod, taki jak:
kompilator może być w stanie zoptymalizować instrukcję1 w oparciu o fakt, że instrukcja2 ma gwarancję wykonania przed instrukcją3. Jeśli kompilator rozpozna, że rzecz 1 nie ma skutków ubocznych, a rzecz 2 faktycznie nie używa x, może bezpiecznie całkowicie pominąć rzecz 1. Jeśli [jak w tym przypadku] rzecz1 była kosztowna, mogłaby to być znaczna optymalizacja, chociaż przypadki, w których rzecz1 jest droga, to także te, które kompilator najmniej by zoptymalizował. Załóżmy, że kod został zmieniony:
Teraz istnieje sekwencja zdarzeń, w których instrukcja3 mogłaby zostać wykonana bez wykonania instrukcji2. Nawet jeśli nic w kodzie dla
thing2
nie może wyrzucić wyjątku, możliwe jest, że inny wątek użyje znakuInterlocked.CompareExchange
powiadomienia, któryq
został wyczyszczony i ustawiony naThread.ResetAbort
, a następnie wykonaThread.Abort()
instrukcję przed, do której zapisała swoją wartośćx
. Następniecatch
wykonałbyThread.ResetAbort()
[przez delegataq
], umożliwiając kontynuowanie wykonywania z instrukcją3. Taka sekwencja zdarzeń byłaby oczywiście wyjątkowo nieprawdopodobna, ale do wygenerowania kodu, który działa zgodnie ze specyfikacją, wymagany jest kompilator, nawet jeśli wystąpią takie nieprawdopodobne zdarzenia.Ogólnie rzecz biorąc, kompilator znacznie częściej dostrzega możliwości pominięcia prostych bitów kodu niż złożone, a zatem rzadkie byłoby, gdyby próba / złapanie mogło wpłynąć na wydajność znacznie, gdyby nigdy nie zgłoszono wyjątków. Mimo to istnieją sytuacje, w których istnienie bloku try / catch może uniemożliwić optymalizację, która - gdyby nie try / catch - pozwoliłaby na szybsze działanie kodu.
źródło
Chociaż „ Zapobieganie jest lepsze niż postępowanie ”, w perspektywie wydajności i wydajności moglibyśmy wybrać try-catch zamiast wstępnej odmiany. Rozważ poniższy kod:
Oto wynik:
źródło
Zobacz dyskusję na temat implementacji try / catch, aby dowiedzieć się, jak działają bloki try / catch i jak niektóre implementacje mają wysoki narzut, a niektóre zerowy narzut, gdy nie występują wyjątki. W szczególności myślę, że 32-bitowa implementacja systemu Windows ma duży narzut, a 64-bitowa implementacja nie.
źródło