Rozumiem, dlaczego auto
typ w C ++ 11 poprawia poprawność i łatwość konserwacji. Czytałem, że może to również poprawić wydajność ( Prawie zawsze auto autorstwa Herb Sutter), ale brakuje mi dobrego wyjaśnienia.
- Jak
auto
poprawić wydajność? - Czy ktoś może podać przykład?
c++
performance
c++11
auto
DaBrain
źródło
źródło
Odpowiedzi:
auto
może poprawić wydajność, unikając cichych niejawnych konwersji . Przykład, który uważam za przekonujący, jest następujący.Widzisz błąd? Oto my, myśląc, że elegancko bierzemy każdy element na mapie przez odniesienie do stałej i używamy nowego wyrażenia zakresu dla wyrażenia naszej intencji, ale w rzeczywistości kopiujemy każdy element. To dlatego, że
std::map<Key, Val>::value_type
jeststd::pair<const Key, Val>
, niestd::pair<Key, Val>
. Zatem gdy (domyślnie) mamy:Zamiast odwoływać się do istniejącego obiektu i pozostawić go przy tym, musimy wykonać konwersję typu. Możesz wziąć stałe odniesienie do obiektu (lub tymczasowego) innego typu, o ile dostępna jest niejawna konwersja, np .:
Konwersja typu jest dozwoloną niejawną konwersją z tego samego powodu, dla którego można przekonwertować a
const Key
naKey
, ale musimy skonstruować tymczasowy nowy typ, aby na to pozwolić. Zatem skutecznie nasza pętla:(Oczywiście, nie ma w rzeczywistości żadnego
__tmp
przedmiotu, jest tylko dla ilustracji, w rzeczywistości nienazwany tymczasowy jest po prostu związany zitem
jego życiem).Zmieniam się na:
właśnie zaoszczędziliśmy nam mnóstwo kopii - teraz typ odniesienia odpowiada typowi inicjalizującemu, więc nie jest potrzebna tymczasowa ani konwersja, możemy po prostu zrobić bezpośrednie odniesienie.
źródło
std::pair<const Key, Val> const &
jakostd::pair<Key, Val> const &
? Nowy w C ++ 11, nie jestem pewien, w jaki sposób zasięg iauto
gra.auto
zwiększyć wydajność. Więc napiszę to własnymi słowami poniżej.auto
poprawia wydajność”. To tylko przykład, który „auto
pomaga zapobiec błędom programisty, które niszczą wydajność”. Uważam, że istnieje między nimi subtelna, ale ważna różnica. Nadal +1.Ponieważ
auto
dedukuje typ wyrażenia inicjującego, konwersja typu nie jest wymagana. W połączeniu z algorytmami szablonowymi oznacza to, że możesz uzyskać bardziej bezpośrednie obliczenia, niż gdybyś sam tworzył typ - szczególnie, gdy masz do czynienia z wyrażeniami, których typu nie możesz nazwać!Typowy przykład pochodzi z (ab) przy użyciu
std::function
:Za pomocą
cmp2
icmp3
cały algorytm może wstawić wywołanie porównania, podczas gdy jeśli zbudujeszstd::function
obiekt, nie tylko nie można wstawić wywołania, ale musisz również przejść przez wyszukiwanie polimorficzne w wymazanym typem wnętrzu opakowania funkcji.Innym wariantem tego tematu jest to, że możesz powiedzieć:
Jest to zawsze odwołanie powiązane z wartością wyrażenia wywołania funkcji i nigdy nie konstruuje żadnych dodatkowych obiektów. Jeśli nie znasz typu zwracanej wartości, możesz zostać zmuszony do skonstruowania nowego obiektu (być może tymczasowego) za pomocą czegoś podobnego
T && f = MakeAThing()
. (Ponadtoauto &&
działa nawet wtedy, gdy typ zwracany nie jest ruchomy, a zwracana wartość jest wartością wstępną.)źródło
auto
. Twój drugi wariant to „unikaj przypadkowych kopii”, ale wymaga upiększenia; dlaczegoauto
dajesz szybkość po prostu wpisując tam typ? (Myślę, że odpowiedź brzmi: „źle wpisujesz ten typ, a on po cichu przekształca”). Co sprawia, że jest to mniej dobrze wyjaśniony przykład odpowiedzi Barry'ego, prawda? Tzn. Istnieją dwa podstawowe przypadki: auto, aby uniknąć kasowania typu, i auto, aby uniknąć cichych błędów typu, które przypadkowo się przekonwertowały, a oba mają koszt działania.std::bind
,std::function
astd::stable_partition
wszystkie zostały inlined? Czy po prostu, że w praktyce żaden kompilator C ++ nie będzie wystarczająco agresywny, aby rozwiązać problem?std::function
konstruktora przejrzenie rzeczywistego wywołania będzie bardzo skomplikowane, szczególnie przy optymalizacji małych funkcji (więc tak naprawdę nie chcesz dewirtualizacji). Oczywiście w zasadzie wszystko jest jak gdyby ...Istnieją dwie kategorie.
auto
można uniknąć usunięcia typu. Istnieją typy nienazwane (jak lambdas) i typy prawie nienazwane (jak wynikstd::bind
lub inne podobne do szablonu wyrażenia rzeczy).Bez tego
auto
musisz w końcu skasować dane do czegoś podobnegostd::function
. Usuwanie typu ma koszty.task1
ma narzut kasowania typu - możliwy przydział sterty, trudność w jego wstawianiu oraz narzut wywołania tablicy funkcji wirtualnych.task2
nie ma Jagnięta potrzebują automatycznego lub innego rodzaju odliczenia typu, aby przechowywać bez usuwania typu; inne typy mogą być tak złożone, że potrzebują go tylko w praktyce.Po drugie, możesz pomylić typy. W niektórych przypadkach niewłaściwy typ będzie działał pozornie idealnie, ale spowoduje kopiowanie.
skompiluje, jeśli
expression()
zwraca,Bar const&
aBar
nawetBar&
, gdzieFoo
można zbudowaćBar
. TymczasowyFoo
zostanie utworzony, a następnie związanyf
, a jego żywotność zostanie przedłużona, ażf
zniknie.Programista mógł mieć na myśli
Bar const& f
i nie zamierzać tam tworzyć kopii, ale kopia jest tworzona niezależnie.Najczęstszym przykładem jest typ
*std::map<A,B>::const_iterator
, którystd::pair<A const, B> const&
nie jeststd::pair<A,B> const&
, ale błąd jest kategorią błędów, które dyskretnie obniżają wydajność. Możesz zbudowaćstd::pair<A, B>
zstd::pair<const A, B>
. (Klucz na mapie to const, ponieważ edytowanie jej to zły pomysł)Zarówno @Barry, jak i @KrekrekSB najpierw zilustrowały te dwie zasady w swoich odpowiedziach. Jest to po prostu próba podkreślenia dwóch problemów w jednej odpowiedzi, przy użyciu sformułowania, które ma na celu problem, a nie koncentrowanie się na przykładach.
źródło
Istniejące trzy odpowiedzi podają przykłady, w których użycie
auto
pomaga „zmniejszyć prawdopodobieństwo niezamierzonego pesymalizowania” skutecznie, sprawiając, że „poprawia wydajność”.Moneta ma drugą stronę. Używanie
auto
z obiektami, które mają operatory, które nie zwracają podstawowego obiektu, może spowodować niepoprawny (nadal możliwy do skompilowania i uruchomienia) kod. Na przykład to pytanie dotyczy tego, w jaki sposób użycieauto
dało różne (niepoprawne) wyniki przy użyciu biblioteki Eigen, tj . Następujące liniespowodowało inną wydajność. Wprawdzie wynika to głównie z leniwej oceny Eigensa, ale ten kod jest / powinien być przezroczysty dla użytkownika (biblioteki).
Chociaż nie ma to większego wpływu na wydajność, użycie w
auto
celu uniknięcia niezamierzonej pesymizacji może zostać zaklasyfikowane jako przedwczesna optymalizacja, a przynajmniej źle;źródło