7
votes

String Pool: "Te" + "st" plus vite que "Test"?

J'essaie un point de repère de performance concernant la piscine à cordes. Cependant, le résultat n'est pas attendu.

J'ai fait 3 méthodes statiques p>

  • Méthode Sectacle0 () ... Crée un nouvel objet à chaque fois li>
  • EFFORT1 () Méthode ... String Literie "Test" Li>
  • Perform2 () Méthode ... String Expression constante "Te" + "ST" LI> ul>

    Mon attente était (1. Le plus rapide -> 3. Plus lent) P>

    1. "test" à cause de la mise en commun STRING LI>
    2. "te" + "st" à cause de la mise en commun des ficelles, mais un peu plus lentement que 1 à cause de + opérateur li>
    3. nouvelle chaîne (..) En raison de la mise en commun STRING. LI> OL>

      Mais le repère montre que "Te" + "ST" est légèrement plus rapide que "Test". P>

      import java.util.concurrent.TimeUnit;
      
      
      public class StringPoolPerformance {
      
          public static long perform0() {
              long start = System.nanoTime();
              for (int i=0; i<1000000; i++) {
                  String str = new String("Test");
              }
              return System.nanoTime()-start;
          }
      
          public static long perform1() {
              long start = System.nanoTime();
              for (int i=0; i<1000000; i++) {
                  String str = "Test";
              }
              return System.nanoTime()-start;
          }
      
          public static long perform2() {
              long start = System.nanoTime();
              for (int i=0; i<1000000; i++) {
                  String str = "Te"+"st";
              }
              return System.nanoTime()-start;
          }
      
          public static void main(String[] args) {
              long time0=0, time1=0, time2=0;
              for (int i=0; i<100; i++) {
                  // result
                  time0 += perform0();
                  time1 += perform1();
                  time2 += perform2();
              }
      
              System.out.println("new String(): " +  time0 + " ns");
              System.out.println("\"Test\"      : " + time1 + " ns");
              System.out.println("\"Te\"+\"st\"   : " + time2 + " ns");
          }
      }
      


7 commentaires

Le compilateur ne doit-il pas combiner deux constantes?


Strings "Te", "ST" est littéral, alors le compilateur les modifierait-il pour "tester" en phase d'optimisation? Je ne sais pas


Changez la commande que vous appelez Effectuer1 et exécutant2 et voyez si vous obtenez toujours les mêmes résultats.


Il peut y avoir un attente de fil en raison d'un commutateur de contexte


Vous voudrez peut-être lire ceci: Anatomie d'une microbenchmark défectueux


Courir sur OS X 10.8 a des résultats similaires. String Str = "T" + "E" + "S" + "T" est même légèrement plus rapide!


À tout le moins, le programme doit exécuter la même séquence de test une seconde fois et jeter le premier ensemble de résultats, pour que les choses soient assez bien jitées et obtenir le tas "réchauffé". Sans que les résultats sont assez significatifs.


5 Réponses :


10
votes

"te" + "st" code> est une expression constante du temps compilateur, et donc se comportera à l'exécution pas différemment fort> que tout simplement . Tout coup de performance sera lorsque vous essayez de le compiler, pas lorsque vous essayez de l'exécuter.

qui est facilement prouvé en désassemblant votre classe de référence compilée en utilisant javap -c stringpoolformance code>: p>

public static long perform1();
  Code:
...
   7:   ldc #3; //int 1000000
   9:   if_icmpge   21
   12:  ldc #5; //String Test
   14:  astore_3
   15:  iinc    2, 1
...

public static long perform2();
  Code:
...
   7:   ldc #3; //int 1000000
   9:   if_icmpge   21
   12:  ldc #5; //String Test
   14:  astore_3
   15:  iinc    2, 1
...


1 commentaires

La course pour TE + ST est également probablement plus rapide car l'optimisation de la performance1 est immédiatement utilisée par le spectacle2 (puisqu'elles sont exactement les mêmes).



3
votes

Peut-être que le compilateur JIT commence et le troisième exécute du code natif. Peut-être que la concaténation a été déplacée à l'extérieur de la boucle. Peut-être que la concaténation n'est jamais terminée car la variable n'est jamais lue. Peut-être que la différence est le bruit et vos trois échantillons de coïncidence pointe de la même manière.

