Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 PEAR – Einführung
2 Authentication
3 Caching
4 Date and Time
5 File Formats
6 HTTP
7 Internationalization
8 Mail
9 Networking
10 PHP
11 Text
12 Web Services
13 Benchmarking
14 Configuration
15 Database
16 File System
17 HTML
18 Images
19 Logging
20 Math
21 Numbers
22 Tools and Utilities
23 XML
24 Selbst Pakete erstellen
25 PECL
Index
Ihre Meinung?

Spacer
 <<   zurück
PHP PEAR von Carsten Möhrke
Anwendung und Entwicklung – Erweiterungen für PHP schreiben
Buch: PHP PEAR

PHP PEAR
798 S., 39,90 Euro
Rheinwerk Computing
ISBN 3-89842-580-0
gp 13 Benchmarking
  gp 13.1 Benchmark

13 Benchmarking

Die Performance von PHP-Scripts ist immer wieder ein Problem. Das liegt zum einen sicher oft an einer nicht ganz so geschickten Programmierung, zum anderen ist PHP aber auch eine Sprache, die nicht in allen Bereichen zu den schnellsten gehört. Um feststellen zu können, an welchen Stellen ein Script Performance-Probleme hat, können Sie zu Benchmarks greifen.


Rheinwerk Computing

13.1 Benchmark  toptop


Besprochene Version: 1.2.2 Lizenz: PHP-Lizenz 3.0
Klassendatei(en): Benchmark/Timer.php; Benchmark/Profiler.php; Benchmark/Iterate.php

Mit dem Paket Benchmark haben Sie die Möglichkeit, die für die Ausführung eines Programms oder Programmabschnitts benötigte Zeit zu stoppen und auszuwerten.

Im einfachsten Fall kann die Nutzung so aussehen:

require_once('Benchmark/Timer.php'); 
 
// Neues Objekt ableiten 
$timer = new Benchmark_Timer(); 
echo 'Start der Ausf&uuml;hrung<br />'; 
// Startzeit erfassen 
$timer->start(); 
 
// Code, fuer den die Rechenzeit bestimmt werden soll 
for ($i=1; $i<100000; $i+=1) 
{ 
   $sqrt[]=sqrt($i); 
} 
 
// Endzeit erfassen 
$timer->stop(); 
echo 'Ende der Ausf&uuml;hrung<br />'; 
echo 'Dauer der Ausf&uuml;hrung: '; 
// Zeitdifferenz ausgeben 
echo $timer->timeElapsed();

Listing 13.1 Einfache Zeitmessung mit PEAR::Benchmark

Die Datei Benchmark/Timer.php stellt die Klasse Benchmark_Timer zur Verfügung, die eine einfache Möglichkeit bereitstellt, um Zeiten zu messen. In Listing 13.1 wird erst ein Objekt abgeleitet. Die Methode start() erfasst die Uhrzeit, zu der die Ausführung beginnt, und die Methode stop() erfasst die Zeit, zu der die Ausführung endet. timeElapsed() berechnet schließlich die Differenz.

Nun kann es aber in einigen Fällen passieren, dass Sie nicht nur an einer Stelle die Zeit »stoppen« wollen, sondern an mehreren Punkten die Zeit nehmen wollen. Hierzu kann das Paket mit so genannten »Markern« arbeiten. Ein Marker wird mit der Methode setMarker() eingefügt und bezeichnet eine Stelle, an der die Zeit erfasst und gespeichert wird. Die Methoden start() und stop() machen übrigens nichts anderes. Sie setzen die Marker Start und Stop. Die Marker, die Sie selbst setzen, dürfen frei definierte Namen haben, die innerhalb des Programms allerdings eindeutig sein müssen. In dem Fall müssen Sie der Methode timeElapsed() dann auch mitteilen, wie die beiden Marker heißen, zwischen denen die Zeitdifferenz bestimmt werden soll.

$timer->setMarker('Anfang'); 
// komplizierter Code 
$timer->setMarker('Ende'); 
echo $timer->timeElapsed('Anfang', 'Ende');

Bei der Arbeit mit mehreren Markern können Sie alternativ zur Methode timeElapsed() auch display() nutzen. Sie gibt alle Zeiten aus, geht jedoch davon aus, dass Sie entweder die Methoden start() und stop() aufgerufen haben. Alternativ können Sie dem Konstruktor ein true übergeben. Dies führt einerseits dazu, dass die Zeitmessung automatisch gestartet wird, und andererseits wird zum Ende der Laufzeit automatisch eine Tabelle mit den relevanten Informationen ausgegeben.

