Mam program, który wymaga szybkiego działania. W ramach jednej z wewnętrznych pętli muszę przetestować typ obiektu, aby zobaczyć, czy dziedziczy on z określonego interfejsu.
Jednym ze sposobów byłoby użycie wbudowanej funkcji sprawdzania typu środowiska CLR. Najbardziej elegancka metoda, która prawdopodobnie zawiera słowo kluczowe „jest”:
if (obj is ISpecialType)
Innym podejściem byłoby nadanie klasie bazowej mojej własnej wirtualnej funkcji GetType (), która zwraca wstępnie zdefiniowaną wartość wyliczenia (w moim przypadku potrzebuję tylko wartości bool). Ta metoda byłaby szybka, ale mniej elegancka.
Słyszałem, że istnieje instrukcja IL przeznaczona specjalnie dla słowa kluczowego „is”, ale nie oznacza to, że jest ona wykonywana szybko po przetłumaczeniu na natywny asembler. Czy ktoś może podzielić się spostrzeżeniami na temat wykonania metody „jest” w porównaniu z inną metodą?
AKTUALIZACJA: Dzięki za wszystkie odpowiedzi! Wydaje się, że kilka pomocnych punktów jest rozłożonych na odpowiedzi: uwaga Andrew dotycząca automatycznego wykonywania rzutów jest niezbędna, ale dane dotyczące wydajności zebrane przez Binary Worrier i Iana są również niezwykle przydatne. Byłoby wspaniale, gdyby jedna z odpowiedzi została zredagowana tak, aby zawierała wszystkie te informacje.
źródło
Odpowiedzi:
Używanie
is
może zaszkodzić wydajności, jeśli po sprawdzeniu typu rzutujesz na ten typ.is
w rzeczywistości rzutuje obiekt na sprawdzany typ, więc każde kolejne rzutowanie jest zbędne.Jeśli mimo wszystko zamierzasz przesyłać, oto lepsze podejście:
źródło
as
zasadniczo wykonuje tę samą operację cois
(a mianowicie sprawdzenie typu). Jedyną różnicą jest to, żenull
zamiast tego wracafalse
.Jestem z Ianem , prawdopodobnie nie chcesz tego robić.
Jednak, żebyś wiedział, różnica między tymi dwoma jest bardzo mała, ponad 10 000 000 iteracji
Osobiście nie rozwiązałbym tego problemu w ten sposób, ale gdybym był zmuszony wybrać jedną metodę, byłaby to wbudowana kontrola IS, różnica w wydajności nie jest warta rozważenia narzutu kodowania.
Moje klasy bazowe i pochodne
JubJub: Zgodnie z prośbą, więcej informacji na temat testów.
Uruchomiłem oba testy z aplikacji konsoli (kompilacja do debugowania), a każdy test wygląda następująco
Działając w zwolnionym tempie, dostaję różnicę 60 - 70 ms, jak Ian.
Dalsza aktualizacja - 25 października 2012 r.
Po kilku latach nieobecności zauważyłem coś na ten temat, kompilator może zdecydować się na pominięcie
bool b = a is MyClassB
w wydaniu, ponieważ b nie jest nigdzie używany.Ten kod. . .
. . . konsekwentnie pokazuje, że
is
czek nadchodzi po około 57 milisekundach, a porównanie wyliczeniowe po 29 milisekundach.NB nadal wolałbym
is
czek, różnica jest zbyt mała, żeby się nią przejmowaćźródło
is
operator, i że głośno o projektowaniu i kodowaniu wokółis
operatora będzie kosztować fortunę jakości kodu i ostatecznie będzie również samoobronny pod względem wydajności. W tym przypadku podtrzymuję moje oświadczenie. „Is” operator nie będzie problem z wykonania wykonawczego.Ok, więc rozmawiałem z kimś o tym i postanowiłem przetestować to więcej. O ile wiem, wydajność
as
iis
oba są bardzo dobre w porównaniu z testowaniem własnego elementu członkowskiego lub funkcji do przechowywania informacji o typie.Skorzystałem
Stopwatch
, czego właśnie się nauczyłem, może nie jest najbardziej niezawodnym podejściem, więc też spróbowałemUtcNow
. Później wypróbowałem także podejście oparte na czasie procesora, które wydaje się podobne doUtcNow
uwzględniania nieprzewidywalnych czasów tworzenia. Próbowałem też uczynić klasę bazową nieabstrakcyjną bez wirtuali, ale nie przyniosło to znaczącego efektu.Uruchomiłem to na Quad Q6600 z 16 GB pamięci RAM. Nawet przy iteracjach 50 mil, liczby nadal odbijają się w okolicach +/- 50 milisekund, więc nie czytałbym zbyt wiele z drobnych różnic.
Ciekawie było zobaczyć, że x64 tworzył się szybciej, ale wykonywany jako / jest wolniejszy niż x86
x64 Tryb wydania:
Stoper:
As: 561 ms
Jest: 597 ms
Właściwość podstawowa: 539 ms
Pole podstawowe: 555 ms Pole
podstawowe RO: 552 ms
Wirtualny test GetEnumType (): 556 ms
Wirtualny test IsB (): 588 ms
Czas tworzenia: 10416 ms
UtcNow:
As: 499ms
Is: 532ms
Właściwość podstawowa: 479ms
Pole bazowe: 502ms Pole
bazowe RO: 491ms
Wirtualne GetEnumType (): 502ms
Wirtualne bool IsB (): 522ms
Czas tworzenia: 285ms (Ta liczba wydaje się niewiarygodna z UtcNow. Uzyskuję również 109ms i 806 ms.)
x86 Tryb wydania:
Stoper:
As: 391 ms
Jest: 423 ms
Właściwość podstawowa: 369 ms
Pole podstawowe: 321 ms Pole
podstawowe RO: 339 ms
Wirtualny test GetEnumType (): 361 ms
Wirtualny test IsB (): 365 ms
Czas tworzenia: 14106 ms
UtcNow:
As: 348ms
Is: 375ms
Właściwość podstawowa: 329ms
Pole podstawowe: 286ms Pole
bazowe RO: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Czas tworzenia: 544ms (ta liczba wydaje się niewiarygodna z UtcNow.)
Oto większość kodu:
źródło
Andrew ma rację. W rzeczywistości w przypadku analizy kodu program Visual Studio zgłasza to jako niepotrzebne rzutowanie.
Jeden pomysł (nie wiedząc, co robisz, to trochę strzał w ciemno), ale zawsze radzono mi unikać sprawdzania w ten sposób i zamiast tego mieć inne zajęcia. Zamiast więc sprawdzać i wykonywać różne czynności w zależności od typu, spraw, aby klasa wiedziała, jak sama przetworzyć ...
np. Obj może być ISpecialType lub IType;
oba mają zdefiniowaną metodę DoStuff (). Dla IType może po prostu zwrócić lub wykonać niestandardowe rzeczy, podczas gdy ISpecialType może zrobić inne rzeczy.
To następnie całkowicie usuwa wszelkie rzutowanie, sprawia, że kod jest czystszy i łatwiejszy w utrzymaniu, a klasa wie, jak wykonywać własne zadania.
źródło
Dokonałem porównania wydajności dla dwóch możliwości porównania typów
Rezultat jest następujący: użycie „is” jest około 10x szybsze !!!
Wynik:
Czas na porównanie typów: 00: 00: 00.456
Czas na porównanie: 00: 00: 00.042
Mój kod:
źródło
Andrew Hare powiedział o utracie wydajności podczas
is
sprawdzania, a następnie rzucania było prawidłowe, ale w C # 7.0 możemy to zrobić, to sprawdzić dopasowanie wzorca czarownicy, aby uniknąć późniejszego rzucania:Co więcej, jeśli musisz sprawdzić między wieloma typami C # 7.0 konstrukcje dopasowywania wzorców teraz umożliwiają to
switch
na typach:Możesz przeczytać więcej o dopasowywaniu wzorców w C # w dokumentacji tutaj .
źródło
OneOf<T...>
ale mają one poważne wady) .Na wypadek gdyby ktoś się zastanawiał, testy wykonałem w silniku Unity 2017.1 ze skryptami w wersji runtime .NET4.6 (Experimantal) na notebooku z procesorem i5-4200U. Wyniki:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Cały artykuł: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
źródło
Zawsze radzono mi unikać sprawdzania w ten sposób i zamiast tego mieć inne zajęcia. Zamiast więc sprawdzać i wykonywać różne czynności w zależności od typu, spraw, aby klasa wiedziała, jak sama przetworzyć ...
np. Obj może być ISpecialType lub IType;
oba mają zdefiniowaną metodę DoStuff (). Dla IType może po prostu zwrócić lub wykonać niestandardowe rzeczy, podczas gdy ISpecialType może zrobić inne rzeczy.
To następnie całkowicie usuwa wszelkie rzutowanie, sprawia, że kod jest czystszy i łatwiejszy w utrzymaniu, a klasa wie, jak wykonywać własne zadania.
źródło