Différence entre déclarer des variables avant ou en boucle?

312

Je me suis toujours demandé si, en général, déclarer une variable jetable avant une boucle, par opposition à plusieurs reprises à l'intérieur de la boucle, faisait une différence (de performance)? Un exemple (assez inutile) en Java:

a) déclaration avant boucle:

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

b) déclaration (à plusieurs reprises) à l'intérieur de la boucle:

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

Lequel est le meilleur, a ou b ?

Je soupçonne que la déclaration de variable répétée (exemple b ) crée plus de surcharge en théorie , mais que les compilateurs sont suffisamment intelligents pour que cela n'ait pas d'importance. L'exemple b a l'avantage d'être plus compact et de limiter la portée de la variable à l'endroit où elle est utilisée. Pourtant, j'ai tendance à coder selon l'exemple a .

Edit: je suis particulièrement intéressé par le cas Java.

Rabarberski
la source
Cela est important lors de l'écriture de code Java pour la plate-forme Android. Google suggère que pour que le code à temps critique déclare les variables d'incrémentation en dehors d'une boucle for, comme s'il était à l'intérieur de la boucle for, il le re-déclare chaque fois dans cet environnement. La différence de performances est très sensible pour les algorithmes coûteux.
AaronCarson
1
@AaronCarson pourriez-vous s'il vous plaît fournir un lien vers cette suggestion de Google
Vitaly Zinchenko

Réponses:

256

Quel est le meilleur, a ou b ?

Du point de vue des performances, il faudrait le mesurer. (Et à mon avis, si vous pouvez mesurer une différence, le compilateur n'est pas très bon).

Du point de vue de la maintenance, b est meilleur. Déclarez et initialisez les variables au même endroit, dans la portée la plus étroite possible. Ne laissez pas de trou béant entre la déclaration et l'initialisation, et ne polluez pas les espaces de noms dont vous n'avez pas besoin.

Daniel Earwicker
la source
5
Au lieu de Double, s'il s'agit de String, toujours le cas "b" mieux?
Antoops le
3
@Antoops - oui, b vaut mieux pour des raisons qui n'ont rien à voir avec le type de données de la variable déclarée. Pourquoi serait-ce différent pour les cordes?
Daniel Earwicker
215

Eh bien, j'ai exécuté vos exemples A et B 20 fois chacun, en bouclant 100 millions de fois. (JVM - 1.5.0)

A: temps d'exécution moyen: 0,074 sec

B: temps d'exécution moyen: 0,067 sec

À ma grande surprise, B était légèrement plus rapide. Aussi vite que les ordinateurs sont maintenant difficiles à dire si vous pouvez mesurer avec précision cela. Je le coderais également de la manière A, mais je dirais que cela n'a pas vraiment d'importance.

marque
la source
12
Vous m'avez battu, j'étais sur le point de publier mes résultats pour le profilage, je suis devenu plus ou moins le même et oui, étonnamment, B est plus rapide aurait vraiment pensé A si j'avais eu besoin de parier dessus.
Mark Davidson
14
Pas beaucoup de surprise - lorsque la variable est locale à la boucle, elle n'a pas besoin d'être conservée après chaque itération, donc elle peut rester dans un registre.
142
+1 pour l'avoir testé , pas seulement une opinion / théorie que l'OP aurait pu se faire.
MGOwen
3
@GoodPerson pour être honnête, j'aimerais que cela se fasse. J'ai effectué ce test environ 10 fois sur ma machine pour 50 000 000 à 100 000 000 itérations avec un morceau de code presque identique (que j'aimerais partager avec quiconque souhaite exécuter des statistiques). Les réponses ont été divisées presque également dans les deux sens, généralement par une marge de 900 ms (plus de 50 millions d'itérations), ce qui n'est pas vraiment beaucoup. Bien que ma première pensée soit que ça va être du "bruit", il pourrait se pencher un peu. Cet effort me semble cependant purement académique (pour la plupart des applications de la vie réelle). J'aimerais quand même voir un résultat;) Quelqu'un est d'accord?
javatarz
3
Afficher les résultats des tests sans documenter la configuration est sans valeur. C'est particulièrement vrai dans ce cas, où les deux fragments de code produisent un bytecode identique, donc toute différence mesurée n'est qu'un signe de conditions de test insuffisantes.
Holger
66

Cela dépend de la langue et de l'utilisation exacte. Par exemple, en C # 1, cela ne faisait aucune différence. En C # 2, si la variable locale est capturée par une méthode anonyme (ou expression lambda en C # 3), cela peut faire une différence très significative.

Exemple:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        int outer;
        for (int i=0; i < 10; i++)
        {
            outer = i;
            int inner = i;
            actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

Production:

Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9

La différence est que toutes les actions capturent la même outervariable, mais chacune a sa propre innervariable distincte .

Jon Skeet
la source
3
dans l'exemple B (question d'origine), crée-t-il réellement une nouvelle variable à chaque fois? que se passe-t-il aux yeux de la pile?
Royi Namir
@Jon, était-ce un bug en C # 1.0? Ne devrait pas idéalement Outeravoir 9 ans?
nawfal
@nawfal: Je ne sais pas ce que tu veux dire. Les expressions lambda n'étaient pas dans 1.0 ... et Outer est 9. Quel bug voulez-vous dire?
Jon Skeet
@nawfal: Mon point est qu'il n'y avait pas de fonctionnalités de langage en C # 1.0 où vous pourriez faire la différence entre déclarer une variable à l'intérieur d'une boucle et la déclarer à l'extérieur (en supposant que les deux ont été compilées). Cela a changé en C # 2.0. Pas de bug.
Jon Skeet
@ JonSkeet Oh oui, je vous comprends maintenant, j'ai complètement ignoré le fait que vous ne pouvez pas fermer des variables comme celle-ci en 1.0, mon mauvais! :)
nawfal
35

Voici ce que j'ai écrit et compilé dans .NET.

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

C'est ce que j'obtiens de .NET Reflector lorsque CIL est rendu en code.

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

Les deux se ressemblent donc exactement après la compilation. Dans les langages gérés, le code est converti en code CL / octet et au moment de l'exécution, il est converti en langage machine. Ainsi, en langage machine, un double peut même ne pas être créé sur la pile. Il peut simplement s'agir d'un registre car le code indique qu'il s'agit d'une variable temporaire pour la WriteLinefonction. Il existe un ensemble de règles d'optimisation pour les boucles. Donc, le gars moyen ne devrait pas s'en inquiéter, surtout dans les langues gérées. Il existe des cas où vous pouvez optimiser la gestion du code, par exemple, si vous devez concaténer un grand nombre de chaînes en utilisant simplement string a; a+=anotherstring[i]vs en utilisantStringBuilder. Il y a une très grande différence de performances entre les deux. Il existe de nombreux cas où le compilateur ne peut pas optimiser votre code, car il ne peut pas comprendre ce qui est prévu dans une plus grande portée. Mais cela peut à peu près optimiser les choses de base pour vous.

particule
la source
int j = 0 pour (; j <0x3e8; j ++) de cette manière déclaré une fois fois à la fois variable, et non chacun pour le cycle. 2) l'affectation est plus grosse que toutes les autres options. 3) La règle de la meilleure pratique est donc toute déclaration en dehors de l'itération pour.
luka
24

Il s'agit d'un piège dans VB.NET. Le résultat Visual Basic ne réinitialise pas la variable dans cet exemple:

For i as Integer = 1 to 100
    Dim j as Integer
    Console.WriteLine(j)
    j = i
Next

' Output: 0 1 2 3 4...

