91 votes

Déclarer plusieurs baies avec 64 éléments 1000 fois plus rapide que de déclarer le tableau de 65 éléments

Récemment, j'ai remarqué que le fait de déclarer un tableau contenant 64 éléments est beaucoup plus rapide (>1000 fois) que de déclarer le même type de tableau avec 65 éléments.

Voici le code que j'ai utilisé pour tester ceci:

public class Tests{
    public static void main(String args[]){
        double start = System.nanoTime();
        int job = 100000000;//100 million
        for(int i = 0; i < job; i++){
            double[] test = new double[64];
        }
        double end = System.nanoTime();
        System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
    }
}

Cela fonctionne dans environ 6 ms, si je remplace new double[64] avec new double[65] , il faut environ 7 secondes. Ce problème devient d'autant plus grave si le travail est réparti sur plus et plus de threads, qui est l'endroit où mon problème provient.

Ce problème se produit également avec différents types de tableaux tels que int[65] ou String[65]. Ce problème ne se produit pas avec les grandes chaînes de caractères: String test = "many characters";, mais ne commencez produisent lorsque cela est changé en String test = i + "";

Je me demandais pourquoi c'est le cas et si il est possible de contourner ce problème.

88voto

nosid Points 20267

Vous observez un problème qui est causé par les optimisations effectuées par le compilateur JIT de votre machine virtuelle Java. Ce problème est reproductible déclenché scalaires tableaux jusqu'à 64 éléments, et n'est pas déclenchée avec les tableaux de plus de 64 ans.

Avant de rentrer dans les détails, nous allons regarder de plus près le corps de la boucle:

double[] test = new double[64];

Le corps n'a pas d'effet (comportements observables). Cela signifie qu'il ne fait pas de différence à l'extérieur de l'exécution du programme si cette instruction est exécutée ou non. Le même est vrai pour l'ensemble de la boucle. Donc il se peut que le code de l'optimiseur se traduit par la boucle de quelque chose (ou rien) avec la même fonctionnelle et chronologique de comportement.

Pour les repères, vous devriez au moins respecter les présentes directives. Si vous l'avait fait, la différence aurait été nettement plus petit.

  • Warm-up le compilateur JIT (et optimizer) par l'exécution de l'indice de référence, à plusieurs reprises.
  • Utiliser le résultat de chaque expression et de l'imprimer à la fin de la référence.

Maintenant, nous allons entrer dans les détails. Il n'est pas surprenant, il s'agit d'une optimisation qui est déclenchée pour des scalaires tableaux pas plus de 64 éléments. L'optimisation fait partie de la Échapper à l'analyse. Il met les petits objets et petits tableaux sur la pile au lieu de leur allouer sur le tas - ou mieux encore, de les optimiser disparaître entièrement. Vous pouvez trouver des informations à ce sujet dans l'article de Brian Goetz écrit en 2005:

L'optimisation peut être désactivé avec l'option de ligne de commande -XX:-DoEscapeAnalysis. La magie de la valeur 64 scalaires tableaux peuvent également être modifié sur la ligne de commande. Si vous exécutez votre programme de la façon suivante, il n'y aura pas de différence entre les tableaux avec 64 et 65 éléments:

java -XX:EliminateAllocationArraySizeLimit=65 Tests

Cela dit, je déconseille vivement à l'aide de ces options de ligne de commande. Je doute que cela fait une énorme différence dans la façon réaliste. Je tiens seulement à l'utiliser, si je voulais être absolument convaincu de la nécessité - et non pas sur la base des résultats de certains pseudo repères.

2voto

Hot Licks Points 25075

Il y a un certain nombre de façons qu'il peut y avoir une différence, basée sur la taille d'un objet.

Comme nosid a déclaré, le JITC peut-être (probablement) l'allocation de petites "local" des objets sur la pile, et le seuil de taille pour les "petits" tableaux peuvent être à 64 éléments.

L'allocation sur la pile est nettement plus rapide que l'allocation dans le tas, et, plus au point, la pile n'a pas besoin d'être nettoyée, donc GC surcharge est considérablement réduit. (Et pour ce cas test GC-dessus de nous est susceptible de 80 à 90% du temps d'exécution total.)

De plus, une fois que la valeur est pile-attribué le JITC pouvez effectuer "l'élimination du code mort", de déterminer que le résultat de l' new n'est jamais utilisé n'importe où, et, après l'assurance il n'y a pas d'effets secondaires qui seraient perdus, d'éliminer la totalité de l' new de l'opération, puis le (vide) de la boucle elle-même.

Même si le JITC ne fait pas de l'allocation de pile, il est tout à fait possible pour des objets plus petits que d'une certaine taille pour être affectés dans un tas différemment (par exemple, à partir d'un autre "espace") que des objets plus grands. (Normalement, ce ne serait pas produire assez dramatique des différences de calendrier, cependant).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X