2010-04-09 7 views
8

J'ai les deux programmes:vitesse Iteration de int vs longue

long startTime = System.currentTimeMillis(); 
for (int i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

et

long startTime = System.currentTimeMillis(); 
for (long i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

Note: la seule différence est le type de la variable de boucle (int et long).

Lorsque j'exécute cela, le premier programme imprime systématiquement entre 0 et 16 ms, indépendamment de la valeur de N. La seconde prend beaucoup plus de temps. Pour N == Integer.MAX_VALUE, il fonctionne à environ 1800 msecs sur ma machine. La durée d'exécution semble être plus ou moins linéaire dans N.

Alors pourquoi est-ce?

Je suppose que le compilateur JIT optimise la boucle int à la mort. Et pour cause, car évidemment, ça ne fait rien. Mais pourquoi ne le fait-il pas aussi pour la boucle long? Un collègue a pensé que nous pourrions mesurer le compilateur JIT en train de faire son travail dans la boucle long, mais comme le temps d'exécution semble être linéaire dans N, ce n'est probablement pas le cas.

J'utilise JDK 1.6.0 update 17:

C:\>java -version 
java version "1.6.0_17" 
Java(TM) SE Runtime Environment (build 1.6.0_17-b04) 
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode) 

Je suis sous Windows XP Professionnel Édition x64 Service Pack 2, avec un processeur Intel Core2 Quad CPU à 2,40 GHz.


AVERTISSEMENT

Je sais que microbenchmarks ne sont pas utiles dans la production. Je sais aussi que System.currentTimeMillis() n'est pas aussi précis que son nom l'indique. C'est juste quelque chose que j'ai remarqué en flânant, et j'étais simplement curieux de savoir pourquoi cela se produit; rien de plus.

+0

Je suis absolument d'accord avec @Andrzej ici, mais si elle est vraiment juste pour l'amour de la curiosité, vous pouvez utiliser le plugin PrintAssembly pour vraiment regarder le code généré: http://wikis.sun.com/display/HotSpotInternals/PrintAssembly –

+0

Que se passe-t-il si vous utilisez le drapeau -Xint? Cela empêche la compilation de Hotspot, donc vous obtiendrez une meilleure comparaison. –

+0

@Steven: mais avec '-Xint' le résultat est * encore moins * significatif pour tout ce qui ressemble à un usage réel. –

Répondre

5

C'est une question intéressante, mais pour être honnête, je ne suis pas convaincu qu'étant donné le comportement de Hotspot ici, vous obtiendrez des informations utiles. Toutes les réponses que vous obtenez ne seront pas transférables dans un cas général (parce que nous examinons les optimisations que Hotspot effectue dans une situation spécifique), ainsi ils vous aideront à comprendre pourquoi une no-op est plus rapide qu'une autre, mais ils ne vous aideront pas à écrire plus rapidement les "vrais" programmes.

Il est également incroyablement facile d'écrire des micro-benchmarks très trompeurs autour de ce genre de chose - voir this IBM DW article pour certains des pièges courants, comment les éviter et quelques commentaires généraux sur ce que vous faites.

Donc vraiment c'est une réponse "sans commentaire", mais je pense que c'est la seule réponse valide.Une boucle no-op de compilation-temps-trivial n'a pas besoin de pour être rapide, de sorte que le compilateur n'est pas optimisé pour être rapide dans certaines de ces conditions.

+0

Vous avez bien sûr raison. La raison pour laquelle je demande est une pure curiosité à propos de quelque chose que j'ai remarqué en flânant; Je n'ai pas l'intention de le faire dans un vrai programme. :) – jqno

4

Vous utilisez probablement une machine virtuelle Java 32 bits. Les résultats seront probablement différents avec une JVM 64 bits. Dans une machine virtuelle Java 32 bits, un entier peut être mappé à un entier 32 bits natif et être incrémenté avec une seule opération. La même chose ne tient pas longtemps, ce qui nécessitera plus d'opérations à incrémenter.

Voir ce question pour une discussion sur les tailles int et longues.

+0

Bon point. Cependant, j'utilise une JVM 64 bits. Je vais mettre à jour la question. – jqno

+0

Il sera également utile de poster le système d'exploitation et quelques détails sur votre configuration matérielle (principalement le CPU). – kgiannakakis

3

Je pense - et il est seulement une estimation - est la suivante:

La machine virtuelle Java conclut que la première boucle fait effectivement rien, il supprime complètement. Aucune variable "ne s'échappe" de la boucle for.

Dans le second cas, la boucle ne fait rien non plus. Mais il se peut que le code JVM qui détermine que la boucle ne fasse rien ait une clause "if (type of i) == int". Dans ce cas, l'optimisation pour supprimer la boucle for-do ne fonctionne qu'avec int.

Une optimisation qui supprime du code doit être sûre qu'il n'y a pas d'effets secondaires. Les codeurs JVM semblent avoir fait preuve de prudence.

1

Le micro-benchmarking comme celui-ci n'a pas beaucoup de sens, car les résultats dépendent beaucoup du fonctionnement interne du Hotspot JIT. Notez également que les valeurs d'horloge système obtenues avec System.currentTimeMillis() n'ont pas une résolution de 1 ms sur tous les systèmes d'exploitation. Vous ne pouvez pas l'utiliser pour très précisément chronométrer des événements de très courte durée.

Jetez un oeil à cet article, qui explique pourquoi faire des micro-benchmarks Java est pas aussi facile que la plupart des gens pensent: Anatomy of a flawed microbenchmark