Cela affichera 0 la première fois (les variables Visual Basic ont des valeurs par défaut lorsqu'elles sont déclarées!) Mais à ichaque fois par la suite.

Si vous ajoutez un = 0, cependant, vous obtenez ce que vous attendez:

For i as Integer = 1 to 100
    Dim j as Integer = 0
    Console.WriteLine(j)
    j = i
Next

'Output: 0 0 0 0 0...
Michael Haren
la source
1
J'utilise VB.NET depuis des années et je ne l'avais pas rencontré !!
ChrisA
12
Oui, c'est désagréable de comprendre cela dans la pratique.
Michael Haren
Voici une référence à ce sujet de Paul Vick: panopticoncentral.net/archive/2006/03/28/11552.aspx
ferventcoder
1
@eschneider @ferventcoder Malheureusement @PaulV a décidé de supprimer ses anciens articles de blog , c'est donc maintenant un lien mort.
Mark Hurd
oui, récemment, j'ai rencontré cela; cherchait des documents officiels à ce sujet ...
Eric Schneider
15

J'ai fait un test simple:

int b;
for (int i = 0; i < 10; i++) {
    b = i;
}

contre

for (int i = 0; i < 10; i++) {
    int b = i;
}

J'ai compilé ces codes avec gcc - 5.2.0. Et puis j'ai démonté le principal () de ces deux codes et c'est le résultat:

1º:

   0x00000000004004b6 <+0>:     push   rbp
   0x00000000004004b7 <+1>:     mov    rbp,rsp
   0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret

contre

   0x00000000004004b6 <+0>: push   rbp
   0x00000000004004b7 <+1>: mov    rbp,rsp
   0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret 

Qui sont exactement le même résultat asm. n'est-ce pas une preuve que les deux codes produisent la même chose?

UserX
la source
3
oui, et c'est cool que vous ayez fait cela, mais cela revient à ce que les gens disaient à propos de la dépendance langage / compilateur. Je me demande comment JIT ou les performances du langage interprété seraient affectées.
user137717
12

Cela dépend du langage - IIRC C # optimise cela, donc il n'y a pas de différence, mais JavaScript (par exemple) fera à chaque fois l'intégralité de l'allocation de mémoire.

annakata
la source
Oui, mais cela ne représente pas grand-chose. J'ai effectué un test simple avec une boucle for s'exécutant 100 millions de fois et j'ai trouvé que la plus grande différence en faveur de la déclaration en dehors de la boucle était de 8 ms. Cela ressemblait généralement plus à 3-4 et déclarait parfois en dehors de la boucle effectuée PIRE (jusqu'à 4 ms), mais ce n'était pas typique.
user137717
11

J'utiliserais toujours A (plutôt que de compter sur le compilateur) et je pourrais également réécrire:

