.NET 4 Änderungen an string.IndexOf Methode

1. November 2010

Mit der Umstellung auf ein neues Framework erhofft man sich ja immer auch ein bisschen bessere Software und auch eine schnellere Anwendung. Das dies aber nicht unbedingt gilt, bewies in einem Projekt die Umstellung der Software von .NET 2.0 auf .NET 4. Bei Lasttests wurden daraufhin Performance-Probleme in einer Methode identifiziert, die einen String entgegen nahm und diesen nach Patterns durchsucht hat. Nähere Untersuchungen haben ergeben, dass der Aufruf der Methode string.IndexOf der Verursacher für den Performanceeinbruch war.

Für die Nachstellung des Problems durch ein kleines Consolenprogramm wurde eine Textdatei (~400 KB) in einen string eingelesen und dann alle Auftreten eines Patterns gesucht. Der eingelesene String ist somit sehr groß und das Pattern tritt 14.700 mal in dem String auf. Hier die Methode:

   1: private static void SearchPattern(string strPattern, string strSearchString)

   2: {

   3:   int iStartIndex = 0;

   4:   int iIndex = 0;

   5:   int iContentLength = strSearchString.Length - 1;

   6:  

   7:   while (iStartIndex < iContentLength && iIndex != -1)

   8:   {

   9:     iIndex = strSearchString.IndexOf(strPattern, iStartIndex);

  10:     if (iIndex != -1)

  11:       iStartIndex = iIndex + strPattern.Length;

  12:   }

  13: }

Die Zeitmessungen mit einer unterschiedlichen Anzahl von Iterationen ergab einen dramatischen Performanceeinbruch für das Framework .NET 4.

Anzahl Iterationen .NET 2.0 .NET 4
10 00:00:00.0950380 00:00:11.4375732
100 00:00:00.9233692 00:01:58.5309650
1000 00:00:09.1416552 00:20:59.8498171

Mit Hilfe des Reflectors kann man sehen, dass die Methode System.String.IndexOf bzw. die Methode System.Globalization.CompareInfo.IndexOf (diese wird letztendlich aufgerufen) in der Implementierung eine Veränderung erfahren hat beim Wechsel von .NET 2.0 nach .NET 4. Nutzt man die IndexOf-Methode wie in Zeile 9, dann wird auf Basis der CurrentCulture und CompareOptions.None, die Suche durchgeführt.

Eine Prüfung ergab, dass die Suche nach dem Pattern auch mit der InvariantCulture und mit CompareOptions.Ordinal durchgeführt werden kann, so dass die Zeile 9 in Folgende umgewandelt werden kann

   9: iIndex = strSearchString.IndexOf(strPattern, iStartIndex,StringComparison.Ordinal);

Mit diesem Aufruf der IndexOf-Methode ergaben sich nun folgende gemessene Zeiten:

Anzahl Iterationen .NET 2.0 .NET 4
10 00:00:00.0225000 00:00:00.0170009
100 00:00:00.2380000 00:00:00.2005000
1000 00:00:02.0960000 00:00:02.0290000

Als Fazit kann man zweierlei Dinge ziehen:

  1. Nach einem Framework Wechsel sind bei zeitkritischen Routinen unbedingt Performance-Messungen durchzuführen. Nicht alles was sich unter der Haube verändert hat ist bekannt und kann somit potentiell zu Einbußen bei der Performance führen
  2. String Operationen, ob es nun die IndexOf-Methode oder andere wie Compare usw., sind, sind genau zu analysieren und explizit mit CultureInfo bzw. CompareOptions aufzurufen. Dies zeigt auch die Performance Steigerung bei .NET 2.0 im obigen Beispiel.

Mittlerweile ist diese Problematik Microsoft zur Untersuchung übergeben worden. Eine erste Einschätzung geht von einem Bug im .NET 4 Framework bei der Behandlung von großen Strings aus und es wird an einer Lösung gearbeitet. Diese wird jedoch voraussichtlich noch einige Zeit auf sich warten lassen.