C# – Optimisation des concaténations de chaînes de caractères

Lorsque vous devez concaténer un important volume de chaînes de caractères dans un traitement répétitif, l’utilisation de l’opérateur de concaténation « + » n’est pas recommandé car il dégrade les performances de manière significative. Voici un exemple créant une chaîne de caractères en concaténant 50000 nombres :

string sChaine;
Stopwatch stopwatch;
int[] listeNombres;

// Génération d'un tableau avec 50000 chaînes de caractères
listeNombres = Enumerable.Range(1, 50000).ToArray();
stopwatch = new Stopwatch();
stopwatch.Start();
sChaine = "0";

foreach(int i in listeNombres)
{
    sChaine += $" {i}";
}

stopwatch.Stop();
Console.WriteLine($"{listeNombres.Count()} concaténations en {stopwatch.Elapsed.TotalSeconds} secondes");

Le résultat d’exécution de ce traitement est le suivant :
50000 concaténations en 10,4598558 secondes
Que se passe-t-il pendant ces 10 secondes ? Pourquoi les performances sont-elles dégradées ? Examinons ce qu’il se passe en mémoire lors de chaque concaténation :

Lors de la concaténation d’un nouveau nombre au contenu de la variable s, deux copies de données sont réalisées dans le tas :

  • Le contenu de la variable s est copié dans un nouvel espace mémoire
  • La valeur de la variable i est recopiée à la suite dans ce nouvel espace mémoire

Pour éviter ces recopies de données qui pénalisent les performances d’exécution, il est recommandé d’utiliser la classe System.Text.StringBuilder du Framework .NET. Voici un exemple :

StringBuilder stringBuilder = new StringBuilder("0");
stopwatch.Restart();

foreach (int i in listeNombres)
{
    stringBuilder.Append($" {i}");
}

stopwatch.Stop();
Console.WriteLine($"StringBuilder : {listeNombres.Count()} concaténations en {stopwatch.Elapsed.TotalSeconds} secondes");

Le résultat d’exécution de ce traitement est le suivant :

StringBuilder : 50000 concaténations en 0,0312285 secondes

Il est évident que les performances sont nettement améliorées (335 fois plus rapide sur mon PC). Ceci est lié au fait que la classe StringBuilder gère une liste linéaire chaînée de chaînes de caractères, c’est-à-dire une collection de chaînes où chaque chaîne de caractères est liée à la chaîne de caractères suivante via une adresse mémoire. Les copies de données, comme présentées précédemment, ne sont pas réalisées.

About: James RAVAILLE

Travaillant avec la plateforme Microsoft .NET depuis 2002, j’alterne les missions de formation et d’ingénierie avec cette plateforme. J’écris ce blog pour transmettre mes connaissances à tout développeur, qu’il soit débutant ou expérimenté.