Czasami twierdzi się, że C ++ 11/14 może zwiększyć wydajność, nawet po prostu kompilując kod C ++ 98. Uzasadnienie jest zwykle zgodne z semantyką ruchu, ponieważ w niektórych przypadkach konstruktory wartości są generowane automatycznie lub są teraz częścią STL. Teraz zastanawiam się, czy te przypadki były wcześniej obsługiwane przez RVO lub podobne optymalizacje kompilatora.
Moje pytanie brzmi zatem, czy możesz podać mi prawdziwy przykład fragmentu kodu C ++ 98, który bez modyfikacji działa szybciej przy użyciu kompilatora obsługującego nowe funkcje językowe. Rozumiem, że standardowy kompilator nie jest wymagany do wykonania kopiowania i właśnie z tego powodu semantyka ruchu może przynieść szybkość, ale chciałbym zobaczyć mniej patologiczny przypadek, jeśli wolisz.
EDYCJA: Żeby było jasne, nie pytam, czy nowe kompilatory są szybsze niż stare kompilatory, ale raczej czy istnieje kod, dzięki któremu dodanie -std = c ++ 14 do moich flag kompilatora działałoby szybciej (unikaj kopii, ale jeśli mogę wymyślić coś innego niż semantykę ruchów, byłbym również zainteresowany)
std::move
i przenoszenia konstruktorów (co wymagałoby modyfikacji istniejącego kodu). Jedyne, co naprawdę związane było z moim pytaniem, to zdanie „Natychmiastowe korzyści prędkości po prostu poprzez rekompilację”, które nie jest poparte żadnymi przykładami (wspomina STL na tym samym slajdzie, jak zrobiłem w moim pytaniu, ale nic konkretnego ). Prosiłem o kilka przykładów. Jeśli źle czytam slajdy, daj mi znać.Odpowiedzi:
Zdaję sobie sprawę z 5 ogólnych kategorii, w których rekompilacja kompilatora C ++ 03 jako C ++ 11 może powodować nieograniczony wzrost wydajności, który jest praktycznie niezwiązany z jakością implementacji. Są to wszystkie odmiany semantyki ruchu.
std::vector
zmienić przydziałkażdym razem, gdy
foo
bufor „s jest przesunięte w C ++ 03 jest kopiowany każdyvector
wbar
.W C ++ 11 zamiast tego przenosi
bar::data
s, który jest w zasadzie darmowy.W tym przypadku zależy to od optymalizacji wewnątrz
std
konteneravector
. W każdym przypadku poniżej użyciestd
kontenerów wynika tylko z tego, że są to obiekty C ++, które mają wydajnąmove
semantykę w C ++ 11 „automatycznie” podczas aktualizacji kompilatora. Obiekty, które go nie blokują i które zawierająstd
kontener, również dziedziczą automatycznie ulepszonemove
konstruktory.Awaria NRVO
Kiedy NRVO (optymalizacja nazw zwracanych wartości) zawiedzie, w C ++ 03 wraca do kopii, w C ++ 11 wraca do ruchu. Awarie NRVO są łatwe:
lub nawet:
Mamy trzy wartości - wartość zwracaną i dwie różne wartości w funkcji. Elision pozwala „scalić” wartości w funkcji z wartością zwracaną, ale nie ze sobą. Oba nie mogą zostać połączone z wartością zwracaną bez połączenia ze sobą.
Podstawową kwestią jest to, że wybór NRVO jest kruchy, a kod ze zmianami, które nie znajdują się w pobliżu
return
witryny, może nagle spowodować znaczne obniżenie wydajności w tym miejscu bez emitowania diagnostyki. W większości przypadków awarii NRVO C ++ 11 kończy się na amove
, podczas gdy C ++ 03 kończy się na kopii.Zwracanie argumentu funkcji
Wykluczenie jest również tutaj niemożliwe:
w C ++ 11 jest to tanie: w C ++ 03 nie ma możliwości uniknięcia kopiowania. Argumenty funkcji nie mogą być pomijane za pomocą wartości zwracanej, ponieważ czasem życia i lokalizacją parametru i wartości zwracanej zarządza kod wywołujący.
Jednak C ++ 11 może przenosić się z jednego do drugiego. (W mniej zabawkowym przykładzie coś można zrobić z
set
).push_back
lubinsert
Wreszcie nie następuje elucja do kontenerów: ale C ++ 11 przeciąża operatorów wstawiania wartości rvalue, co zapisuje kopie.
w C ++ 03
whatever
tworzony jest plik tymczasowy , a następnie kopiowany do wektorav
.std::string
Przydzielono 2 bufory, każdy z identycznymi danymi, a jeden odrzucono.W C ++ 11
whatever
tworzony jest tymczasowy .whatever&&
push_back
Przeciążenie następniemove
y, że tymczasowa do wektorav
. Jedenstd::string
bufor jest przydzielany i przenoszony do wektora. Pustystd::string
jest odrzucany.Zadanie
Skradzione z odpowiedzi @ Jarod42 poniżej.
Wykluczenie nie może nastąpić wraz z przypisaniem, ale przejście może.
tutaj
some_function
zwraca kandydata do ucieczki, ale ponieważ nie jest on używany do bezpośredniej budowy obiektu, nie można go uciec. W C ++ 03 powyższe powoduje skopiowanie zawartości tymczasowejsome_value
. W C ++ 11 jest przeniesiony dosome_value
, który w zasadzie jest darmowy.Aby uzyskać pełny efekt powyższego, potrzebujesz kompilatora, który syntetyzuje dla ciebie konstruktory ruchów i przypisania.
MSVC 2013 implementuje konstruktory ruchu w
std
kontenerach, ale nie syntezuje konstruktorów ruchu na twoich typach.Tak więc typy zawierające
std::vector
si i podobne nie uzyskują takich ulepszeń w MSVC2013, ale zaczną je otrzymywać w MSVC2015.clang i gcc już dawno zaimplementowały niejawne konstruktory ruchów. Kompilator Intela 2013 będzie obsługiwał niejawne generowanie konstruktorów ruchów, jeśli zdasz
-Qoption,cpp,--gen_move_operations
(domyślnie nie robią tego, aby być kompatybilnym z MSVC2013).źródło
std
kontenery biblioteczne zostaną zaktualizowanemove
konstruktorami „za darmo” i (jeśli go nie zablokowałeś) konstrukcje wykorzystujące te obiekty ( i wspomniane obiekty) zaczną uzyskiwać konstrukcję swobodnego ruchu w wielu sytuacjach. Wiele z tych sytuacji jest objętych wyborem w C ++ 03: nie wszystkie.std
powyższych kontenerów wynika głównie z tego, że są one tanie w przenoszeniu za dużo, aby skopiować typ, który dostajesz „za darmo” w C ++ 11 podczas ponownej kompilacji C ++ 03.vector::resize
Wyjątek: używamove
C ++ 11.jeśli masz coś takiego:
Masz kopię w C ++ 03, a zadanie przeniesienia w C ++ 11. więc masz darmową optymalizację w takim przypadku.
źródło
foo().swap(v);