file exists () est trop lent en PHP. Quelqu'un peut-il Suggérer une solution plus rapide?
lors de l'affichage d'images sur notre site web, nous vérifions si le fichier existe avec un appel à file_exists()
. On revient à une image factice si le fichier a disparu.
cependant, le profilage a montré que c'est la partie la plus lente de générer nos pages avec file_exists()
prenant jusqu'à 1/2 ms par fichier. Nous testons seulement une quarantaine de fichiers, mais cela repousse toujours 20ms sur le temps de chargement de la page.
est-ce que quelqu'un peut suggérer un moyen de rendre cela plus rapide? Est-il un meilleur moyen de tester si le fichier est présent? Si je construis une cache, comment la synchroniser?
19 réponses
file_exists()
devrait être une opération très peu coûteuse. Notez aussi que file_exists
construit son propre cache pour aider à la performance.
utilisez des chemins absolus! en fonction de votre include_path
paramétrage PHP vérifie tout(!) ces DRS si vous vérifiez les chemins relatifs des fichiers! Vous pourriez désactiver include_path
temporairement avant de vérifier l'existence.
realpath()
fait la même chose mais je ne sais pas si c'est plus rapide.
mais l'accès aux fichiers e / s est toujours lent. Un accès disque dur est plus lent que le calcul quelque chose dans le processeur, normalement.
la manière la plus rapide de vérifier l'existence d'un fichier local est stream_resolve_include_path() :
if (false !== stream_resolve_include_path($s3url)) {
//do stuff
}
résultats de la Performance stream_resolve_include_path() vs file_exists() :
Test name Repeats Result Performance
stream_resolve 10000 0.051710 sec +0.00%
file_exists 10000 0.067452 sec -30.44%
dans le test utilisé chemins absolus. La source d'essai est ici . Version PHP:
PHP 5.4.23-1~dotdeb.1 (cli) (construit en Décembre 13 2013 21:53:21)
Copyright (c) 1997-2013 Le Groupe PHP
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
Nous fait retomber en une image factice si le fichier est manquant
si vous êtes simplement intéressé à revenir à cette image factice, vous pourriez envisager de laisser le client négocier avec le serveur au moyen d'une redirection (vers l'image factice) sur le fichier-not-found.
de cette façon, vous aurez juste une petite redirection au-dessus et un retard non-perceptible du côté du client. Au moins, vous vous débarrasserez du "cher"" (ce qui n'est pas, je sais) appel à file_exists
.
juste une pensée.
"151960920 des" points de référence avec PHP 5.6:
Fichier Existant:
0.0012969970 : stream_resolve_include_path + include
0.0013520717 : file_exists + include
0.0013728141 : @include
Fichier Invalide:
0.0000281333 : file_exists + include
0.0000319480 : stream_resolve_include_path + include
0.0001471042 : @include
Dossier Non Valide:
0.0000281333 : file_exists + include
0.0000360012 : stream_resolve_include_path + include
0.0001239776 : @include
Code:
// microtime(true) is less accurate.
function microtime_as_num($microtime){
$time = array_sum(explode(' ', $microtime));
return $time;
}
function test_error_suppression_include ($file) {
$x = 0;
$x = @include($file);
return $x;
}
function test_file_exists_include($file) {
$x = 0;
$x = file_exists($file);
if ($x === true) {
include $file;
}
return $x;
}
function test_stream_resolve_include_path_include($file) {
$x = 0;
$x = stream_resolve_include_path($file);
if ($x !== false) {
include $file;
}
return $x;
}
function run_test($file, $test_name) {
echo $test_name . ":\n";
echo str_repeat('=',strlen($test_name) + 1) . "\n";
$results = array();
$dec = 10000000000; // digit precision as a multiplier
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_error_suppression_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time*$dec] = '@include';
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_stream_resolve_include_path_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time * $dec] = 'stream_resolve_include_path + include';
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_file_exists_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time * $dec ] = 'file_exists + include';
ksort($results, SORT_NUMERIC);
foreach($results as $seconds => $test) {
echo number_format($seconds/$dec,10) . ' : ' . $test . "\n";
}
echo "\n\n";
}
run_test($argv[1],$argv[2]);
exécution en ligne de commande:
php test.php '/path/to/existing_but_empty_file.php' 'Existing File'
php test.php '/path/to/non_existing_file.php' 'Invalid File'
php test.php '/path/invalid/non_existing_file.php' 'Invalid Folder'
crée une routine de hachage pour découper les fichiers en plusieurs sous-répertoires.
nom du fichier.jpg -> 012345 -> /01/23/45.jpg
aussi, vous pouvez utiliser mod_rewrite pour retourner votre image de remplacement pour les requêtes à votre répertoire d'image que 404.
Je ne sais pas exactement ce que vous voulez faire, mais vous pourriez juste laisser le client s'en charger .
si vous ne faites que vérifier l'existence de files
, utilisez is_file()
.
file_exists()
vérifie pour un fichier ou un répertoire existant, donc peut-être is_file()
pourrait être un peu plus rapide.
Sont-ils tous dans le même répertoire? Si c'est le cas, il peut vaut la peine d'obtenir la liste des fichiers et de les stocker dans un hachage et de comparer avec cela plutôt que toutes les recherches de file_exists.
Si vous voulez vérifier l'existence d'un fichier image, un beaucoup plus rapide façon est d'utiliser des getimagesize !
plus rapide localement et à distance!
if(!@GetImageSize($image_path_or_url)) // False means no imagefile
{
// Do something
}
je trouve 1/2 ms par appel très, très abordable. Je ne pense pas qu'il existe des alternatives plus rapides, car les fonctions de fichiers sont très proches des couches inférieures qui gèrent les opérations de fichiers.
vous pouvez cependant écrire un wrapper à file_exists() qui cache les résultats dans une memcache ou une installation similaire. Cela devrait réduire le temps à presque rien dans l'utilisation quotidienne.
vous pouvez faire un cronjob pour créer périodiquement une liste d'images et les stocker dans DB/file/BDB/...
toutes les demi-heures devrait être parfait, mais assurez-vous de créer une interface pour réinitialiser le cache en cas d'Ajout/Suppression de fichier.
et puis, il est aussi facile de trouver . -mmin -30 -print0 sur la coque et ajouter de nouveaux fichiers.
lorsque vous enregistrez un fichier dans un dossier, si le téléchargement a été réussi, vous pouvez stocker le chemin vers une Table DB.
alors vous aurez juste à faire une requête à la base de données afin de trouver le chemin du fichier demandé.
je suis venu à cette page à la recherche d'une solution, et il semble que fopen peut faire l'affaire. Si vous utilisez ce code, vous pouvez désactiver la journalisation des erreurs pour les fichiers qui ne sont pas trouvés.
<?php
for ($n=1;$n<100;$n++){
clearstatcache();
$h=@fopen("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>
question ancienne, je vais ajouter une réponse ici. Pour php 5.3.8, is_file() (pour un fichier existant) est d'un ordre de grandeur plus rapide. Pour un fichier non-existant, les temps sont presque identiques. Pour PHP 5.1 avec eaccelerator, ils sont un peu plus proches.
PHP 5.3.8 w & w/o APC
time ratio (1000 iterations)
Array
(
[3."is_file('exists')"] => 1.00x (0.002305269241333)
[5."is_link('exists')"] => 1.21x (0.0027914047241211)
[7."stream_resolve_inclu"(exists)] => 2.79x (0.0064241886138916)
[1."file_exists('exists')"] => 13.35x (0.030781030654907)
[8."stream_resolve_inclu"(nonexists)] => 14.19x (0.032708406448364)
[4."is_file('nonexists)"] => 14.23x (0.032796382904053)
[6."is_link('nonexists)"] => 14.33x (0.033039808273315)
[2."file_exists('nonexists)"] => 14.77x (0.034039735794067)
)
PHP 5.1 w /eaccelerator
time ratio (1000x)
Array
(
[3."is_file('exists')"] => 1.00x (0.000458002090454)
[5."is_link('exists')"] => 1.22x (0.000559568405151)
[6."is_link('nonexists')"] => 3.27x (0.00149989128113)
[4."is_file('nonexists')"] => 3.36x (0.00153875350952)
[2."file_exists('nonexists')"] => 3.92x (0.00179600715637)
[1."file_exists('exists"] => 4.22x (0.00193166732788)
)
il y a quelques mises en garde.
1) tous les fichiers "" sont des fichiers, is_file() teste régulier les fichiers, pas les liens symboliques. Donc sur un système * nix, vous ne pouvez pas vous en tirer avec is_file () à moins que vous ne soyez sûr que vous ne traitiez que des fichiers réguliers. Pour les téléchargements, etc, cela peut être une hypothèse raisonnable, ou si le serveur est basé sur Windows, qui n'a pas réellement de liens symboliques. Sinon, vous devrez tester is_file($file) || is_link($file)
.
2) La Performance se dégrade définitivement pour toutes les méthodes si le fichier est manquant et devient à peu près égale.
3) la plus grosse mise en garde. Toutes les méthodes mettent en cache les statistiques de fichier pour accélérer la recherche, donc si le fichier change régulièrement ou rapidement, supprimé, réapparaît, supprime, alors clearstatcache();
doit être exécuté pour s'assurer que l'information d'existence de fichier correcte est dans le cache. J'ai donc testé les. J'ai laissé de côté tous les noms de fichiers et autres. L'important est que presque tous les temps convergent, sauf stream_resolve_include, qui est 4x aussi vite. Encore une fois, ce serveur a eaccelerator sur lui, donc YMMV.
time ratio (1000x)
Array
(
[7."stream_resolve_inclu...;clearstatcache();"] => 1.00x (0.0066831111907959)
[1."file_exists(...........;clearstatcache();"] => 4.39x (0.029333114624023)
[3."is_file(................;clearstatcache();] => 4.55x (0.030423402786255)
[5."is_link(................;clearstatcache();] => 4.61x (0.030798196792603)
[4."is_file(................;clearstatcache();] => 4.89x (0.032709360122681)
[8."stream_resolve_inclu...;clearstatcache();"] => 4.90x (0.032740354537964)
[2."file_exists(...........;clearstatcache();"] => 4.92x (0.032855272293091)
[6."is_link(...............;clearstatcache();"] => 5.11x (0.034154653549194)
)
fondamentalement, l'idée est, si vous êtes sûr à 100% que c'est un fichier, pas un lien symbolique ou un répertoire, et selon toute probabilité, il existera, puis utiliser is_file()
. Vous verrez un atout certain. Si le fichier peut être un fichier ou un lien symbolique à tout moment, alors le fichier is_file() 14x + is_link() 14x ( is_file() || is_link()
) qui a échoué sera 2 fois plus lent dans l'ensemble. Si l'existence du fichier change beaucoup, alors utilisation stream_resolve_include_path().
Donc, cela dépend de votre scénario d'utilisation.
je pense que la meilleure façon est de garder l'url de l'image dans la base de données et ensuite la mettre dans une variable de session en particulier quand vous avez l'authentification. De cette façon, vous n'avez pas à vérifier chaque fois qu'une page se recharge
Je ne suis même pas sûr si ce sera plus rapide, mais il semble que vous voudriez encore benchmark soooo:
construisez une cache d'un large éventail de tous les chemins d'image.
$array = array('/path/to/file.jpg' => true, '/path/to/file2.gif' => true);
mettre à Jour le cache horaire ou quotidien en fonction de vos exigences. Vous feriez cela en utilisant cron pour exécuter un script PHP qui ira récursivement à travers le répertoire de fichiers pour générer le tableau de chemins.
quand vous voulez vérifier si un fichier existe, chargez votre tableau caché et faites simplement isset () vérifiez pour une recherche d'index de tableau rapide:
if (isset($myCachedArray[$imgpath])) {
// handle display
}
il y aura toujours des frais généraux à partir du chargement du cache, mais on espère qu'il sera assez petit pour rester en mémoire. Si vous avez plusieurs images que vous enregistrez sur une page, vous remarquerez probablement plus de gains importants comme vous pouvez charger le cache sur le chargement de page.