Exceptions – performance

Ileż to rozmów już prowadziłem na temat

Czy w tym miejscu powinien być rzucany wyjątek czy zwracany status?

Najlepszą na to odpowiedzią jest

To zależy 😉

Ale dzisiaj nie o tym. Kilka razy podczas takich rozmów padł argument

Wyjątki są wolne…

W tym wpisie chciałbym przeprowadzić kilka testów w celu potwierdzenia lub obalenia tej tezy.

Zbadajmy więc czy rzucanie wyjątków faktycznie spowalnia naszą aplikację. W tym celu skorzystam ze znanej już z poprzednich wpisów biblioteki BenchmarkDotNet. Porównam dwa podejścia, w pierwszym metoda będzie zwracała jakiś StatusCode, a w drugim metoda będzie rzucała wyjątkiem.

Aby testy miały podobną strukturę to po zwróceniu/przechwyceniu wyniku metody, odwołam się do obiektu oraz pobiorę jego wiadomość z pola Message. Poniżej zaimplementowane testy:

Jak widać testów jest pięć: Chciałbym zbadać następujące kwestie:

  • Czy rzucanie wyjątków faktycznie jest wolniejsze niż zwrócenie statusu
  • Jaki jest narzut czasowy na tworzenie obiektu typu Exception
  • Czy blok try…catch ma jakikolwiek wpływ na szybkość wykonania metody

Jeszcze dla dopełnienia, implementacja metod z obiektu Helper:

Chciałem aby obiekt statusu miał podobną strukturę do obiektu Exception i aby były wykorzystane tego samego typu konstruktory.

No to wyniki!!!

Faktycznie, wyniki mówią same za siebie. Potwierdzają tezę, wyjątki są znacznie wolniejsze niż zwykłe zwrócenie statusu. Różnica faktycznie jest kolosalna, ponad 2700 razy na jednostkowym wywołaniu. Ale należy zwrócić uwagę na jednostki. Nanosekundy. Po szybkim przeliczeniu wyjdzie, że  rzucenie wyjątku trwa dużo, dużo mniej niż 1 milisekunda. Biorąc pod uwagę specyfikę w której używa się wyjątków (przeważnie w „górnych” partiach wykonywanego kodu) mogę z całą pewnością stwierdzić, że to podejście nie ma najmniejszego znaczenia jeżeli chodzi o wydajność aplikacji.

Możemy jeszcze porównać podejście z tworzeniem obietów StatusCode oraz Exception. Tworzenie wyjątku poprzez new faktycznie trwa dłużej. Jest to spowodowane dodatkową inicjalizacją pól wewnątrz klasy.

Poniżej wkleiłem częściowa implementację klasy Exception:

Jeżeli chodzi o samą konstrukcję try…catch. Widać tu niewielki narzut na wydajność. Wynika on najprawdopodobniej z implementacji tego mechanizmu. Chciałem wrzucić kod maszynowy generowany z bloku  try…catch oraz bez niego, ale nie do końca go jeszcze rozumiem ;D

Podsumowanie

Czy to wszystko oznacza, że rzucanie wyjątków może być używane zawsze i wszędzie ? Nie wydaje mi się.

Wpis ten pokazuje jedynie, że obsługa wyjątków trwa, ale trwa na tyle krótko, że na pewno nie jest to miejsce gdzie bym zaczął optymalizację swojego kodu.

Poniżej jeszcze dopełnienie tego co napisałem powyżej: