2010-10-29 12 views
35

Przed przeczytaniem pytania:
To pytanie nie dotyczy tego, jak użyteczne jest użycie dynamic_cast. Chodzi tylko o jego wydajność.Wydajność dynamic_cast?

Niedawno opracowałem projekt, w którym bardzo często używany jest kod dynamic_cast.
Podczas dyskusji ze współpracownikami prawie wszyscy mówią, że dynamic_cast nie powinien być używany z powodu jego złych wyników (są to współpracownicy, którzy mają różne pochodzenie, aw niektórych przypadkach nie znają się nawzajem. ogromna firma)

Postanowiłem przetestować działanie tej metody zamiast im tylko uwierzyć.

Poniższy kod został wykorzystany:

ptime firstValue(microsec_clock::local_time()); 

ChildObject* castedObject = dynamic_cast<ChildObject*>(parentObject); 

ptime secondValue(microsec_clock::local_time()); 
time_duration diff = secondValue - firstValue; 
std::cout << "Cast1 lasts:\t" << diff.fractional_seconds() << " microsec" << std::endl; 

Powyższy kod używa metody z boost::date_time na Linuksie, aby uzyskać wartości użytkowej.
Zrobiłem 3 dynamic_cast w jednym wykonaniu, kod do ich pomiaru jest taki sam.

Wyniki 1 realizacją były następujące:
Cast1 trwa: 74 microsec
Cast2 trwa: 2 microsec
Cast3 trwa: 1 microsec

Pierwszy odlew zawsze brał 74-111 microsec następujący rzuty w tej samej egzekucji trwały 1-3 mikrosekundy.

W końcu moje pytania:
Czy dynamic_cast naprawdę działa źle?
Zgodnie z wynikami testu jego nie. Czy mój kod testowy jest poprawny?
Dlaczego tak wielu programistów uważa, że ​​jest powolny, jeśli tak nie jest?

+13

Czy brakuje mi czegoś? Nie widzę żadnego kodu dla cast2 lub cast3. – Flexo

+4

Kto może powiedzieć, co jest złe? Czy twój program działa wystarczająco dobrze na wszystkich? Jeśli tak, to wydajność nie jest zła. Czy łączny czas w dynamicznych rzutach stanowi duży procent twojego czasu wykonania? Jeśli nie, to najpierw przejmuj się innymi rzeczami. Ogólnie rzecz biorąc, 74 mikrosekundy są bardzo powolne w przypadku niektórych aplikacji - w mojej ostatniej pracy otrzymywałbym i analizował cały rekord aktualizacji z giełdy, aktualizował bazę danych i powiadamiał o tym aplikacje klienckie o połowę krótszym. Jeśli jesteś zainteresowany, porównaj to z innymi sposobami uzyskania tego samego zachowania. –

+3

Posiadanie wielu dynamic_casts w kodzie jest pewnym wskaźnikiem problemów projektowych. –

Odpowiedz

42

Po pierwsze, należy zmierzyć wydajność znacznie więcej niż kilka iteracji, ponieważ wyniki będą zdominowane przez rozdzielczość timera. Spróbuj na przykład 1 milion +, aby stworzyć reprezentatywny obraz. Również ten wynik jest bez znaczenia, jeśli nie porównasz go z czymś, tj. Z odpowiednikiem, ale bez dynamicznego rzucania.

Po drugie, musisz upewnić się, że kompilator nie daje fałszywych wyników, optymalizując wiele rzutów dynamicznych na tym samym wskaźniku (użyj pętli, ale za każdym razem użyj innego wskaźnika wejściowego).

Dynamiczne rzucanie będzie wolniejsze, ponieważ musi uzyskać dostęp do tabeli RTTI (informacja o typie wykonania) dla obiektu i sprawdzić, czy rzutowanie jest prawidłowe. Następnie, aby go poprawnie użyć, musisz dodać kod obsługi błędów, który sprawdza, czy zwróconym wskaźnikiem jest NULL. Wszystko to zajmuje cykle.

Wiem, że nie chcesz o tym mówić, ale „projekt gdzie dynamic_cast służy wiele” Prawdopodobnie jest to wskaźnik, że robisz coś złego ...

+5

