Performance Tipps: RegExp(), Split(), Substr() und indexOf()

Artikel: ActionScript, Performance Tipps | 25.11.2010

Es gibt in der allgemeinen Programmierung verschiedene Möglichkeiten einen String oder größeren Text zu durchsuchen.
Die meisten verwenden RegExp um entsprechende Texte zu suchen, jedoch ist RegExp nicht immer die beste Wahl.
Mit RegExp kann der Quellcode relative klein gehalten werden, aber man erreicht damit nicht immer die beste Performance.

Inhalt

Kurze Erklärung zu RegExp(), Split(), Substr() und indexOf()

Zuerst eine kurze Erklärung was die einzelnen Funktionen können und für welches Gebiet diese gedacht sind.

RegExp()

RegExp() ist eines der mächtigsten Wergzeuge um Text zu durchsuchen.
Hierbei kann genau variable festgelegt werden wonach genau gesucht wird, auch können so genannte Suchmuster definiert werden.
RegExp() wird hauptsächlich für komplizierte Suchen verwendet wo ein dynamisches Suchmuster nötige ist.
Das Suchmuster /id([0-5]{3,5})/ wurde alle Werte finden die mit "id" anfangen und 3 bis 5 Zahlen enthalten die im Bereich von 0-5 sind.
Beispiel: id3234, id233, id000, id12345, id00011.... um diese mit Split(), Substr() und indexOf() umgesetzten zu können wäre eine komplizierte Programmierung nötig.
RegExp besitzt mehrere Möglichkeiten, so kann man anhand eines RegExp Wert suche, ersetzten, testen oder sich die Position oder die gefundenen Werte zurück geben lassen.

Syntax:
// Werte ermitteln [RegExp Object].exec([Text Object]) // Testen ob Suchmuster vorhanden ist [RegExp Object].test([Text Object]) ...
Beispiel:
var suchtext = "321kdsmladsid00011adsöklk32l1321" var suchmuster = /id([0-5]{3,5})/; var ergebnis = suchmuster.exec(suchtext); if (ergebnis) { trace('Wert gefunden: ' + ergebnis[1]); } else { trace('Wert nicht gefunden !'); }

Split()

Split() teilt einen Text bei der angegebenen Trennzeichen / Trenntext auf und speichert das Ergeniss in einem Array.
Es ist also ideal um Daten mit einem festen Trennzeichen / Trenntext zu bearbeiten z.B: CSV Daten oder andere Trennzeichen separierte Werte.

Syntax:
[Text Object].split([Trennzeichen / Trenntext],[Limit])
Beispiel 1:
var suchtext = "a1;a2;a3;a4;a5;a6;a7;a8;a9"; var trennzeichen = ";"; var ergebnis = suchtext.split(trennzeichen); // Ausgabe for (var eintrag in ergebnis) { trace(eintrag + ": " + ergebnis[eintrag]); }
Beispiel 2:
var suchtext = "a1-000-a2-000-a3-000-a4-000-a5-000-a6-000-a7-000-a8-000-a9"; var trennzeichen = "-000-"; var ergebnis = suchtext.split(trennzeichen); // Ausgabe for (var eintrag in ergebnis) { trace(eintrag + ": " + ergebnis[eintrag]); }

Substr()

Substr() erlaubt es von einer bestimmten Position eine bestimmte Anzahl von Zeichen auszulesen.
Wenn sich die Länge des Suchtext nicht ändert, kann man also realtive schnell entsprechende Werte auslesen.
Wird als Startposition ein negativer Wert verwendet so wird das Ende des Suchtext als Startposition verwendet.
Ein Wert von -1 entspricht also dem letzte Zeichen des Suchtexts.

Syntax:
[Text Object].substr([Start Position],[Länge])
Beispiel:
var suchtext = "a1-000-a2-000-a3-000-a4-000-a5-000-a6-000-a7-000-a8-000-a9"; var ergebnis = [ suchtext.substr(0,2), suchtext.substr(7,2), suchtext.substr(14,2), suchtext.substr(21,2), suchtext.substr(28,2), suchtext.substr(35,2), suchtext.substr(42,2), suchtext.substr(49,2), suchtext.substr(56,2) ]; // Ausgabe for (var eintrag in ergebnis) { trace(eintrag + ": " + ergebnis[eintrag]); }

indexOf()

