Jak wolno działają wyjątki .NET?

143

Nie chcę dyskusji o tym, kiedy i nie należy rzucać wyjątków. Chcę rozwiązać prosty problem. W 99% przypadków argument za niewyrzucaniem wyjątków obraca się wokół ich powolności, podczas gdy druga strona twierdzi (za pomocą testu porównawczego), że prędkość nie jest problemem. Przeczytałem wiele blogów, artykułów i postów dotyczących jednej lub drugiej strony. Więc o co chodzi?

Kilka linków z odpowiedzi: Skeet , Mariani , Brumme .

Goran
źródło
13
są kłamstwa, cholerne kłamstwa i wzorce. :)
gbjbaanb
Niestety, w kilku wysoko ocenionych odpowiedziach pominięto pytanie „jak powolne są wyjątki?” I poproszono o uniknięcie tematu, jak często ich używać. Jedna prosta odpowiedź na pytanie, które tak naprawdę zostało zadane, brzmi ..... W środowisku CLR systemu Windows wyjątki są 750 razy wolniejsze niż zwracane wartości.
David Jeske,

Odpowiedzi:

207

Jestem po stronie „nie wolno” - a dokładniej „nie na tyle wolno, by warto było ich unikać przy normalnym użytkowaniu”. Napisałem na ten temat dwa krótkie artykuły . Istnieje krytyka aspekcie odniesienia, które są głównie w dół do „w prawdziwym życiu nie byłoby bardziej stos przejść, więc chcesz dmuchać cache etc” - ale za pomocą kodów błędów na swój sposób pracy stosu byłoby również wysadzić pamięć podręczną, więc nie uważam tego za szczególnie dobry argument.

Żeby było jasne - nie popieram używania wyjątków, jeśli nie są one logiczne. Na przykład int.TryParsejest całkowicie odpowiedni do konwersji danych od użytkownika. Jest to odpowiednie podczas odczytywania pliku wygenerowanego maszynowo, gdzie błąd oznacza „Plik nie jest w formacie, w jakim ma być, naprawdę nie chcę tego robić, ponieważ nie wiem, co jeszcze może być nie tak. "

Korzystając z wyjątków w „tylko rozsądnych okolicznościach”, nigdy nie widziałem aplikacji, której wydajność byłaby znacznie ograniczona przez wyjątki. Zasadniczo wyjątki nie powinny zdarzać się często, chyba że masz poważne problemy z poprawnością, a jeśli masz znaczące problemy z poprawnością, wydajność nie jest największym problemem, z którym się spotykasz.

Jon Skeet
źródło
2
niestety, ludzie słyszą, że wyjątki są bezpłatne, używaj ich do trywialnych
``
4
Tak, ludzie z pewnością powinni być świadomi, że niewłaściwe używanie wyjątków wiąże się z kosztami wydajności. Po prostu myślę, że to nie problem, gdy odpowiednio używane :)
Jon Skeet
7
@PaulLockwood: Powiedziałbym, że jeśli masz ponad 200 wyjątków na sekundę , nadużywasz wyjątków. Z pewnością nie jest to „wyjątkowe” wydarzenie, jeśli występuje 200 razy na sekundę. Zwróć uwagę na ostatnie zdanie odpowiedzi: „Zasadniczo wyjątki nie powinny zdarzać się często, chyba że masz poważne problemy z poprawnością, a jeśli masz znaczące problemy z poprawnością, wydajność nie jest największym problemem, z którym się spotykasz”.
Jon Skeet
4
@PaulLockwood: Chodzi mi o to, że jeśli masz ponad 200 wyjątków na sekundę, prawdopodobnie już oznacza to, że nadużywasz wyjątków. Nie dziwi mnie, że tak jest często, ale oznacza to, że aspekt wykonawczy nie byłby moim pierwszym zmartwieniem - byłoby nadużywaniem wyjątków. Po usunięciu wszystkich niewłaściwych zastosowań wyjątków nie spodziewałbym się, że będą one istotną częścią wydajności.
Jon Skeet
4
@DavidJeske: Przegapiłeś punkt odpowiedzi. Oczywiście zgłoszenie wyjątku jest znacznie wolniejsze niż zwrócenie normalnej wartości. Nikt się z tym nie kłóci. Pytanie brzmi, czy są one zbyt wolne. Jeśli jesteś w odpowiedniej sytuacji do rzucenia wyjątku, a to powoduje problem z wydajnością, prawdopodobnie masz większe problemy - ponieważ sugeruje to, że jest dużo błędów w Twoim systemie. Zazwyczaj problem jest naprawdę , że używasz wyjątków, gdy są nieodpowiednie na początek.
Jon Skeet
31

