J'ai toujours pensé que c'est la sagesse générale qui std::vector
est "implémentée comme un tableau", bla bla bla. Aujourd'hui, je suis descendu et l'ai testé, et il semble que ce ne soit pas le cas:
Voici quelques résultats de test:
UseArray completed in 2.619 seconds
UseVector completed in 9.284 seconds
UseVectorPushBack completed in 14.669 seconds
The whole thing completed in 26.591 seconds
C'est environ 3 à 4 fois plus lent! Ne justifie pas vraiment les vector
commentaires " peut être plus lent pour quelques nanosecs".
Et le code que j'ai utilisé:
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
class TestTimer
{
public:
TestTimer(const std::string & name) : name(name),
start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
{
}
~TestTimer()
{
using namespace std;
using namespace boost;
posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
posix_time::time_duration d = now - start;
cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
" seconds" << endl;
}
private:
std::string name;
boost::posix_time::ptime start;
};
struct Pixel
{
Pixel()
{
}
Pixel(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b)
{
}
unsigned char r, g, b;
};
void UseVector()
{
TestTimer t("UseVector");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
std::vector<Pixel> pixels;
pixels.resize(dimension * dimension);
for(int i = 0; i < dimension * dimension; ++i)
{
pixels[i].r = 255;
pixels[i].g = 0;
pixels[i].b = 0;
}
}
}
void UseVectorPushBack()
{
TestTimer t("UseVectorPushBack");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
std::vector<Pixel> pixels;
pixels.reserve(dimension * dimension);
for(int i = 0; i < dimension * dimension; ++i)
pixels.push_back(Pixel(255, 0, 0));
}
}
void UseArray()
{
TestTimer t("UseArray");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
Pixel * pixels = (Pixel *)malloc(sizeof(Pixel) * dimension * dimension);
for(int i = 0 ; i < dimension * dimension; ++i)
{
pixels[i].r = 255;
pixels[i].g = 0;
pixels[i].b = 0;
}
free(pixels);
}
}
int main()
{
TestTimer t1("The whole thing");
UseArray();
UseVector();
UseVectorPushBack();
return 0;
}
Suis-je en train de faire mal ou quelque chose? Ou ai-je juste brisé ce mythe de la performance?
J'utilise le mode Release dans Visual Studio 2005 .
Dans Visual C ++ , #define _SECURE_SCL 0
réduit UseVector
de moitié (le ramenant à 4 secondes). C'est vraiment énorme, OMI.
vector
s'agit d'un tableau redimensionnable à usage général. Toutes nos félicitations. Comme avec tous les outils à usage général, il est possible de trouver des situations spécialisées où il est sous-optimal. C'est pourquoi la sagesse conventionnelle est de commencer par unvector
et d'envisager des alternatives si nécessaire.Réponses:
En utilisant ce qui suit:
Le tableau est donc deux fois plus rapide que le vecteur.
Mais après avoir examiné le code plus en détail, cela est attendu; lorsque vous parcourez le vecteur deux fois et le tableau une seule fois. Remarque: lorsque vous êtes
resize()
le vecteur, vous allouez non seulement la mémoire, mais vous parcourez également le vecteur et appelez le constructeur sur chaque membre.Réorganiser légèrement le code pour que le vecteur n'initialise chaque objet qu'une seule fois:
Maintenant, refaites le même timing:
Les performances du vecteur ne sont désormais que légèrement inférieures à celles du tableau. OMI, cette différence est insignifiante et pourrait être causée par tout un tas de choses non associées au test.
Je tiens également compte du fait que vous n'initialisez / ne détruisez pas correctement l'objet Pixel dans la
UseArrray()
méthode car aucun constructeur / destructeur n'est appelé (cela peut ne pas être un problème pour cette classe simple mais quelque chose de légèrement plus complexe (c'est-à-dire avec des pointeurs ou des membres). avec des pointeurs) causera des problèmes.la source
reserve()
place deresize()
. Cela alloue de l'espace aux objets (c'est-à-dire qu'il modifie la capacité du vecteur) mais ne crée pas les objets (c'est-à-dire que la taille du vecteur reste inchangée).resize()
parreserve()
, car cela n'ajuste pas l'idée interne du vecteur de sa propre taille, donc les écritures suivantes sur ses éléments sont techniquement "écrivant après la fin" et produiront UB. Bien que, dans la pratique, chaque implémentation STL "se comporte" à cet égard, comment resynchronisez-vous la taille du vecteur? Si vous essayez d'appelerresize()
après avoir rempli le vecteur, cela écrasera très probablement tous ces éléments avec des valeurs construites par défaut!reserve
ne change que la capacité d'un vecteur, pas sa taille./EHsc
aux commutateurs de compilation a nettoyé cela etassign()
bat réellement le tableau maintenant. Yay.Grande question. Je suis venu ici en espérant trouver une solution simple qui accélérerait les tests vectoriels. Cela n'a pas fonctionné comme je m'y attendais!
L'optimisation aide, mais ce n'est pas suffisant. Avec l'optimisation activée, je constate toujours une différence de performances 2X entre UseArray et UseVector. Fait intéressant, UseVector était beaucoup plus lent que UseVectorPushBack sans optimisation.
Idée n ° 1 - Utilisez new [] au lieu de malloc
J'ai essayé de passer
malloc()
ànew[]
UseArray pour que les objets soient construits. Et passer de l'affectation de champ individuel à l'attribution d'une instance Pixel. Oh, et renommer la variable de boucle interne enj
.Étonnamment (pour moi), aucun de ces changements n'a fait la moindre différence. Pas même le changement
new[]
auquel par défaut construira tous les pixels. Il semble que gcc puisse optimiser les appels du constructeur par défaut lors de l'utilisationnew[]
, mais pas lors de l'utilisationvector
.Idée n ° 2 - Supprimer les appels répétés de l'opérateur []
J'ai également tenté de me débarrasser de la triple
operator[]
recherche et de mettre en cache la référencepixels[j]
. Cela a en fait ralenti UseVector! Oups.Idée n ° 3 - Supprimer les constructeurs
Qu'en est-il de la suppression complète des constructeurs? Alors peut-être que gcc peut optimiser la construction de tous les objets lorsque les vecteurs sont créés. Que se passe-t-il si nous changeons Pixel en:
Résultat: environ 10% plus rapide. Encore plus lent qu'un tableau. Hm.
Idée n ° 4 - Utiliser l'itérateur au lieu de l'index de boucle
Que diriez-vous d'utiliser un
vector<Pixel>::iterator
au lieu d'un index de boucle?Résultat:
Non, pas différent. Au moins, ce n'est pas plus lent. Je pensais que cela aurait des performances similaires à # 2 où j'ai utilisé une
Pixel&
référence.Conclusion
Même si certains cookies intelligents comprennent comment rendre la boucle vectorielle aussi rapide que celle du tableau, cela ne parle pas bien du comportement par défaut de
std::vector
. Tant pis pour que le compilateur soit suffisamment intelligent pour optimiser toutes les fonctionnalités C ++ et rendre les conteneurs STL aussi rapides que les tableaux bruts.L'essentiel est que le compilateur est incapable d'optimiser les appels du constructeur par défaut sans opération lors de l'utilisation
std::vector
. Si vous utilisez de la plaine,new[]
il les optimise très bien. Mais pas avecstd::vector
. Même si vous pouvez réécrire votre code pour éliminer les appels du constructeur qui vont à l'encontre du mantra ici: "Le compilateur est plus intelligent que vous. La STL est aussi rapide que le simple C. Ne vous en faites pas."la source
vector<int>
.C'est une question ancienne mais populaire.
À ce stade, de nombreux programmeurs travailleront en C ++ 11. Et en C ++ 11, le code de l'OP tel qu'il est écrit s'exécute aussi rapidement pour
UseArray
ouUseVector
.Le problème fondamental était que pendant que votre
Pixel
structure n'était pas initialisée, ellestd::vector<T>::resize( size_t, T const&=T() )
prend une construction par défautPixel
et la copie . Le compilateur n'a pas remarqué qu'on lui demandait de copier des données non initialisées, il a donc effectué la copie.En C ++ 11,
std::vector<T>::resize
a deux surcharges. Le premier eststd::vector<T>::resize(size_t)
, l'autre l'eststd::vector<T>::resize(size_t, T const&)
. Cela signifie que lorsque vous invoquezresize
sans second argument, il s'agit simplement de constructions par défaut et le compilateur est suffisamment intelligent pour réaliser que la construction par défaut ne fait rien, il saute donc le passage sur le tampon.(Les deux surcharges ont été ajoutées pour gérer les types mobiles, constructibles et non copiables - l'amélioration des performances lors du travail sur des données non initialisées est un bonus).
La
push_back
solution effectue également la vérification de la clôture, ce qui la ralentit, elle reste donc plus lente que lamalloc
version.exemple en direct (j'ai également remplacé la minuterie par
chrono::high_resolution_clock
).Notez que si vous avez une structure qui nécessite généralement une initialisation, mais que vous souhaitez la gérer après avoir augmenté votre tampon, vous pouvez le faire avec un
std::vector
allocateur personnalisé . Si vous voulez ensuite le déplacer dans un environnement plus normalstd::vector
, je pense qu'une utilisation prudenteallocator_traits
et un remplacement de cela==
pourraient réussir, mais je ne suis pas sûr.la source
emplace_back
fonctionne vspush_back
ici.clang++ -std=c++11 -O3
aUseArray completed in 2.02e-07 seconds
etUseVector completed in 1.3026 seconds
. J'ai également ajouté uneUseVectorEmplaceBack
version qui fait env. 2,5 fois plus viteUseVectorPushBack
.Pour être honnête, vous ne pouvez pas comparer une implémentation C ++ à une implémentation C, comme j'appellerais votre version malloc. malloc ne crée pas d'objets - il alloue uniquement de la mémoire brute. Que vous traitez ensuite cette mémoire comme des objets sans appeler le constructeur est un mauvais C ++ (peut-être invalide - je laisse cela aux juristes du langage).
Cela dit, changer simplement le malloc en
new Pixel[dimensions*dimensions]
et gratuitdelete [] pixels
ne fait pas beaucoup de différence avec la simple implémentation de Pixel que vous avez. Voici les résultats sur ma box (E6600, 64 bits):Mais avec un léger changement, les tableaux tournent:
Pixel.h
Pixel.cc
main.cc
Compilé de cette façon:
on obtient des résultats très différents:
Avec un constructeur non intégré pour Pixel, std :: vector bat désormais un tableau brut.
Il semblerait que la complexité de l'allocation via std :: vector et std: allocator soit trop importante pour être optimisée aussi efficacement qu'un simple
new Pixel[n]
. Cependant, nous pouvons voir que le problème est simplement lié à l'allocation et non à l'accès vectoriel en modifiant quelques fonctions de test pour créer le vecteur / tableau une fois en le déplaçant hors de la boucle:et
Nous obtenons ces résultats maintenant:
Ce que nous pouvons apprendre de ceci est que std :: vector est comparable à un tableau brut pour l'accès, mais si vous devez créer et supprimer le vecteur / tableau plusieurs fois, la création d'un objet complexe prendra plus de temps que la création d'un tableau simple lorsque le constructeur de l'élément n'est pas inséré. Je ne pense pas que cela soit très surprenant.
la source
Essayez avec ceci:
J'obtiens presque exactement les mêmes performances qu'avec la baie.
Le fait
vector
est que c'est un outil beaucoup plus général qu'un tableau. Et cela signifie que vous devez considérer comment vous l'utilisez. Il peut être utilisé de différentes manières, offrant des fonctionnalités qu'un tableau n'a même pas. Et si vous l'utilisez «mal» à vos fins, vous encourez beaucoup de frais généraux, mais si vous l'utilisez correctement, il s'agit généralement d'une structure de données sans frais généraux. Dans ce cas, le problème est que vous avez initialisé séparément le vecteur (ce qui fait que tous les éléments ont leur ctor par défaut appelé), puis écrasez chaque élément individuellement avec la valeur correcte. C'est beaucoup plus difficile à optimiser pour le compilateur que lorsque vous faites la même chose avec un tableau. C'est pourquoi le vecteur fournit un constructeur qui vous permet de faire exactement cela:N
X
.Et lorsque vous l'utilisez, le vecteur est tout aussi rapide qu'un tableau.
Donc non, vous n'avez pas brisé le mythe de la performance. Mais vous avez montré que ce n'est vrai que si vous utilisez le vecteur de manière optimale, ce qui est également un bon point. :)
Du côté positif, c'est vraiment l' utilisation la plus simple qui s'avère la plus rapide. Si vous comparez mon extrait de code (une seule ligne) avec la réponse de John Kugelman, contenant des tas et des tas de réglages et d'optimisations, qui n'éliminent toujours pas tout à fait la différence de performances, il est assez clair que
vector
c'est assez intelligemment conçu après tout. Vous n'avez pas à sauter à travers des cerceaux pour obtenir une vitesse égale à un tableau. Au contraire, vous devez utiliser la solution la plus simple possible.la source
new[]
exécute les mêmes constructions par défaut que levector.resize()
fait, mais c'est beaucoup plus rapide.new[]
La boucle intérieure devrait avoir la même vitesse quevector.resize()
la boucle intérieure, mais ce n'est pas le cas, elle est presque deux fois plus rapide.malloc
sans initialiser ni construire quoi que ce soit, il s'agit donc en fait d' un algorithme à passage unique, tout comme monvector
exemple. Et en ce qui concernenew[]
la réponse, il est évident que les deux nécessitent deux passes, mais dans lenew[]
cas, le compilateur est en mesure d'optimiser cette surcharge supplémentaire, ce qu'il ne fait pas dans levector
cas. Mais je ne vois pas pourquoi il est intéressant de voir ce qui se passe dans les cas sous-optimaux. Si vous vous souciez des performances, vous n'écrivez pas de code comme ça.vector::resize()
de me donner un morceau de mémoire contingent sans perdre de temps à appeler des constructeurs inutiles.malloc
ce qui n'effectue pas l'initialisation, mais cela ne fonctionnera pas en C ++ avec des types non POD. Donc, dans le cas général, un tableau C ++ serait tout aussi mauvais. Peut-être que la question est, si vous effectuez souvent ce blitting, ne réutilisez-vous pas le même tableau / vecteur? Et si vous faites cela, vous ne payez qu'une seule fois le coût des "constructeurs inutiles", au tout début. Le blitting réel est tout aussi rapide après tout.Ce n'était pas une comparaison juste quand j'ai regardé votre code pour la première fois; Je pensais vraiment que vous ne compariez pas des pommes avec des pommes. Alors j'ai pensé, faisons appel aux constructeurs et aux destructeurs sur tous les tests; puis comparer.
Mes pensées étaient, qu'avec cette configuration, elles devraient être exactement les mêmes. Il s'avère que j'avais tort.
Alors pourquoi cette perte de performances de 30% s'est-elle même produite? La STL a tout dans les en-têtes, donc il aurait dû être possible pour le compilateur de comprendre tout ce qui était requis.
Mes pensées étaient que c'est dans la façon dont la boucle initialise toutes les valeurs au constructeur par défaut. J'ai donc effectué un test:
Les résultats étaient comme je le soupçonnais:
C'est clairement la source du ralentissement, le fait que le vecteur utilise le constructeur de copie pour initialiser les éléments à partir d'un objet construit par défaut.
Cela signifie que l'ordre de pseudo-opération suivant se produit lors de la construction du vecteur:
Ce qui, en raison du constructeur de copie implicite créé par le compilateur, est développé comme suit:
Ainsi , la valeur par défaut
Pixel
reste non initialisées, tandis que le reste sont initialisés avec la valeur par défautPixel
de » un-initialisées valeurs.Par rapport à la situation alternative avec
New[]
/Delete[]
:Ils sont tous laissés à leurs valeurs non initialisées, et sans la double itération sur la séquence.
Armé de ces informations, comment pouvons-nous les tester? Essayons d'écraser le constructeur de copie implicite.
Et les résultats?
Donc en résumé, si vous créez très souvent des centaines de vecteurs: repensez votre algorithme .
Dans tous les cas, l' implémentation STL n'est pas plus lente pour une raison inconnue, elle fait exactement ce que vous demandez; en espérant en savoir plus.
la source
Essayez de désactiver les itérateurs vérifiés et de créer en mode de publication. Vous ne devriez pas voir beaucoup de différence de performances.
la source
#define _SECURE_SCL 0
. Cela faitUseVector
environ 4 secondes (similaire àgcc
ci-dessous) mais c'est toujours deux fois plus lent.-O3
._HAS_ITERATOR_DEBUGGING
est désactivé dans la version: msdn.microsoft.com/en-us/library/aa985939(VS.80).aspxLa STL de GNU (et d'autres), étant donné
vector<T>(n)
, construit par défaut un objet prototypiqueT()
- le compilateur optimisera le constructeur vide - mais une copie de tout ce qui se trouve être dans les adresses mémoire maintenant réservées à l'objet est prise par les STL__uninitialized_fill_n_aux
, qui boucles remplissant des copies de cet objet comme valeurs par défaut dans le vecteur. Donc, "ma" STL n'est pas la construction en boucle, mais la construction puis la boucle / copie. C'est contre-intuitif, mais j'aurais dû m'en souvenir en commentant une récente question de stackoverflow sur ce point: la construction / copie peut être plus efficace pour les objets comptés par référence, etc.Alors:
ou
est - sur de nombreuses implémentations STL - quelque chose comme:
Le problème étant que la génération actuelle des optimiseurs de compilateur ne semble pas fonctionner à partir du fait que temp est une ordure non initialisée et ne parvient pas à optimiser la boucle et les appels de constructeur de copie par défaut. Vous pourriez affirmer de manière crédible que les compilateurs ne devraient absolument pas optimiser cela, car un programmeur écrivant ce qui précède a une attente raisonnable que tous les objets seront identiques après la boucle, même si des ordures (mises en garde habituelles sur `` identique '' / opérateur == vs memcmp / operator = etc s'appliquent). On ne peut pas s'attendre à ce que le compilateur ait un aperçu supplémentaire du contexte plus large de std :: vector <> ou de l'utilisation ultérieure des données qui suggérerait cette optimisation en toute sécurité.
Cela peut être mis en contraste avec la mise en œuvre directe la plus évidente:
Que nous pouvons attendre d'un compilateur pour optimiser.
Pour être un peu plus explicite sur la justification de cet aspect du comportement du vecteur, considérez:
C'est clairement une différence majeure si nous faisons 10000 objets indépendants contre 10000 référençant les mêmes données. Il y a un argument raisonnable que l'avantage de protéger les utilisateurs C ++ occasionnels de faire accidentellement quelque chose de si cher surpasse le très petit coût réel de la construction de copie difficile à optimiser.
RÉPONSE ORIGINALE (pour référence / donner un sens aux commentaires): Aucune chance. le vecteur est aussi rapide qu'un tableau, du moins si vous réservez judicieusement de l'espace. ...
la source
La réponse de Martin York me dérange car elle semble être une tentative de balayer le problème d'initialisation sous le tapis. Mais il a raison d'identifier la construction par défaut redondante comme la source de problèmes de performances.
[EDIT: la réponse de Martin ne suggère plus de changer le constructeur par défaut.]
Pour le problème immédiat, vous pouvez certainement appeler la version à 2 paramètres du
vector<Pixel>
ctor à la place:Cela fonctionne si vous souhaitez initialiser avec une valeur constante, ce qui est un cas courant. Mais le problème le plus général est: comment pouvez-vous initialiser efficacement avec quelque chose de plus compliqué qu'une valeur constante?
Pour cela, vous pouvez utiliser un
back_insert_iterator
, qui est un adaptateur d'itérateur. Voici un exemple avec un vecteur deint
s, bien que l'idée générale fonctionne aussi bien pourPixel
s:Vous pouvez également utiliser
copy()
ou à latransform()
place degenerate_n()
.L'inconvénient est que la logique de construction des valeurs initiales doit être déplacée dans une classe distincte, ce qui est moins pratique que de l'avoir en place (bien que les lambdas en C ++ 1x rendent cela beaucoup plus agréable). De plus, je m'attends à ce que ce ne soit toujours pas aussi rapide qu'une
malloc()
version non STL basée sur des bases, mais je m'attends à ce qu'elle soit proche, car elle ne fait qu'une seule construction pour chaque élément.la source
Les vecteurs appellent également des constructeurs Pixel.
Chacun provoque près d'un million de cycles de calcul que vous chronométrez.
edit: puis il y a la boucle externe 1 ... 1000, alors faites qu'un milliard de ctor appelle!
edit 2: il serait intéressant de voir le démontage du cas UseArray. Un optimiseur pourrait optimiser le tout, car il n'a aucun effet autre que la gravure du processeur.
la source
Voici comment fonctionne la
push_back
méthode dans le vecteur:Après avoir appelé
push_back
X éléments:Répéter. Si vous n'êtes pas dans l'
reserving
espace, cela va certainement être plus lent. Plus que cela, s'il est cher de copier l'élément, alors 'push_back' comme ça va vous manger vivant.En ce qui concerne le
vector
tableau versus, je vais devoir être d'accord avec les autres personnes. Exécutez la version, activez les optimisations et placez quelques indicateurs supplémentaires pour que les sympathiques de Microsoft ne le # @% $ ^ fassent pas pour vous.Encore une chose, si vous n'avez pas besoin de redimensionner, utilisez Boost.Array.
la source
reserve
comme je le devrais.push_back
a amorti le temps constant. On dirait que vous décrivez un processus O (N). (Les étapes 1 et 3 semblent complètement hors de propos.) Ce quipush_back
ralentit pour OP est la vérification de la plage pour voir si la réallocation doit avoir lieu, la mise à jour des pointeurs, la vérification par rapport à NULL à l'intérieur du placementnew
et d'autres petites choses qui sont normalement noyées par le travail réel du programme.reserve
s'il doit encore effectuer cette vérification (s'il doit être réaffecté) à chaque foispush_back
.vector
fonctionnalité de redimensionnement, c'est juste "magique". Ici, permettez-moi de clarifier un peu plus.Certaines données du profileur (le pixel est aligné sur 32 bits):
Blabla
Dans
allocator
:vector
:tableau
La plupart des frais généraux se trouvent dans le constructeur de copie. Par exemple,
Il a les mêmes performances qu'un tableau.
la source
pixels.size()
sera rompue.Mon ordinateur portable est Lenova G770 (4 Go de RAM).
Le système d'exploitation est Windows 7 64 bits (celui avec ordinateur portable)
Le compilateur est MinGW 4.6.1.
L'IDE est Code :: Blocks .
Je teste les codes sources du premier post.
Les resultats
Optimisation d'O2
UseArray terminé en 2,841 secondes
UseVector terminé en 2,548 secondes
UseVectorPushBack terminé en 11,95 secondes
Le tout terminé en 17.342 secondes
pause système
Optimisation O3
UseArray terminé en 1,452 secondes
UseVector terminé en 2,514 secondes
UseVectorPushBack terminé en 12,967 secondes
Le tout terminé en 16.937 secondes
Il semble que les performances du vecteur soient pires sous l'optimisation O3.
Si vous changez la boucle en
La vitesse du réseau et du vecteur sous O2 et O3 sont presque les mêmes.
la source
Un meilleur benchmark (je pense ...), le compilateur dû aux optimisations peut changer le code, car les résultats des vecteurs / tableaux alloués ne sont utilisés nulle part. Résultats:
Compilateur:
CPU:
Et le code:
la source
J'ai fait quelques tests approfondis que je voulais depuis un moment maintenant. Autant partager cela.
Il s'agit de ma machine à double démarrage i7-3770, 16 Go de RAM, x86_64, sur Windows 8.1 et sur Ubuntu 16.04. Plus d'informations et conclusions, remarques ci-dessous. Testé à la fois MSVS 2017 et g ++ (à la fois sur Windows et sur Linux).
Programme de test
Résultats
Remarques
std::sort()
trop (vous pouvez le voir commenté) mais les ai supprimés plus tard car il n'y avait pas de différences relatives significatives.Mes conclusions et remarques
std::array
les variations de temps entre des exécutions consécutives, tandis que d'autres, en particulier les structures std :: data, variaient énormément en comparaisonstd::array
tableaux de style c sont plus rapides sur Windows sans optimisationVerdict
Bien sûr, c'est du code pour une construction optimisée. Et puisque la question portait sur
std::vector
alors oui c'est bien! plus lent que les tableaux simples (optimisé / non optimisé). Mais lorsque vous faites un benchmark, vous voulez naturellement produire du code optimisé.La star du spectacle pour moi a cependant été
std::array
.la source
Avec les bonnes options, les vecteurs et les tableaux peuvent générer un asm identique . Dans ces cas, ils ont bien sûr la même vitesse, car vous obtenez le même fichier exécutable de toute façon.
la source
Par ailleurs, le ralentissement de votre vision dans les classes utilisant vector se produit également avec des types standard comme int. Voici un code multithread:
Le comportement du code montre que l'instanciation du vecteur est la partie la plus longue du code. Une fois que vous avez traversé le goulot de la bouteille. Le reste du code s'exécute extrêmement rapidement. Cela est vrai quel que soit le nombre de threads que vous exécutez.
Soit dit en passant, ignorez le nombre absolument insensé d'includes. J'utilise ce code pour tester des choses pour un projet, donc le nombre d'inclusions continue de croître.
la source
Je veux juste mentionner que le vecteur (et smart_ptr) n'est qu'une couche mince ajoutée au-dessus des tableaux bruts (et des pointeurs bruts). Et en fait, le temps d'accès d'un vecteur en mémoire continue est plus rapide que le tableau. Le code suivant montre le résultat de l'initialisation et de l'accès au vecteur et au tableau.
La sortie est:
Ainsi, la vitesse sera presque la même si vous l'utilisez correctement. (comme d'autres l'ont mentionné en utilisant reserve () ou resize ()).
la source
Eh bien, parce que vector :: resize () fait beaucoup plus de traitement que l'allocation de mémoire ordinaire (par malloc).
Essayez de mettre un point d'arrêt dans votre constructeur de copie (définissez-le afin que vous puissiez faire un point d'arrêt!) Et le temps de traitement supplémentaire augmente.
la source
Je dois dire que je ne suis pas un expert en C ++. Mais pour ajouter quelques résultats d'expériences:
compiler: gcc-6.2.0 / bin / g ++ -O3 -std = c ++ 14 vector.cpp
machine:
OS:
Production:
Ici, la seule chose qui me semble étrange est la performance de "UseFillConstructor" par rapport à "UseConstructor".
Le code:
Ainsi, la "valeur" supplémentaire fournie ralentit beaucoup les performances, ce qui, je pense, est dû à plusieurs appels au constructeur de copie. Mais...
Compiler:
Production:
Donc dans ce cas, l'optimisation gcc est très importante mais elle ne peut pas vous aider beaucoup quand une valeur est fournie par défaut. C'est contre mes frais de scolarité en fait. Espérons que cela aide le nouveau programmeur à choisir le format d'initialisation du vecteur.
la source
Cela semble dépendre des drapeaux du compilateur. Voici un code de référence:
Différents indicateurs d'optimisation donnent des réponses différentes:
Vos résultats exacts varieront mais c'est assez typique sur ma machine.
la source
D'après mon expérience, parfois, juste parfois,
vector<int>
peut être beaucoup plus lent queint[]
. Une chose à garder à l'esprit est que les vecteurs de vecteurs sont très différentsint[][]
. Comme les éléments ne sont probablement pas contigus en mémoire. Cela signifie que vous pouvez redimensionner différents vecteurs à l'intérieur du vecteur principal, mais le processeur peut ne pas être en mesure de mettre en cache les éléments aussi bien que dans le cas deint[][]
.la source