файл exists () слишком медленный в PHP. Кто может предложить более быструю альтернативу?
при отображении изображений на нашем веб-сайте мы проверяем, существует ли файл с вызовом file_exists()
. Мы возвращаемся к фиктивному изображению, если файл отсутствовал.
однако профилирование показало, что это самая медленная часть генерации наших страниц с file_exists()
взяв 1/2 МС в файл. Мы тестируем только 40 или около того файлов, но это все еще толкает 20 мс на время загрузки страницы.
может ли кто-нибудь предложить способ сделать это быстрее? есть ли лучший способ проверки, если файл существует? Если я создаю какой-то кэш, как я должен синхронизировать его.
19 ответов
file_exists()
должно быть очень недорогой операцией. Заметьте также, что file_exists
строит собственный кэш, чтобы помочь с производительностью.
использовать абсолютные пути! в зависимости от вашего include_path
настройка PHP проверяет все (!) эти dirs, если вы проверяете относительные пути к файлам! Вы можете сбросить include_path
временно перед проверкой существования.
realpath()
делает то же самое, но я не знаю, если это быстрее.
но доступ к файлу I / O всегда медленный. Жесткий диск IS медленнее, чем вычисление чего-то в процессоре, обычно.
самый быстрый способ проверить наличие локального файла -stream_resolve_include_path ():
if (false !== stream_resolve_include_path($s3url)) {
//do stuff
}
результаты 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%
в тесте используются абсолютные пути. Тестовый источник здесь. Версия PHP:
PHP 5.4.23-1~dotdeb.1 (cli) (построенный: Dec 13 2013 21:53:21)
Авторское право (c) 1997-2013 PHP Группа
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
мы возвращаемся к фиктивному изображению, если файл отсутствует
Если вы просто заинтересованы в том, чтобы вернуться к этому фиктивному изображению, вы можете подумать о том, чтобы позволить клиенту вести переговоры с сервером посредством перенаправления (на фиктивный образ) на файл-не-найден.
таким образом, у вас будет немного накладных расходов на перенаправление и не заметная задержка на стороне клиента. По крайней мере, вы избавитесь от "дорогого" (которого, я знаю, нет) звонка file_exists
.
просто мысли.
бенчмарки с PHP 5.6:
Существующий Файл:
0.0012969970 : stream_resolve_include_path + include
0.0013520717 : file_exists + include
0.0013728141 : @include
Недопустимый Файл:
0.0000281333 : file_exists + include
0.0000319480 : stream_resolve_include_path + include
0.0001471042 : @include
Недействительным Папку:
0.0000281333 : file_exists + include
0.0000360012 : stream_resolve_include_path + include
0.0001239776 : @include
код:
// 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]);
выполнение командной строки:
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'
file_exists()
автоматически кэшируется PHP. Я не думаю, что вы найдете более быструю функцию в PHP, чтобы проверить существование файла.
посмотреть этой теме.
создайте процедуру хэширования для разделения файлов на несколько подкаталогов.
именем.jpg -> 012345 -> /01/23/45.формат JPG
кроме того, вы можете использовать mod_rewrite для возврата изображения-заполнителя для запросов в каталог изображений, который 404.
Я не знаю, что вы хотите сделать, но вы могли бы просто пусть клиент справится с этим.
если вы проверяете только существующие files
используйте is_file()
.
file_exists()
проверяет наличие существующего файла или каталога, поэтому, возможно,is_file()
может быть немного быстрее.
Они все в одном каталоге? Если так мая стоит получить список файлов и хранить их в хэше и сравнивать с этим, а не со всеми поисками file_exists.
Если вы хотите проверить наличие файла изображения, a быстрее способ применения getimagesize !
быстрее локально и удаленно!
if(!@GetImageSize($image_path_or_url)) // False means no imagefile
{
// Do something
}
Я нахожу 1 / 2ms за вызов очень, очень доступным. Я не думаю, что есть намного более быстрые альтернативы, так как файловые функции очень близки к нижним слоям, которые обрабатывают файловые операции.
однако вы можете написать оболочку в file_exists (), которая кэширует результаты в memcache или аналогичный объект. Это должно сократить время почти до нуля в повседневном использовании.
вы можете сделать cronjob, чтобы периодически создавать список изображений и хранить их в DB/file/BDB/...
каждые полчаса должно быть хорошо, но обязательно создайте интерфейс для сброса кэша в случае добавления/удаления файла.
и затем, это также легко запустить find . - mmin -30-print0 на оболочке и добавить новые файлы.
при сохранении файла в папку, если загрузка прошла успешно, можно сохранить путь к таблице БД.
тогда вам просто нужно будет сделать запрос к базе данных, чтобы найти путь к запрошенному файлу.
Я пришел на эту страницу в поисках решения, и кажется, что fopen может сделать трюк. При использовании этого кода может потребоваться отключить ведение журнала ошибок для файлов, которые не найдены.
<?php
for ($n=1;$n<100;$n++){
clearstatcache();
$h=@fopen("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>
старый вопрос, я собираюсь добавить ответ здесь. Для PHP 5.3.8, is_file() (для существующего файла) на порядок быстрее. Для несуществующего файла время почти идентично. Для PHP 5.1 с eaccelerator, они немного ближе.
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)
)
есть несколько предостережений.
1) Не все "файлы" являются файлами, is_file () тесты для обычный файлы, не символические ссылки. Таким образом, в системе *nix вы не можете уйти просто с is_file (), если вы не обязательно что вы имеете дело только с обычными файлами. Для загрузок и т. д. Это может быть справедливым предположением или если сервер основан на Windows, который фактически не имеет символических ссылок. В противном случае вам придется проверить is_file($file) || is_link($file)
.
2) производительность определенно ухудшается для всех методов, если файл отсутствует и становится примерно равным.
3) Самое большое предостережение. Все методы кэшируйте статистику файла для ускорения поиска, поэтому, если файл меняется регулярно или быстро, удаляется, появляется, удаляет, то clearstatcache();
должен быть запущен, чтобы убедиться, что правильная информация о существовании файла находится в кэше. Поэтому я проверил их. Я опустил все имена файлов и все такое. Важно то, что почти все времена сходятся, за исключением stream_resolve_include, который в 4 раза быстрее. Опять же, на этом сервере есть eaccelerator, поэтому 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)
)
в принципе, идея в том, если вы на 100% уверены, что это файл, а не символическая ссылка или каталог, и, по всей вероятности, он будет существовать, а затем использовать is_file()
. Вы увидите определенный выигрыш. Если файл может быть файлом или ссылкой в любой момент, то не is_file() 14х + is_link() 14х (is_file() || is_link()
), и в конечном итоге будет 2x медленнее в целом. Если существование файла сильно меняется, используйте stream_resolve_include_path ().
так это зависит от вашего сценария использования.
Я думаю, что лучший способ-сохранить url-адрес изображения в базе данных, а затем поместить его в переменную сеанса, особенно когда у вас есть аутентификация. Таким образом, вам не нужно проверять каждый раз, когда страница перезагружается
Я даже не уверен, что это будет быстрее, но кажется, что вы все равно хотели бы проверить soooo:
создайте кэш большого массива всех путей к изображениям.
$array = array('/path/to/file.jpg' => true, '/path/to/file2.gif' => true);
обновить кэш часам или ежедневно в зависимости от ваших требований. Вы бы сделали это, используя cron для запуска PHP-скрипта, который будет рекурсивно проходить через каталог файлов для создания массива пути.
Если вы хотите проверить, существует ли файл, загрузите кэшированный массив и сделайте просто isset () проверьте быстрый поиск индекса массива:
if (isset($myCachedArray[$imgpath])) {
// handle display
}
все еще будут накладные расходы от загрузки кэша, но, надеюсь, он будет достаточно мал, чтобы остаться в памяти. Если у вас есть несколько изображений, которые вы проверяете на странице, вы, вероятно, заметите более значительные выгоды при загрузке кэша при загрузке страницы.