Existe-t-il une différence de performances entre i ++ et ++ i en C?

Réponses:

398

Résumé analytique: Non.

i++pourrait potentiellement être plus lent que ++i, car l'ancienne valeur de i pourrait devoir être sauvegardée pour une utilisation ultérieure, mais dans la pratique, tous les compilateurs modernes optimiseront cela.

Nous pouvons le démontrer en regardant le code de cette fonction, à la fois avec ++iet 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, à l'exception de ++iet i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Nous les compilerons et obtiendrons également l'assembleur généré:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

Et nous pouvons voir que les fichiers objet et assembleur générés sont identiques.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
Mark Harrison
la source
9
Je sais que cette question concerne C, mais je serais intéressé de savoir si les navigateurs peuvent effectuer cette optimisation pour javascript.
TM.
69
Le "Non" est donc vrai pour le compilateur avec lequel vous avez testé.
Andreas
173
@Andreas: Bonne question. J'étais rédacteur de compilateur et j'ai eu l'occasion de tester ce code sur de nombreux processeurs, systèmes d'exploitation et compilateurs. Le seul compilateur que j'ai trouvé qui n'optimise pas le cas i ++ (en fait, le compilateur qui a attiré mon attention sur le plan professionnel) était le compilateur Software Toolworks C80 de Walt Bilofsky. Ce compilateur était destiné aux systèmes Intel 8080 CP / M. Il est sûr de dire que tout compilateur qui n'inclut pas cette optimisation n'est pas destiné à un usage général.
Mark Harrison,
22
Même si la différence de performances est négligeable et optimisée dans de nombreux cas, veuillez noter qu'il est toujours recommandé d'utiliser à la ++iplace de i++. Il n'y a absolument aucune raison de ne pas le faire, et si votre logiciel passe par une chaîne d'outils qui ne l'optimise pas, votre logiciel sera plus efficace. Étant donné qu'il est tout aussi facile de taper ++ique de taper i++, il n'y a vraiment aucune excuse pour ne pas l'utiliser ++ien premier lieu.
monokrome
7
@monokrome Étant donné que les développeurs peuvent fournir leur propre implémentation pour les opérateurs préfixe et postfixe dans de nombreuses langues, cela ne peut pas toujours être une solution acceptable pour un compilateur sans d'abord comparer ces fonctions, qui peuvent être non triviales.
pickypg
112

De l' efficacité contre l'intention d'Andrew Koenig:

Premièrement, il est loin d'être évident qu'il ++iest plus efficace que i++, du moins en ce qui concerne les variables entières.

Et :

Donc, la question que l'on devrait se poser n'est pas laquelle de ces deux opérations est la plus rapide, mais laquelle de ces deux opérations exprime plus précisément ce que vous essayez d'accomplir. Je soumets que si vous n'utilisez pas la valeur de l'expression, il n'y a jamais de raison d'utiliser à la i++place de ++i, car il n'y a jamais de raison de copier la valeur d'une variable, d'incrémenter la variable, puis de jeter la copie.

Donc, si la valeur résultante n'est pas utilisée, j'utiliserais ++i. Mais pas parce qu'il est plus efficace: parce qu'il énonce correctement mon intention.

Sébastien RoccaSerra
la source
5
N'oublions pas que d'autres opérateurs unaires sont également préfixés. Je pense que ++ i est la façon "sémantique" d'utiliser un opérateur unaire, tandis que i ++ est là pour répondre à un besoin spécifique (évaluation avant addition).
TM.
9
Si la valeur résultante n'est pas utilisée, il n'y a pas de différence de sémantique: c'est-à-dire qu'il n'y a aucune base pour préférer l'une ou l'autre construction.
Eamon Nerbonne
3
En tant que lecteur, je vois une différence. Donc en tant qu'écrivain, je vais montrer mon intention en choisissant l'un plutôt que l'autre. C'est juste une habitude que j'ai, d'essayer de communiquer avec mes amis programmeurs et mes coéquipiers via le code :)
Sébastien RoccaSerra
11
Suivant les conseils de Koenig, le code I de i++la même façon que j'avais le code i += nou i = i + n, par exemple, sous la forme cible verbe objet , avec l' objectif opérande à gauche du verbe opérateur. Dans le cas de i++, il n'y a pas d' objet droit , mais la règle s'applique toujours, en gardant la cible à gauche de l' opérateur verbe .
David R Tribble
4
Si vous essayez d'incrémenter i, alors i ++ et ++ i expriment correctement votre intention. Il n'y a aucune raison de préférer l'un à l'autre. En tant que lecteur, je ne vois aucune différence, vos collègues sont-ils confus quand ils voient i ++ et pensent, c'est peut-être une faute de frappe et il ne voulait pas incrémenter i?
dan carter
46