Ostateczną odpowiedź na to pytanie udzielił facet, który je wdrożył - Chris Brumme. Napisał na ten temat świetny artykuł na blogu (uwaga - jest bardzo długi) (ostrzeżenie2 - bardzo dobrze napisany, jeśli jesteś technikiem, przeczytasz go do końca, a potem będziesz musiał nadrobić godziny po pracy :) )

Streszczenie: są powolne. Są zaimplementowane jako wyjątki Win32 SEH, więc niektóre nawet przekraczają granicę procesora ring 0! Oczywiście w prawdziwym świecie będziesz wykonywać wiele innych czynności, więc dziwny wyjątek w ogóle nie zostanie zauważony, ale jeśli użyjesz ich do przepływu programu, spodziewaj się, że Twoja aplikacja zostanie wbita. To kolejny przykład marketingowej maszyny MS wyrządzającej nam krzywdę. Przypominam sobie, że jeden Microsoft opowiadał nam, jak ponieśli absolutnie zerowe koszty ogólne, co jest kompletne.

Chris podaje trafny cytat:

W rzeczywistości środowisko CLR wewnętrznie używa wyjątków nawet w niezarządzanych częściach silnika. Istnieje jednak poważny, długoterminowy problem z wydajnością, z wyjątkami i należy to uwzględnić przy podejmowaniu decyzji.

gbjbaanb
źródło
Mogę o tym wspomnieć w rzeczywistych testach, gdzie typ dopuszczający wartość null powoduje wielokrotne zgłaszanie wyjątku w „to jest normalny przepływ programu”, co zakończyło się znacznymi problemami z wydajnością. Pamiętaj jednak, wyjątki dotyczą wyjątkowych przypadków, nie wierz nikomu, kto mówi inaczej, bo skończysz z takim wątkiem na githubie!
gbjbaanb
8

Nie mam pojęcia, o czym ludzie mówią, kiedy mówią, że są powolni tylko wtedy, gdy są rzucani.

EDYCJA: Jeśli wyjątki nie są zgłaszane, oznacza to, że robisz nowy wyjątek () lub coś w tym stylu. W przeciwnym razie wyjątek spowoduje zawieszenie wątku i przejście stosu. Może to być w porządku w mniejszych sytuacjach, ale w witrynach o dużym ruchu poleganie na wyjątkach jako przepływie pracy lub mechanizmie ścieżki wykonania z pewnością spowoduje problemy z wydajnością. Wyjątki same w sobie nie są złe i są przydatne do wyrażania wyjątkowych warunków

Przepływ pracy wyjątków w aplikacji .NET korzysta z wyjątków pierwszej i drugiej szansy. W przypadku wszystkich wyjątków, nawet jeśli je przechwytujesz i obsługujesz, obiekt wyjątku jest nadal tworzony, a struktura nadal musi przejść stos, aby znaleźć procedurę obsługi. Jeśli złapiesz i wyrzucisz ponownie, oczywiście zajmie to więcej czasu - dostaniesz wyjątek pierwszej szansy, złapiesz go, wyrzucisz ponownie, powodując kolejny wyjątek pierwszej szansy, który następnie nie znajdzie obsługi, co spowoduje wyjątek drugiej szansy.

Wyjątki to także obiekty na stercie - więc jeśli wyrzucasz mnóstwo wyjątków, powoduje to zarówno problemy z wydajnością, jak i pamięcią.

Ponadto, zgodnie z moją kopią „Testowania wydajności aplikacji internetowych Microsoft .NET” napisaną przez zespół ACE:

„Obsługa wyjątków jest kosztowna. Wykonywanie zaangażowanego wątku jest zawieszane, podczas gdy CLR powtarza się przez stos wywołań w poszukiwaniu właściwej procedury obsługi wyjątków, a kiedy zostanie znaleziony, procedura obsługi wyjątku i pewna liczba końcowych bloków muszą mieć szansę na wykonanie zanim będzie można przeprowadzić regularne przetwarzanie ”.

Moje własne doświadczenie w tej dziedzinie pokazało, że redukcja wyjątków znacząco poprawiła wydajność. Oczywiście są inne rzeczy, które bierzesz pod uwagę podczas testowania wydajności - na przykład, jeśli twoje operacje wejścia / wyjścia dysku są przerywane lub twoje zapytania są w sekundach, to powinno być twoim celem. Jednak znajdowanie i usuwanie wyjątków powinno być istotną częścią tej strategii.

Cory Foy
źródło
1
Nic, co napisałeś, nie jest sprzeczne z twierdzeniem, że wyjątki są powolne tylko wtedy, gdy są rzucane. Mówiłeś tylko o sytuacjach, w których rzucane. Kiedy „znacząco poprawiłeś wydajność”, usuwając wyjątki: 1) Czy były to prawdziwe warunki błędu, czy tylko błąd użytkownika ?
Jon Skeet
2) Czy pracowałeś pod debugerem, czy nie?
Jon Skeet
Jedyną możliwą rzeczą, którą możesz zrobić z wyjątkiem, jeśli go nie rzucisz, jest stworzenie go jako obiektu, co jest bez znaczenia. Bycie pod debuggerem lub nie ma znaczenia - nadal będzie wolniejsze. Tak, są haczyki, które będą się zdarzać po podłączeniu debuggera, ale nadal jest powolny
Cory Foy
4
Wiem - byłem częścią zespołu Premier w MSFT. :) Powiedzmy, wiele - tysiące na sekundę w niektórych ekstremalnych przypadkach, które widzieliśmy. Nie ma to jak połączenie się z debugerem na żywo i po prostu zobaczenie wyjątków tak szybko, jak można je przeczytać. Ex są powolne - tak samo jak łączenie się z DB, więc robisz to, kiedy ma to sens.
Cory Foy
5
Cory, myślę, że celem „powolnego rzucania” jest to, że nie musisz martwić się o wydajność z powodu samej obecności bloków catch / final. To znaczy, że same w sobie nie powodują spadku wydajności, a jedynie wystąpienie rzeczywistego wystąpienia wyjątku.
Ian Horwill,
6

Jak rozumiem, argumentem nie jest to, że rzucanie wyjątków jest złe, ale samo w sobie jest powolne. Zamiast tego chodzi o użycie konstrukcji throw / catch jako pierwszej klasy sposobu kontrolowania normalnej logiki aplikacji, zamiast bardziej tradycyjnych konstrukcji warunkowych.

Często w normalnej logice aplikacji wykonujesz pętle, w których ta sama akcja jest powtarzana tysiące / miliony razy. W tym przypadku, przy bardzo prostym profilowaniu (patrz klasa Stopwatch), możesz sam przekonać się, że rzucanie wyjątku zamiast powiedzieć proste polecenie if może okazać się znacznie wolniejsze.

W rzeczywistości kiedyś przeczytałem, że zespół .NET w firmie Microsoft wprowadził metody TryXXXXX w .NET 2.0 do wielu podstawowych typów FCL, szczególnie dlatego, że klienci narzekali, że działanie ich aplikacji jest tak wolne.

Okazuje się, że w wielu przypadkach było to spowodowane próbą konwersji typu przez klientów w pętli, a każda próba zakończyła się niepowodzeniem. Wyjątek konwersji został zgłoszony, a następnie przechwycony przez program obsługi wyjątków, który następnie połknął wyjątek i kontynuował pętlę.