// Objekt mit Autostart ableiten 
$timer = new Benchmark_Timer(true); 
// selbst definierten Marker setzen 
$timer->setMarker('Anfang'); 
   // Komplizierter Code 
// Zweiter Marker 
$timer->setMarker('Nach Schleife 1'); 
   // Noch mehr komplizierter Code
// Weitere Marker 
$timer->setMarker('Nach Schleife 2'); 
 
$timer->setMarker('Ende');

Ein solches Script generiert automatisch eine Ausgabe wie die in Abbildung 13.1.

Abbildung 13.1 Ausgabe einer Laufzeit-Analyse

Wie Sie sehen, sind auch hier die Marker Start und Stop enthalten, die durch die Nutzung der Autostart-Option automatisch eingefügt wurden. Bei den Zeitangaben in der Spalte time index handelt es sich um Timestamps, die um die Sekundenbruchteile nach dem Komma ergänzt wurden.

Möchten Sie die Daten nicht direkt ausgeben, sondern sie selbst weiterverarbeiten, können Sie diese auch in Form eines Arrays mit der Methode getProfiling() auslesen. Hierbei handelt es sich um ein indiziertes Array, in dem für jede Zeile aus Abbildung 13.1 ein neues, assoziatives Array vorgesehen ist. Dieses hat den folgenden Aufbau:

array(4) { 
    ["name"]=> 
    string(5) "Start" 
    ["time"]=> 
    string(19) "1107088632.22679300" 
    ["diff"]=> 
    string(1) "-" 
    ["total"]=> 
    string(17) "1107088632.226793" 
  }

Bitte beachten Sie, dass Sie in einem solchen Fall nicht die Autostart-Option des Konstruktors nutzen sollten, da diese automatisch eine Bildschirmausgabe erzeugt.

Die Klasse Benchmark_Profiler, die in der Datei Benchmark/Profiler.php enthalten ist, verfolgt noch einen etwas anderen Ansatz. Grundsätzlich ist die Funktionsweise sehr ähnlich, so dass eine Autostart-Funktionalität genau so vorhanden ist wie die Methoden start(), stop() und display(). Allerdings geht es in diesem Fall darum, die Laufzeit von bestimmten Abschnitten wie beispielsweise Funktionen zu erfassen.

require_once ('Benchmark/Profiler.php'); 
 
// Neues Objekt mit Autostart ableiten 
$profiler = new Benchmark_Profiler(true); 
 
function foo($param) 
{ 
   // Zugriff auf globales Objekt bereitstellen 
   global $profiler; 
   // Zeitmessung fuer die Section starten 
   $profiler->enterSection("foo $param"); 
      // Komplizierter Code 
   // Zeitmessung fuer die Section beenden 
   $profiler->leaveSection("foo $param"); 
   return; 
} 
 
function bar() 
{ 
   // Zugriff auf globales Objekt bereitstellen 
   global $profiler; 
   // Zeitmessung fuer die Section starten 
   $profiler->enterSection("bar"); 
      // Komplizierter Code 
   // foo mit Parameter 4 aufrufen 
   foo(4); 
   $profiler->leaveSection("bar"); 
   return; 
} 
 
foo(5); 
foo(1); 
bar(); 
bar();

Listing 13.2 Laufzeit-Messung für verschiedene Sections

Wie Sie sehen, ist die Nutzung hier sehr ähnlich. Allerdings gibt es kein setMarker(), dafür aber ein enterSection() und ein leaveSection(). Diese beiden Methoden definieren, wann die Ausführung eines Abschnitts beginnt und wann sie endet. Hierbei muss jeweils derselbe Bezeichner genutzt werden. In der Funktion foo() wurde der Parameter als Teil des Section-Namens genutzt. Somit können Sie in der generierten Übersicht auch gleich erkennen, wie die Parameter Einfluss auf die Laufzeit nehmen.

Abbildung 13.2 Ausgabe des Scripts