Une meilleure réponse est que ce ++isera parfois plus rapide mais jamais plus lent.

Tout le monde semble supposer qu'il is'agit d'un type intégré standard tel que int. Dans ce cas, il n'y aura pas de différence mesurable.

Cependant, s'il is'agit d'un type complexe, vous pouvez très bien trouver une différence mesurable. Car i++vous devez faire une copie de votre classe avant de l'incrémenter. Selon ce qui est impliqué dans une copie, cela pourrait en effet être plus lent car avec ++itvous, 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 qu'avec ++ivous, vous avez la possibilité de renvoyer une référence au lieu d'une valeur. Encore une fois, selon ce qui est impliqué dans la création d'une copie de votre objet, cela pourrait être plus lent.

Un exemple concret où cela peut se produire serait l'utilisation d'itérateurs. La copie d'un itérateur est peu susceptible d'être un goulot d'étranglement dans votre application, mais il est toujours bon de prendre l'habitude d'utiliser ++iau lieu de i++ne pas affecter le résultat.

Andrew Grant
la source
37
La question indique explicitement C, aucune référence à C ++.
Dan Cristoloveanu
5
Cette question (certes ancienne) concernait le C, pas le C ++, mais je pense qu'il vaut également la peine de mentionner qu'en C ++, même si une classe implémente des opérateurs post et pré-fix, ils ne sont même pas nécessairement liés. Par exemple, bar ++ peut incrémenter un membre de données, tandis que ++ bar peut incrémenter un autre membre de données, et dans ce cas, vous n'auriez pas la possibilité d'utiliser non plus, car la sémantique est différente.
Kevin
-1 Bien que ce soit un bon conseil pour les programmeurs C ++, cela ne répond pas du tout à la question, qui est étiquetée C. En C, cela ne fait absolument aucune différence que vous utilisiez un préfixe ou un postfix.
Lundin
@Pacerier La question est étiquetée C et C uniquement. Pourquoi pensez-vous qu'ils ne sont pas intéressés par cela? Étant donné le fonctionnement de SO, ne serait-il pas plutôt intelligent de supposer qu'ils ne sont intéressés que par C, plutôt que Java, C #, COBOL ou tout autre langage hors sujet?
Lundin
18

Réponse courte:

Il n'y a jamais de différence entre i++et ++ien termes de vitesse. Un bon compilateur ne devrait pas générer de code différent dans les deux cas.

Longue réponse:

Ce que toutes les autres réponses ne mentionnent pas, c'est que la différence entre ++iversus i++n'a de sens que dans l'expression qu'elle trouve.

Dans le cas de for(i=0; i<n; i++), le i++est seul dans sa propre expression: il y a un point de séquence avant le i++et il y en a un après. Ainsi, le seul code machine généré est "augmenter ide 1" et il est bien défini comment il est séquencé par rapport au reste du programme. Donc, si vous le changiez en préfixe ++, cela n'aurait aucune importance, vous obtiendriez toujours le code machine "augmenter ide 1".

Les différences entre ++iet i++ne comptent que dans des expressions telles que array[i++] = x;versus array[++i] = x;. Certains peuvent argumenter et dire que le suffixe sera plus lent dans de telles opérations car le registre où iréside doit être rechargé plus tard. Mais notez ensuite que le compilateur est libre de commander vos instructions comme bon lui semble, tant qu'il ne "casse pas le comportement de la machine abstraite" comme l'appelle la norme C.

Donc, alors que vous pouvez supposer que cela array[i++] = x;est traduit en code machine comme:

  • Enregistrer la valeur de idans le registre A.
  • Enregistrer l'adresse du tableau dans le registre B.
  • Ajoutez A et B, stockez les résultats dans A.
  • À cette nouvelle adresse représentée par A, stockez la valeur de x.
  • Stocker la valeur du iregistre A // inefficace car des instructions supplémentaires ici, nous l'avons déjà fait une fois.
  • Registre d'incrémentation A.
  • Enregistrer le registre A dans i.