Firma Microsoft zaleca teraz, aby metody TryXXX były używane szczególnie w tej sytuacji, aby uniknąć takich możliwych problemów z wydajnością.

Mogę się mylić, ale wygląda na to, że nie jesteś pewien prawdziwości „testów porównawczych”, o których czytałeś. Proste rozwiązanie: wypróbuj to sam.

Popiół
źródło
Myślałem, że te funkcje „try” również używają wyjątków?
greg
1
Te funkcje „Try” nie zgłaszają wyjątków wewnętrznie w przypadku niepowodzenia analizowania wartości wejściowej. Jednak nadal zgłaszają wyjątki dla innych sytuacji błędów, takich jak ArgumentException.
Ash,
Myślę, że ta odpowiedź jest bliższa sedna problemu niż jakakolwiek inna. Powiedzenie `` używaj wyjątków tylko w rozsądnych okolicznościach '' tak naprawdę nie odpowiada na pytanie - prawdziwy wgląd jest taki, że używanie wyjątków języka C # dla przepływu sterowania jest znacznie wolniejsze niż zwykłe konstrukcje warunkowe. Można by wybaczyć, że myślisz inaczej. W OCaml wyjątki to mniej więcej GOTO i akceptowany sposób implementacji przerwy podczas korzystania z funkcji imperatywnych. W moim konkretnym przypadku zastąpienie w ciasnej pętli int.Parse () plus try / catch w porównaniu z int.TryParse () dało znaczący wzrost wydajności.
Hugh W
4

Mój serwer XMPP zyskał znaczne przyspieszenie (przepraszam, brak rzeczywistych liczb, czysto obserwacyjne) po tym, jak konsekwentnie próbowałem im zapobiec (na przykład sprawdzałem, czy gniazdo jest podłączone, zanim spróbujesz odczytać więcej danych) i dałem sobie sposoby ich uniknięcia (wspomniane metody TryX). To było tylko z około 50 aktywnymi (czatującymi) wirtualnymi użytkownikami.