In Abbildung 13.2 können Sie sehen, dass Sections mit identischen Namen zusammengefasst werden. Die Aufrufe von foo() erscheinen nur dann als identische Sections, wenn die Parameter identisch sind. Für jede Section wird eine komplette Ausführungszeit und eine Netto-Ausführungszeit ausgegeben. Ruft eine Section keine Funktion auf, sind diese identisch. In der Zeile, die mit bar beginnt, können Sie aber sehr schön den Unterschied erkennen. Hier wird der Aufruf foo(4) aus der »total execution time« herausgerechnet, was die Netto-Zeit ergibt.

Neben der Laufzeit wird zusätzlich noch ausgewiesen, von wo die Section aufgerufen wurde und welche anderen Sections sie wie oft aufgerufen hat.

Auch hier können Sie die Laufzeitinformationen als Rohdaten auslesen, ohne sie direkt auf dem Bildschirm auszugeben. Um die Information zu einer Section auszulesen, können Sie die Methode getSectionInformations() nutzen, die den Namen der gewünschten Section übergeben bekommt. Das zurückgelieferte Array für die Section bar ist folgendermaßen aufgebaut:

array(6) { 
  ["time"]=> 
  float(0.670147180557) 
  ["percentage"]=> 
  string(4) "0.00" 
  ["calls"]=> 
  array(1) { 
    ["foo 4"]=> 
    int(2) 
  } 
  ["num_calls"]=> 
  int(2) 
  ["callers"]=> 
  array(1) { 
    ["Global"]=> 
    int(2) 
  } 
  ["netto_time"]=> 
  float(0.488699197769) 
}

Zusätzlich steht die Methode getAllSectionsInformations() zur Verfügung, die die Daten zu allen Sections auf einmal zurückgibt.

Um zu testen, wie schnell oder langsam eine bestimmte Funktion implementiert ist, reicht es oft nicht aus, diese einmal aufzurufen, da die Laufzeit bei einem Aufruf einfach zu kurz ist, um ein valides Ergebnis generieren zu können. Mit anderen Worten: Die fragliche Funktion muss mehrfach ausgeführt werden. Hierzu können Sie natürlich die Funktion mithilfe einer Schleife mehrfach aufrufen und Sections in die Funktion einfügen. Allerdings sieht das Paket für dieses Problem auch eine elegantere Lösung vor.

Die Klasse Benchmark_Iterate, die in Benchmark/Iterate.php definiert ist, stellt noch Funktionalitäten zur Verfügung, die sich genau dieser Problematik annehmen.

Die Methode run() bekommt die Anzahl der gewünschten Wiederholungen sowie den Namen der Funktion oder Methode übergeben, die ausgeführt werden soll. Weitere Marker oder Abschnitte müssen nicht deklariert werden. Übergeben Sie dem Konstruktor ein true, wird automatisch eine Ausgabe generiert. Hierbei werden die Daten für jeden einzelnen Funktionsaufruf dargestellt. Da das eine sehr lange Liste ergeben kann, können Sie die ermittelten Daten alternativ auch mit get() auslesen.

require_once ('Benchmark/Iterate.php'); 
$benchmark = new Benchmark_Iterate(true); 
function foo($param1, $param2) 
{ 
   // komplizierter Code; 
} 
$benchmark->run(5, 'foo', 'p1', 2);

Listing 13.3 Laufzeit-Messung einer Funktion

Wie Sie in Listing 13.3 sehen können, bekommt run() die Anzahl der Aufrufe und den Namen der Funktion übergeben. Da die Funktion foo() aber Parameter erwartet, müssen auch diese an run() übergeben werden. Sie werden einfach nach dem Funktionsnamen übergeben und bei jedem Aufruf weitergereicht. Das Resultat finden Sie in Abbildung 13.3.

Abbildung 13.3 Ausgabe des Scripts im Browser

 <<   zurück
     
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: PHP PEAR
PHP PEAR
Jetzt Buch bestellen!
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: PHP 5.6 und MySQL 5.7






 PHP 5.6 und
 MySQL 5.7


Zum Rheinwerk-Shop: Einstieg in PHP 5.6 und MySQL 5.6






 Einstieg in PHP 5.6
 und MySQL 5.6


Zum Rheinwerk-Shop: Responsive Webdesign






 Responsive Webdesign


Zum Rheinwerk-Shop: Moderne Websites entwickeln






 Moderne Websites
 entwickeln


Zum Rheinwerk-Shop: MySQL 5.6






 MySQL 5.6


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo








Copyright © Rheinwerk Verlag GmbH 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern