web-dev-qa-db-fra.com

Grande différence de vitesse des méthodes statiques et non statiques équivalentes

Dans ce code, lorsque je crée un objet dans la méthode main et que j'appelle ensuite cette méthode d'objets: ff.twentyDivCount(i) (s'exécute en 16010 ms), il s'exécute beaucoup plus rapidement que de l'appeler à l'aide de cette annotation: twentyDivCount(i) (s'exécute en 59516 ms). Bien sûr, lorsque je l'exécute sans créer d'objet, je rend la méthode statique, elle peut donc être appelée de manière principale.

public class ProblemFive {

    // Counts the number of numbers that the entry is evenly divisible by, as max is 20
    int twentyDivCount(int a) {    // Change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i<21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        ProblemFive ff = new ProblemFive();

        for (int i = start; i > 0; i--) {

            int temp = ff.twentyDivCount(i); // Faster way
                       // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}

EDIT: Jusqu'à présent, il semble que différentes machines produisent des résultats différents, mais l'utilisation de JRE 1.8. * Est l'endroit où le résultat d'origine semble être reproduit de manière cohérente.

86
Stabbz

En utilisant JRE 1.8.0_45, j'obtiens des résultats similaires.

Enquête:

  1. en cours d'exécution Java avec le -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining VM montrent que les deux méthodes sont compilées et intégrées
  2. L'examen de l'assembly généré pour les méthodes elles-mêmes ne montre aucune différence significative
  3. Une fois qu'ils sont en ligne, cependant, l'assembly généré dans main est très différent, la méthode d'instance étant optimisée de manière plus agressive, en particulier en termes de déroulement de boucle

J'ai ensuite relancé votre test mais avec différents paramètres de déroulement de boucle pour confirmer les soupçons ci-dessus. J'ai exécuté votre code avec:

  • -XX:LoopUnrollLimit=0 et les deux méthodes s'exécutent lentement (similaire à la méthode statique avec les options par défaut).
  • -XX:LoopUnrollLimit=100 et les deux méthodes s'exécutent rapidement (similaire à la méthode d'instance avec les options par défaut).

En conclusion, il semble qu'avec les paramètres par défaut, le JIT du hotspot 1.8.0_45 ne soit pas en mesure de dérouler la boucle lorsque la méthode est statique (même si je ne sais pas pourquoi elle se comporte de cette façon ). D'autres machines virtuelles Java peuvent donner des résultats différents.

72
assylias

Juste une supposition non prouvée basée sur une réponse d'assylias.

La machine virtuelle Java utilise un seuil pour le déroulement de la boucle, qui est quelque chose comme 70. Pour une raison quelconque, l'appel statique est légèrement plus grand et ne se déroule pas.

Mettre à jour les résultats

  • Avec le LoopUnrollLimit dans les 52 ci-dessous, les deux versions sont lentes.
  • Entre 52 et 71, seule la version statique est lente.
  • Au-dessus de 71, les deux versions sont rapides.

C'est étrange car je suppose que l'appel statique est juste légèrement plus grand dans la représentation interne et l'OP a frappé un cas étrange. Mais la différence semble être d'environ 20, ce qui n'a aucun sens.

-XX:LoopUnrollLimit=51
5400 ms NON_STATIC
5310 ms STATIC
-XX:LoopUnrollLimit=52
1456 ms NON_STATIC
5305 ms STATIC
-XX:LoopUnrollLimit=71
1459 ms NON_STATIC
5309 ms STATIC
-XX:LoopUnrollLimit=72
1457 ms NON_STATIC
1488 ms STATIC

Pour ceux qui souhaitent expérimenter, ma version peut être utile.

33
maaartinus

Lorsque cela est exécuté en mode débogage, les nombres sont les mêmes pour l'instance et les cas statiques. Cela signifie en outre que le JIT hésite à compiler le code en code natif dans le cas statique de la même manière que dans le cas de la méthode d'instance.

Pourquoi le fait-il? C'est dur à dire; cela ferait probablement la bonne chose s'il s'agissait d'une application plus large ...

0
Dragan Bozanovic

S'il vous plaît essayez:

public class ProblemFive {
    public static ProblemFive PROBLEM_FIVE = new ProblemFive();

    public static void main(String[] args) {
        long startT = System.currentTimeMillis();
        int start = 500000000;
        int result = start;


        for (int i = start; i > 0; i--) {
            int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way
            // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
                System.out.println((System.currentTimeMillis() - startT) + " ms");
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();
        System.out.println((end - startT) + " ms");
    }

    int twentyDivCount(int a) {  // change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i < 21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }
}
0
chengpohi

J'ai juste légèrement modifié le test et j'ai obtenu les résultats suivants:

Sortie:

Dynamic Test:
465585120
232792560
232792560
51350 ms
Static Test:
465585120
232792560
232792560
52062 ms

[~ # ~] note [~ # ~]

Pendant que je les testais séparément, j'ai obtenu ~ 52 sec pour dynamique et ~ 200 sec pour statique.

Voici le programme:

public class ProblemFive {

    // Counts the number of numbers that the entry is evenly divisible by, as max is 20
    int twentyDivCount(int a) {  // Change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i<21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }

    static int twentyDivCount2(int a) {
         int count = 0;
         for (int i = 1; i<21; i++) {

             if (a % i == 0) {
                 count++;
             }
         }
         return count;
    }

    public static void main(String[] args) {
        System.out.println("Dynamic Test: " );
        dynamicTest();
        System.out.println("Static Test: " );
        staticTest();
    }

    private static void staticTest() {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        for (int i = start; i > 0; i--) {

            int temp = twentyDivCount2(i);

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }

    private static void dynamicTest() {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        ProblemFive ff = new ProblemFive();

        for (int i = start; i > 0; i--) {

            int temp = ff.twentyDivCount(i); // Faster way

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}

J'ai également changé l'ordre du test en:

public static void main(String[] args) {
    System.out.println("Static Test: " );
    staticTest();
    System.out.println("Dynamic Test: " );
    dynamicTest();
}

Et j'ai obtenu ceci:

Static Test:
465585120
232792560
232792560
188945 ms
Dynamic Test:
465585120
232792560
232792560
50106 ms

Comme vous le voyez, si dynamique est appelé avant statique, la vitesse de statique a considérablement diminué.

Sur la base de cette référence:

I émet l'hypothèse que tout dépend de l'optimisation JVM. je vous recommande donc de suivre la règle générale pour l'utilisation de méthodes statiques et dynamiques.

RÈGLE DE POUCE:

Java: quand utiliser des méthodes statiques

0
nafas