Jonathan C. Dickinson
źródło
3
Liczby byłyby niestety przydatne :( Rzeczy takie jak operacje na gniazdach powinny znacznie przewyższać koszty wyjątków, z pewnością bez debugowania. Jeśli kiedykolwiek wykonasz pełny test porównawczy, byłbym bardzo zainteresowany zobaczeniem wyników.
Jon Skeet
3

Aby dodać moje własne niedawne doświadczenia do tej dyskusji: zgodnie z większością tego, co napisano powyżej, stwierdziłem, że rzucanie wyjątków jest niezwykle powolne, gdy jest wykonywane wielokrotnie, nawet bez uruchomionego debugera. Po prostu zwiększyłem wydajność dużego programu, który piszę, o 60%, zmieniając około pięciu wierszy kodu: przełączając się na model kodu powrotnego zamiast rzucać wyjątki. To prawda, że ​​kod, o którym mowa, działał tysiące razy i potencjalnie rzucał tysiące wyjątków, zanim go zmieniłem. Zgadzam się więc z powyższym stwierdzeniem: wyrzucaj wyjątki, gdy coś ważnego faktycznie pójdzie nie tak, a nie jako sposób kontrolowania przepływu aplikacji w "oczekiwanych" sytuacjach.

Ray Prisament
źródło
2

Jeśli porównasz je z kodami zwrotnymi, są powolne jak diabli. Jednak, jak wskazywali poprzednie postery, nie chcesz wrzucać do normalnego działania programu, więc osiągasz perfekcję tylko wtedy, gdy pojawia się problem, aw większości przypadków wydajność nie ma już znaczenia (ponieważ wyjątek i tak oznacza blokadę drogi).

Zdecydowanie warto ich używać zamiast kodów błędów, zalety to ogromna IMO.

Kłopotliwe
źródło
2

Nigdy nie miałem żadnych problemów z wydajnością z wyjątkami. Często korzystam z wyjątków - nigdy nie używam kodów zwrotnych, jeśli mogę. Są złą praktyką i moim zdaniem pachną jak kod spaghetti.

Myślę, że wszystko sprowadza się do tego, jak używasz wyjątków: jeśli używasz ich jak kodów powrotu (każde wywołanie metody w stosie łapie i ponownie rzuca), to tak, będą one powolne, ponieważ masz nad głową każdy pojedynczy złapanie / rzut.

Ale jeśli rzucisz na dół stosu i złapiesz na górze (zastąpisz cały łańcuch kodów powrotu jednym rzutem / złapaniem), wszystkie kosztowne operacje są wykonywane raz.

Pod koniec dnia są one ważną funkcją językową.

Żeby udowodnić mój punkt widzenia

Uruchom kod pod tym linkiem (zbyt duży, aby uzyskać odpowiedź).

Wyniki na moim komputerze:

marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM

Znaczniki czasu są wyprowadzane na początku, między kodami powrotu a wyjątkami, na końcu. W obu przypadkach zajmuje to tyle samo czasu. Pamiętaj, że musisz skompilować się z optymalizacjami.

Sklivvz
źródło
2

Ale mono zgłasza wyjątek 10x szybciej niż tryb autonomiczny .net, a tryb autonomiczny .net zgłasza wyjątek 60x szybciej niż tryb debuggera .net. (Maszyny testujące mają ten sam model procesora)

int c = 1000000;
int s = Environment.TickCount;
for (int i = 0; i < c; i++)
{
    try { throw new Exception(); }
    catch { }
}
int d = Environment.TickCount - s;

Console.WriteLine(d + "ms / " + c + " exceptions");
linquize
źródło
1

W trybie zwolnienia narzut jest minimalny.

O ile nie zamierzasz używać wyjątków do sterowania przepływem (na przykład nielokalnych wyjść) w sposób rekurencyjny, wątpię, czy będziesz w stanie zauważyć różnicę.

leppie
źródło
1

W środowisku CLR systemu Windows dla łańcucha wywołań o głębokości 8 zgłoszenie wyjątku jest 750 razy wolniejsze niż sprawdzanie i propagowanie wartości zwracanej. (patrz poniżej testy porównawcze)

Ten wysoki koszt wyjątków wynika z faktu, że środowisko CLR systemu Windows integruje się z czymś, co nazywa się obsługą wyjątków strukturalnych systemu Windows . Dzięki temu wyjątki mogą być prawidłowo przechwytywane i zgłaszane w różnych środowiskach wykonawczych i językach. Jednak jest to bardzo powolne.

Wyjątki w środowisku wykonawczym Mono (na dowolnej platformie) są znacznie szybsze, ponieważ nie integruje się z SEH. Jednak podczas przekazywania wyjątków w wielu środowiskach wykonawczych występuje utrata funkcjonalności, ponieważ nie używa niczego takiego jak SEH.

Oto skrócone wyniki z mojego testu porównawczego wyjątków i zwracanych wartości dla środowiska CLR systemu Windows.

baseline: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.25 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.5 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 0.75 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 1 (0), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0 (0), time elapsed 13.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.25 (249999), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.5 (499999), time elapsed 16.0009 ms
retval_error: recurse_depth 5, error_freqeuncy 0.75 (999999), time elapsed 16.001 ms
retval_error: recurse_depth 5, error_freqeuncy 1 (999999), time elapsed 16.0009 ms
retval_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 20.0011 ms
retval_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 21.0012 ms
retval_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 24.0013 ms
exception_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 31.0017 ms
exception_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 5607.3208     ms
exception_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 11172.639  ms
exception_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 22297.2753 ms
exception_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 22102.2641 ms

A oto kod ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

public class TestIt {
    int value;

    public class TestException : Exception { } 

    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    public bool baseline_null(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            return shouldfail;
        } else {
            return baseline_null(shouldfail,recurse_depth-1);
        }
    }

    public bool retval_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                return false;
            } else {
                return true;
            }
        } else {
            bool nested_error = retval_error(shouldfail,recurse_depth-1);
            if (nested_error) {
                return true;
            } else {
                return false;
            }
        }
    }

    public void exception_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                throw new TestException();
            }
        } else {
            exception_error(shouldfail,recurse_depth-1);
        }

    }

    public static void Main(String[] args) {
        int i;
        long l;
        TestIt t = new TestIt();
        int failures;

        int ITERATION_COUNT = 1000000;


        // (0) baseline null workload
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    t.baseline_null(shoulderror,recurse_depth);
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "baseline: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }


        // (1) retval_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    if (!t.retval_error(shoulderror,recurse_depth)) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "retval_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }

        // (2) exception_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    try {
                        t.exception_error(shoulderror,recurse_depth);
                    } catch (TestException e) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "exception_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));         }
        }
    }
}


}
David Jeske
źródło
5
Poza pominięciem sensu pytania, proszę nie używać DateTime, a teraz dla benchmarków - użyj Stopwatch, który jest przeznaczony do pomiaru upływającego czasu. Nie powinno to stanowić problemu, ponieważ mierzysz dość długie okresy, ale warto przyzwyczaić się.
Jon Skeet,
Wręcz przeciwnie, pytanie brzmi „czy wyjątki są powolne” i kropka. Specjalnie poprosił o unikanie tematu, kiedy rzucać wyjątki, ponieważ ten temat przesłania fakty. Jakie są wyniki wyjątków?
David Jeske,
0

