file_exists () слишком медленный в PHP. Может ли кто-нибудь предложить более быструю альтернативу?

При отображении изображений на нашем веб-сайте мы проверяем, существует ли файл с вызовом file_exists() . Мы возвращаемся к фиктивному изображению, если файл отсутствует.

Однако профилирование показало, что это самая медленная часть генерации наших страниц с file_exists() занимающим до 1/2 мс на файл. Мы тестируем только 40 или около того файлов, но это все равно толкает 20 мс на время загрузки страницы.

Может ли кто-нибудь предложить способ сделать это быстрее? Есть ли лучший способ тестирования, если файл присутствует? Если я создам какой-то кеш, как мне его синхронизировать.

file_exists() должна быть очень недорогой. Также обратите внимание, что file_exists создает собственный кеш для повышения производительности.

См .: http://php.net/manual/en/function.file-exists.php

Используйте абсолютные пути! В зависимости от вашего параметра 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 ()? Но я не уверен, что это быстро.

http://www.php.net/manual/en/function.glob.php

Я нахожу 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 } 

Потребуется накладные расходы на загрузку кеша, но, надеюсь, он будет достаточно мал, чтобы оставаться в памяти. Если у вас есть несколько изображений, которые вы просматриваете на странице, вы, вероятно, заметите более значительную прибыль, так как вы можете загрузить кеш при загрузке страницы.