IPC 2012 Spring

Dateien performant einfügen

Vor einigen Wochen habe ich verschiedene include-Varianten miteinander verglichen und festgestellt, dass es keine Performance-Unterschiede gibt. Heute möchte ich untersuchen, ob und welchen Unterschied es macht, wenn man die einzufügende Datei absolut oder relativ adressiert.

Die Adressierung von Dateien kann bei der Webprogrammierung stets absolut oder relativ erfolgen, sowohl clientseitig als auch serverseitig. Während auf der Clientseite relative Pfadangaben genutzt werden sollten, um flexibler zu sein (und da der Browser die Pfade sowieso umwandelt, um beispielsweise ein Bild nachzuladen, das relativ referenziert wurde).

Bei der serverseitigen Programmierung gibt der Entwickler selbst an, welche Datei einzufügen ist und auch den Pfad zu dieser Datei. Grundsätzlich habe ich die Pfadangabemöglichkeiten in 3 Varianten unterteilt.
1. Relativ:

include("datei.php");

Diese Variante sieht man am häufigsten. Die einzufügende Datei liegt (meist) im gleichen Verzeichnis wie das aufrufende Script.

2. Semiabsolut (wahrscheinlich heißt das anders, ich habs mal so genannt):

include("./datei.php");

Auch hier liegt die einzufügende Datei im gleichen Verzeichnis wie das aufrufende Script. Das wird eindeutig gekennzeichnet durch das ./ vor dem Dateinamen.

3. Absolut:

include("c:/pfad/zu/datei/datei.php"); //Windows
include("/pfad/von/wurzel/datei.php"); //Linux

Hierbei wird der absolute Pfad von der Wurzel des Dateisystems angegeben. Dies ist recht unflexibel, da nach einem möglichen Umzug oder einem Ändern der Verzeichnisstruktur alle include-Befehle angepasst werden müssten (oder man setzt einen symbolischen Link).

Die Ergebnisse
Der Test erfolgte mit 1000 Durchläufen mit je 100 parallelen Aufrufen. Hier die Ergebnisse:

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
include_absolute.php 57.175 s 57.175 ms 100 %
include_semiabsolute.php 83.883 s 83.883 ms 146 % (+46%)
include_relative.php 93.548 s 93.548 ms 163% (+63%)

Die Unterschiede sind doch ziemlich groß. Also wer bisher, wie oben beschrieben, die relative Variante benutzt hat, sollte sich eventuell die Arbeit machen und seine Scripts auf eine der beiden anderen Varianten umschreiben. Ob nun die absolute oder die semiabsolute Variante gewählt wird, hängt auch davon ab, wie flexibel man bei einem möglichen Serverumzug (oder Hosterwechsel) sein möchte. Denn sehr oft hat der neue Server eine andere Ordnerstruktur als der alte und dann würde es schon nicht mehr funktionieren (bzw. müsste ein symbolischer Link gesetzt werden, siehe oben). Wer aber ohne Nachteil seine include-Befehle etwas beschleunigen möchte, der schreibt einfach ./ vor den Dateinamen.

Der Grund für den Geschwindigkeitsunterschied ist, dass die relativen Pfade erst aufgelöst werden müssen, damit die Datei auch wirklich gefunden werden.

PS: Hier habe ich gerade noch ein schönes Snippet gefunden, womit die absoluten Pfade genutzt werden können, ohne dass man so unflexibel wird, wie oben beschrieben:

<?php
include $_SERVER['DOCUMENT_ROOT']."/lib/sample.lib.php";
?>

Das funktioniert, da in der Variable $_SERVER['DOCUMENT_ROOT'] der Pfad zum Dokumentenverzeichnis gespeichert ist (standardmäßig htdocs bei Apache). Hierbei ist zu beachten, dass unter Umständen $_SERVER['DOCUMENT_ROOT'] den trailing slash bereits enthält. Dann muss entsprechend der nachfolgende Slash vor dem Verzeichnis weggelassen werden (hier: include $_SERVER['DOCUMENT_ROOT']."lib/sample.lib.php";), deshalb sollte man den Inhalt zuerst kurz prüfen.
Damit kann also jeder die schnellste include-Variante nutzen und man hat eigentlich keinerlei Nachteile – oder seht ihr welche?
Wie immer freue ich mich auf jede Menge Kommentare.

PS: Diesmal ganz vergessen: Die eingesetzten Scripts.

Schlagwörter: , , , ,