le compilateur pourrait aussi bien produire le code plus efficacement, comme:

  • Enregistrer la valeur de idans le registre A.
  • Enregistrer l'adresse du tableau dans le registre B.
  • Ajoutez A et B, stockez les résultats dans B.
  • Registre d'incrémentation A.
  • Enregistrer le registre A dans i.
  • ... // reste du code.

Tout simplement parce que vous, en tant que programmeur C, êtes entraîné à penser que le suffixe ++se produit à la fin, le code machine ne doit pas être commandé de cette façon.

Il n'y a donc pas de différence entre le préfixe et le suffixe ++en C. Maintenant, en tant que programmeur C, vous devriez être différent, ce sont les personnes qui utilisent le préfixe de manière incohérente dans certains cas et le suffixe dans d'autres cas, sans aucune justification. Cela suggère qu'ils ne savent pas comment C fonctionne ou qu'ils ont une mauvaise connaissance de la langue. C'est toujours un mauvais signe, cela suggère à leur tour qu'ils prennent d'autres décisions douteuses dans leur programme, basées sur la superstition ou des "dogmes religieux".

"Le préfixe ++est toujours plus rapide" est en effet un tel faux dogme qui est courant parmi les programmeurs C potentiels.

Lundin
la source
3
Personne n'a dit que "Prefix ++ est toujours plus rapide". C'est mal cité. Ce qu'ils ont dit était "Postfix ++ n'est jamais plus rapide".
Pacerier
2
@Pacerier Je ne cite aucune personne en particulier, mais juste une croyance erronée et répandue.
Lundin
@rbaleksandar C ++! = C.
Lundin
@rbaleksandar La question et la réponse parlent toutes deux de C ++. Ce qui est différent de C, car il a des constructeurs de surcharge d'opérateurs et de copie, etc.
Lundin
3
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh
17

Prenant une feuille de Scott Meyers, Plus efficace c ++ Point 6: Distinguer les formes préfixées et postfixes des opérations d'incrémentation et de décrémentation .

La version préfixe est toujours préférée au suffixe en ce qui concerne les objets, en particulier en ce qui concerne les itérateurs.

La raison de cela si vous regardez le modèle d'appel des opérateurs.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

En regardant cet exemple, il est facile de voir comment l'opérateur de préfixe sera toujours plus efficace que le suffixe. En raison de la nécessité d'un objet temporaire dans l'utilisation du suffixe.

C'est pourquoi lorsque vous voyez des exemples utilisant des itérateurs, ils utilisent toujours la version du préfixe.

Mais comme vous le signalez pour les int, il n'y a effectivement aucune différence en raison de l'optimisation du compilateur qui peut avoir lieu.

JProgrammer
la source
4
Je pense que sa question s'adressait à C, mais pour C ++, vous avez absolument raison, et en outre, les personnes C devraient l'adopter car elles peuvent également l'utiliser pour C ++. Je vois beaucoup trop souvent les programmeurs C utiliser la syntaxe postfixe ;-)
Anders Rune Jensen
-1 Bien que ce soit un bon conseil pour les programmeurs C ++, cela ne répond pas du tout à la question, qui est étiquetée C. En C, cela ne fait absolument aucune différence que vous utilisiez un préfixe ou un postfix.
Lundin
1
Le préfixe @Lundin et le postifx sont importants en C selon la réponse d' Andreas . Vous ne pouvez pas supposer que le compilateur s'optimisera, et c'est juste une bonne pratique pour n'importe quelle langue de préférer Alwas ++ i à i ++
JProgrammer
@JProgrammer Ils n'ont pas d'importance selon la réponse de la vôtre sincèrement :) Si vous trouvez qu'ils produisent du code différent, c'est soit parce que vous n'avez pas réussi à activer les optimisations, soit parce que le compilateur est mauvais. Quoi qu'il en soit, votre réponse est hors sujet car la question portait sur C.
Lundin
16

Voici une observation supplémentaire si vous vous inquiétez de la micro-optimisation. Les boucles de décrémentation peuvent «éventuellement» être plus efficaces que les boucles d'incrémentation (selon l'architecture du jeu d'instructions, par exemple ARM), étant donné:

for (i = 0; i < 100; i++)

