Performance : i++ ou ++i ?

Soyez le premier à donner votre avis sur cette source.

Snippet vu 6 964 fois - Téléchargée 37 fois

Contenu du snippet

Bien sûr, il y a parfois des raisons pour choisir i++ plutôt que ++i
mais vous êtes vous posé la question de la performance ?
Surtout lorsque l'on utilise cette construction à répétition ...
dans une boucle par exemple. Alors :
votez vous pour i++ (comme la majorité des développeurs),
pour ++i (la minorité) ou pensez vous que c'est équivalent (ne sait pas) ?

Voyons cela de l'intérieur : à l'occasion de la surcharge des opérateurs ++

Source / Exemple :


/*
On vous donne la classe A suivante :
class A
{
private: int i_;
public:
  A(int i) : i_(i){}
  void print() const {cout << "valeur A=" << i_ << endl;}
};
On vous demande de supporter le "Use Case" suivant :

int main(){
  A a(0);
  a++.print();
  (++a).print();
}

0) Question préliminaire : 
---------------------------
Quelle est l'arité de ++ dans a++ ? et dans ++a ?
(l'arité d'un opérateur est son nombre de paramètres)
la réponse dans les deux cas devrait être UN
Donc, en l'implémentant comme méthode de la classe A, et en tenant compte
du paramètre caché 'this', elle ne doit avoir aucun paramètre
Comment différencier alors le cas a++ du cas ++a ?
Les standards répondent à ce problème en imposant un paramètre 'int' 
à la version postfixée

I) Commençons par le premier opérateur : 
-----------------------------------------
l'implémentation de la version postfixée de l'opérateur ++
Quel est le prototype ?
Ecrivons la version complète de l'opérateur (sa vision compilateur)
a++ est équivalent à a.operator++(int)
Que doit faire cet opérateur ?
 d'abord incrémenter 'a', ensuite renvoyer l'ancienne valeur de 'a'
On remarquera qu'ici, il est "NECESSAIRE DE CREER UN OBJET TEMPORAIRE"
voici notre implémentation comme méthode de la classe A:
?A? operator++(int)
{ A clone(*this); //ceci est un appel au Ctor de copie de A
               //permet de mémoriser l'ancienne valeur
  ++i_;     //incrémente l'objet courant
  return clone;   //renvoie l'ancienne valeur
  } // ici appel au "DTOR DE L'OBJET TEMPORAIRE"

où ?A? représente soit 'A', 'const A', 'A&' ou 'const A&'
On élimine les pointeurs car le résultat de a++ est forcément un espèce de A
Comment choisir ?
Eliminons les références car l'objet renvoyé est temporaire (créé sur la pile)
il disparait dès la sortie du bloc '}', il est "NECESSAIRE DE RENVOYER UNE COPIE"
J'espère maintenant que vous êtes dégoutés du POST-FIXE !

Pour départager les ex-aequos (A et const A) voyons comment se comporte 
l'opérateur des types de base (l'int par exemple)
Testons le code suivant :
 int i(0); i++++;
Une erreur de compilation survient car l'opérateur ++ postfixé des 'int'
attend une L-VALUE (Left-value). 
 L-VALUE : variable possédant une zone de mémoire accessible en lecture/écriture
      donc en particulier, pas une variable temporaire, ni une const.
      En résumé, une variable pouvant être placée à gauche du signe =
Ceci nous donne notre réponse : 
le type de retour DOIT être un 'const A' plutôt que 'A' pour être conforme au standards

On peut alors tester le premier opérateur ainsi
int main()
{
	int i(0);
	//i++++; ne compile pas ++ requiert l-value
	A a(0);
	//a++++; // ne compile pas si operator++(int) retourne un const
	cout << "valeur i=" << i++ << endl;//renvoie 0
	cout << "valeur i=" << i << endl;//renvoie 1
	a++.print();//renvoie 0 : attention à bien prototyper la méthode print const
	a.print();//renvoie 1

	return 0;
}

II) Deuxième opérateur : 
-----------------------------------------
Quel est le prototype de la version préfixée de l'opérateur ++ ?
++a est équivalent à a.operator++()
Que doit faire cet opérateur ?
 d'abord incrémenter 'a', ensuite renvoyer la nouvelle valeur de 'a'
On remarquera qu'ici, que l'on n'a pas besoin d'objet temporaire (COOL)

voici notre implémentation comme méthode de la classe A:
?A? operator++()
{ ++i_;            //incrémente l'objet courant
  return *this;   //le renvoie
  }
On peut renvoyer un 'A', 'const A', 'A&' ou 'const A&'
Le meilleur pour les performances est de renvoyer par référence
Voyons si elle doit être const ou non avec la même technique que précédemment
on teste 
++++i; ... et ça marche !
On peut donc retourner un A& (et c'est préférable pour imiter les types de base)

  • /
#include <iostream.h> class A { private: int i_; public: A(int i) : i_(i){} void print() const {cout << "valeur A=" << i_ << endl;} const A operator++(int) { A clone(*this); //ceci est un appel au Ctor de copie de //permet de mémoriser l'ancienne valeur ++i_; //incrémente l'objet courant return clone; //renvoie l'ancienne valeur } A& operator++() { ++i_; //incrémente l'objet courant return *this; //le renvoie } }; int main() { int i(0); //i++++; ne compile pas ++ requiert l-value A a(0); //a++++; // ne compile pas si operator++(int) retourne un const //test post fixé cout << "valeur i=" << i++ << endl;//renvoie 0 cout << "valeur i=" << i << endl;//renvoie 1 a++.print();//=> 0 : attention à bien prototyper la méthode print const a.print();//=> 1 //test pré fixé cout << "valeur i=" << ++i << endl;//renvoie 2 cout << "valeur i=" << i << endl;//renvoie 2 (++a).print(); //=> 2 //parenthèses nécessaires (précédence de . sur ++) a.print(); //=> 2 return 0; } /* "CONCLUSION": Faisons le bilan : en notation "POST-FIXE" : un Ctor pour l'objet temporaire (Ctor de Copie ou Ctor défaut+Affectation) l'incrémentation un Ctor de Copie pour le retour par valeur un Dtor de l'objet temporaire en notation "PRE-FIXE" : l'incrémentation Un dernier mot pour dire que les types de bases se comportent de la même manière que les classes (une temporaire), même si les dégâts sont moins flagrants "il n'y a pas de différence dans le cas d'int" (merci aux commentaires ci-dessous . le 04/11) Et si notre classe avait des données membres de type classe, et/ou des classes de bases ... le poids des Ctor/Dtors devient ENORME...
  • /

A voir également

Ajouter un commentaire Commentaires
Messages postés
3
Date d'inscription
mercredi 14 septembre 2005
Statut
Membre
Dernière intervention
28 octobre 2006

Un détail trés important que vous avez tous oublié: le ++i incrémente i avant même de l'utiliser, alors que i++ fait de la post-incrémentation, en performance pure c'est identique. Il n'y a que la manière de la utiliser en fonction de l'optimisation des boucles que ça change...
Messages postés
95
Date d'inscription
mercredi 5 décembre 2001
Statut
Membre
Dernière intervention
27 juin 2006

il ne faut pas oublier que le compilateur optimise le code, et transforme les i++ en ++i si ça s'avere utile.

le i++ requiert de creer une variable temporaire, car on peut faire tableau[i++]; et i va exister dans 2 etat un etat non incrementé temporaire pour l'indice du tableau, et un etat incrementer pour la nouvelle valeur de i.

il faudrai transformer en asm une class integer refaite avec post incrementation et pre incrementation et la transformer en asm, en desactivant si possible les optimisations.
Messages postés
168
Date d'inscription
mardi 31 décembre 2002
Statut
Membre
Dernière intervention
21 avril 2005

heu... en asm 68k:
int *a=x;
b=*(++a); est beaucoup plus lente que b=*(a++);
equivalent a: (a= a0 b=d0 );
addq #1,a0
move.l (a0),d0
contre:
move.l (a0)+,d0
...
@+
Messages postés
16
Date d'inscription
lundi 27 mai 2002
Statut
Membre
Dernière intervention
12 novembre 2004

Et Ben !!!!!!!!!!!!!!!!!!!!!!!!
Messages postés
3
Date d'inscription
lundi 11 février 2002
Statut
Membre
Dernière intervention
19 novembre 2002

Différence entre i++ et ++i :
Pour les opérations sur les types de données simples (int par exemple), la différence ne se situe pas dans l'opération elle - même (les codes asm sont équivalents) mais sur l'évaluation de l'expression. Je m'explique :
int i = 5;
( ++i == 5 ) est faux car l'incrémentation a lieu avant l'évaluation
int i = 5;
( i ++ == 5 ) est vrai car l'incrémentation a lieu après l'évaluation
J'espère que c'est clair...
Afficher les 30 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.