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.

Jan hat 152 Beiträge geschrieben

24 Kommentare zu “Dateien performant einfügen

  1. Kevin sagt:

    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:

    Die Variante mit $_SERVER[‘DOCUMENT_ROOT’] ist bei mir schon immer Standard und ich hatte damit noch nie ein Problem 🙂

  3. Jan sagt:

    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:

    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:

    @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:

    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:

    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:

    @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. Ralf sagt:

    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);

  10. Benni sagt:

    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.

  11. NightWalker sagt:

    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.

  12. Sören sagt:

    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’

  13. Christian sagt:

    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.

  14. Hopper sagt:

    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…

  15. GhostGambler sagt:

    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.

  16. Daniel sagt:

    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.

  17. Michael sagt:

    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?

  18. Michael sagt:

    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!)

  19. Jan sagt:

    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.

  20. Kira-Bianca sagt:

    Moin Jan,
    moin Andere,

    Dein Beitrag, Jan, ist zwar schon uralt. Doch ich bin durch Zufall bei der Suche nach etwas ganze anderem darauf gestoßen (Herr google.de hat mal wieder nicht kapiert, was ich will. 😉 ) Und ich habe da noch eine Variante, die ich selber schon seit Jahren bei allen meinen Projekten einsetze.

    Denn viele Script-Pakete liegen nicht direkt auf der Server-Root, sondern sind im Ganzen in einem Unterverzeichnis. Wenn ich da mit

    require($_SERVER[‘DOCUMENT_ROOT’].”/Paketverzeichnis/…)

    in einem Skript etwas includen will, läuft das Skript nicht mehr, sobald aus welchen Gründen auch immer der Name des Paketverzeichnisses geändert wird. Es müsste in allen Skripten des Pakets (und das sind oft nicht wenige) “Paketverzeichnis” in “neuesPaketverzeichnis” geändert werden. Administratorischen ein unhaltbarer Zustand!

    Aber wie Du schon schreibst, ist die direkte Adressierung die optimalste. Um die ohne administratorischen Aufwand anwenden zu können, muss einfach (am besten auf der Root des Skriptpakets) eine Datei erstellt werden, welche die Angaben zur direkten Adressierung enthält. Bei mir heißt sie immer .umgebung und enthält alle Daten als definierte Konstanten, die sich auf die Umgebung und die verwendeten Datenbanken beziehen (deshalb auch der Punkt am Anfang, damit diese Datei von Außen nicht einlesbar ist; sie enthält u. a. auch Zugriffsdaten auf Server u. ä.). Beispiel:

    .umgebung:
    <?php
        define('ROOT', 'J:/xampp/htdocs/projektverzeichnis/');
    ?>
    Und bei einem Umzug auf einen durchs Internet erreichbaren Servers, ändere ich diese nur Datei entsprechend:
    .umgebung:
    <?php
        define('ROOT', 'http://kira-bianca.eu/projektverzeichnis/');
    ?>
    Wenn alle Projektskripte mit einem
    require(‘./indirekter_Pfad_zur_Projektroot/.umgebung’);

    beginnen und sich damit die Umgebungswerte holen, können sie im weiteren Verlauf mit direkter Adressierung arbeiten, egal, wo sich das Projektpaket befindet.

    Dieses ist zwar eine nicht optimale indirekte Adressierung, aber es ist auch die einzige im ganzen Skript. Und der indirekter_Pfad_zur_Projektroot ist in einem Projekt selbst bei einer Zusammenarbeit mehrerer Programmierer_innen fest definiert und bleibt konstant. Nach Einbindung der .umgebung kann immer direkt adressiert werden, (zum Beispiel:

    require(ROOT . ‘Unterverzeichnis_von_der_Projektroot_ausgehend/datei.php’

    . So muss bei einem Umzug des Gesamtpakets keines der Skripte geändert werden, sondern nur immer die .umgebung. Außerdem kann das Skript eins zu eins in andere Projekte übernommen werden, sofern diese ebenfalls mit der .umgebung arbeiten und dort die Konstante ROOT mit gleichem Zweck definiert ist.

    Besonders vorteilhaft ist diese Technik bei einem Modul für Datenbanken. Für Datenbank-Zugriffe habe ich nur ein einziges Skript (eine Klasse), welches ich immer ins Projektpaket kopiere (nicht include, sondern echtes physikalisches Kopieren). Die Methoden für den Zugriff (selectSingle, selectMulti, update, insert …) sind letztlich ja bei identischen Datenbanksystem doch immer dieselben; also warum immer neu schreiben?. Diese Datenbank-Klasse bezieht u. a. auch ihre Login-Daten aus der .umgebung. So kann ich die Klasse überall und für jede Datenbank unverändert verwenden und muss nur die entsprechende Datei .umgebung anpassen.

    P.S. Bei Erstellung dieses Threads (2009) waren Klassen in PHP ja noch nicht möglich. Aber heutzutage sollten sie genutzt werden, womit das Includen fast komplett entfällt.

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>