Ich kann jedem Interessierten nur empfehlen sich das Video anzuschauen.
Im Folgenden möchte ich auf ein paar Dinge hinweisen, die ich auch noch nicht kannte und die mir speziell aufgefallen sind:
Selbstaufrufende Funktionsdefinition
(function myfunc(param) {
// Funktionsrumpf hier einfügen
})(myvar);
In obigem Code wird eine (anonyme) Funktion namens myfunc(param) erstellt, die direkt im Anschluss mit dem Parameter myvar aufgerufen wird.
Zeitgesteuerte Wiederholungen
Wenn man ein Ereignis oder eine Funktion nach einer bestimmten Zeitspanne immer wieder wiederholen will, gibt es in JavaScript die Methode setInterval(func, delay). Nachteilig ist aber, dass die Funktion funcimmer nach der Ablauf der Zeitspanne delay aufgerufen wird, egal ob func längere Zeit zur Ausführung benötigt als delay. So kann es zum „Stau“ kommen.
setInterval(function() {
myfunc();
}, 100);
Wenn myfunc() im Beispiel länger als 100 Millisekunden zur Ausführung benötigt, wird trotzdem nach 100 Millisekunden myfunc() erneut aufgerufen, usw. Doch mit der eben definierten selbstaufrufenden Funktionsdefinition kann man dem entgegenwirken:
myfunc() wird nun erst vollständig abgearbeitet und erst dann nach einer Wartezeit von 100 Millisekunden wird myloop() und damit myfunc() erneut aufgerufen. Einen Nachteil hat die Lösung aber trotzdem in meinen Augen: myloop() wird rekursiv aufgerufen und erzeugt dadurch natürlich einen Overhead der bei häufiger periodischer Ausführung nicht zu unterschätzen ist.
Elementfilter
In jQuery lassen sich relativ einfach alle Elemente eines bestimmten Typs finden. $(':password') findet beispielsweise alle Passwortfelder. Doch diese Methode ist sehr langsam, weil per Definition alle Elemente innerhalb des DOM durchgegangen werden und geprüft wird, ob die Elemente vom Typ password sind (also quasi $('*:password')). Beschleunigen lässt sich das ganze, indem man ein wenig spezifischer wird. Den Typ password können nur Input-Felder haben. Also ist es sinnvoll, die Auswahl ein wenig einzuschränken. $('input:password') sucht nur nach Input-Felder vom Typ password, was wesentlich schneller ist.
Das Gleiche gilt auch für die Filter text, radio, checkbox, file, password, submit, image, reset und button.
JSON-Parsing
JavaScipt Object Notation ist ein einfaches Datenaustauschformat auf der Basis von anonymen JavaScript-Objekten. In JavaScript kann man so ein JSON-Objekt theoretisch einfach über eval() auswerten. Aber wie die sicherheitsbewussten unter euch wissen, ist eval() evil! jQuery nutzt deshalb nativ die browsereigenen JSON-Parsing-Methoden. Für den Fall, dass diese nicht existieren (bspw. in älteren Browsern), haben sich die jQuery-Entwickler eine andere trickreiche Implementierung überlegt, ohne auf eval() zurückgreifen zu müssen:
var x = (new Function('return ' + jsonobj))()
Es wird schlicht ein neues Funktionsobjekt erzeugt, welches dann das JSON-Objekt zurückgibt. Auf diese Weise haben wir auch JSON geparsed, aber ohne Sicherheitsbedenken. Denn im Gegensatz zu eval() darf das Funktionsobjekt nur auf seinen eigenen Namensraum und Gültigkeitsbereich zugreifen und nicht auf den globalen. Daher kann es nicht zum Überschreiben von Variablen, Objekten, usw. kommen. (siehe Kommentare) Trickreich, stimmt’s?
jQuery-Performance
Abschließend möchte ich auch noch auf einen Vortrag von Paul Irish zum Thema „jQuery Anti-Patterns for Performance & Compression“ hinweisen, der einiges noch einmal aufgreift und vertieft. Für jeden jQuery-Entwickler ein Muss!
Heute werde ich wieder einmal eine nützliche PHP-Funktion vorstellen. Genauer gesagt handelt es sich um eine Klasse, die CSS und JavaScript komprimieren kann. Bei diesem Minification (oder minify) genannten Prozess, werden unnötige Kommentare, Leerzeichen, Tabulatoren und Zeilenumbrüche entfernt, wodurch die Datei kleiner wird und so schneller an den Benutzer übertragen werden kann. Zusätzlich kann in der Klasse der „bereinigte“ Quellcode auch noch vor der Übertragung mit gzip oder Deflate komprimiert werden, damit er noch schneller übertragen werden kann.
Im Gegensatz zu vielen anderen Minifiern habe ich versucht die Optimierungen „leichtgewichtig“ zu gestalten, also einen Kompromiss aus Bandbreiteneinsparung und Ressourcenschonung zu finden. Viele andere Minifier nehmen nämlich noch mehr Optimierungen vor (Variablenumbenennung, komplizierte RegEx-Konstrukte), die viel Rechenzeit benötigen und daher nur bedingt für die On-the-fly-Erstellung von komprimiertem Code geeignet sind. Sicherlich lassen sich damit die Dateigrößen teils erheblich weiter reduzieren aber auf Kosten der serverseitigen Performance.
Auf blogeum wird die Klasse in einer erweiterten Form auch eingesetzt, um CSS und JS vor der Übertragung zu verkleinern und die Übertragungszeiten zu vermindern.
In einem Gastbeitrag bei Peter Kröner informiert Christian Schaefer, wie man die Ladezeiten einer Website ohne großen Aufwand drücken kann, um die Besucher nicht durch lange Ladezeiten zu vergraulen und einen guten Eindruck zu hinterlassen. Schließlich zählt die Ladezeit mit zu den Erfolgsfaktoren einer Website und wird zukünftig auch in das Ranking bei den Google-Suchergebnissen eingehen.
Am Ende des Artikels stellt er noch seine PHP5-Bibliothek CSS-JS-Booster vor, die einem die Arbeit bei einigen der vorgestellten Tipps abnimmt und automatisch kombiniert.
Alles in allem ein lesenswerter Artikel und einige sinnvolle Tipps, die einfach umzusetzen sind!
In PHP sind mehrere Funktionen eingebaut, um eine Zeichenfolge zu durchsuchen. Leider wird viel zu oft mit den sprichwörtlichen Kanonen auf Spatzen geschossen. Viel zu oft werden überfrachtete Funktionen benutzt, um eine einfache Existenzprüfung in einem String durchzuführen. Dabei bietet PHP auch wesentlich effizientere aber bedauerlicherweise unbekannte Funktionen für diesen Zweck. Im Folgenden werden einmal einige dieser Funktionen näher beleuchtet und deren Effizienz getestet.
Die Kontrahenten
Für einen repräsentativen Test wurden die beliebte Perl-kompatible Funktion preg_match() sowie die beiden String-Funktionen strpbrk() und strpos() herangezogen.
preg_match()
Mit Hilfe der Funktion preg_match() kann man eine Zeichenkette anhand eines regulären Ausdrucks durchsuchen. Ein regulärer Ausdruck ist dabei, vereinfacht ausgedrückt, eine Art von speziellem Suchmuster mit dem man geschickt eine Zeichenfolge nach bestimmten Kriterien durchsuchen kann.
Die Schnittstelle von preg_match() ist nachfolgend angegeben:
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags [, int $offset ]]] )
Für diesen Test sind nur die ersten beiden obligatorischen Parameter interessant, die das Suchmuster und die zu untersuchenden Zeichenfolge angeben.
strpbrk()
strpbrk() ist eine relativ unbekannte Funktion mit deren Hilfe man eine Zeichenfolge auf das erste Vorkommen eines beliebigen Zeichens aus einer vorgegebenen Zeichenliste untersuchen kann. Der Rückgabewert ist der Rest-String, beginnend bei der ersten Fundstelle des zuerst gefundenen Zeichens. Bei einer einfachen Existenzprüfung würde $char_list demnach dem regulären Ausdruck [abcd] entsprechen (wobei a, b, c und d beliebige Zeichen sind).
Diese Funktion ähnelt strpbrk(). Sie durchsucht eine Zeichenfolge nach dem ersten Vorkommen einer Such-Zeichenfolge und liefert deren Position innerhalb der Zeichenfolge zurück.
int strpos ( string $haystack , mixed $needle [, int $offset= 0 ] )
Benchmark
Zur Untersuchung der Performance der drei Funktionen wurde nachfolgender Code benutzt:
<?php
# Array aus a - z erzeugen und 10-mal hintereinander hängen
$array = range('a', 'z');
$array = array_merge($array, $array, $array, $array, $array, $array, $array, $array, $array, $array);
# String erzeugen und mischen
$string = str_shuffle(join('', $array));
# einzelnes Zeichen für preg_match(), strpbrk() und strpos()
$char = chr(mt_rand(97, 122));
# Zeichenfolge für preg_match() und strpos()
$char = chr(mt_rand(97, 122)).chr(mt_rand(97, 122)).chr(mt_rand(97, 122));
# mehrere Zeichen für preg_match()
$char = chr(mt_rand(97, 122)).'|'.chr(mt_rand(97, 122)).'|'.chr(mt_rand(97, 122));
# Timer starten
$timer = microtime(TRUE);
# 100.000 Schleifendurchläufe für die zu testende Funktion
for($x = 0; $x < 100000; ++$x) {
# zu untersuchende Funktionen hier
}
# vergangene Zeit ausgeben
echo microtime(TRUE) - $timer;
?>
Die Zeilen 7, 10 und 13 werden je nach Test entfernt um nur eine Situation zu testen.
Zunächst wird eine zu untersuchende Zeichenfolge bestehend aus den Kleinbuchstaben a bis z erstellt. Diese Zeichenfolge besteht aus ingesamt 260 Zeichen, die zufällig angeordnet werden, um etwaige PHP-Cache-Mechanismen zu umgehen. Danach wird ein zufälliges Zeichen aus dem Zeichenvorrat ausgewählt, nach dem im Anschluss gesucht werden soll.
Dann wird 100.000 Mal nach diesem Zeichen (dieser Zeichenfolge) gesucht und die dabei vergangene Zeit gemessen. Dieser Test wurde 5 Mal hintereinander für jede Funktion ausgeführt und die gemessenen Zeiten gemittelt, um etwaige „Ausreißer“ nach oben oder unten zu glätten.
Alle Testläufe wurden auf einem virtuellen Windows 2000 Server mit Apache 2.2.11 und PHP 5.2.8 durchgefüht.
Die Ergebnisse
Funktion
Gesamtlaufzeit
pro Durchlauf
relative Performance
preg_match
0,20465045 s
2,0465045 µs
+ 421,55%
strpbrk
0,07036624 s
0,7036624 µs
+ 144,94%
strpos
0,04854722 s
0,4854722 µs
100,00%
Bei der Suche nach einem einzelnen Zeichen spielt strpos() seine Stärken aus und ist deutlich schneller als seine Konkurrenten. strpbrk() braucht rund 1,5-mal so lang und preg_match() sogar beachtliche 4,2-mal.
Funktion
Gesamtlaufzeit
pro Durchlauf
relative Performance
preg_match
0,853334379 s
8,53334379 µs
+ 1192,82%
strpbrk
0,071539068 s
0,71539068 µs
100,00%
Bei der Suche nach drei beliebigen Zeichen ist strpbrk() um den eindrucksvollen Faktor 12 schneller als preg_match().
Funktion
Gesamtlaufzeit
pro Durchlauf
relative Performance
preg_match
0,494307613 s
4,94307613 µs
+ 595,96%
strpos
0,082943153 s
0,82943153 µs
100,00%
Auch bei diesem Testlauf können die regulären Ausdrücke nicht punkten. Im Schnitt ist preg_match() rund 6-mal langsamer gegenüber strpos(). Anmerken muss man hier, dass die zu suchende Zeichenfolge evtl. auch nicht in der zu untersuchenden Zeichenfolge vorkommen kann, weil die zu suchende Zeichenfolge zufällig erstellt wird.
Fazit
Für einfache Existenzprüfungen (also ob ein Zeichen in einem String vorkommt) sind jegliche PCRE-kompatiblen Funktionen mehr als ungeeignet. Sie sind deutlich langsamer als die eingebauten String-Funktionen von PHP. Dieser Performanceunterschied lässt sich auf den Overhead durch die Regex-Engine von PHP zurückführen. Zudem benötigen die PCRE-Funktionen kurzzeitig mehr Speicher bei der Ausführung auf dem Server, was für diesen Test aber nicht von Bedeutung war.
Dementsprechend sollte man für einfache Aufgaben auf die Funktionen strpbrk(), strpos(), strstr(), etc. zurückgreifen. Der Dedicated- oder Shared-Server wird es einem danken!
blogeum ist das persönliche Weblog von Christian Gürtler – seines Zeichens begeisterter Webworker und Programmierer. Er studiert gegenwärtig Angewandte Informatik an der TU Chemnitz.
Abonnieren
Immer auf dem Laufenden mit den Einträgen aus dem Weblog als Atom-Feed. Nunmehr 72 Abonnenten!
Neue Kommentare
Christian: Hallo, interessante Funktion. Ich denke ich werde das Beispiel für JavaScript…
Tobias Neumann: Hallo, interessante Funktion. Ich denke ich werde das Beispiel für JavaScript…
Christian: OK, den Zugriff auf "superglobale" Objekte wie window habe ich nicht bedacht.…
ChrisB: Auf diese Weise haben wir auch JSON geparsed, aber ohne Sicherheitsbedenken.…
Martha: Das wundert mich auch. Rot ist doch eine Warnfarbe. Ich kann mir das auch nur…