Czy nowoczesny C++ da ci wydajność za darmo?

Czasami twierdzi się, że C++11/14 może zwiększyć wydajność nawet podczas kompilacji kodu C++98. Uzasadnienie jest zwykle zgodne z semantyką move, ponieważ w niektórych przypadkach konstruktory rvalue są generowane automatycznie lub są teraz częścią STL. Teraz zastanawiam się, czy te sprawy były wcześniej faktycznie już obsługiwane przez RVO lub podobnych optymalizacji kompilatora.

Moje pytanie brzmi, czy mógłbyś podać mi przykład fragmentu kodu C++98, który, bez modyfikacji działa szybciej przy użyciu kompilatora obsługującego nowe funkcje języka. Rozumiem, że standardowy kompilator nie jest wymagany do kopiowania i właśnie z tego powodu semantyka move może przynieść szybkość, ale chciałbym zobaczyć mniej patologiczny przypadek, jeśli wolisz.

EDIT: dla jasności, nie pytam, czy nowe Kompilatory są szybsze od starych, ale raczej, jeśli jest kod, w którym dodanie -std = c++14 do flag kompilatora byłoby uruchamiane szybciej (unikaj kopiowania, ale jeśli możesz wymyślić coś jeszcze poza semantyką move, też byłbym zainteresowany)

Author: alarge, 2014-12-22

2 answers

Znam 5 ogólnych kategorii, w których przekompilowanie kompilatora C++03 jako C++11 może spowodować nieograniczony wzrost wydajności, który jest praktycznie niezwiązany z jakością implementacji. Są to wszystkie odmiany semantyki ruchu.

std::vector reallocate

struct bar{
  std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03

Za każdym razem, gdy bufor foo jest realokowany w C++03, kopiuje on co vector w bar.

W C++11 zamiast tego przesuwa bar::datas, który jest w zasadzie wolny.

W tym przypadku polega to na optymalizacje wewnątrz std kontenera vector. W każdym przypadku poniżej, użycie kontenerów std jest tylko dlatego, że są to obiekty C++, które mają wydajną semantykę move W C++11 "automatycznie" podczas aktualizacji kompilatora. Obiekty, które go nie blokują, które zawierają kontener std również dziedziczą automatyczne ulepszone konstruktory move.

Awaria NRVO

Gdy NRVO (named return value optimization) zawiedzie, w C++03 spada z powrotem na kopię, w C++11 spada z powrotem na ruszaj się. Awarie NRVO są łatwe:

std::vector<int> foo(int count){
  std::vector<int> v; // oops
  if (count<=0) return std::vector<int>();
  v.reserve(count);
  for(int i=0;i<count;++i)
    v.push_back(i);
  return v;
}

Lub nawet:

std::vector<int> foo(bool which) {
  std::vector<int> a, b;
  // do work, filling a and b, using the other for calculations
  if (which)
    return a;
  else
    return b;
}

Mamy trzy wartości -- wartość zwracaną i dwie różne wartości w funkcji. Elision pozwala wartości wewnątrz funkcji być "scalone" z wartością zwracaną, ale nie ze sobą. Oba nie mogą być połączone z wartością zwracaną bez scalania się ze sobą.

Podstawowym problemem jest to, że nrvo jest kruche, a kod ze zmianami Nie w pobliżu return witryny może nagle mieć ogromne zmniejszenie wydajności w tym miejscu bez emisji diagnostycznej. C++11 kończy się move, Podczas Gdy C++03 kończy się kopią.

Zwracanie argumentu funkcji

Tutaj też nie da się uciec:

std::set<int> func(std::set<int> in){
  return in;
}

W C++11 jest to tanie: w C++03 nie ma sposobu na uniknięcie kopii. Argumenty funkcji nie mogą zostać uzupełnione o zwracaną wartość, ponieważ żywotność i lokalizacja parametru oraz zwracanej wartości jest zarządzana przez wywołanie kod.

Jednakże, C++11 może przenosić się z jednego do drugiego. (W mniej zabawkowym przykładzie można coś zrobić z set).

push_back lub insert

Ostatecznie usunięcie kontenerów nie następuje: ale C++11 przeciąża operatory rvalue move insert, które zapisują kopie.

struct whatever {
  std::string data;
  int count;
  whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );

W C++03 tworzony jest tymczasowy whatever, a następnie kopiowany do wektora v. 2 std::string są przydzielane bufory, każdy z identycznymi danymi, a jeden jest odrzucany.

W C++11 tworzony jest tymczasowy whatever. Na whatever&& push_back przeciążenie wtedy move S, że tymczasowe do wektora v. Jeden bufor std::string jest alokowany i przenoszony do wektora. Pusty std::string jest odrzucany.

Przypisanie

Skradzione z odpowiedzi @Jarod42 poniżej.

Elision nie może wystąpić z przypisaniem, ale przenieść-z can.

std::set<int> some_function();

std::set<int> some_value;

// code

some_value = some_function();

Tutaj some_function zwraca kandydata, z którego można uciec, ale ponieważ nie jest on używany do bezpośredniej budowy obiektu, nie może być / align = "left" / W C++03 powyższe powoduje skopiowanie zawartości tymczasowej do some_value. W C++11 jest przenoszony do some_value, który w zasadzie jest wolny.


Aby uzyskać pełny efekt powyższego, potrzebujesz kompilatora, który syntetyzuje konstruktory ruchu i przypisania dla Ciebie.

MSVC 2013 implementuje konstruktory ruchu w kontenerach std, ale nie syntetyzuje konstruktorów ruchu na typach.

Więc typy zawierające std::vector s i podobne nie otrzymują takich ulepszenia w MSVC2013, ale zacznie je otrzymywać w MSVC2015.

Clang i gcc już dawno wprowadziły Ukryte konstruktory ruchu. Kompilator Intela z 2013 roku będzie obsługiwał domyślne generowanie konstruktorów move, jeśli przejdziesz -Qoption,cpp,--gen_move_operations (nie robią tego domyślnie, aby być kompatybilnym krzyżowo z MSVC2013).

 215
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 15:40:16

Jeśli masz coś takiego:

std::vector<int> foo(); // function declaration.
std::vector<int> v;

// some code

v = foo();

Masz kopię w C++03, podczas gdy masz zadanie move W C++11. więc masz darmową optymalizację w tym przypadku.

 44
Author: Jarod42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-22 04:50:36