Datei, bist du da?

Nachdem schon ein paar Mal angefragt wurde, was am Performantesten ist auf die Existenz einer Datei zu prüfen, hab ich ein paar wenige Funktionen in Augenschein genommen. Darunter hab ich für den lokalen Zugriff: file_exists und die jeweiligen aufgeschlüsselten Funktionen: is_file und is_dir getestet, sowie für dem remote Zugriff: get_headers, fsockopen, file_get_contents und curl.

Zuerst möchte ich mit den lokalen Varianten anfangen:
Es gibt keine Großen Unterschieden, doch zeigen über mehrere Testläufe die direkten Funktionen: is_file und is_dir einen kleinen Vorsprung.
Der Benchmark lief mit 100.000 Durchläufen und das 100 mal um einen guten Mittelwert zu erhalten, nachdem es ja auch immer auf den Load des Rechners ankommt. Doch auch nach mehrmaligem Durchlaufen beläuft sich das Ergebnis immer darauf, dass file_exists bei 100.000 Zugriffe um 0.03-0.05s langsamer ist. Das bedeutet, file_exists ist als einzelner Aufruf, wenn wir den Aufruf von clearstatcache() nicht beachten um 0.3-0.5μs langsamer.

file_exists  -> is_file
0.522522807121 -> 0.471928157806
0.634317629337 -> 0.579254751205
0.525248453617 -> 0.468847446442

auch meine späteren Tests mit Ordnern haben das Ergebnis noch einmal bestätigt.

datei  -> file_exists: 0.56094949007
datei  -> is_file : 0.503009655476
ordner -> is_dir : 0.503238601685
ordner -> file_exists: 0.558529658318

Man sollte also, wenn man weiß, dass es sich um eine Datei oder einen Ordner handelt eher auf is_file oder is_dir zurückgreifen um dessen Existenz zu überprüfen. Ich frag mich woran das liegt und wenn jemand Lust hat, kann er gerne mal Code wälzen. Meine Vermutung ist, dass file_exists erst prüft, was es ist und dass das den Overhead ausmacht.

Man sollte hier noch dazusagen, dass sehr viele File-Funktionen in PHP ihre Rückgabe cachen. Das bedeutet, wenn ihr einmal den Status abgefragt habt, dann wird er beim nächsten mal aus dem Cache geladen. Wenn ihr also 2 mal den Status einer Datei in einer Datei abfragen wollt, müsst ihr UNBEDINGT den Cache wieder mit clearstatcache() löschen (sonst erhaltet ihr beim zweiten Aufruf die gleiche Rückgabe wie beim ersten Mal)

Wie sieht es nun mit Remote-Files aus. Hier unterscheiden sich die Ergebnisse extrem, das hat auch mehrere Gründe. Am Performantesten ist wohl eine eigene Socket-Variante. Aber hier muss ich dazu erwähnen, dass ich nur auf die Existenz einer Datei geprüft habe, deswegen hab ich auch über den Socket nicht die komplette Datei geholt, sondern nur die ersten 1024 Bytes um den Status Code auszulesen. Damit war es sogar noch schneller als get_headers.
Ich habe nicht probiert diese 1024 Bytes noch zu tweaken und nur soviele Bytes zu holen, wie ich wirklich für den Status Code bräuchte (damit könnte man sicher nochmal ein paar Micro/Nano-Sekunden rausholen).

Natürlich schwanken diese Ergebnisse von Server zu Server und von Datei zu Datei, aber es sollte klar sein, dass, wenn man nur einen Teil der zurückgelieferten Nachricht braucht, es deutlich schneller ist sich nicht die ganze Datei zu holen.
Der Vollständigkeit halber hier die kompletten Testergebnisse:

Ergebnisse:
get_headers: 0.90237588644
file_get_contents: 4.92363333464
fsockopen: 0.611620130539
curl: 4.66347056866

Zu den Durchläufen und Mittelwerten muss ich auch noch etwas sagen. Ich konnte nicht soviele Durchläufe machen (ist ja doch recht server-belastend). Der Benchmark ist mit 5 Iterationen gelaufen und das 100mal. D.h. fsockopen hat im Mittelwert 0.611s gebraucht um 5mal eine GET Abfrage rauszuschicken und den Status Code auszulesen. Die Abfrage lief auf ein Bild, dass auf einem Server liegt. Bildgröße weiß ich leider nicht, ist aber auch nicht entscheidend.