Sur chaque boucle, vous aurez chacun une instruction pour:

  1. Ajout 1à i.
  2. Comparez si iest inférieur à a 100.
  3. Une branche conditionnelle si iest inférieure à a 100.

Alors qu'une boucle décrémentante:

for (i = 100; i != 0; i--)

La boucle aura une instruction pour chacun des éléments suivants:

  1. Décrémenter i, en définissant l'indicateur d'état du registre CPU.
  2. Une branche conditionnelle en fonction de l'état du registre CPU ( Z==0).

Bien sûr, cela ne fonctionne que lors d'une décrémentation à zéro!

Rappelé dans le Guide du développeur d'ARM System.

tonylo
la source
Bon. Mais cela ne crée-t-il pas moins d'accès au cache?
AndreasT
Il y a une vieille astuce étrange dans les livres d'optimisation de code, combinant l'avantage de la branche zéro-test avec l'adresse incrémentée. Voici un exemple en langage de haut niveau (probablement inutile, car de nombreux compilateurs sont assez intelligents pour le remplacer par du code de boucle moins efficace mais plus courant): int a [N]; pour (i = -N; i; ++ i) a [N + i] + = 123;
2010
@noop pourriez-vous éventuellement préciser à quels livres d'optimisation de code vous vous référez? J'ai eu du mal à en trouver de bons.
mezamorphique
7
Cette réponse n'est pas du tout une réponse à la question.
monokrome
@mezamorphic Pour x86 mes sources sont: les manuels d'optimisation Intel, Agner Fog, Paul Hsieh, Michael Abrash.
2012 à 9h54
11

Veuillez ne pas laisser la question de "lequel est le plus rapide" être le facteur décisif à utiliser. Il est probable que vous ne vous en soucierez jamais autant, et en outre, le temps de lecture du programmeur est beaucoup plus cher que le temps machine.

Utilisez celui qui a le plus de sens pour l'homme qui lit le code.

Andy Lester
la source
3
Je pense qu'il est faux de préférer de vagues améliorations de lisibilité aux gains d'efficacité réels et à la clarté générale de l'intention.
2010 à 11h53
4
Mon terme «temps de lecture programmeur» est à peu près analogue à «clarté d'intention». Les "gains d'efficacité réels" sont souvent incommensurables, assez proches de zéro pour les appeler zéro. Dans le cas de l'OP, à moins que le code n'ait été profilé pour trouver que le ++ i est un goulot d'étranglement, la question de savoir lequel est le plus rapide est une perte de temps et des unités de pensée de programmeur.
Andy Lester
2
La différence de lisibilité entre ++ i et i ++ n'est qu'une question de préférence personnelle, mais ++ i implique clairement un fonctionnement plus simple que i ++, bien que le résultat soit équivalent pour les cas triviaux et les types de données simples lors de l'optimisation du compilateur. C'est pourquoi ++ i est un gagnant pour moi, lorsque des propriétés spécifiques de post-incrémentation ne sont pas nécessaires.
2010
Vous dites ce que je dis. Il est plus important de montrer l'intention et d'améliorer la lisibilité que de se soucier de «l'efficacité».
Andy Lester
2
Je ne suis toujours pas d'accord. Si la lisibilité est plus élevée dans votre liste de priorités, alors votre choix de langage de programmation est peut-être faux. Le but principal de C / C ++ est d'écrire du code efficace.
2013
11

Tout d'abord: la différence entre i++et ++iest négligeable en C.


Pour les détails.

1. Le problème C ++ bien connu: ++iest plus rapide

En C ++, ++iest plus efficace si siff iest une sorte d'objet avec un opérateur d'incrémentation surchargé.

Pourquoi?
Dans ++i, l'objet est d'abord incrémenté et peut ensuite être transmis comme référence const à toute autre fonction. Ce n'est pas possible si l'expression est foo(i++)parce que maintenant l'incrément doit être fait avant d' foo()être appelé, mais l'ancienne valeur doit être passée à foo(). Par conséquent, le compilateur est obligé de faire une copie de iavant d'exécuter l'opérateur d'incrémentation sur l'original. Les appels supplémentaires de constructeur / destructeur sont la mauvaise partie.

Comme indiqué ci-dessus, cela ne s'applique pas aux types fondamentaux.

2. Le fait peu connu: i++ peut être plus rapide

