2013-07-07 5 views
3

Java 5 nous a donné les boucles for-each, qui devraient être utilisées là où c'est possible.Quel idiome pour itérer un tableau est le plus efficace?

Mais quel est l'idiome le plus efficace si vous avez besoin d'utiliser l'index du tableau dans le bloc?

// Option (1) 
for (int i = array.length - 1; i >= 0; i--) { 
    // Code. 
} 

// Option (2) 
for (int i = 0; i < array.length; i++) { 
    // Code. 
} 

// Option (3) 
for (int i = 0, n = array.length; i < n; i++) { 
    // Code. 
} 

(De toute évidence, cela ne fera pas une grande différence de performance dans la plupart des programmes, mais l'humour moi.) :-)

  1. boucles en arrière et est hideux. Peut-être même cache hostile? Ou le transformateur moderne peut-il détecter la marche arrière dans la mémoire?

  2. est plus courte et je peux voir comment le compilateur JIT peut comprendre que array ne change jamais et donc length est constante, de sorte qu'il peut essentiellement le remplacer par (3). Mais fait-il cela? (Supposons que la machine virtuelle Java est Hotspot/Java Oracle 7.)

  3. est suggérée par Joshua Bloch au point 45 Effective Java comme l'option la plus rapide si elle est une Collection.size() qui est la limite supérieure. Mais s'applique-t-il également aux tableaux? Du bytecode (voir ci-dessous) je peux voir celui-là enregistrer une instruction arraylength par cycle (pré-optimisé).

This question au sujet des boucles dans la machine virtuelle Dalvik, listes (1) - (3) le plus rapide au plus lent. Cependant, l'information date de 2008 et Dalvik est beaucoup plus mature aujourd'hui, donc je ne pense pas que ce soit toujours le cas.

En regardant le bytecode généré à partir des exemples ci-dessus, il existe des différences évidentes:

Compiled from "ForLoops.java" 
public class ForLoops extends java.lang.Object{ 
static int[] array; 

public ForLoops(); 
    Code: 
    0: aload_0 
    1: invokespecial #10; //Method java/lang/Object."<init>":()V 
    4: return 

public static void forLoop1(); 
    Code: 
    0: getstatic #17; //Field array:[I 
    3: arraylength 
    4: iconst_1 
    5: isub 
    6: istore_0 
    7: goto 13 
    10: iinc 0, -1 
    13: iload_0 
    14: ifge 10 
    17: return 

public static void forLoop2(); 
    Code: 
    0: iconst_0 
    1: istore_0 
    2: goto 8 
    5: iinc 0, 1 
    8: iload_0 
    9: getstatic #17; //Field array:[I 
    12: arraylength 
    13: if_icmplt 5 
    16: return 

public static void forLoop3(); 
    Code: 
    0: iconst_0 
    1: istore_0 
    2: getstatic #17; //Field array:[I 
    5: arraylength 
    6: istore_1 
    7: goto 13 
    10: iinc 0, 1 
    13: iload_0 
    14: iload_1 
    15: if_icmplt 10 
    18: return 

} 
+0

En théorie, 1 et 3 sont plus efficaces que 2, dans le cas général, puisque 'array.length' n'a besoin d'être évalué qu'une seule fois. Cependant, l'évaluation de 'longueur 'en Java est assez simple, surtout si JITCed, et ainsi la différence théorique disparaît pratiquement dans la pratique. Très probablement, cela dépend de ce qui se trouve dans le corps de la boucle et comment l'optimiseur JITC fusionne le contrôle de boucle avec le corps de la boucle. –

+0

@Nicholas Beau travail tirant le bytecode! Mais il est important de garder à l'esprit que le bytecode n'est finalement pas ce qui est exécuté en raison du JIT. Si nous pouvions obtenir des instructions JITed, cela * serait * la réponse finale, mais c'est probablement irréaliste car (a) il est spécifique à la plateforme, et (b) il y a différents "niveaux" de JITing. De plus, je ne connais pas de JIT qui donne de la transparence sur le code généré pendant l'exécution, du moins pas sans fouiller vous-même dans l'espace mémoire de la JVM. – sigpwned

+0

Il va sans dire que le choix "le plus efficace" est généralement celui qui correspond le mieux au reste du code. Par exemple, itérer à l'envers est parfois gênant et parfois très utile. De même, vérifier 'array.length' à chaque itération peut être requis, si l'objet tableau est remplacé dans la boucle. –

Répondre

1

Vous pouvez facilement tester par vous-même; Si vous le faites, vous devriez probablement jeter un oeil à these tips about performance testing des créateurs HotSpot eux-mêmes. This answer peut être utile aussi. Si vous décidez de tester ces implémentations, faites-nous savoir ce que vous trouvez!

Cependant, dans l'ensemble, vous ne devriez pas trop vous inquiéter de ces choses. Au lieu de cela, se concentrer sur l'écriture de code lisible et faire avancer les choses. La plupart du temps, vous constaterez que votre code s'exécute assez vite sans "trucs". Le matériel moderne est très rapide, et le JIT est plutôt bon.

Si vous constatez que votre code est trop lent, commencez par profile, puis optimisez. Tout le reste est prématuré. Et rappelez-vous, d'un homme qui est plus intelligent que nous tous:

« L'optimisation prématurée est la racine de tout le mal. » - Donald Knuth

EDIT: Puisque vous semblez curieux à ce sujet moins en termes de « comment devrais-je écrire mon code? » et plus en termes d'une expérience de pensée, je attendre toutes ces options pour fonctionner à des vitesses plus ou moins identiques.

Aucune de ces boucles n'est susceptible de modifier le prédicteur de branche (pour des ensembles de taille raisonnable, de toute façon). Je m'attendrais à ce que le JIT sous-jacent transforme toute référence de longueur de tableau récurrente de style (2) en style (3).Toutes choses étant égales par ailleurs, les performances du cache (1) ne sont pas plus mauvaises que celles de (2) ou (3) simplement parce qu'il fonctionne en arrière; pour un tableau donné, les mêmes lignes de cache seront chargées et frappées (ou pas) tout aussi souvent.

Bien sûr, mes attentes ne sont pas pertinentes. La seule façon de savoir est de tester! Lors des tests, cependant, rappelez-vous que writing good microbenchmarks is hard.

+1

+1 ... a édité mon commentaire :) –

+1

Je pourrais le tester, mais je préférerais avoir un aperçu général de la façon dont la JVM fonctionne. Supposons également que la performance soit critique. – Nicholas

+0

@Nicholas Je pense que vous vouliez écrire du code en langage C mais on vous a demandé d'utiliser Java :) –

Questions connexes