indexOf() liefert anhand eines Suchmuster die Position des ersten Treffers diese Suchmusters zurück.
Die Position beginnt mit 0 für das erste Zeichen, wenn also das Suchmuster nicht gefunden wird, so wird hier -1 zurück gegeben.
Man kann also indexOf() dazu verwenden um schnell zu prüfen ob ein bestimmtes Suchmuster vorhanden ist.
Auch kann man mit indexOf() und SubStr() eine alternative zu Split() aufbauen die auch dynamische Längen verarbeiten kann.

Syntax:
[Text Object].indexOf([Suchmuster],[Start Position])
Beispiel:
var suchtext = "a1-000-a2-000-a3-" + Math.random + "000-a4-000-a5-000-a6-000-a7-000-a8-000-a9"; var suchmuster = "a4"; var ergebnis = suchtext.indexOf(suchmuster); if (ergebnis !== -1) { trace('Suchermuster: ' + suchmuster + ' wurde bei Position: ' + ergebnis + ' gefunden.'); } else { trace('Suchermuster: ' + suchmuster + ' wurde nicht gefunden !'); }

Tipps im Umgang mit Suchmuster oder Filterungen

Tip #1 - RegExp() meiden

RegExp sollte so gut es geht gemieden werden, klar es ist meist einfacher und übersichtlicher, jedoch schneidet RegExp bei der Performance am schlechtesten ab.
Es gibt viele Fälle wo RegExp einfach nicht nötig ist oder nicht die beste Wahl ist.
Für komplizierte Prüfungen oder Suchmuster ist es natürlich kein Problem RegExp zu verwenden.
Ich selber liebe es mit RegExp zu arbeiten, jedoch nur wenn die Performance keine größere Rolle spielt.

Dieser Test macht deutlich das selbst für kompliziertere Suchmuster nicht immer RegExp nötig oder die beste Wahl ist.
Wie man sieht erreicht man mit split(), substr(), substring() und indexOf() das selbe Ergebniss in einer kürzen Zeit.
In diesem Fall benötigt die RegExp() Lösung ca. 9 Codezeilen wo gegen die andere Lösung ca. 26 Codezeilen benötigt.

Tip #2 - Verwendung von substr() oder substring() bei fester Zeichenanzahl

Wenn ein Suchmuster immer die selbe Anzahl von Zeichen hat, sollte man substr() oder substring() verwenden um die Ergebniss aufzuteilen.
Die Funktion substr() bzw. substring() ist einer der schnellsten Funktionen um entsprechende Werte aufzuteilen.
Durch kleine Optimierungen ist es möglich noch ein wenig mehr Geschwindigkeit zu bekommen.

Beispiel Code:
... for (var entry2 in a2) { r2[entry2] = { e1: a2[entry2].substr(0,19), e2: a2[entry2].substr(20,19), e3: a2[entry2].substr(40,19), e4: a2[entry2].substr(60,19) } } ...

Bei diesem Beispiel sieht man recht deutlich was substr() anstatt RegExp ausmachen kann.
Auch in Vergleich zu den anderen Methoden wie split() oder indexOf() schneidet substr() am besten ab.
Technisch gesehen sollte dies nicht verwundern, da ja eigentlich Methoden auf substr() aufbauen.

Tip #3 - Verwendung von substring() und indexOf() für variable Trennzeichen Position

Wenn das entsprechende Trennzeichen dynamisch ist sprich es sich immer an einer anderen Stelle befindet, so kann mit Hilfe von indexOf() auch hier substr() bzw. substring() verwendet werden.
Die meisten vermuten nun das split() hierfür besser geeignet ist oder schnell ist, dies ist aber leider nicht der Fall.
Die Kombination von substr() bzw. substring() mit indexOf() is ca. 20% schneller als split().
Natürlich ist dies nur Möglich wenn bekannt ist das das sich immer die selbe Anzahl von Informationen handelt.

Beispiel Code für substr() bzw. substring() und indexOf():
... for (var entry2 in a2) { var entry = a2[entry2]; var pos1 = entry.indexOf(":")+1; var pos2 = entry.indexOf(":", pos1)+1; var pos3 = entry.indexOf(":", pos2)+1; r2[entry2] = { e1: entry.substring(0,pos1-2), e2: entry.substring(pos1,pos2-2), e3: entry.substring(pos2,pos3-2), e4: entry.substr(pos3) } } ...
Beispiel: RegExp() vs indexOf() und substring()

Hier sind man recht deutlich das RegExp() keine Change gegen indexOf() und substring() hat und weit hinter derren Performance liegt.

Beispiel: split() vs indexOf() und substring()

Selbst gegen split() kann sich die Kombination von indexOf() und substring() sehen lassen.
Wobei ich zugeben muss das dies mich am meisten verwundert hat bei meinen Tests.

