2009-09-22 14 views
33

robię szybki test wydajności na bloku kodustd :: vector reserve() i push_back() jest szybszy niż resize() i indeks tablicy, dlaczego?

void ConvertToFloat(const std::vector<short>& audioBlock, 
        std::vector<float>& out) 
{ 
    const float rcpShortMax = 1.0f/(float)SHRT_MAX; 
    out.resize(audioBlock.size()); 
    for(size_t i = 0; i < audioBlock.size(); i++) 
    { 
     out[i] = (float)audioBlock[i] * rcpShortMax; 
    } 
} 

byłem zadowolony z prędkością ponad pierwotnego bardzo naiwnej realizacji to trwa niewiele ponad 1 ms przetworzyć 65536 próbki audio.

Jednak tylko dla zabawy próbowałem następujące

void ConvertToFloat(const std::vector<short>& audioBlock, 
        std::vector<float>& out) 
{ 
    const float rcpShortMax = 1.0f/(float)SHRT_MAX; 
    out.reserve(audioBlock.size()); 
    for(size_t i = 0; i < audioBlock.size(); i++) 
    { 
     out.push_back((float)audioBlock[i] * rcpShortMax); 
    } 
} 

Teraz pełni oczekiwana to aby nadać dokładnie taką samą wydajność jak oryginalny kod. Jednak nagle pętla pobiera teraz 900usec (tj. Jest o 100 sekund szybszy niż inne wdrożenie).

Czy ktoś może wyjaśnić, dlaczego dałoby to lepszą wydajność? Czy resize() inicjuje nowo przydzielony wektor, w którym rezerwa po prostu alokuje, ale nie tworzy? To jedyna rzecz, o której mogę myśleć.

PS zostało to przetestowane na jednordzeniowym 2Ghz AMD Turion 64 ML-37.

+0

Rutynowe sprawdzanie, czy podczas kompilacji kodu używasz ustawień zwolnienia zamiast debugowania? – Laserallan

+0

hehehe, tak, używałem wersji Release. Było to raczej pytanie o pomoc dla kompilatora, aby mi pomóc :) – Goz

Odpowiedz

52

Czy rozmiar inicjalizuje nowo przydzielony wektor, w którym rezerwa tylko alokuje, ale nie tworzy?

Tak.

+0

Odniesienie SGL do SGL wyjaśnia, że ​​zmiana rozmiaru "wstawia lub kasuje elementy na końcu", podczas gdy rezerwa po prostu przypisuje pamięć. http://www.sgi.com/tech/stl/Vector.html – user7116

+0

Jak to działa? malloc? – pyon

+0

Będzie używał tego, do którego alokatora jest przypisany wektor. – user7116

1
out.resize(audioBlock.size()); 

Ponieważ out jest wielkość (= 0) jest mniejszy niż audioBlock.size(), dodatkowe elementy są utworzone i dołączane do końca out. Tworzy to nowe elementy, wywołując ich domyślny konstruktor.

Zarezerwować tylko przydziela pamięć.

3

Pierwszy kod zapisuje się na out[i], który sprowadza się do begin() + i (tj. Dodanie). Drugi kod używa push_back, który prawdopodobnie natychmiast zapisuje do znanego wskaźnika równoważnego end() (tj. Bez dodatku). Prawdopodobnie możesz wykonać pierwszy przebieg tak szybko, jak drugi, używając iteratorów zamiast indeksowania całkowitoliczbowego.

Edit: również wyjaśnić kilka innych uwag: Wektor zawiera pływaków i konstruowania pływak jest faktycznie no-op (ten sam sposób deklarowania „pływak f;” nie emituje kod, tylko mówi, że kompilator zaoszczędzić miejsce na float na stosie). Sądzę więc, że każda różnica w wydajności między resize() i reserve() dla wektora elementów pływających nie ma związku z konstrukcją.

+3

Przepraszamy, ale Twój punkt konstrukcyjny jest nieprawdziwy. pływak f = 0,0f; jest oczywiście wolniejszy niż "float f;". ten ostatni jest NOP, pierwszy nie jest. – Goz

+0

O, punkt fair, nie wiedziałem, jak skonstruować float przypisany to 0. Vector przypisuje T() do każdego elementu podczas zmiany rozmiaru, która jest float(), która jest 0. Mimo to, używanie iteratorów zamiast indeksowania całkowitoliczbowego może być szybsze. – AshleysBrain

3

Zmiana wielkości()

Modyfikowane pojemnika tak, że posiada dokładnie n elementów wkładania elementów na końcu lub usuwanie elementów od strony, jeśli jest to konieczne. Jeśli jakieś elementy zostaną wstawione, są to kopie t. Jeśli jest to n > a.size(), to wyrażenie jest równoważne z a.insert(a.end(), n - size(), t). Jeśli jest to n < a.size(), jest to odpowiednik a.erase(a.begin() + n, a.end()).

rezerwowy()

Jeżeli n jest mniejsza niż lub równa capacity() wywołanie to nie ma żadnego wpływu. W przeciwnym razie jest to żądanie przydzielenia dodatkowej pamięci.Jeśli żądanie zakończy się pomyślnie, wówczas capacity() jest większe lub równe n; w przeciwnym razie capacity() pozostanie niezmieniona. W obu przypadkach size() pozostaje niezmieniona.

Pamięć zostanie automatycznie ponownie przydzielona, ​​jeśli do wektora zostanie wstawionych więcej niż capacity() - size() elementów. Realokacja nie zmienia się size(), ani nie zmienia wartości żadnych elementów wektora. To jednak zwiększa rezerwę, która powoduje ręczną realokację rezerwy. Głównym powodem używania reserve() jest efektywność: jeśli wiesz, do której pojemności w końcu Twój wektor musi się rozwinąć, zwykle bardziej wydajnie jest przydzielić tę pamięć naraz, niż polegać na schemacie automatycznej realokacji.

Powiązane problemy