Anders sieht es natürlich aus, wenn man noch den Inhalt der Datei braucht, dann würde ich auf jeden Fall auf curl zurückgreifen.

Wenn ihr noch Möglichkeiten habt, auf die Existenz zu testen, dann Benchmarke ich die gerne noch. Bitte beschreibt dieVariante einfach in einem Kommentar.

hinzugefuegt:
Nachdem es Anscheinend nicht ganz klar geworden ist, wie ich Benchmarke, füge ich hier mal einen Teil des Bench.-Codes hinzu:

1
2
3
4
5
6
7
8
9
for($a = 0; $a < 100; $a++) {
    $bench->mark('1');
    for($i = 0; $i < 100000; $i++) {
        file_exists($file);
        clearstatcache();
    }
    $bench->mark('2');
}
print $bench->print('2-1');

was er macht ist: er lässt 10^5 mal durchlaufen auf 1ne datei zuzugreifen…misst aber alle 100.000 zugriff in einer Zeit. Alle Ergebnisse die oben sind, sind im prinzip 100.000 Zugriffe.
Der Benchmark für die Remote sachen sehen nicht anders aus, ausser, dass dort nur 10 durchläufe in der inneren Schleife gemacht wurden, aber 100 in der äusseren, um ein möglichst breites feld zu kriegen 🙂
Hoffe, das hat ein paar Sachen geklärt

Dieser Beitrag wurde in   PHP veröffentlicht.
Fügen Sie ein Lesezeichen für den   permanenten Link hinzu.

nuit hat 1 Beitrag geschrieben

9 Kommentare zu “Datei, bist du da?

  1. Nick sagt:

    Hallo und danke für diesen Test 🙂
    Eine Frage noch (v.a. zu den lokalen Tests): Sind die Werte nun für je 1 Durchlauf oder je 100.000 Durchläufe?
    Weil du schreibst die Unterschiede sind „nicht groß“, 30ms finde ich extrem viel, wenn es sich auf 1 Zugriff bezieht. Wenn ich da in einer Schleife mal eben kurz 500 Dateien auf Existenz prüfen möchte, liegt alleine der Unterschied bei 15 (!) Sekunden.
    Für 100.000 Zugriffe erscheinen mir dann 30ms Unterschied schon wieder extrem wenig, das wären dann ja nur 300 Nanosekunden / 0,3 Mikrosekunden pro Zugriff?

  2. nuit sagt:

    @nick
    die werte sind für 100.000 Durchläufe in der je auf 1ne Datei getestet wird. Dazu kommt in der Schleife aber jedes mal noch das löschen des Stats. Ich ignoriere mal das stat, dann sind wir auf 0.05s unterschied, bei 100.000 Tests…d.h. es ist ein Unterschied von 0.5μs unterschied in der Abfrage (im Schnitt)
    also dein zweites stimmt schon 😉 ich sag ja: extrem wenig 🙂

  3. R0b sagt:

    Ich gehe davon aus, dass die oben genannten Zahlen jeweils Mittelwerte sind. Daher kann ich Nicks Aussage nur zustimmen.
    Danke für den Benchmark. Werde ich mir zu Herzen nehmen.

    *thumbs up*

  4. Christian sagt:

    Sehr interessant, wobei die Unterschiede eher marginal sind. Interessieren würde mich auch noch die Performance von is_readable(), was gleichzeitig auch überprüft, ob die Datei lesbar ist.

  5. Tobi sagt:

    if(@fopen(‚lol.txt‘,’r‘))
    {
    echo ‚Datei vorhanden‘;
    }
    else
    {
    echo ‚Datei nicht vorhanden‘;
    }

    (Sorry beim ersten Mal hat er meinen PHP-Code nicht gespeichert)

  6. Benjamin sagt:

    Aber ist es nicht so, dass file_exists() wirklich die Existenz einer Datei prüft und is_file() nur auf einen richtigen Link?

    Falls dem so wäre, wäre is_readable() interessant, damit könnte man file_exists() außen vor lassen.

  7. Julius sagt:

    Sinn machen die Ergebnisse natürlich schon, und es ist sicher auch nett, zu sehen, wie groß, bzw. eher, wie klein die Unterschiede ausfallen. Sehe da keinen Anpassungsbedarf nun, aber immerhin hat man die Gewissheit, dass die Unterschiede komplett vernachlässigbar sind.

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>