Si aucun constructeur / destructeur n'a besoin d'être appelé, ce qui est toujours le cas en C, ++iet i++devrait être tout aussi rapide, non? Non. Ils sont pratiquement aussi rapides, mais il peut y avoir de petites différences, que la plupart des autres répondants ont mal résolues.

Comment peut-on i++être plus rapide?
Il s'agit des dépendances aux données. Si la valeur doit être chargée à partir de la mémoire, deux opérations subséquentes doivent être effectuées avec elle, l'incrémenter et l'utiliser. Avec ++i, l'incrémentation doit être effectuée avant que la valeur puisse être utilisée. Avec i++, l'utilisation ne dépend pas de l'incrément et la CPU peut effectuer l'opération d'utilisation en parallèle de l'opération d'incrément. La différence est au plus un cycle CPU, donc c'est vraiment négociables, mais c'est là. Et c'est l'inverse alors que beaucoup s'attendraient.

cmaster - réintégrer monica
la source
À propos de votre point 2: si le ++iou i++est utilisé dans une autre expression, le changement entre les deux change la sémantique de l'expression, donc tout gain / perte de performance possible est hors de question. S'ils sont autonomes, c'est-à-dire que le résultat de l'opération n'est pas utilisé immédiatement, alors n'importe quel compilateur décent le compilera pour la même chose, par exemple une INCinstruction d'assemblage.
Shahbaz
1
@Shahbaz C'est parfaitement vrai, mais pas le point. 1) Même si la sémantique sont différentes, à la fois i++et ++ipeuvent être utilisés de façon interchangeable dans presque toutes les situations possibles en ajustant les constantes de boucle par un, ils sont donc équivalent dans ce qu'ils font pour le programmeur. 2) Même si les deux compilent selon la même instruction, leur exécution diffère pour le CPU. Dans le cas de i++, le CPU peut calculer l'incrément en parallèle à une autre instruction qui utilise la même valeur (les CPU le font vraiment!), Tandis qu'avec ++ile CPU il doit planifier l'autre instruction après l'incrément.
cmaster - réintègre monica
4
@Shahbaz Par exemple: if(++foo == 7) bar();et if(foo++ == 6) bar();sont fonctionnellement équivalents. Cependant, le second peut être un cycle plus rapide, car la comparaison et l'incrément peuvent être calculés en parallèle par le CPU. Ce n'est pas que ce cycle unique compte beaucoup, mais la différence est là.
cmaster - réintègre monica
1
Bon point. Les constantes apparaissent beaucoup (ainsi que <par exemple vs <=) où ++est généralement utilisé, donc la conversion entre le bien est souvent facilement possible.
Shahbaz
1
J'aime le point 2, mais cela ne s'applique que si la valeur est utilisée, non? La question dit "si la valeur résultante n'est pas utilisée?", Cela peut donc prêter à confusion.
jinawee
7

@Mark Même si le compilateur est autorisé à optimiser la copie temporaire (basée sur la pile) de la variable et que gcc (dans les versions récentes) le fait, cela ne signifie pas que tous les compilateurs le feront toujours.

Je viens de le tester avec les compilateurs que nous utilisons dans notre projet actuel et 3 sur 4 ne l'optimisent pas.

Ne présumez jamais que le compilateur réussit, surtout si le code éventuellement plus rapide mais jamais plus lent est aussi facile à lire.

Si vous n'avez pas d'implémentation vraiment stupide de l'un des opérateurs de votre code:

J'ai toujours préféré ++ i à i ++.

Andreas
la source
Juste curieux ... pourquoi utilisez-vous 4 compilateurs C différents dans un même projet? Ou dans une équipe ou une entreprise, d'ailleurs?
Lawrence Dol
3
Lors de la création de jeux pour consoles, chaque plateforme apporte son propre compilateur / chaîne d'outils. Dans un monde parfait, nous pourrions utiliser gcc / clang / llvm pour toutes les cibles, mais dans ce monde, nous devons accepter Microsoft, Intel, Metroworks, Sony, etc.
Andreas
5

En C, le compilateur peut généralement les optimiser pour qu'elles soient identiques si le résultat n'est pas utilisé.