Jedna krótka uwaga na temat wydajności związanej z wyłapywaniem wyjątków.

Kiedy ścieżka wykonania wchodzi w blok „try”, nic magicznego się nie dzieje. Nie ma instrukcji „try” i żadnych kosztów związanych z wejściem lub wyjściem z bloku try. Informacje o bloku try są przechowywane w metadanych metody, a te metadane są używane w czasie wykonywania za każdym razem, gdy zostanie zgłoszony wyjątek. Silnik wykonawczy przechodzi w dół stosu, szukając pierwszego wywołania zawartego w bloku try. Wszelkie narzuty związane z obsługą wyjątków występują tylko wtedy, gdy są zgłaszane wyjątki.

Drew Noakes
źródło
1
Jednak obecność wyjątków może mieć wpływ na optymalizację - metody z jawnymi procedurami obsługi wyjątków są trudniejsze do wbudowania, a zmiana kolejności instrukcji jest przez nie ograniczona.
Eamon Nerbonne
-1

Podczas pisania klas / funkcji do wykorzystania przez innych wydaje się, że trudno powiedzieć, kiedy wyjątki są odpowiednie. Jest kilka przydatnych części BCL, z których musiałem zrezygnować i wybrać pinvoke, ponieważ rzucają wyjątki zamiast zwracać błędy. W niektórych przypadkach można to obejść, ale w innych, takich jak System.Management i Performance Counters, istnieją zastosowania, w których trzeba wykonywać pętle, w których wyjątki są często generowane przez BCL.

Jeśli piszesz bibliotekę i istnieje niewielkie prawdopodobieństwo, że Twoja funkcja może być używana w pętli i istnieje możliwość dużej liczby iteracji, użyj wzorca Try .. lub innego sposobu, aby ujawnić błędy obok wyjątków. I nawet wtedy trudno powiedzieć, jak bardzo Twoja funkcja zostanie wywołana, jeśli jest używana przez wiele procesów we wspólnym środowisku.

W moim własnym kodzie wyjątki są generowane tylko wtedy, gdy sytuacja jest tak wyjątkowa, że ​​trzeba spojrzeć na ślad stosu i zobaczyć, co poszło nie tak, a następnie to naprawić. Więc prawie ponownie napisałem części BCL, aby używać obsługi błędów w oparciu o wzorzec Try .. zamiast wyjątków.

Anonimowy tchórz
źródło
2
Wydaje się, że nie pasuje to do stwierdzenia zamieszczającego „ Nie chcę dyskusji o tym, kiedy i nie rzucać wyjątków ”.
hrbrmstr