Porównaj ceny domen i usług IT, sprzedawców z całego świata

Wydajny sposób na zwrócenie std :: vector w języku C ++


Ile danych jest kopiowanych, gdy std :: vector wraca do funkcji i ile optymalizacji oznaczałoby przydzielenie std :: vector w wolnej pamięci (na stercie) i zamiast tego zwrócenie wskaźnika, tj .:
std::vector *f()
{
std::vector *result = new std::vector();
/*
Insert elements into result
*/
return resu<
}

bardziej wydajny niż:
std::vector f()
{
std::vector resu<
/*
Insert elements into result
*/
return resu<
}

?
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

W C ++ 11 jest to preferowany sposób:
std::vector<X> f();

Oznacza to, że zwraca wartość.
Od C ++ 11,
std :: vector
ma semantykę przenoszenia, co oznacza, że

lokalny

wektor zadeklarowany w twojej funkcji byłby

przeniósł

podczas powrotu, aw niektórych przypadkach nawet przenoszenie może zostać wykluczone przez kompilator.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Musisz zwrócić wartość.
Norma ma tę szczególną cechę, że poprawia efektywność zwrotu kosztów. Nazywa się to „elizją kopiowania”, a raczej w tym przypadku „nazwaną optymalizacją wartości zwracanej (NRVO)”.
Kompilatory nie muszą tego implementować, ale znowu kompilatory nie

powinien

wdrożyć inlining funkcji (lub w ogóle przeprowadzić optymalizację). Jednak wydajność standardowych bibliotek może być dość słaba, jeśli kompilatory nie są zoptymalizowane, a wszystkie poważne kompilatory implementują wbudowane i NRVO (oraz inne optymalizacje).
Podczas korzystania z NRVO kopiowanie w następującym kodzie nie powiedzie się:
std::vector<int> f() {
std::vector<int> resu<
... populate the vector ...
return resu<
}std::vector<int> myvec = f();

Ale użytkownik może chcieć to zrobić:
std::vector<int> myvec;... some time later ...
myvec = f();

Opcja Copy Elision nie zapobiega tutaj kopiowaniu, ponieważ jest to przypisanie, a nie inicjalizacja. Jednak jesteście wszyscy

na równi

musi zwracać wartość. W C ++ 11 przypisanie jest zoptymalizowane za pomocą czegoś innego, co nazywa się „semantyką przenoszenia”. W C ++ 03 powyższy kod rzeczywiście wywołuje kopię i chociaż

W teorii

optymalizator może tego uniknąć, w praktyce jest to zbyt trudne. Więc zamiast
myvec = f ()
w C ++ 03 powinieneś napisać:
std::vector<int> myvec;... some time later ...
f().swap(myvec);

Jest jeszcze jedna opcja, która ma zaoferować użytkownikowi bardziej elastyczny interfejs:
template <typename OutputIterator> void f(OutputIterator it) {
... write elements to the iterator like this ...
*it++ = 0;
*it++ = 1;
}

Alternatywnie możesz również obsługiwać istniejący interfejs wektorowy:
std::vector<int> f() {
std::vector<int> resu<
f(std::back_inserter(result));
return resu<
}

to

mogą

być mniej wydajnym niż istniejący kod, jeśli istniejący kod wykorzystuje metodę
Reserve ()
w bardziej złożony sposób niż tylko ustalona z góry kwota. Ale jeśli twój istniejący kod w zasadzie ponownie wywołuje
push_back
w wektorze, to ten standardowy kod powinien być równie dobry.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nadszedł czas, abym opublikował odpowiedź na temat