for(int i=0, double intermediateResult=0; i<1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

Cela se limite toujours intermediateResultà la portée de la boucle, mais ne se redéclare pas à chaque itération.

Triptyque
la source
12
Souhaitez-vous conceptuellement que la variable vive pendant la durée de la boucle plutôt que séparément par itération? Je le fais rarement. Écrivez du code qui révèle votre intention aussi clairement que possible, sauf si vous avez une très, très bonne raison de faire autrement.
Jon Skeet
4
Ah, joli compromis, je n'y ai jamais pensé! OMI, le code devient cependant un peu moins clair visuellement)
Rabarberski
2
@ Jon - Je n'ai aucune idée de ce que l'OP fait réellement avec la valeur intermédiaire. Je pensais juste que c'était une option qui méritait d'être envisagée.
Triptyque du
6

À mon avis, b est la meilleure structure. Dans un, la dernière valeur de intermédiaireRésultat reste après la fin de votre boucle.

Modifier: cela ne fait pas beaucoup de différence avec les types de valeur, mais les types de référence peuvent être quelque peu lourds. Personnellement, j'aime que les variables soient déréférencées dès que possible pour le nettoyage, et b le fait pour vous,

Powerlord
la source
sticks around after your loop is finished- même si cela n'a pas d'importance dans un langage comme Python, où les noms liés restent jusqu'à la fin de la fonction.
new123456
@ new123456: L'OP a demandé des spécificités Java, même si la question a été posée de façon assez générique. De nombreux langages dérivés de C ont une portée au niveau du bloc: C, C ++, Perl (avec le mymot - clé), C # et Java pour nommer 5 que j'ai utilisé.
Powerlord
Je sais - c'était une observation, pas une critique.
new123456
5

Je soupçonne que quelques compilateurs pourraient optimiser les deux pour être le même code, mais certainement pas tous. Je dirais donc que vous êtes mieux avec l'ancien. La seule raison de ce dernier est si vous voulez vous assurer que la variable déclarée est utilisée uniquement dans votre boucle.

Ragoût S
la source
5

En règle générale, je déclare mes variables dans la portée la plus interne possible. Donc, si vous n'utilisez pas intermédiaireRésultat en dehors de la boucle, alors j'irais avec B.

Chris
la source
5

Un collègue préfère le premier formulaire, disant qu'il s'agit d'une optimisation, préférant réutiliser une déclaration.

Je préfère le second (et j'essaie de convaincre mon collègue! ;-)), après avoir lu cela:

  • Cela réduit la portée des variables là où elles sont nécessaires, ce qui est une bonne chose.
  • Java optimise suffisamment pour ne faire aucune différence significative dans les performances. IIRC, peut-être que la deuxième forme est encore plus rapide.

Quoi qu'il en soit, il tombe dans la catégorie de l'optimisation prématurée qui repose sur la qualité du compilateur et / ou de la JVM.

PhiLho
la source
5

Il y a une différence en C # si vous utilisez la variable dans un lambda, etc. Mais en général, le compilateur fera essentiellement la même chose, en supposant que la variable n'est utilisée que dans la boucle.

Étant donné qu'ils sont fondamentalement les mêmes: notez que la version b rend beaucoup plus évident pour les lecteurs que la variable n'est pas et ne peut pas être utilisée après la boucle. De plus, la version b est beaucoup plus facilement refactorisée. Il est plus difficile d'extraire le corps de la boucle dans sa propre méthode dans la version a.De plus, la version b vous assure qu'il n'y a aucun effet secondaire à un tel refactoring.

Par conséquent, la version a me dérange sans fin, car elle ne présente aucun avantage et rend beaucoup plus difficile de raisonner sur le code ...

Mark Sowul
la source
5

Eh bien, vous pouvez toujours créer une portée pour cela:

{ //Or if(true) if the language doesn't support making scopes like this
    double intermediateResult;
    for (int i=0; i<1000; i++) {
        intermediateResult = i;
        System.out.println(intermediateResult);
    }
}

De cette façon, vous ne déclarez la variable qu'une seule fois, et elle mourra lorsque vous quitterez la boucle.

Marcelo Faísca
la source
4

J'ai toujours pensé que si vous déclarez vos variables à l'intérieur de votre boucle, vous perdez de la mémoire. Si vous avez quelque chose comme ça:

for(;;) {
  Object o = new Object();
}

Ensuite, non seulement l'objet doit être créé pour chaque itération, mais il doit y avoir une nouvelle référence allouée pour chaque objet. Il semble que si le garbage collector est lent, vous aurez un tas de références pendantes qui doivent être nettoyées.

Cependant, si vous avez ceci:

Object o;
for(;;) {
  o = new Object();
}

Ensuite, vous ne créez qu'une seule référence et lui attribuez un nouvel objet à chaque fois. Bien sûr, cela peut prendre un peu plus de temps pour sortir de la portée, mais il n'y a alors qu'une seule référence pendante à traiter.

R. Carr
la source
3
Une nouvelle référence n'est pas allouée pour chaque objet, même si la référence est déclarée dans la boucle «for». Dans les DEUX cas: 1) «o» est une variable locale et l'espace de pile lui est alloué une fois au début de la fonction. 2) Un nouvel objet est créé à chaque itération. Il n'y a donc pas de différence de performances. Pour l'organisation, la lisibilité et la maintenabilité du code, il est préférable de déclarer la référence dans la boucle.
Ajoy Bhatia
1
Bien que je ne puisse pas parler pour Java, dans .NET, la référence n'est pas «allouée» pour chaque objet dans le premier exemple. Il y a une seule entrée sur la pile pour cette variable locale (à la méthode). Pour vos exemples, l'IL créé est identique.
Jesse C. Slicer
3

Je pense que cela dépend du compilateur et qu'il est difficile de donner une réponse générale.

SquidScareMe
la source
3

Ma pratique est la suivante:

  • si le type de variable est simple (int, double, ...) je préfère la variante b (à l'intérieur).
    Motif: réduction de la portée de la variable.

  • si le type de variable n'est pas simple (une sorte de classou struct) je préfère la variante a (extérieur).
    Raison: réduction du nombre d'appels ctor-dtor.

graisse
la source
1

Du point de vue des performances, l'extérieur est (beaucoup) mieux.

public static void outside() {
    double intermediateResult;
    for(int i=0; i < Integer.MAX_VALUE; i++){
        intermediateResult = i;
    }
}

public static void inside() {
    for(int i=0; i < Integer.MAX_VALUE; i++){
        double intermediateResult = i;
    }
}

J'ai exécuté les deux fonctions 1 milliard de fois chacune. outside () a pris 65 millisecondes. inside () a pris 1,5 seconde.

Alex
la source
2
Ça devait être la compilation Debug non optimisée alors, hein?
Tomasz Przychodzki
int j = 0 pour (; j <0x3e8; j ++) de cette manière déclaré une fois fois à la fois variable, et non chacun pour le cycle. 2) l'affectation est plus grosse que toutes les autres options. 3) La règle de la meilleure pratique est donc toute déclaration en dehors de l'itération pour.
luka
1

J'ai testé JS avec Node 4.0.0 si quelqu'un est intéressé. La déclaration en dehors de la boucle a entraîné une amélioration des performances de ~ 0,5 ms en moyenne sur 1000 essais avec 100 millions d'itérations de boucle par essai. Je vais donc dire allez-y et écrivez-le de la manière la plus lisible / maintenable qui soit B, imo. Je mettrais mon code dans un violon, mais j'ai utilisé le module Node performance-now. Voici le code:

var now = require("../node_modules/performance-now")

// declare vars inside loop
function varInside(){
    for(var i = 0; i < 100000000; i++){
        var temp = i;
        var temp2 = i + 1;
        var temp3 = i + 2;
    }
}

// declare vars outside loop
function varOutside(){
    var temp;
    var temp2;
    var temp3;
    for(var i = 0; i < 100000000; i++){
        temp = i
        temp2 = i + 1
        temp3 = i + 2
    }
}

// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;

// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varInside()
    var end = now()
    insideAvg = (insideAvg + (end-start)) / 2
}

// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varOutside()
    var end = now()
    outsideAvg = (outsideAvg + (end-start)) / 2
}

console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)
user137717
la source
0

A) est une valeur sûre que B) ......... Imaginez si vous initialisez la structure en boucle plutôt que «int» ou «float» alors quoi?

comme

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

Vous êtes certainement lié à des problèmes de fuites de mémoire !. Par conséquent, je pense que «A» est un pari plus sûr tandis que «B» est vulnérable à l'accumulation de mémoire, notamment en travaillant dans les bibliothèques de sources proches.

enthousiaste
la source
0

C'est une question intéressante. D'après mon expérience, il y a une question ultime à considérer lorsque vous débattez de cette question pour un code:

Y a-t-il une raison pour laquelle la variable devrait être globale?

Il est logique de ne déclarer la variable qu'une seule fois, globalement, par opposition à plusieurs fois localement, car elle est meilleure pour organiser le code et nécessite moins de lignes de code. Cependant, si elle doit seulement être déclarée localement dans une méthode, je l'initialiserais dans cette méthode afin qu'il soit clair que la variable est exclusivement pertinente pour cette méthode. Veillez à ne pas appeler cette variable en dehors de la méthode dans laquelle elle est initialisée si vous choisissez cette dernière option - votre code ne saura pas de quoi vous parlez et signalera une erreur.