Benchmarking robuste Java, partie 1: Problèmes Explique beaucoup de façons que la benchmarking Java peut aller mal.

L'analyse comparative est extrêmement difficile. De nombreux facteurs, à la fois évidents et subtils, peuvent affecter vos résultats. Pour obtenir des résultats précis, vous avez besoin d'une commande approfondie de ces problèmes, éventuellement en utilisant un cadre d'analyse comparative qui en aborde. Aller à Partie 2 Pour en savoir plus tellement robuste Cadre de référence Java.

Ne vous attendez pas à ce que les micro-repères du code Java vous disent quelque chose d'utile jusqu'à ce que vous comprenez les pièges spécifiques que l'architecture JVM introduit et ne vous attendez pas à ce que même les meilleurs micro-repères prédisent la performance d'une application réelle.

Je ne sais pas quel est votre objectif, mais apprendre à utiliser un bon profileur et l'utiliser sur votre application réelle vous dira généralement si la ligne en question est vraiment la source d'inefficacité et vous permettra de mesurer l'effet d'un changement de code. Temps passé à apprendre qu'un profileur est probablement mieux dépensé que le temps écrit et déboguer des micro-repères.


5 commentaires

Il n'y a pas de concaténation (runtime).


@Hotlicks, comment savez-vous? La spécification de la langue n'exige pas que les expressions constantes soient pliées par le compilateur lorsqu'il n'est pas utilisé lorsqu'une expression constante est requise.


@Hotlicks, je sais quelle expression constante est, mais il n'y a aucune obligation que Javac est pliant constant dans des contextes où des expressions sont attendues et que l'expression est également une expression constante.


C'est dans la priorité inhérente à la grammaire BNF.


@Hotlicks, vous avez raison (bien que ce ne soit pas inhérent à la grammaire). 15.18.1 Corder la concaténation opérateur dit explicitement "" Le résultat est une référence à un objet de chaîne ( nouvellement créé, à moins que l'expression soit une expression constante de la compilation ) qui est la concaténation des deux Cordes d'opérande "



0
votes

Tout d'abord, il serait bien de savoir que:

Si vous rencontrez des chaînes de concaténage, disons dans une boucle, alors vous savez que parce qu'ils sont immuables, de nouvelles chaînes continuent à être générées. Le compilateur Javac utilise à l'interne un Stringbuffer pour le faire, par exemple, vous avez xxx

dans une boucle.

Que se passe-t-il, dans la boucle, deux objets sont générés. L'un est un stringbuffer - xxx

L'autre est la chaîne qui est attribuée à itemlist via la totring (). `

Source: http: // pensé -Bytes.Blogspot.com/2007/03/java-string-string-performance.html

Je pense que cela ne s'applique pas à votre cas. Dans le premier test de performance, vous créez toujours un nouvel objet afin de créer 1000000 chaîne ("test") objets. Dans les deuxième et troisième exemples, il n'y a créé qu'un seul objet à qui est pointé par de nombreux référencés. Comme cela a été dit avant: "te" + "st" a été traité comme une constante de temps de compilateur et des différences sont trop petites pour dire qu'il est plus rapide que "Test".


0 commentaires

0
votes

Mark Peters a raison, deux constantes de cordes seront concaténées sans pitié.

Ceci est dû à la période de copie nécessaire pour concaténer des objets de chaîne, après leur taille.

Maintenant, celles-ci sont compilées dans des objets Stringbuffer / StringBuilder par le compilateur, vous pouvez le voir en décompilant un fichier .class.

Vous devez jeter un coup d'œil à ces classes, mais attention à ce que lorsque vous rendantez une chaîne StringBuilder ou Stringbuffer en tant que chaîne, un nouvel objet à chaîne sera créé.


2 commentaires

@Bartoszkp ok je suis trop rapide écrit cela, "exponentiel" est un terme déterminant. Je modifie ma réponse.


@Bartoszkp (coincé par le délai de 5 minutes pour modifier le commentaire) Vous avez raison, "exponentiel" est un terme certainement incorrect! Je modifie ma réponse, merci d'être attentif à de telles erreurs.



0
votes

Désolé de poster une réponse, mais je ne pouvais pas mettre cela dans un commentaire pour montrer à quel point cette référence est défectueuse. Sur Linux, j'ai changé l'ordre et obtenez:

La commande importe de manière défiant. xxx


0 commentaires