Cependant, en C ++ si vous utilisez d'autres types qui fournissent leurs propres opérateurs ++, la version préfixe est susceptible d'être plus rapide que la version postfix. Donc, si vous n'avez pas besoin de la sémantique postfix, il est préférable d'utiliser l'opérateur de préfixe.

Kristopher Johnson
la source
4

Je peux penser à une situation où postfix est plus lent que l'incrément de préfixe:

Imaginez un processeur avec registre Autilisé comme accumulateur et c'est le seul registre utilisé dans de nombreuses instructions (certains petits microcontrôleurs sont en fait comme ça).

Imaginez maintenant le programme suivant et sa traduction en un assemblage hypothétique:

Incrément de préfixe:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Incrément de suffixe:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

Notez comment la valeur de a bété forcée d'être rechargée. Avec l'incrémentation du préfixe, le compilateur peut simplement incrémenter la valeur et continuer à l'utiliser, évitant éventuellement de la recharger car la valeur souhaitée est déjà dans le registre après l'incrément. Cependant, avec l'incrémentation de suffixe, le compilateur doit gérer deux valeurs, l'une ancienne et l'autre la valeur incrémentée qui, comme je le montre ci-dessus, entraîne un accès mémoire supplémentaire.

Bien sûr, si la valeur de l'incrément n'est pas utilisée, comme une seule i++;instruction, le compilateur peut (et fait) simplement générer une instruction d'incrément indépendamment de l'utilisation du suffixe ou du préfixe.


En remarque, je voudrais mentionner qu'une expression dans laquelle il y a un b++ne peut pas être simplement convertie en une avec ++bsans aucun effort supplémentaire (par exemple en ajoutant un - 1). Donc, comparer les deux s'ils font partie d'une expression n'est pas vraiment valable. Souvent, lorsque vous utilisez b++une expression que vous ne pouvez pas utiliser ++b, même si elle ++bétait potentiellement plus efficace, ce serait tout simplement faux. L'exception est bien sûr si l'expression le demande (par exemple a = b++ + 1;qui peut être changé en a = ++b;).

Shahbaz
la source
4

J'ai lu la plupart des réponses ici et de nombreux commentaires, et je n'ai vu aucune référence au seul cas où je pourrais penser où i++est plus efficace que ++i(et peut-être étonnamment --i était plus efficace que i--). C'est pour les compilateurs C pour le DEC PDP-11!

Le PDP-11 avait des instructions d'assemblage pour la pré-décrémentation d'un registre et la post-incrémentation, mais pas l'inverse. Les instructions permettaient à tout registre "à usage général" d'être utilisé comme pointeur de pile. Donc, si vous avez utilisé quelque chose comme *(i++)ça, cela pourrait être compilé en une seule instruction d'assemblage, alors que *(++i)non.

Ceci est évidemment un exemple très ésotérique, mais il fournit l'exception lorsque post-augmentation est plus efficace (ou devrais - je dire était , car il n'y a pas beaucoup de demande pour le PDP-11 code C ces jours -ci ).

