web-dev-qa-db-fra.com

std :: vector reserve () et Push_back () est plus rapide que resize () et array index, pourquoi?

Je faisais un test de performance rapide sur un bloc de code

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;
    }
}

J'étais satisfait de la vitesse par rapport à l'implémentation très naïve d'origine, il faut un peu plus de 1 ms pour traiter 65536 échantillons audio.

Mais juste pour le plaisir, j'ai essayé ce qui suit

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 );
    }
}

Maintenant, je m'attendais à ce que cela donne exactement les mêmes performances que le code d'origine. Cependant, la boucle prend maintenant 900usec (c'est-à-dire qu'elle est 100usec plus rapide que l'autre implémentation).

Quelqu'un peut-il expliquer pourquoi cela donnerait de meilleures performances? resize() initialise-t-il le vecteur nouvellement alloué où reserve alloue simplement mais ne construit pas? C'est la seule chose à laquelle je peux penser.

PS cela a été testé sur un seul cœur 2Ghz AMD Turion 64 ML-37.

42
Goz

Le redimensionnement initialise-t-il le vecteur nouvellement alloué où reserve alloue simplement mais ne construit pas?

Oui.

65
sepp2k

Redimensionner ()

Modifie le conteneur pour qu'il ait exactement n éléments, en insérant des éléments à la fin ou en supprimant des éléments de la fin si nécessaire. Si des éléments sont insérés, ce sont des copies de t. Si n > a.size(), cette expression est équivalente à a.insert(a.end(), n - size(), t). Si n < a.size(), c'est équivalent à a.erase(a.begin() + n, a.end()).

Réserve ()

Si n est inférieur ou égal à capacity(), cet appel n'a aucun effet. Sinon, il s'agit d'une demande d'allocation de mémoire supplémentaire. Si la requête aboutit, capacity() est supérieur ou égal à n; sinon, capacity() est inchangé. Dans les deux cas, size() est inchangé.

La mémoire sera réallouée automatiquement si plus de capacity() - size() éléments sont insérés dans le vecteur. La réallocation ne change pas size(), ni les valeurs des éléments du vecteur. Il augmente cependant capacity()

Réserve provoque une réallocation manuellement. La principale raison d'utiliser reserve() est l'efficacité: si vous connaissez la capacité à laquelle votre vecteur doit finalement croître, il est généralement plus efficace d'allouer cette mémoire en une seule fois plutôt que de compter sur le schéma de réallocation automatique.

5
Satbir

Le premier code écrit dans out[i] Qui se résume à begin() + i (c'est-à-dire un ajout). Le deuxième code utilise Push_back, Qui écrit probablement immédiatement dans un pointeur connu équivalent à end() (c'est-à-dire sans ajout). Vous pouvez probablement effectuer la première exécution aussi rapidement que la seconde en utilisant des itérateurs plutôt qu'une indexation entière.

Edit: aussi pour clarifier certains autres commentaires: le vecteur contient des flottants, et la construction d'un flotteur est en fait un no-op (de la même façon, déclarer "float f;" n'émet pas de code, dit seulement au compilateur pour économiser de la place pour un flotteur sur la pile). Je pense donc que toute différence de performances entre resize() et reserve() pour un vecteur de flottants n'a rien à voir avec la construction.

3
AshleysBrain
out.resize( audioBlock.size() );

Puisque la taille de out (= 0) est inférieure à audioBlock.size(), des éléments supplémentaires sont créés et ajoutés à la fin de out. Cela crée les nouveaux éléments en appelant leur constructeur par défaut.

Reserve alloue uniquement la mémoire.

1
aJ.