web-dev-qa-db-fra.com

X + = est-il plus rapide que x = x + a?

Je lisais "Le langage de programmation C++" de Stroustrup, où il dit que sur deux façons d'ajouter quelque chose à une variable

x = x + a;

et

x += a;

Il préfère += car il est probablement mieux implémenté. Je pense qu'il veut dire que ça marche plus vite aussi.
Mais est-ce vraiment le cas? Si cela dépend du compilateur et d'autres choses, comment puis-je vérifier?

84
Chiffa

Tout compilateur digne de ce nom générera exactement la même séquence de langage machine pour les deux constructions pour tout type intégré (int, float, etc.) tant que l'instruction est vraiment aussi simple que x = x + a;et l'optimisation est activée. (Notamment, le CCG -O0, qui est le mode par défaut, effectue anti-optimisations, par exemple en insérant des magasins complètement inutiles dans la mémoire, afin de garantir que les débogueurs puissent toujours trouver des valeurs variables.)

Si l'énoncé est plus compliqué, cependant, ils pourraient être différents. Supposons que f est une fonction qui renvoie un pointeur, puis

*f() += a;

appelle f une seule fois, alors que

*f() = *f() + a;

l'appelle deux fois. Si f a des effets secondaires, l'un des deux sera faux (probablement le dernier). Même si f n'a pas d'effets secondaires, le compilateur peut ne pas être en mesure d'éliminer le deuxième appel, donc ce dernier peut en effet être plus lent.

Et puisque nous parlons de C++ ici, la situation est entièrement différente pour les types de classe qui surchargent operator+ et operator+=. Si x est un tel type, alors - avant l'optimisation - x += a Se traduit par

x.operator+=(a);

tandis que x = x + a Se traduit par

auto TEMP(x.operator+(a));
x.operator=(TEMP);

Maintenant, si la classe est correctement écrite et l'optimiseur du compilateur est assez bon, les deux finiront par générer le même langage machine, mais ce n'est pas une chose sûre comme pour les types intégrés. C'est probablement ce à quoi Stroustrup pense quand il encourage l'utilisation de +=.

211
zwol

Vous pouvez vérifier en regardant le démontage, qui sera le même.

Pour les types de base, les deux sont également rapides.

C'est la sortie générée par une version de débogage (c'est-à-dire aucune optimisation):

    a += x;
010813BC  mov         eax,dword ptr [a]  
010813BF  add         eax,dword ptr [x]  
010813C2  mov         dword ptr [a],eax  
    a = a + x;
010813C5  mov         eax,dword ptr [a]  
010813C8  add         eax,dword ptr [x]  
010813CB  mov         dword ptr [a],eax  

pour les types définis par l'utilisateur, où vous pouvez surcharger operator + et operator +=, cela dépend de leurs implémentations respectives.

56
Luchian Grigore

Oui! C'est plus rapide à écrire, plus rapide à lire et plus rapide à comprendre, pour ce dernier dans le cas où x pourrait avoir des effets secondaires. C'est donc globalement plus rapide pour les humains. Le temps humain en général coûte beaucoup plus cher que le temps informatique, donc ce doit être ce que vous demandiez. Droite?

12
Mark Adler

La différence entre x = x + a et x += a est la quantité de travail que la machine doit effectuer - certains compilateurs peuvent (et font généralement) l'optimiser, mais généralement, si nous ignorons l'optimisation pendant un certain temps, ce qui se passe est que dans l'ancien extrait de code, la machine a pour rechercher la valeur de x deux fois, alors que dans ce dernier, cette recherche ne doit se produire qu'une seule fois.

Cependant, comme je l'ai mentionné, la plupart des compilateurs sont aujourd'hui suffisamment intelligents pour analyser les instructions et réduire les instructions machine résultantes requises.

PS: Première réponse sur Stack Overflow!

8
Sagar Ahire

Cela dépend vraiment du type de x et de a et de l'implémentation de +. Pour

   T x, a;
   ....
   x = x + a;

le compilateur doit créer un T temporaire pour contenir la valeur de x + a pendant qu'il l'évalue, qu'il peut ensuite affecter à x. (Il ne peut pas utiliser x ou a comme espace de travail pendant cette opération).

Pour x + = a, il n'a pas besoin de temporaire.

Pour les types triviaux, il n'y a pas de différence.

8
Tom Tanner

Comme vous avez étiqueté ce C++, il n'y a aucun moyen de savoir à partir des deux déclarations que vous avez publiées. Vous devez savoir ce qu'est "x" (c'est un peu comme la réponse "42"). Si x est un POD, cela ne fera pas vraiment de différence. Cependant, si x est une classe, il peut y avoir des surcharges pour le operator + et operator += méthodes pouvant avoir des comportements différents conduisant à des temps d'exécution très différents.

6
Skizz

Vous posez la mauvaise question.

Il est peu probable que cela améliore les performances d'une application ou d'une fonctionnalité. Même si c'était le cas, le moyen de le savoir est de profile le code et de savoir comment il vous affecte à coup sûr. Au lieu de s'inquiéter à ce niveau de ce qui est le plus rapide, il est beaucoup plus important de penser en termes de clarté, d'exactitude et de lisibilité.

Cela est particulièrement vrai si l'on considère que, même s'il s'agit d'un facteur de performance important, les compilateurs évoluent au fil du temps. Quelqu'un peut trouver une nouvelle optimisation et la bonne réponse aujourd'hui peut se tromper demain. C'est un cas classique d'optimisation prématurée.

Cela ne veut pas dire que la performance n'a pas d'importance du tout ... Juste que ce n'est pas la bonne approche pour atteindre vos objectifs de perf. La bonne approche consiste à utiliser des outils de profilage pour savoir où votre code passe réellement son temps, et donc où concentrer vos efforts.

6
Joel Coehoorn

Si vous dites +=, Vous simplifiez la vie du compilateur. Pour que le compilateur reconnaisse que x = x+a Est identique à x += a, Le compilateur doit

  • analysez le côté gauche (x) pour vous assurer qu'il n'a pas d'effets secondaires et se réfère toujours à la même valeur l. Par exemple, il peut s'agir de z[i], Et il doit s'assurer que z et i ne changent pas.

  • analysez le côté droit (x+a) et assurez-vous qu'il s'agit d'une somme, et que le côté gauche apparaît une seule et unique fois sur le côté droit, même s'il pourrait être transformé, comme dans la fonction z[i] = a + *(z+2*0+i).

Si vous voulez ajouter a à x, le rédacteur du compilateur l'apprécie lorsque vous dites simplement ce que vous voulez dire. De cette façon, vous n'exercez pas la partie du compilateur dont son auteur espère il/elle a éliminé tous les bugs, et cela ne en fait rend la vie plus facile pour vous, sauf si vous ne pouvez honnêtement pas sortir la tête du mode Fortran.

6
Mike Dunlavey

Pour un exemple concret, imaginez un type de nombre complexe simple:

struct complex {
    double x, y;
    complex(double _x, double _y) : x(_x), y(_y) { }
    complex& operator +=(const complex& b) {
        x += b.x;
        y += b.y;
        return *this;
    }
    complex operator +(const complex& b) {
        complex result(x+b.x, y+b.y);
        return result;
    }
    /* trivial assignment operator */
}

Pour le cas a = a + b, il doit créer une variable temporaire supplémentaire puis la copier.

5
Random832

Non, les deux façons sont traitées de la même manière.

2
CloudyMarble