+1, ale iteracje 10K prawdopodobnie nie wystarczą. Coś jak 100 milionów jest lepsze. – sharptooth

+0

@sharptooth: Fair point! –

+0

@Oliver Charlesworth "... aby użyć go poprawnie, będziesz musiał dodać kod obsługi błędów, który sprawdzi, czy zwracany wskaźnik ma wartość NULL" Analogiczna wersja sprawdzanego wzorca jest obecna w każdej metodzie znalezienia typ środowiska wykonawczego obiektu, więc nie jest to argument. – spectre

24

Wydajność jest bez sensu porównywanie równoważnej funkcjonalności. Większość ludzi twierdzi, że dynamic_cast jest powolny, bez porównywania z równoważnym zachowaniem. Zadzwoń do nich w tej sprawie. Innymi słowy:

Jeśli "działa" nie jest wymagane, mogę napisać kod, który zawodzi szybciej niż twój.

Istnieje kilka sposobów wdrożenia dynamic_cast, a niektóre są szybsze niż inne. Stroustrup opublikował na przykład artykuł na temat używania primes to improve dynamic_cast. Niestety, nietypowe jest kontrolowanie sposobu, w jaki twój kompilator implementuje obsadę, ale jeśli wydajność naprawdę ma dla ciebie znaczenie, wtedy masz kontrolę nad tym, którego kompilatora używasz.

Jednak nie używając dynamic_cast będzie zawsze szybciej niż przy użyciu go - ale jeśli nie faktycznie trzeba dynamic_cast, to nie należy go używać! Jeśli potrzebujesz dynamicznego wyszukiwania, to będzie trochę narzut, a następnie możesz porównać różne strategie.

+4

+1. Tak, przy okazji każda żywa osoba umiera. Co nie znaczy, że to zły pomysł, żeby żyć. – sharptooth

4

Przykro mi to mówić, ale twój test jest praktycznie bezużyteczny dla określenia, czy obsada jest wolna, czy nie. Rozdzielczość w mikrosekundach nie jest wystarczająco dobra. Mówimy o operacji, która nawet w najgorszym przypadku nie powinna zająć więcej niż, powiedzmy, 100 tyknięć zegara lub mniej niż 50 nanosekund na typowym komputerze.

Nie ulega wątpliwości, że rzutowanie dynamiczne będzie wolniejsze niż rzut statyczny lub rzut reinterpretacyjny, ponieważ na poziomie zespołu te dwa ostatnie będą stanowić zadanie (naprawdę szybko, rzędu 1 taktu zegara), oraz dynamiczna obsada wymaga podania kodu i sprawdzenia obiektu w celu ustalenia jego prawdziwego typu.

Nie mogę powiedzieć, jak bardzo jest to wolne, prawdopodobnie różniło się od kompilatora do kompilatora, musiałbym zobaczyć wygenerowany kod zespołu dla tej linii kodu. Ale, jak powiedziałem, 50 nanosekund na telefon jest górną granicą tego, co może wydawać się rozsądne.

+0

dynamic_cast potrzebuje dostępu do RTTI, to zajmie cykle. – doron

16

Oto kilka punktów odniesienia:
http://tinodidriksen.com/2010/04/14/cpp-dynamic-cast-performance/
http://www.nerdblog.com/2006/12/how-slow-is-dynamiccast.html

Według nich, jest 5-30 razy dynamic_cast wolniej niż reinterpret_cast i najlepszą alternatywą wykonuje prawie taka sama jak reinterpret_cast.

będę cytować zawarcia z pierwszego artykułu:

  • dynamic_cast jest powolny, ale za nic odlewania do typu bazowego; że szczególności obsada jest zoptymalizowany na
  • poziom dziedziczenie ma duży wpływ na dynamic_cast
  • zmienną składową + reinterpret_cast jest najszybszym niezawodny sposób
    określić typ; jednak, że ma dużo wyższy narzut konserwacyjnych
    podczas kodowania

Liczby bezwzględne są rzędu 100 ns dla pojedynczej obsadzie. Wartości takie jak 74 ms nie wydają się bliskie rzeczywistości.

+1

Wartość, którą uzyskał, wynosiła 74 ms (mikrosekund), a nie 74 ms (milisekund). Mimo to nadal nie brzmi realistycznie. – Ponkadoodle