RVO
http://en.wikipedia.org/wiki/R ... ation, Ja też...
Jeśli zwracasz obiekt według wartości, kompilator często optymalizuje go, aby nie był budowany dwukrotnie, ponieważ nie jest konieczne tworzenie go w funkcji jako tymczasowej, a następnie kopiowanie. Nazywa się to optymalizacją wartości zwracanej: utworzony obiekt zostanie przeniesiony, a nie skopiowany.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Typowym idiomem sprzed C ++ 11 jest przekazanie odwołania do obiektu, który można wypełnić.
Wtedy nie ma kopiowania wektorów.
void f( std::vector & result )
{
/*
Insert elements into result
*/
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli kompilator obsługuje optymalizację nazwanych wartości zwracanych (

http://msdn.microsoft.com/en-us/ library/ms364057(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx), możesz bezpośrednio zwrócić wektor, pod warunkiem, że nie istnieje:
  • Różne ścieżki zwracające różne nazwane obiekty
  • Wiele ścieżek zwrotnych (nawet jeśli ten sam nazwany obiekt jest zwracany przez wszystkie ścieżki) wraz z wprowadzeniem stanów EH.
  • Do nazwanego zwracanego obiektu odwołuje się wbudowany blok ASM.

NRVO optymalizuje nadmiarowe wywołania konstruktora kopiującego i destruktora, poprawiając w ten sposób ogólną wydajność.
W twoim przykładzie nie powinno być żadnych rzeczywistych różnic.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

vector<string> getseq(char * db_file)

A jeśli chcesz wydrukować to do main (), musisz to zrobić w pętli.
int main() {
vector<string> str_vec = getseq(argv[1]);
for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
cout << *it << endl;
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Tak samo dobre jak „zwracanie przez wartość”, jest to rodzaj kodu, który może prowadzić do błędu. Rozważ następujący program:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
static std::vector<std::string> strings;
std::vector<std::string> vecFunc(void) { return strings; };
int main(int argc, char * argv[]){
// set up the vector of strings to hold however
// many strings the user provides on the command line
for(int idx=1; (idx<argc); ++idx){
strings.push_back(argv[idx]);
}// now, iterate the strings and print them using the vector function
// as accessor
for(std::vector<std::string>::interator idx=vecFunc().begin(); (idx!=vecFunc().end()); ++idx){
cout << "Addr: " << idx->c_str() << std::endl;
cout << "Val: " << *idx << std::endl;
}
return 0;
};

  • P: Co się stanie, gdy wszystkie powyższe czynności zostaną wykonane ODPOWIEDŹ: coredump.
  • Pytanie brzmi: dlaczego kompilator nie złapał błędu Odpowiedź: Ponieważ program składniowo, choć nie semantycznie, poprawne.
  • Pytanie: Co się stanie, jeśli zmienisz funkcję vecFunc (), aby zwrócić łącze Odpowiedź: Program działa do końca i daje oczekiwany wynik.
  • P: jaka jest różnica Odpowiedź: kompilator tego nie robi musisz tworzyć i zarządzać anonimowymi obiektami. Programista poinstruował kompilator, aby użył dokładnie jednego obiektu dla iteratora i punktu końcowego, a nie dwóch różnych obiektów, jak to ma miejsce w zepsutym przykładzie.

Powyższy program błędu nie wskaże błędów, nawet jeśli używasz opcji raportowania GNU G ++-Wall-Wextra-Weffc ++
Jeśli musisz podać wartość, zamiast dwukrotnego wywoływania vecFunc () zadziała następujące działanie:
std::vector<std::string> lclvec(vecFunc());
for(std::vector<std::string>::iterator idx=lclvec.begin(); (idx!=lclvec.end()); ++idx)...

Powyższe również nie tworzy anonimowych obiektów podczas iteracji pętli, ale wymaga możliwej operacji kopiowania (która, jak niektórzy zauważyli, może być zoptymalizowana w pewnych okolicznościach. Ale metoda referencyjna zapewnia, że ​​żadna kopia nie zostanie utworzona). Przekonanie, że kompilator będzie Wykonaj RVO. nie zastąpi próby zbudowania najbardziej wydajnego kodu, jaki możesz. Jeśli możesz spierać się o potrzebę wykonania RVO przez kompilator, wyprzedzasz grę.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

vector<string> func1() const
{
vector<string> parts;
return vector<string>(parts.begin(),parts.end()) ;
}

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się