23 Kommentare bisher »

  1. Kevin sagt

    am 3. März 2009 @ 11:21

    Hi,
    hast du mal überprüft, wie es sich verhält, wenn du die Chose in OOP-Manier mit __autoload() includierst?

    Ansonsten find ich den Artikel interessant. Hab mich selbst noch nicht so sehr mit einfügen von Dateien beschäftigt und deshalb umso mehr erstaunt, wieviel das ausmacht.

    Gruß

  2. Felix sagt

    am 3. März 2009 @ 11:31

    Die Variante mit $_SERVER['DOCUMENT_ROOT'] ist bei mir schon immer Standard und ich hatte damit noch nie ein Problem :)

  3. Jan sagt

    am 3. März 2009 @ 11:32

    Die Funktion __autoload() wurde ja schon in dem anderen Beitrag angesprochen. Ich verstehe nicht, inwiefern das hierfür relevant ist, denn in dieser Methode muss ja dann auch irgendwie eine Datei eingefügt werden – und dann wirds ja gerade wieder interessant, weil man sich dann entscheiden muss, welche Funktion (include, require usw.) man nimmt und wie man die Datei adressiert (siehe dieser Beitrag).
    Aber __autoload() an sich stellt ja keine Möglichkeit dar, eine Datei einzufügen.

  4. Jan sagt

    am 3. März 2009 @ 11:55

    Zu deinem PS-Snippet:
    Das / vor lib sollte man da wohl meist weglassen, zu 99% ist das / schon in $_SERVER['DOCUMENT_ROOT'] mit drin. Aber natürlich jeweils überprüfen, kann je nach System variieren.

  5. Andreas sagt

    am 3. März 2009 @ 12:29

    @Jan: Deine Aussage "zu 99%" stimmt sicher nicht. Das Vorkommen eines "/" am Ende vom DocumentRoot sollte eher die Ausnahme sein (wobei ich nicht genau weiß, wovon es abhängt – aber auf allen meinen System ist kein / mit drin).

  6. Jan sagt

    am 3. März 2009 @ 13:35

    Ich habe im Beitrag einen entsprechenden Hinweis über den trailing slash eingebaut. Man sollte es eben vorher überprüfen, was genau in $_SERVER['DOCUMENT_ROOT'] steht.
    Vielen Dank für den Hinweis.

  7. LandsersFaust sagt

    am 3. März 2009 @ 14:26

    Das die absolute Variante die Schnellste ist, ist doch logisch. Schließlich wird bei relativen Angaben noch der includepath durchsucht und je nachdem wieviel Pfade da angegeben sind desto länger dauerts.

    Und ne $_SERVER hierfür zu nutzen ist ja wohl ziemlich unkuhl. Dafür nimmt man eine Kombination aus realpath, dirname und __FILE__

  8. Jan sagt

    am 3. März 2009 @ 15:02

    @LandsersFaust: In __FILE__ steht doch nur der aktuelle Dateiname. Gib mal bitte ein Beispiel für das Einfügen einer Datei, die in einem Unterordner lib liegt (wie in meinen Beispielen).

  9. lala sagt

    am 3. März 2009 @ 15:17

    was hier noch fehlt:
    - ausgabe von get_inlcude_path
    - ergebnisse mit set_include_path('.');

  10. Ralf sagt

    am 3. März 2009 @ 17:31

    Interessant wäre es noch zu wissen ob es einen Unterschied macht wenn man den Pfad in einer variablen ablegt. Also z.B.:
    $foo = "/pfad/zu/der/";
    include ($foo."datei.php");

    Unter Umständen könnte man sich den Pfad zu jeder Datei auch vorher basteln:
    $path = "/pfad/zu/der/";
    $foo = $path."datei1.php";
    $bar = $foo."andere_datei.php";

    include($foo);
    include($bar);

  11. Benni sagt

    am 3. März 2009 @ 19:01

    Was in diesem Zusammenhang auch interessant wäre ist, ob es einen deutlichen Unterschied gibt, wenn man seinen Includepfad auf ein Verzeichnis setzt und von diesem aus relative Pfade nutzt, wie z.B. im Zend Framework.

  12. NightWalker sagt

    am 3. März 2009 @ 22:11

    Mich würde auch interessieren wie die Performance ist, wenn man $_SERVER['DOCUMENT_ROOT']/datei.php mit /datei.php vergleicht.
    Denn $_SERVER['DOCUMENT_ROOT'] muss ja auch erstmal aufgelöst werden. Könnte mir vorstellen das der Performance gewinn dadurch sehr gering sein würde.

  13. Sören sagt

    am 3. März 2009 @ 22:39

    LandsersFaust mein folgendes:

    dirname(__FILE__)

    um ein Verzeichnis weiter "hoch" zu kommen, muss dann

    dirname(dirname(__FILE__)) genutzt werden, oder eben dirname(__FILE__).'/../test.php'

  14. Christian sagt

    am 4. März 2009 @ 20:17

    Die Sache mit "$_SERVER[‘DOCUMENT_ROOT’]" kann unter umständen zu Sicherheitsproblemen führen (register_globals – PHPList hatte vor kurzem ein ähnliches Problem)

    Das einfachste ist es, im Bootstrappingfile ein "define('[PREFIX]_ROOT', '…') und dann immer mit absoluten Pfaden arbeiten.

    Ein Vergleich mit realpath wäre noch interessant – ist vermutlich aber langsamer, da jedes mal ein stat-Call ausgefuehrt werden muss.

  15. Hopper sagt

    am 5. März 2009 @ 08:48

    Wie wäre es denn wenn Du Deine Testskripte veröffentlichst? Lässt sich das machen? Dann könnte man die Test mit dem Pfad in einer Variablen schnell selbst ausprobieren…

  16. Jan sagt

    am 5. März 2009 @ 09:21

    @Hopper: Huch, die habe ich diesmal ganz vergessen. Habe Sie nun im Beitrag als PS noch angehangen.

  17. GhostGambler sagt

    am 5. März 2009 @ 11:41

    document_root ist eine Variable (das gilt übrigens auch für den anderen Kommentar mit den Variablen), deren Auflösung für jeden PHP-Programmierer praktisch in konstanter Zeit angenommen werden kann (auch wenn es wahrscheinlich auf log n hinaus laufen dürfte, aber die Feinheit ist einfach irrelevant).
    Da wird so viel mehr an Ressourcen dadurch verbrannt, weil man Objektorientiert programmiert, da macht das hier keinen Kohl fett…

    Ein Sicherheitsproblem sehe ich bei der Verwendung von document_root nicht. Klar, kann es da ungünstige Konstellationen geben, wo das tatsächlich zum Sicherheitsproblem wird. Aber die Beispiele, die mir aktuell einfallen sind … da muss schon so viel gleichzeitig schief laufen, das ist fast unmöglich…
    Mit dem Stichwort phplist habe ich bei Google nichts aussagekräftiges gefunden. Scheint da also scheinbar auch nicht so gewichtig gewesen zu sein.

  18. Daniel sagt

    am 5. März 2009 @ 14:37

    Ich mach das schon länger so, dass ich die relativen und absoluten Pfade in meiner Config-Datei in Konstanten speichere und dann nur noch diese Konstanten einfügen muss.

    Spart viel Zeit und man muss sich um nichts mehr Gedanken machen.

  19. Michael sagt

    am 15. März 2009 @ 20:48

    Ich hatte eigentlich gedacht, dass eine Datei nur einmal während der Skriptlaufzeit inkludiert wird.

    Verstehe ich das richtig, dass eine Include-Datei innerhalb einer Schleife bei jedem Durchlauf neu vom Server geladen wird?

  20. Michael sagt

    am 15. März 2009 @ 21:32

    Es ist tatsächlich so, hab's eben getestet an einem Skript, dessen Performance ich schon länger optimiere.

    Das Skript erzeugt einen Bulk für rund 200.000 Shop-Artikel, und das Include enthält die Preiskalkulation.

    Nachdem ich das Include durch den eigentlichen Code ersetzt hatte, lief das Skript 10x so schnell!

    Von daher kann ich folgenden Tipp zu diesem Artikel ergänzen:

    Wenn es auf maximale Performance ankommt, sollte man auf Includes innerhalb von häufig durchlaufenen Schleifen möglichst verzichten, und stattdessen den Include-Code direkt dort hinkopieren.

    (Bei Änderungen im Include-Code darf man diese zweite Stelle natürlich nicht vergessen!)

  21. Jan sagt

    am 16. März 2009 @ 13:14

    In einer Schleife einen Include auszuführen, ist meist der falsche Weg. Wenn includes innerhalb der Schleifen notwendig sind, dann ist die einzufügende Datei meist einfach "runtergeschrieben". Durch den Einsatz von Funktionen kann dies behoben werden.

  22. David Lepold sagt

    am 26. April 2009 @ 12:03

    Ich nutze in 95% aller Fälle auch die $DOCUMENT_ROOT Lösung und bin froh zu lesen, dass es die richtige Wahl ist/war :-) Danke für die Arbeit!

  23. Jannik sagt

    am 29. April 2009 @ 17:33

    @Michael: include_once wäre da wohl die passende Lösung…

Komentar RSS · TrackBack URI

Hinterlasse einen Kommentar

Name: (erforderlich)

eMail: (erforderlich)

Website:

Kommentar: