W moich projektach klas intensywnie korzystam z klas abstrakcyjnych i funkcji wirtualnych. Miałem wrażenie, że funkcje wirtualne wpływają na wydajność. Czy to prawda? Ale myślę, że ta różnica w wydajności nie jest zauważalna i wygląda na to, że wykonuję przedwczesną optymalizację. Dobrze?
c++
performance
optimization
virtual-functions
Navaneeth KN
źródło
źródło
Odpowiedzi:
Praktyczna zasada:
Użycie funkcji wirtualnych będzie miało bardzo niewielki wpływ na wydajność, ale jest mało prawdopodobne, aby wpłynęło na ogólną wydajność aplikacji. Lepszymi miejscami do poszukiwania ulepszeń wydajności są algorytmy i operacje we / wy.
Doskonały artykuł, który mówi o funkcjach wirtualnych (i nie tylko), to wskaźniki funkcji składowych i najszybsze możliwe delegaty C ++ .
źródło
Twoje pytanie zaciekawiło mnie, więc poszedłem naprzód i przeprowadziłem taktowanie na procesorze PowerPC 3GHz w kolejności, z którym pracujemy. Test, który przeprowadziłem, polegał na stworzeniu prostej klasy wektorowej 4d z funkcjami get / set
Następnie ustawiłem trzy tablice, z których każda zawierała 1024 tych wektorów (wystarczająco małe, aby zmieścić się w L1) i uruchomiłem pętlę, która dodała je do siebie (Ax = Bx + Cx) 1000 razy. Pobiegłem to z funkcjami zdefiniowanymi jako
inline
,virtual
oraz regularnych połączeń funkcyjnych. Oto wyniki:Tak więc w tym przypadku (gdzie wszystko mieści się w pamięci podręcznej) wywołania funkcji wirtualnych były około 20 razy wolniejsze niż wywołania wbudowane. Ale co to naprawdę oznacza? Każde przejście przez pętlę powodowało dokładnie
3 * 4 * 1024 = 12,288
wywołania funkcji (1024 wektory razy cztery składniki razy trzy wywołania na dodanie), więc te czasy reprezentują1000 * 12,288 = 12,288,000
wywołania funkcji. Wirtualna pętla trwała 92 ms dłużej niż bezpośrednia pętla, więc dodatkowe obciążenie na wywołanie wyniosło 7 nanosekund na funkcję.Z tego dochodzę do wniosku: tak , funkcje wirtualne są znacznie wolniejsze niż funkcje bezpośrednie i nie , chyba że planujesz wywoływać je dziesięć milionów razy na sekundę, to nie ma znaczenia.
Zobacz też: porównanie wygenerowanego zespołu.
źródło
Kiedy Objective-C (gdzie wszystkie metody są wirtualne) jest podstawowym językiem dla iPhone'a, a dziwaczna Java jest głównym językiem dla Androida, myślę, że używanie wirtualnych funkcji C ++ na naszych dwurdzeniowych wieżach 3 GHz jest całkiem bezpieczne.
źródło
W aplikacjach o krytycznym znaczeniu dla wydajności (takich jak gry wideo) wywołanie funkcji wirtualnej może być zbyt wolne. W przypadku nowoczesnego sprzętu największym problemem dotyczącym wydajności jest brak pamięci podręcznej. Jeśli danych nie ma w pamięci podręcznej, mogą minąć setki cykli, zanim będą dostępne.
Normalne wywołanie funkcji może wygenerować brak pamięci podręcznej instrukcji, gdy procesor pobiera pierwszą instrukcję nowej funkcji i nie ma jej w pamięci podręcznej.
Wywołanie funkcji wirtualnej musi najpierw załadować wskaźnik vtable z obiektu. Może to spowodować utratę pamięci podręcznej danych. Następnie ładuje wskaźnik funkcji z tabeli vtable, co może skutkować kolejnym brakiem pamięci podręcznej danych. Następnie wywołuje funkcję, która może spowodować pominięcie pamięci podręcznej instrukcji, podobnie jak funkcja niewirtualna.
W wielu przypadkach dwa dodatkowe chybienia w pamięci podręcznej nie stanowią problemu, ale w ścisłej pętli kodu krytycznego dla wydajności może znacznie zmniejszyć wydajność.
źródło
Ze strony 44 podręcznika „Optimizing Software in C ++” firmy Agner Fog :
źródło
switch
.case
Oczywiście z całkowicie arbitralnymi wartościami. Ale jeśli wszystkiecase
są następujące po sobie, kompilator może być w stanie zoptymalizować to do postaci tabeli skoków (ach, to przypomina mi stare dobre dni Z80), która powinna być (z braku lepszego terminu) stała. Nie żebym polecał próbę zastąpienia vfuncsswitch
, co jest niedorzeczne. ;)absolutnie. Dawno temu był to problem, gdy komputery pracowały z częstotliwością 100 MHz, ponieważ każde wywołanie metody wymagało przeszukania tabeli vtable przed jej wywołaniem. Ale dzisiaj… na procesorze 3 GHz z pamięcią podręczną pierwszego poziomu z większą ilością pamięci niż mój pierwszy komputer? Ani trochę. Przydzielanie pamięci z głównej pamięci RAM będzie kosztować więcej czasu, niż gdyby wszystkie funkcje były wirtualne.
To jak za dawnych czasów, kiedy ludzie mówili, że programowanie strukturalne było powolne, ponieważ cały kod został podzielony na funkcje, każda funkcja wymagała alokacji stosu i wywołania funkcji!
Jedyny przypadek, w którym pomyślałbym o zawracaniu sobie głowy rozważaniem wpływu funkcji wirtualnej na wydajność, to sytuacja, gdy była ona bardzo intensywnie używana i tworzona w kodzie opartym na szablonach, który kończy się we wszystkim. Nawet wtedy nie poświęcałbym na to zbyt wiele wysiłku!
PS myślę o innych „łatwych w użyciu” językach - wszystkie ich metody są wirtualne pod osłonami i obecnie się nie indeksują.
źródło
Oprócz czasu wykonania istnieją inne kryteria wydajności. Vtable również zajmuje miejsce w pamięci, aw niektórych przypadkach można tego uniknąć: ATL używa „ symulowanego dynamicznego wiązania w czasie kompilacji” ” w z szablonamiuzyskać efekt „statycznego polimorfizmu”, który jest dość trudny do wyjaśnienia; w zasadzie przekazujesz klasę pochodną jako parametr do szablonu klasy bazowej, więc w czasie kompilacji klasa bazowa „wie”, jaka jest jej klasa pochodna w każdym wystąpieniu. Nie pozwoli ci przechowywać wielu różnych klas pochodnych w kolekcji typów podstawowych (jest to polimorfizm w czasie wykonywania), ale z punktu widzenia statycznego, jeśli chcesz utworzyć klasę Y, która jest taka sama jak wcześniej istniejąca klasa szablonu X, która ma punkty zaczepienia dla tego rodzaju nadpisywania, wystarczy przesłonić metody, na których Ci zależy, a następnie uzyskać podstawowe metody klasy X bez konieczności posiadania tabeli vtable.
W klasach z dużymi śladami pamięci koszt pojedynczego wskaźnika vtable jest niewielki, ale niektóre klasy ATL w COM są bardzo małe i warto zaoszczędzić na vtable, jeśli przypadek polimorfizmu w czasie wykonywania nigdy nie wystąpi.
Zobacz także to inne pytanie SO .
Przy okazji, oto post, który znalazłem, który mówi o aspektach wydajności czasu procesora.
źródło
Tak, masz rację i jeśli ciekawi Cię koszt wywołania funkcji wirtualnej, ten post może Cię zainteresować.
źródło
Jedynym sposobem, w jaki widzę, że funkcja wirtualna stanie się problemem z wydajnością, jest wywołanie wielu funkcji wirtualnych w ścisłej pętli i wtedy i tylko wtedy, gdy gdy powodują błąd strony lub inną „ciężką” operację pamięci.
Chociaż, jak mówili inni, prawie nigdy nie będzie to dla ciebie problemem w prawdziwym życiu. A jeśli myślisz, że tak, uruchom program profilujący, przeprowadź testy i sprawdź, czy rzeczywiście stanowi to problem, zanim spróbujesz „cofnąć projekt” kodu w celu zwiększenia wydajności.
źródło
Gdy metoda klasy nie jest wirtualna, kompilator zwykle wykonuje wbudowanie. Wręcz przeciwnie, gdy użyjesz wskaźnika do jakiejś klasy z funkcją wirtualną, prawdziwy adres będzie znany tylko w czasie wykonywania.
Dobrze obrazuje to test, różnica czasu ~ 700% (!):
Wpływ wywołania funkcji wirtualnej w dużym stopniu zależy od sytuacji. Jeśli jest kilka wywołań i znaczna ilość pracy wewnątrz funkcji - może to być pomijalne.
Lub, gdy jest to połączenie wirtualne wielokrotnie używane, wykonując jakąś prostą operację - może być naprawdę duże.
źródło
++ia
. Więc co?Wracałem do tego co najmniej 20 razy w moim konkretnym projekcie. Chociaż mogą być pewne znaczne korzyści w zakresie ponownego wykorzystania kodu, przejrzystości, łatwości utrzymania i czytelności, z drugiej strony, hity wydajnościowe nadal są skuteczne istnieć wspólnie z funkcji wirtualnych.
Czy hit wydajnościowy będzie zauważalny na nowoczesnym laptopie / komputerze stacjonarnym / tablecie ... prawdopodobnie nie! Jednak w niektórych przypadkach w przypadku systemów wbudowanych spadek wydajności może być czynnikiem powodującym nieefektywność kodu, zwłaszcza jeśli funkcja wirtualna jest wywoływana wielokrotnie w pętli.
Oto nieco przestarzały dokument, który przedstawia najlepsze praktyki dotyczące języka C / C ++ w kontekście systemów wbudowanych: http://www.open-std.org/jtc1/sc22/wg21/docs/ESC_Boston_01_304_paper.pdf
Podsumowując: to od programisty zależy zrozumienie zalet / wad używania określonej konstrukcji zamiast innej. O ile nie jesteś nastawiony na super wydajność, prawdopodobnie nie przejmujesz się spadkiem wydajności i powinieneś używać wszystkich fajnych rzeczy OO w C ++, aby uczynić swój kod tak użytecznym, jak to tylko możliwe.
źródło
Z mojego doświadczenia wynika, że najważniejszą rzeczą jest możliwość wbudowania funkcji. Jeśli masz potrzeby w zakresie wydajności / optymalizacji, które nakazują, aby funkcja była wbudowana, nie możesz uczynić funkcji wirtualną, ponieważ by temu zapobiec. W przeciwnym razie prawdopodobnie nie zauważysz różnicy.
źródło
Należy zauważyć, że:
może być szybsze niż to:
Dzieje się tak, ponieważ pierwsza metoda wywołuje tylko jedną funkcję, podczas gdy druga może wywoływać wiele różnych funkcji. Dotyczy to wszelkich funkcji wirtualnych w dowolnym języku.
Mówię „może”, ponieważ to zależy od kompilatora, pamięci podręcznej itp.
źródło
Spadek wydajności wynikający z używania funkcji wirtualnych nigdy nie przeważy korzyści uzyskanych na poziomie projektowania. Przypuszczalnie wywołanie funkcji wirtualnej byłoby o 25% mniej wydajne niż bezpośrednie wywołanie funkcji statycznej. Dzieje się tak, ponieważ w VMT istnieje pewien poziom pośredni. Jednak czas potrzebny do wykonania połączenia jest zwykle bardzo krótki w porównaniu z czasem potrzebnym na rzeczywiste wykonanie funkcji, więc całkowity koszt wydajności będzie niewielki, szczególnie przy obecnej wydajności sprzętu. Ponadto kompilator może czasami zoptymalizować i zobaczyć, że nie jest potrzebne żadne wirtualne wywołanie i skompilować je do statycznego wywołania. Więc nie martw się, używaj funkcji wirtualnych i klas abstrakcyjnych tak często, jak potrzebujesz.
źródło
The performance penalty of using virtual functions can sometimes be so insignificant that it is completely outweighed by the advantages you get at the design level.
Kluczowa różnica polega na tym, żesometimes
nienever
.Zawsze to kwestionowałem, zwłaszcza że - kilka lat temu - przeprowadziłem również taki test porównując czasy wywołania standardowej metody składowej z wirtualną i byłem bardzo zły z powodu wyników w tamtym czasie, mając puste wirtualne połączenia 8 razy wolniej niż nie-wirtualne.
Dzisiaj musiałem zdecydować, czy użyć funkcji wirtualnej do przydzielenia większej ilości pamięci w mojej klasie bufora, w aplikacji o bardzo krytycznym znaczeniu dla wydajności, więc wyszukałem go w Google (i znalazłem) i na koniec ponownie przeprowadziłem test.
I był naprawdę zaskoczony, że to - w rzeczywistości - naprawdę nie ma już żadnego znaczenia. Chociaż sensowne jest posiadanie inline szybciej niż nie-wirtualne i że są one szybsze niż wirtualne, często wiąże się to z ogólnym obciążeniem komputera, czy pamięć podręczna zawiera niezbędne dane, czy nie, i chociaż możesz być w stanie zoptymalizować Myślę, że na poziomie pamięci podręcznej powinni to robić programiści kompilatorów, a nie twórcy aplikacji.
źródło