2015-11-10 1 views
1

Juste un petit mot à propos de Java, et permettez-moi de le dire d'abord: je ne suis pas inquiet à cause de l'optimisation des performances, juste curieux de savoir sur les choses en coulisses en général. Depuis que je ne trouve rien à ce que je suppose qu'ils sont équivalents à tous égards, mais juste pour être sûr:Appel de fonction avec i + 1, ++ i différence d'implémentation?

Dans une fonction récursive foo(int i), ou je suppose que dans generel dans un appel de fonction, est-il une différence entre foo(++i) et foo(i + 1)? Je sais que le résultat est le même, mais je pensais que celui avec ++i pourrait faire un extrawork en arrière-plan parce que c'est une tâche? Donc peut-être cela crée-t-il un (anonymous field) = i en arrière-plan qui est incrémenté puis référencé? Ou est-ce que le compilateur optimise simplement ++i à i + 1?

Ma compréhension est un peu floue comme vous le voyez, donc de l'aide serait appréciée.

EDIT pour clarifier:

Au début, cela était dû à une fonction récursive par exemple

public static int foo(int i) { 
     return (i >= 4) ? 0 
      : i + foo(++i); 
    } 

Les fonctions « en général » -part est venu en train d'écrire la question et, comme le remarque, rend cette ambiguïté, etc. Espérons que cela clarifie tout.

+1

Je vous suggère d'utiliser http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html sur deux classes simples (ou sur une classe avec deux méthodes) chacun avec l'un des les deux approches, vous aurez une meilleure compréhension de la chose plutôt que de lire une réponse ici ^^ –

+1

le compilateur JIT pourrait en effet faire une telle optimisation sur la base que 'I' est mort après l'augmentation. Qu'il fasse réellement une telle optimisation est une autre question. Le compilateur Java-to-bytecode n'optimisera pas cela pour vous, cependant; l'incrément sera dans le bytecode. C'est 100% jusqu'au compilateur JIT pour optimiser. –

+0

* "Je sais que le résultat est le même" * Dépend du code. Si vous accédez à "i" plus d'une fois, le résultat n'est pas le même. – Tom

Répondre

2

Si la réponse ne porte pas sur la sémantique mais sur les performances au niveau de la machine après que l'IR a été optimisé, traduit en instructions machine, je dirais, "non sauf si mesuré et prouvé autrement."

Il est très peu probable qu'il y aura une différence de performance après toutes les optimisations sont faites entre f(++i) et f(i+1), en supposant que votre code est telle que vous pouvez réellement considérer comme des solutions de rechange (en supposant l'état de i cesse de devenir pertinentes après la appel de fonction). C'est juste le matériel de base et la conception du compilateur, le coût des instructions pour la mémoire déjà stockées dans un registre, la simplicité d'optimiser ce code au même code machine même dans un compilateur semi-compétent (et je pense que Java JIT serait au moins cela). Il est très fondamental qu'un compilateur reconnaisse les effets secondaires inutiles et les élimine complètement (en fait, l'une des raisons pour lesquelles les tests de niveau micro peuvent être trompeurs, à moins qu'ils ne soient écrits très soigneusement de manière à empêcher l'optimiseur de sauter du code carrément). Parmi les effets secondaires les plus faciles à éliminer, il y a un cas comme celui-ci, où nous incrémentons une variable, i mais pas en fonction du changement d'état par la suite.

Il semble peu probable d'avoir un réel impact sur les performances. Bien sûr, l'ultime moyen est de regarder le code machine final résultant (pas le code IR du bytecode mais le code machine final réel) ou de le mesurer et de le profiler. Mais je serais assez choqué, pour dire le moins, si l'un est plus rapide que l'autre, et cela tendrait à me faire penser que le compilateur fait un mauvais travail dans la sélection d'instruction ou l'allocation de registre.

Cela dit, si l'on était en réalité (la chance à distance) plus vite que l'autre, je pense que vous l'avez en arrière. ++i nécessiterait probablement moins de travail car il peut simplement incrémenter une valeur dans un registre. ++i est une opération unaire sur un opérande, et cela fonctionne bien avec les registres qui sont mutables. i + 1 est une expression qui exige que i être traité comme immuable et demanderait un second registre (mais seulement dans une sorte vraiment horrible scénario du compilateur de jouets qui n'optimiser quoi que ce soit, bien que dans ce cas, nous compilez aussi cette expression dans le contexte d'un appel de fonction et doit considérer des choses comme des renversements de pile, ainsi même un compilateur de jouet pourrait les rendre quelque peu équivalents).

0

Si i est utilisé après foo(++i), la valeur aura été modifiée

Si vous traitez i après foo(i + 1), la valeur n'aura pas été modifiée

foo(int i) 
{ 
    //process stuff A 
    foo(i + 1); 
    //process stuff B 
} 

bar(int i) 
{ 
    //process stuff A 
    bar(i + 1); 
    //process stuff B 
} 

Selon ce qui est dans //process stuff B, vous peut soit vouloir utiliser i + 1 et avoir la valeur de i être le même, soit ++i et avoir i être incrémenté

+0

Cela ne répond pas à la question de l'OP, qui est de savoir si le '++ i' est optimisé pour' i + 1' sur la base que 'i' est mort après l'appel. Bien sûr, l'OP sait déjà ce que «++ i» fait. –

+0

@ ChrisJester-Young cela dépend complètement de ce qu'on appelle après la boucle récursive – phflack

+0

Bien sûr que oui. Mais la question de l'OP est centrée sur le cas 'i'-is-dead-after-' ++ i'. –

2

Il n'y a pas de différence dans l'exécution de foo(++i) et foo(i + 1): la valeur du paramètre passée à foo est la même.

Cependant, dans le premier cas, la variable locale i est augmentée de un; dans ce dernier cas, il conserve sa valeur d'origine.

Donc, si vous faites la même méthode appel deux fois, il y a une différence:

foo(++i); 
foo(++i); // Invoke with i 1 greater than before. 

est différent de

foo(i + 1); 
foo(i + 1); // Invoke with the same argument 

En tant que tel, en général, le compilateur ne peut optimiser tout de suite, parce que ils ont une sémantique différente.

+0

@Downvoter: vous voulez expliquer? –

+0

Cela ne répond pas à la question de l'OP, qui est de savoir si le '++ i' est optimisé pour' i + 1' sur la base que 'i' est mort après l'appel. Bien sûr, l'OP sait déjà ce que «++ i» fait. –

+0

Aussi, un peu de patience s'il vous plaît: je downvote d'abord et expliquer plus tard, pas l'inverse. ;-) –

2

À côté des autres commentaires sur la valeur de i après avoir utilisé l'incrément de préfixe. Il y a une petite différence dans le bytecode généré. Voir ci-dessous les résultats simplifiés.

foo (++ i) compilé

iinc 
iload_1 
invokestatic 

foo (i + 1) Compilés à

iload_1            
iconst_1            
iadd             
invokestatic 
+0

Le bytecode est bien sûr différent. 'javac' n'optimise généralement pas le bytecode généré; il laisse le travail d'optimisation au compilateur JIT. La question d'intérêt est de savoir si le compilateur JIT optimise réellement l'incrément. –

1

oui, ++ i se compose de deux opérations d'addition et assignment.Since, vous Je n'ai pas besoin de plus tard, i + 1 est préférable à utiliser plutôt que ++ i.