[C++] Analyse de performance des boucles for
Cette article est une analyse de performance des différentes forme de boucles for que on peut rencontrer dans un programme C++.
Pour la mesure on utilise une petite class record qui se chargera d’enregistrer le temps avant et après l’exécution des boucles et d’en faire la différence pour avoir le temps total qui prendra chaque boucle for.
class record { public: record() { start = clock(); } void echo(string label) { curr = clock(); cout << label << " : " << (curr - start) / double(CLOCKS_PER_SEC) << "s" << endl; start = curr; } private: time_t start, curr; };
Pour mettre en situation de gros volume de données on vas générer un tableau d’int de 512^3 éléments soit 134.217.728 éléments et le remplir avec des valeurs aléatoire a l’aide de la fonction standard rand().
srand(time(0)); vector array; array.resize(pow(512, 3)); generate(array.begin(), array.end(), rand); cout << "array.size() = " << array.size() << endl;
Ensuit dans les boucle on passera chaque valeur a une fonction cube() qui fera office de traitement.
void cube(int& v) { v *= v; }
Analyse…
Itération normal
On commande donc par une iteration normale de tout ce qu’il ya de plus classique pour la prendre comme base de comparaison.
for(unsigned i=0; i
Résultat ca prend 1 seconde et demi
Normal iteration : 1.5s
Itération avec taille mise en cache
Cette fois si on va enregistrer la taille du tabealux dans une variable pour eviter un appelle de fonction inutile.
unsigned size = array.size(); for(unsigned i=0; i
On constat qu’on a gagner environ 0.2 seconde on commence donc a améliorer.
Cached size : 1.344s
Utilisation des iterator
On passe maintenant a une autre type d’itération, avec les std::iterator, on utilise pour cela les méthode begin() et end() de la class vector.
for(vector::iterator it = array.begin(); it != array.end(); ++it) cube(*it);
La on régresse, en effet on a passer de 1 a 3 secondes, on se rend compte donc que les std::iterator ne sont pas optimiser pour une itération normale qu’on aurait pu faire avec une méthode classique.
Iterator : 3.75s
Utilisation des iterator (mise en cache)
Maintenant on teste avec la mise en cache de l’itérateur de fin.
vector::iterator end = array.end(); for(vector::iterator it = array.begin(); it != end; ++it) cube(*it);
Résultat. une baisse d’à peu près 1.5 secondes.
Cached end iterator : 2.359s
La fonction for_each
Cette fois si, on va utiliser la fonction standard for_each.
for_each(array.begin(), array.end(), cube);
C’est un peu plus long que la méthode précédente.
for_each : 2.422s
Compilation…
Pour les besoin de cette petite étude j’ai compiler le programme de deux façon une normalement, et une avec l’option du compilateur -o2 qui va normalement optimiser les performance de notre programme.
g++ main.cpp -o foreach.exe g++ -O2 main.cpp -o foreach-o2.exe
Résultat avec l’option -o2
Normal iteration : 0.328s Cached size : 0.312s Iterator : 0.328s Cached end iterator : 0.328s for_each : 0.313s
On se rend compte avec l’option -o2 que le compilateur a fait un travaille d’optimisation sur nos boucle et qu’il ya peut de différence entre une méthode et une autre.
On constat alors que quelle que soit la méthode utiliser pour la boucle en mode -o2 le compilateur sait reconnaître et optimiser les zone qui peuvent l’être.
Commentaires récents