Tip #4 - Verwendung von split() für unbekannt Anzahl von Trennzeichen

Selbst wenn man Daten durchsuchen muss die eine unbekannt Anzahl von Trennzeichen besitzten muss man nicht zwangsläufig auf RegExp() zurück greifen.
Split() erfühlt in den meisten Situationen den selben Zweck und das mit einer sehr guten Performance.
Theoretisch könnte man auch mit einer zusätzlichen Schleife und Bedingungen indexOf() und substring() verwenden, jedoch war diese Kombination bei meinen Tests nicht schneller als split().

Wie auch hier zu sehen ist, schneidet RegExp() um ca. 60% schlecher ab als split().

Tip 5# - Verwendung von indexOf() um Zeichenkennten zu prüfen

Normalerweise verwenden die meisten Entwickler RegExp().test um zu prüfen ob eine bestimmte Zeichenkette enthalten ist.
Jedoch kann in den meisten Fällen auch ohne weiteres indexOf() verwendet werden, welches um einiges schneller ist.

Bei der Verwendung von indexOf() erreicht man ca. 40% mehr Performance also mit RegExp().test.
Wichtig ist hierbei auf jedenfall das man nicht vergisst, das indexOf() -1 zurück liefert wenn das Suchmuster nicht gefunden wurde.

Tip 6# - Optimierung der Anweisungen

Bei komplizierteren Anweisungen ist es wichtig diese soweit zu optimieren wie möglich, schon einfache Optimierungen können 10% - 20% mehr Leistung bringen.
Verwendet man indexOf() und substring() so sollte man darauf achten eine Variable für den Array Eintrag zu erzeugen anstatt jedesmal auf das Array zugreifen zu müssen.

Beispiel Code nicht optimiert:
... for (var entry1 in a1) { var pos1 = a1[entry1].indexOf(":")+1; var pos2 = a1[entry1].indexOf(":", pos1)+1; var pos3 = a1[entry1].indexOf(":", pos2)+1; r1[entry1] = { e1: a1[entry1].substring(0,pos1-2), e2: a1[entry1].substring(pos1,pos2-2), e3: a1[entry1].substring(pos2,pos3-2), e4: a1[entry1].substr(pos3) } } ...

Dieser unoptimierter Beispiel Code stellt die einfachste Möglichkeit dar mit indexOf() und substring() zu arbeiten.
Es werden keine zusätzlichen Variablen definiert und der Code erfüllt seinen Zweck.

Beispiel Code leicht optimiert:
... for (var entry2 in a2) { var entry = a2[entry2]; var pos1 = entry.indexOf(":")+1; var pos2 = entry.indexOf(":", pos1)+1; var pos3 = entry.indexOf(":", pos2)+1; r2[entry2] = { e1: entry.substring(0,pos1-2), e2: entry.substring(pos1,pos2-2), e3: entry.substring(pos2,pos3-2), e4: entry.substr(pos3) } } ...

Schon eine leichte Optimierung wie in diesem Fall die zusätzliche Variablenzuweisung, sort dafür das der Code ca. 10% - 20% schneller ausgeführt wird.
Wenn man also ein paar Bytes or Kilobytes für zusätzliche Variablenzuweisungen verschwendet, erreicht man in den meisten Fällen mehr Performance.
Es stellt sich natürlich immer die Frage in wie weit eine solche Optimierung vorgenohmen werden soll.
Im Grund bringt es nichts den best optimierten Code zu haben wenn man diesen später nicht mehr lesen kann.

Vergleich aller Möglichkeiten

Abschließend vergleichen wir in den folgenden Test alle Möglichkeiten miteinander, so das die Ergebnisse nochmal verglichen werden können.
Dieser Test geht vom einfachsten Fall aus und von einem einfachen Suchmuster aus, dies bedeutet das je nach Suchmuster kleinere Unterschiede in der Performance auftretten können.

Dadurch das der Test ein wenig größer ist kann es bis zu 60 sec. dauern bevor ein Ergebniss sichbar ist.
Das Ergebniss bestättigt jedoch klar die vorherigen genannten Tipps um eine bessere Performance zu erhalten.

Zusammenfassung

Benötigt man viel Performance innerhalb der Flashdatei so sollte man komplett auf RegExp() verzichten.
Es spricht aber wie bereits gesagt nichts dagegen RegExp() zu verwenden wenn die Performance keine größere Rolle spielt.
Durch den Verzicht von RegExp() wird der entsprechende Quellcode zwar größer aber dadurch steigt die Performance um ca. 40% - 70% wie in den einzelnen Beispielen zu sehen ist.