daShier
la source
2
Très ésotérique, mais très intéressant!
Mark Harrison
@daShier. +1, bien que je ne sois pas d'accord, ce n'est pas si ésotérique, ou du moins ça ne devrait pas l'être. C a été co-développé avec Unix chez AT&T Bell Labs au début des années 70 lorsque le PDP-11 était le processeur cible. Dans le code source Unix de cette incrément post-ère, "i ++", est plus répandu en partie parce que les développeurs savaient quand la valeur est affectée, "j = i ++", ou utilisée comme index, "a [i ++] = n", le code serait légèrement plus rapide (et plus petit). Il semble qu'ils aient pris l'habitude d'utiliser le post-incrément, sauf si un pré était requis. D'autres ont appris en lisant leur code et ont également repris cette habitude.
jimhark
Le 68000 a la même fonctionnalité, le post-incrément et le pré-décrément sont pris en charge dans le matériel (en tant que modes d'adressage), mais pas l'inverse. L'équipe Motorola était, euh, inspirée par le DEC PDP-11.
jimhark
2
@jimhark, oui, je fais partie de ces programmeurs PDP-11 qui sont passés au 68000 et utilisent toujours --iet i++.
daShier
2

Je préfère toujours le pré-incrément, cependant ...

Je voulais souligner que même dans le cas de l'appel de la fonction operator ++, le compilateur sera en mesure d'optimiser le temporaire si la fonction est en ligne. Étant donné que l'opérateur ++ est généralement court et souvent implémenté dans l'en-tête, il est probable qu'il soit intégré.

Donc, à des fins pratiques, il n'y a probablement pas beaucoup de différence entre les performances des deux formulaires. Cependant, je préfère toujours le pré-incrément, car il semble préférable d'exprimer directement ce que j'essaie de dire, plutôt que de compter sur l'optimiseur pour le comprendre.

En outre, donner moins à l'optmizer de faire signifie probablement que le compilateur s'exécute plus rapidement.


la source
Votre publication est spécifique à C ++ alors que la question concerne C. Dans tous les cas, votre réponse est fausse: pour les opérateurs de post-incrémentation personnalisés, le compilateur ne sera généralement pas en mesure de produire du code aussi efficace.
Konrad Rudolph
Il déclare «si la fonction est intégrée», ce qui rend son raisonnement correct.
Blaisorblade
Étant donné que C ne fournit pas de surcharge d'opérateur, la question pré vs post est en grande partie sans intérêt. Le compilateur peut optimiser une température inutilisée en utilisant la même logique qui est appliquée à toute autre valeur inutilisée, calculée de manière primitive. Voir la réponse sélectionnée pour un échantillon.
0

Mon C est un peu rouillé, je m'excuse donc à l'avance. Speedwise, je peux comprendre les résultats. Mais, je suis confus quant à la façon dont les deux fichiers sont sortis sur le même hachage MD5. Peut-être qu'une boucle for fonctionne de la même manière, mais les 2 lignes de code suivantes ne généreraient-elles pas un assemblage différent?

myArray[i++] = "hello";

contre

myArray[++i] = "hello";

Le premier écrit la valeur dans le tableau, puis incrémente i. Le deuxième incrément i écrit ensuite dans le tableau. Je ne suis pas un expert en assemblage, mais je ne vois pas comment le même exécutable serait généré par ces 2 lignes de code différentes.

Juste mes deux cents.

Jason Z
la source
1
@Jason Z L'optimisation du compilateur se produit avant la fin de la génération de l'assembly, il verrait que la variable i n'est utilisée nulle part ailleurs sur la même ligne, donc conserver sa valeur serait un gaspillage, il la retourne probablement en i ++. Mais ce n'est qu'une supposition. J'ai hâte qu'un de mes professeurs essaie de dire que c'est plus rapide et que je deviens le gars qui corrige une théorie avec des preuves pratiques. Je peux presque déjà ressentir l'animosité ^ _ ^
Tarks
3
"Mon C est un peu rouillé, alors je m'excuse à l'avance." Rien de mal avec votre C, mais vous n'avez pas lu la question d'origine dans son intégralité: "Y a-t-il une différence de performances entre i ++ et ++ i si la valeur résultante n'est pas utilisée? " Notez l'italique. Dans votre exemple, le «résultat» de i ++ / ++ i est utilisé, mais dans la boucle for idiomatique, le «résultat» de l'opérateur pre / postincrement n'est pas utilisé pour que le compilateur puisse faire ce qu'il veut.
Roddy
2
dans votre exemple, le code serait différent, car vous utilisez la valeur. l'exemple où ils étaient identiques utilisait uniquement le ++ pour l'incrément, et n'utilisait pas la valeur retournée par l'un ou l'autre.
John Gardner
1
Correction: "le compilateur verrait que la valeur de retour d'i ++ n'est pas utilisée, donc il la retournerait à ++ i". Ce que vous avez écrit est faux également parce que vous ne pouvez pas avoir i avec l'un de i ++, i ++ sur la même ligne (instruction), ce résultat n'est pas défini.
Blaisorblade
1
Changer foo[i++]en foo[++i]sans changer autre chose changerait évidemment la sémantique du programme, mais sur certains processeurs lors de l'utilisation d'un compilateur sans logique d'optimisation de levage de boucle, l'incrémentation pet qune fois puis l'exécution d'une boucle qui fonctionne, par exemple, *(p++)=*(q++);serait plus rapide que d'utiliser une boucle qui fonctionne *(++pp)=*(++q);. Pour les boucles très serrées sur certains processeurs, la différence de vitesse peut être significative (plus de 10%), mais c'est probablement le seul cas en C où le post-incrément est sensiblement plus rapide que le pré-incrément.
supercat