De plus, en remarque, ne dupliquez pas les noms de variables locales entre différentes méthodes même si leurs objectifs sont presque identiques; ça devient confus.

Joshua Siktar
la source
1
lol je suis en désaccord pour tant de raisons ... Cependant, pas de vote négatif ... Je respecte votre droit de choisir
Grantly
0

c'est la meilleure forme

double intermediateResult;
int i = byte.MinValue;

for(; i < 1000; i++)
{
intermediateResult = i;
System.out.println(intermediateResult);
}

1) a ainsi déclaré une fois fois les deux variables, et non chacune pour le cycle. 2) l'affectation est plus grosse que toutes les autres options. 3) La règle de la meilleure pratique est donc toute déclaration en dehors de l'itération pour.

luka
la source
0

J'ai essayé la même chose dans Go, et j'ai comparé la sortie du compilateur en utilisant go tool compile -S avec go 1.9.4

Zéro différence, selon la sortie de l'assembleur.

SteveOC 64
la source
0

J'avais cette même question depuis longtemps. J'ai donc testé un morceau de code encore plus simple.

Conclusion: pour de tels cas, il n'y a PAS différence de performance.

Cas de boucle extérieure

int intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i+2;
    System.out.println(intermediateResult);
}

Étui à boucle intérieure

for(int i=0; i < 1000; i++){
    int intermediateResult = i+2;
    System.out.println(intermediateResult);
}

J'ai vérifié le fichier compilé sur le décompilateur d'IntelliJ et pour les deux cas, j'ai obtenu le même Test.class

for(int i = 0; i < 1000; ++i) {
    int intermediateResult = i + 2;
    System.out.println(intermediateResult);
}

J'ai également démonté le code pour les deux cas en utilisant la méthode indiquée dans cette réponse . Je ne montrerai que les parties pertinentes pour la réponse

Cas de boucle extérieure

Code:
  stack=2, locals=3, args_size=1
     0: iconst_0
     1: istore_2
     2: iload_2
     3: sipush        1000
     6: if_icmpge     26
     9: iload_2
    10: iconst_2
    11: iadd
    12: istore_1
    13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    16: iload_1
    17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
    20: iinc          2, 1
    23: goto          2
    26: return
LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13      13     1 intermediateResult   I
            2      24     2     i   I
            0      27     0  args   [Ljava/lang/String;

Étui à boucle intérieure

Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: sipush        1000
         6: if_icmpge     26
         9: iload_1
        10: iconst_2
        11: iadd
        12: istore_2
        13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        20: iinc          1, 1
        23: goto          2
        26: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13       7     2 intermediateResult   I
            2      24     1     i   I
            0      27     0  args   [Ljava/lang/String;

Si vous y prêtez attention, seuls les éléments Slotaffectés à iet intermediateResultdans LocalVariableTablesont échangés en tant que produit de leur ordre d'apparition. La même différence de slot se reflète dans les autres lignes de code.

  • Aucune opération supplémentaire n'est en cours
  • intermediateResult est toujours une variable locale dans les deux cas, il n'y a donc pas de différence de temps d'accès.

PRIME

Les compilateurs font une tonne d'optimisation, regardez ce qui se passe dans ce cas.

Cas de travail zéro

for(int i=0; i < 1000; i++){
    int intermediateResult = i;
    System.out.println(intermediateResult);
}

Zéro travail décompilé

for(int i = 0; i < 1000; ++i) {
    System.out.println(i);
}
twitu
la source
-1

Même si je sais que mon compilateur est assez intelligent, je n'aimerai pas m'y fier et utiliserai la variante a).

La variante b) n'a de sens pour moi que si vous avez désespérément besoin de rendre le résultat intermédiaire non disponible après le corps de la boucle. Mais je ne peux pas imaginer une situation aussi désespérée, de toute façon ...

EDIT: Jon Skeet a fait un très bon point, montrant que la déclaration de variable à l'intérieur d'une boucle peut faire une réelle différence sémantique.

Abgan
la source