При отображении изображений на нашем веб-сайте мы проверяем, существует ли файл с вызовом file_exists()
. Мы возвращаемся к фиктивному изображению, если файл отсутствует.
Однако профилирование показало, что это самая медленная часть генерации наших страниц с file_exists()
занимающим до 1/2 мс на файл. Мы тестируем только 40 или около того файлов, но это все равно толкает 20 мс на время загрузки страницы.
Может ли кто-нибудь предложить способ сделать это быстрее? Есть ли лучший способ тестирования, если файл присутствует? Если я создам какой-то кеш, как мне его синхронизировать.
file_exists()
должна быть очень недорогой. Также обратите внимание, что file_exists создает собственный кеш для повышения производительности.
Используйте абсолютные пути! В зависимости от вашего параметра include_path
PHP проверяет все (!) Эти каналы, если вы проверяете относительные пути к файлам! Вы можете временно отключить include_path
прежде чем проверять существование.
realpath()
делает то же самое, но я не знаю, быстрее ли это.
Но вход / выход доступа к файлам всегда медленный. Доступ к жесткому диску медленнее, чем вычисление чего-либо в процессоре, как правило.
Самый быстрый способ проверить наличие локального файла – 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) (построено: 13 декабря 2013 21:53:21)
Copyright (c) 1997-2013 гг. Группа PHP
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
Мы возвращаемся к фиктивному изображению, если файл отсутствует
Если вам просто интересно вернуться к этому фиктивному изображению, вы можете подумать о том, чтобы позволить клиенту согласовать с сервером посредством перенаправления (на фиктивное изображение) на файл-не найден.
Таким образом, у вас будет только небольшая переадресация и небольшая задержка на стороне клиента. По крайней мере, вы избавитесь от «дорогого» (чего не знаю, я знаю) вызова file_exists
.
Просто мысль.
file_exists()
автоматически кэшируется PHP. Я не думаю, что вы найдете более быструю функцию в PHP, чтобы проверить наличие файла.
Смотрите эту тему .
Создайте процедуру хеширования для оверки файлов в несколько подкаталогов.
filename.jpg -> 012345 -> /01/23/45.jpg
Кроме того, вы можете использовать mod_rewrite, чтобы вернуть образ заполнителя для запросов в ваш каталог изображений, который 404.
Тесты с 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'
Если вы проверяете только существующие files
, используйте is_file()
. file_exists()
проверяет наличие существующего каталога файлов ИЛИ, поэтому, возможно, is_file () может быть немного быстрее.
Я точно не знаю, что вы хотите сделать, но вы можете просто позволить клиенту справиться с этим .
Все ли они в одном каталоге? Если это так, возможно , стоит получить список файлов и сохранить их в хеше и сравнить с ними, а не со всеми поисками file_exists.
Если вы хотите проверить наличие файла изображения, гораздо более быстрый способ – использовать getimagesize !
Быстрее локально и удаленно!
if(!@GetImageSize($image_path_or_url)) // False means no imagefile { // Do something }
Как насчет glob ()? Но я не уверен, что это быстро.
Я нахожу 1/2 мс за звонок очень, очень доступным. Я не думаю, что есть намного более быстрые альтернативы, так как функции файла очень близки к нижним уровням, которые обрабатывают операции с файлами.
Однако вы могли написать обертку 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)
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 () 14x + is_link () 14x ( is_file() || is_link()
) и в итоге будет в is_file() || is_link()
раза медленнее. Если существование файла изменяет LOT, используйте 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 }
Потребуется накладные расходы на загрузку кеша, но, надеюсь, он будет достаточно мал, чтобы оставаться в памяти. Если у вас есть несколько изображений, которые вы просматриваете на странице, вы, вероятно, заметите более значительную прибыль, так как вы можете загрузить кеш при загрузке страницы.