Est-il une différence de performance entre i++ et ++i si la valeur obtenue n'est pas utilisé?
Réponses
Trop de publicités?Résumé: Aucun.
i++ pourrait être plus lente que ++i, depuis l'ancienne valeur de i peut-être besoin d'être sauvegardés pour une utilisation ultérieure, mais dans la pratique moderne les compilateurs permettra d'optimiser cette distance.
On peut le démontrer en regardant le code de cette fonction, les deux avec ++i et i++.
$ cat i++.c
extern void g(int i);
void f()
{
int i;
for (i = 0; i < 100; i++)
g(i);
}
Les fichiers sont les mêmes, sauf pour les ++i et i++:
$ diff i++.c ++i.c
6c6
< for (i = 0; i < 100; i++)
---
> for (i = 0; i < 100; ++i)
Nous allons compiler, et également obtenir l'assembleur généré:
$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c
Et nous pouvons voir que les deux objets générés et assembler des fichiers sont les mêmes.
$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e
$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
De l'Efficacité par rapport à l'intention de Andrew Koenig :
Tout d'abord, il est loin d'être évident que ++i est plus efficace que j'++, au moins dans le cas des variables de type entier sont concernés.
Et :
Alors, la question qu'on devrait se poser est de savoir laquelle de ces deux opérations est plus rapide, c'est laquelle de ces deux opérations exprime avec plus de précision ce que vous essayez d'accomplir. Je soutiens que, si vous n'êtes pas à l'aide de la valeur de l'expression, il n'y a jamais une raison pour utiliser i++ ++au lieu de je, car il n'y a jamais une raison pour copier la valeur d'une variable, d'incrémenter la variable, puis lancer la copie à l'écart.
Donc, si la valeur obtenue n'est pas utilisé, je voudrais utiliser ++j'. Mais pas parce qu'il est plus efficace: parce qu'il l'affirme à juste titre à mon intention.
Une meilleure solution est que ++je vais parfois être plus rapide, mais jamais plus lent.
Tout le monde semble être en supposant que " je " est un classique intégré, type int. Dans ce cas, il n'y aura pas de différence mesurable.
Toutefois, si " je " est de type complexe, alors vous pourriez bien trouver une différence mesurable. Pour i++ vous devez faire une copie de votre classe avant l'incrémentation. En fonction de ce qui est impliqué dans une copie qu'il pourrait en effet être plus lent depuis ++, vous pouvez simplement retourner la valeur finale.
Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
Une autre différence est que, avec ++i vous avez la possibilité de retourner une référence au lieu d'une valeur. Encore une fois, selon ce qui est impliqué dans la fabrication d'une copie de votre objet, cela pourrait être plus lente.
Un exemple réel d'où cela peut se produire serait l'utilisation des itérateurs. La copie d'un itérateur est peu probable d'être un goulot d'étranglement dans votre application, mais c'est toujours une bonne pratique de prendre l'habitude d'utiliser ++i au lieu de i++ où le résultat n'est pas affecté.
Voici une autre observation à faire si vous êtes inquiet au sujet de micro-optimisation. La décrémentation de boucles peut "éventuellement" être plus efficace que l'incrémentation de la boucle (en fonction de l'instruction set architecture BRAS par exemple), étant donné:
for (i = 0; i < 100; i++)
Sur chaque boucle de vous, vous aurez une instruction pour chaque:
- L'ajout de 1 à i
- Comparer si i est inférieur à 100
- Une branche conditionnelle si i est inférieur à 100
Alors qu'une décrémentation de la boucle:
for (i = 100; i != 0; i--)
La boucle va avoir un enseignement de chaque:
- Décrémenter i, réglage de la CPU registre de l'indicateur d'état
- Une branche conditionnelle en fonction de l'état des registres du PROCESSEUR (Z==0)
Bien sûr, cela ne fonctionne que lorsque la décrémentation de zéro!
Rappeler à partir du Système de BRAS Guide du Développeur.