Quel est le coût de performance de l'attribution d'une valeur de chaîne unique à l'aide de +
-
03-07-2019 - |
Question
Je me suis souvent posé la question: existe-t-il un coût en performances lié au fractionnement d'une chaîne sur plusieurs lignes afin d'améliorer la lisibilité lors de l'attribution initiale d'une valeur à une chaîne? Je sais que les chaînes sont immuables et qu'une nouvelle chaîne doit donc être créée à chaque fois. En outre, le coût des performances est en réalité hors de propos grâce au matériel très rapide d'aujourd'hui (à moins que vous ne soyez dans une boucle diabolique). Ainsi, par exemple:
String newString = "This is a really long long long long long" +
" long long long long long long long long long long long long " +
" long long long long long long long long long string for example.";
Comment le compilateur de la JVM ou .Net et les autres optimisations le gèrent-ils. Va-t-il créer une seule chaîne? Ou créera-t-il 1 chaîne puis une nouvelle concaténation de la valeur puis une autre concaténant à nouveau les valeurs?
Ceci est pour ma propre curiosité.
La solution
Cela est garanti par la spécification C # identique à la création de la chaîne dans un seul littéral, car il s'agit d'une constante à la compilation. De la section 7.18 de la spécification C # 3:
Chaque fois qu'une expression remplit les exigences énumérées ci-dessus, l'expression est évaluée à temps de compilation. C’est vrai même si le expression est une sous-expression d'un expression plus grande qui contient constructions non constantes.
(Voir les spécifications pour connaître les détails exacts des "conditions requises ci-dessus":)
La spécification du langage Java le spécifie au bas de section 3.10.5 :
Chaînes calculées par constante expressions (§15.28) sont calculées à compiler le temps et ensuite traité comme si ils étaient littéraux.
Autres conseils
En effet, en Java, le compilateur transformera la String
en constante.
class LongLongString
{
public LongLongString()
{
String newString = "This is a really long long long long long" +
" long long long long long long long long long long long long " +
" long long long long long long long long long string for example.";
}
public static void main(String[] args)
{
new LongLongString();
}
}
est compilé dans:
Compiled from "LongLongString.java"
class LongLongString extends java.lang.Object{
public LongLongString();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: ldc #2; //String This is a really long long long long long long long long long long long long long long long long long long long long long long long long long long string for example.
6: astore_1
7: return
public static void main(java.lang.String[]);
Code:
0: new #3; //class LongLongString
3: dup
4: invokespecial #4; //Method "<init>":()V
7: pop
8: return
}
Comme on peut le constater, une seule ligne est chargée dans la ligne 4 au lieu de plusieurs instances String
chargées.
Modifier: Le fichier source a été compilé à l'aide de javac
version 1.6.0_06. En regardant Spécification du langage Java, troisième édition , (et la même section mentionnée dans Réponse de Jon Skeet ), je n’ai trouvé aucune référence permettant de déterminer si un compilateur doit concaténer une Chaîne
dans une seule Chaîne
, ce comportement est donc probablement spécifique à l’implémentation du compilateur.
Testez-le vous-même. En code C # (un équivalent Java fonctionnerait aussi):
string x = "A" + "B" + "C";
string y = "ABC";
bool same = object.ReferenceEquals(x, y); // true
Vous verrez que le résultat est true
.
En passant, vous verrez que la chaîne est également internée dans le pool de chaînes du moteur d'exécution:
bool interned = object.ReferenceEquals(x, string.Intern(x)); // true
Aucun compromis sur les performances. L’optimisation du compilateur le fusionnera en une seule chaîne (au moins en Java).
Autant que je me souvienne, cela ne créera pas plusieurs chaînes, mais celle-ci.
L'équivalent .NET IL complémentaire de réponse de coobird :
Pour le code C #:
string s = "This is a really long long long long long" +
" long long long long long long long long long long long long " +
" long long long long long long long long long string for example.";
Console.WriteLine(s);
Une compilation de débogage produit:
.method public hidebysig static void Main(string[] args) cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
.maxstack 1
.locals init (
[0] string str)
L_0000: ldstr "This is a really long long long long long long long long long long long long long long long long long long long long long long long long long long string for example."
L_0005: stloc.0
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(string)
L_000c: ret
}
Donc, comme vous pouvez le constater, il s’agit d’une chaîne.
Tant que toutes les chaînes sont constantes (comme dans votre exemple), en Java (et j'imagine C #), le compilateur convertit cette chaîne en une seule chaîne.
Vous n'obtenez des problèmes de performance avec + que si vous concaténez beaucoup de chaînes dynamiques, comme dans une boucle. Dans ce cas, utilisez un StringBuilder ou StringBuffer.
Avertissement: Ceci est vrai pour Java. Je suppose que c'est vrai pour c #
Non seulement javac créera une seule chaîne, mais la machine virtuelle Java utilisera une chaîne pour toutes les autres chaînes contenant le même texte.
String a = "He" + "llo th"+ "ere";
String b = "Hell" + "o the"+ "re";
String c = "Hello" +" "+"there";
assert a == b; // these are the same String object.
assert a == c; // these are the same String object.
Remarque: ils seront le même objet String au moment de l'exécution même s'ils appartiennent à des classes différentes dans des fichiers JARS différents, compilés par différents compilateurs.