Question

Existe-t-il une différence de performances entre les boucles for d'un tableau primitif?

Supposons:

double[] doubleArray = new double[300000];


for (double var: doubleArray) 
   someComplexCalculation(var);

ou:

for ( int i = 0, y = doubleArray.length; i < y; i++)
   someComplexCalculation(doubleArray[i]);

Résultat du test

Je l'ai en fait profilé:

Total timeused for modern loop= 13269ms
Total timeused for old loop   = 15370ms

La boucle moderne fonctionne donc plus vite, du moins sur ma JVM Mac OSX 1.5.

Était-ce utile?

La solution

Votre manuscrit, "ancien". formulaire exécute moins d'instructions et peut être plus rapide, bien que vous deviez le profiler sous un compilateur JIT donné pour le savoir avec certitude. Le " nouveau " la forme est définitivement pas plus rapide.

Si vous examinez le code désassemblé (compilé par le kit JDK 1.5 de Sun), vous verrez que le "nouveau" formulaire est équivalent au code suivant:

1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3:   double var = tmp[i];
4:   someComplexCalculation(var);
5: }

Ainsi, vous pouvez voir que davantage de variables locales sont utilisées. L'affectation de doubleArray à tmp à la ligne 1 correspond à "extra", mais ne se produit pas dans la boucle et ne peut probablement pas être mesurée. L'affectation à var à la ligne 3 est également extra. S'il y a une différence de performance, ce sera responsable.

La ligne 1 peut sembler inutile, mais il est normal de mettre en cache le résultat si le tableau est calculé par une méthode avant d'entrer dans la boucle.

Cela dit, j'utiliserais le nouveau formulaire, sauf si vous devez utiliser la variable d'index. Toute différence de performances est susceptible d'être optimisée par le compilateur JIT lors de l'exécution, et le nouveau formulaire est plus clair. Si vous continuez à le faire "à la main", vous risquez de manquer les optimisations futures. En règle générale, un bon compilateur peut optimiser "stupide". codez bien, mais trébuchez sur "intelligent" code.

Autres conseils

Mon avis est que vous ne savez pas et ne devez pas deviner. Essayer de déjouer les compilateurs ces jours-ci est stérile.

Il y a eu des moments où les gens ont appris "Patterns". cela semblait optimiser certaines opérations, mais dans la prochaine version de Java, ces modèles étaient en réalité plus lents.

Écrivez-le toujours aussi clairement que possible et ne vous préoccupez pas de l'optimisation jusqu'à ce que vous ayez certaines spécifications de l'utilisateur dans votre main et que vous ne remplissiez pas certaines exigences, et même dans ce cas, soyez très attentif à l'exécution avant et après les tests. veillez à ce que votre " réparer " effectivement amélioré assez pour faire passer cette exigence.

Le compilateur peut faire des choses étonnantes qui feraient vraiment sauter vos chaussettes, et même si vous effectuez des tests qui itèrent sur une plage étendue, cela peut fonctionner complètement différemment si vous avez une plage réduite ou si vous modifiez ce qui se passe dans la boucle. .

La compilation "juste à temps" signifie qu’elle peut parfois surpasser C et qu’il n’existe aucune raison pour laquelle il ne peut pas surpasser le langage assembleur statique (l’assemblage ne peut pas déterminer à l’avance qu’un appel n’est pas requis, mais parfois Java rien que ça.

Pour résumer: le plus grand intérêt que vous puissiez mettre dans votre code est de l'écrire pour qu'il soit lisible.

Pourquoi ne pas le mesurer vous-même?

Cela semble un peu dur, mais ce genre de questions est très facile à vérifier vous-même.

Créez simplement le tableau et exécutez chaque boucle 1000 fois ou plus, et mesurez la durée. Répétez plusieurs fois pour éliminer les problèmes.

Il n'y a pas de différence. Java transformera le for amélioré en un for normal. L'amélioré pour est juste un "sucre de syntaxe". Le bytecode généré est le même pour les deux boucles.

Je suis devenu très curieux de votre question, même après ma réponse précédente. Alors j'ai décidé de vérifier moi-même aussi. J'ai écrit ce petit morceau de code (veuillez ignorer l'exactitude mathématique pour vérifier si un nombre est premier; -)):

public class TestEnhancedFor {

    public static void main(String args[]){
        new TestEnhancedFor();
    }

    public TestEnhancedFor(){
        int numberOfItems = 100000;
        double[] items = getArrayOfItems(numberOfItems);
        int repetitions = 0;
        long start, end;

        do {
            start = System.currentTimeMillis();
            doNormalFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Normal For. Repetition %d: %d\n", 
                    repetitions, end-start);

            start = System.currentTimeMillis();
            doEnhancedFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Enhanced For. Repetition %d: %d\n\n", 
                    repetitions, end-start);

        } while (++repetitions < 5);
    }

    private double[] getArrayOfItems(int numberOfItems){
        double[] items = new double[numberOfItems];
        for (int i=0; i < numberOfItems; i++)
            items[i] = i;
        return items;
    }

    private void doSomeComplexCalculation(double item){
        // check if item is prime number
        for (int i = 3; i < item / 2; i+=2){
            if ((item / i) == (int) (item / i)) break;
        }
    }

    private void doNormalFor(double[] items){
        for (int i = 0; i < items.length; i++)
            doSomeComplexCalculation(items[i]);
    }

    private void doEnhancedFor(double[] items){
        for (double item : items)
            doSomeComplexCalculation(item);
    }

}

L'exécution de l'application a donné les résultats suivants:

  

Normal pour. Répétition 0: 5594   Amélioré pour. Répétition 0: 5594

     

Normal pour. Répétition 1: 5531   Amélioré pour. Répétition 1: 5547

     

Normal pour. Répétition 2: 5532   Amélioré pour. Répétition 2: 5578

     

Normal pour. Répétition 3: 5531   Amélioré pour. Répétition 3: 5531

     

Normal pour. Répétition 4: 5547   Amélioré pour. Répétition 4: 5532

Comme nous pouvons le constater, la variation entre les résultats est très faible et parfois la boucle normale est plus rapide, parfois la boucle améliorée est plus rapide. Puisqu'il y a d'autres applications ouvertes sur mon ordinateur, je trouve cela normal. De plus, seule la première exécution est plus lente que les autres - je pense que cela a à voir avec les optimisations JIT.

Les temps moyens (à l’exception de la première répétition) sont de 5535,25ms pour la boucle normale et de 5547ms pour la boucle améliorée. Mais nous pouvons voir que les meilleures durées de fonctionnement pour les deux boucles sont les mêmes (5531ms). Je pense donc que nous pouvons en venir à la conclusion que les deux boucles ont les mêmes performances - et que les variations dans le temps écoulé sont dues à d’autres applications (même l’OS) de la machine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top