Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

Смотрите: http://php.net/manual/en/function.file-exists.php

Ответ 2

Использовать абсолютные пути! В зависимости от вашего параметра include_path PHP проверяет все (!) эти диски, если вы проверяете относительные пути к файлам! Вы можете временно отключить include_path до проверки существования.

realpath() делает то же самое, но я не знаю, быстрее ли это.

Но вход/выход доступа к файлам всегда медленный. Доступ к жесткому диску IS медленнее, чем вычисление чего-либо в процессоре, как правило.

Ответ 3

Самый быстрый способ проверить наличие локального файла - 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

Ответ 4

Мы возвращаемся к фиктивному изображению, если файл отсутствует

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

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

Просто мысль.

Ответ 5

file_exists() автоматически кэшируется PHP. Я не думаю, что вы найдете более быструю функцию в PHP, чтобы проверить наличие файла.

Смотрите этот поток.

Ответ 6

Создайте процедуру хеширования для оверки файлов в несколько подкаталогов.

filename.jpg → 012345 → /01/23/45.jpg

Кроме того, вы можете использовать mod_rewrite, чтобы вернуть образ заполнителя для запросов в ваш каталог изображений, который 404.

Ответ 7

Тесты с 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'  

Ответ 8

Если вы проверяете только существующий files, используйте is_file(). file_exists() проверяет наличие существующего каталога файлов ИЛИ, поэтому, возможно, is_file() может быть немного быстрее.

Ответ 10

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

Ответ 11

Если вы хотите проверить наличие файла изображения, гораздо быстрее использовать getimagesize!

Быстрее локально и удаленно!

if([email protected]($image_path_or_url)) // False means no imagefile
 {
 // Do something
 }

Ответ 13

Я нахожу 1/2ms за звонок очень, очень доступным. Я не думаю, что есть намного более быстрые альтернативы, так как функции файла очень близки к нижним уровням, которые обрабатывают операции с файлами.

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

Ответ 14

Вы можете сделать cronjob, чтобы периодически создавать список изображений и хранить их в DB/file/BDB/...

Каждые полчаса должно быть хорошо, но обязательно создайте интерфейс к кешу reset в случае добавления/удаления файла.

И затем, также легко запустить find. -mmin -30 -print0 в оболочке и добавить новые файлы.

Ответ 15

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

Затем вам просто нужно сделать запрос к базе данных, чтобы найти путь к запрашиваемому файлу.

Ответ 16

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

<?php
for ($n=1;$n<100;$n++){
clearstatcache();
[email protected]("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>

Ответ 17

Старый вопрос, я собираюсь добавить здесь ответ. Для php 5.3.8 is_file() (для существующего файла) на порядок быстрее. Для несуществующего файла время почти идентично. Для PHP 5.1 с eaccelerator они немного ближе.

PHP 5.3.8 w и без 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() 14x + is_link() 14x (is_file() || is_link()) и в итоге будет в 2 раза медленнее. Если существование файла изменяет LOT, используйте stream_resolve_include_path().

Так что это зависит от вашего сценария использования.

Ответ 18

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

Ответ 19

Я даже не уверен, что это будет быстрее, но похоже, что вы все равно хотите сравнить soooo:

Создайте кеш большого массива всех путей изображения.

$array = array('/path/to/file.jpg' => true, '/path/to/file2.gif' => true);

Обновите кеш ежечасно или ежедневно в зависимости от ваших требований. Вы сделали бы это, используя cron, чтобы запустить PHP script, который будет рекурсивно проходить через каталог файлов для создания массива путей.

Если вы хотите проверить, существует ли файл, загрузите свой кешированный массив и просто проверьте isset() для быстрого поиска индекса массива:

if (isset($myCachedArray[$imgpath])) {
    // handle display
}

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