web-dev-qa-db-ja.com

file_exists()は、PHPでは遅すぎます。誰かがより速い代替案を提案できますか?

ウェブサイトに画像を表示するとき、file_exists()を呼び出してファイルが存在するかどうかを確認します。ファイルが見つからなかった場合は、ダミーの画像にフォールバックします。

ただし、プロファイリングでは、これがfile_exists()を使用してページを生成する最も遅い部分であり、ファイルごとに最大1/2 msがかかることが示されています。テストするのは40ファイルほどですが、これでもページの読み込み時間に20msがプッシュされます。

これをより速くする方法を誰かが提案できますか?ファイルが存在するかどうかをテストするより良い方法はありますか?ある種のキャッシュを構築する場合、どのようにして同期を保つ必要がありますか。

34
Rik Heywood

file_exists()は、非常に安価な操作である必要があります。 file_existsは、パフォーマンスを向上させるために独自のキャッシュを構築します。

参照: http://php.net/manual/en/function.file-exists.php

28
RC.

絶対パスを使用! _include_path_設定に応じてPHPはすべてのファイルをチェックします!)相対ファイルパスをチェックすると、これらのディレクトリがチェックされます!_include_path_存在を確認する前に一時的に。

realpath()も同じですが、高速かどうかはわかりません。

ただし、ファイルアクセスI/Oは常に低速です。ハードディスクへのアクセス[〜#〜] is [〜#〜]通常、プロセッサで何かを計算するよりも遅くなります。

21
powtac

ローカルファイルの存在を確認する最も速い方法は stream_resolve_include_path() です。

if (false !== stream_resolve_include_path($s3url)) { 
  //do stuff 
}

パフォーマンス結果stream_resolve_include_path()vsfile_exists()

Test name       Repeats         Result          Performance     
stream_resolve  10000           0.051710 sec    +0.00%
file_exists     10000           0.067452 sec    -30.44%

テストでは絶対パスを使用しました。テストソースは here です。 PHPバージョン:

PHP 5.4.23-1〜dotdeb.1(cli)(ビルド:2013年12月13日21:53:21)
著作権(c)1997-2013 PHPグループ
Zend Engine v2.4.0、著作権(c)1998-2013 Zend Technologies

18

ファイルが見つからなかった場合は、ダミー画像にフォールバックします

このダミーイメージにフォールバックするだけの場合は、file-not-foundで(ダミーイメージへの)リダイレクトを使用して、クライアントにサーバーとのネゴシエーションを許可することを検討してください。

これにより、リダイレクトのオーバーヘッドがわずかになり、クライアント側で目立った遅延が発生します。少なくとも、あなたはfile_existsへの "高価"な(そうではない)呼び出しを取り除くでしょう。

ちょっとした考え。

11
jensgram

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'  
5

file_exists()はPHPによって自動的にキャッシュされます。 PHP=にファイルの存在を確認するためのより高速な関数があるとは思いません。

this thread を参照してください。

3
mculp

ファイルを複数のサブディレクトリに分割するためのハッシュルーチンを作成します。

filename.jpg-> 012345-> /01/23/45.jpg

また、mod_rewriteを使用して、404という画像ディレクトリへのリクエストに対するプレースホルダー画像を返すこともできます。

3
racerror

私はあなたが何をしたいのか正確にはわかりません、しかしあなたは単に クライアントにそれを処理させる ができます。

2
ViperArrow

既存のfilesのみをチェックする場合は、is_file()を使用します。 file_exists()は既存のファイルをチェックしますORディレクトリなので、is_file()は少し高速かもしれません。

2
Alex

古い質問です。ここに回答を追加します。 PHP 5.3.8の場合、is_file()(既存のファイルの場合)は桁違いに高速です。存在しないファイルの場合、時間はほぼ同じです。 PHP 5.1 with eacceleratorの場合、少し近づきます。

PHP 5.3.8 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 /アクセラレータ

_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()はregularファイルをテストしますが、シンボリックリンクではありません。したがって、* nixシステムでは、通常のファイルのみを処理しているsureでない限り、is_file()だけで回避することはできません。アップロードなどの場合、これは公正な仮定であるか、サーバーがWindowsベースであり、実際にはシンボリックリンクを持たない場合です。それ以外の場合は、is_file($file) || is_link($file)をテストする必要があります。

2)ファイルが見つからず、ほぼ同じになると、すべての方法でパフォーマンスが確実に低下します。

3)最大の警告。すべてのメソッドはファイルの統計をキャッシュして検索を高速化します。そのため、ファイルが定期的または迅速に変更されている場合、削除、再表示、削除し、clearstatcache();を実行して、正しいファイルの存在情報がキャッシュ。だから私はそれらをテストしました。すべてのファイル名などは省略しました。重要なことは、stream_resolve_includeを除き、ほぼすべての時間で収束することです。stream_resolve_includeは4倍の速さです。繰り返しますが、このサーバーにはアクセラレータがあるため、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)
)
_

基本的には、symlinkやディレクトリではなくファイルであることが100%確実であり、おそらく存在する場合はis_file()を使用するという考え方です。あなたは明確な利益を見るでしょう。ファイルがファイルまたはシンボリックリンクである可能性がある場合、失敗したis_file()14x + is_link()14x(is_file() || is_link())となり、全体で2倍遅くなります。ファイルの存在がLOTを変更する場合は、stream_resolve_include_path()を使用します。

したがって、使用状況によって異なります。

2
Beracah

それらはすべて同じディレクトリにありますか?もしそうならmayすべてのfile_existsルックアップではなく、ファイルのリストを取得してハッシュに保存し、それと比較する価値があります。

1
easement

画像ファイルの存在を確認したい場合、はるかに速い方法はgetimagesizeを使用することです!

ローカルでもリモートでも高速!

if(!@GetImageSize($image_path_or_url)) // False means no imagefile
 {
 // Do something
 }
1
Fabrice Oppitz

glob()はどうですか?しかし、それが速いかどうかはわかりません。

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

0
juno

通話あたり1/2msは非常に手頃な価格です。ファイル関数はファイル操作を処理する下位層に非常に近いため、これよりもはるかに高速な代替手段はないと思います。

ただし、結果をmemcacheまたは同様の機能にキャッシュするラッパーをfile_exists()に書き込むこともできます。これにより、日常の使用において、時間をほぼゼロに短縮できます。

0
Pekka

ファイルをフォルダーに保存するときに、アップロードが成功した場合は、DBテーブルへのパスを保存できます。

次に、要求されたファイルのパスを見つけるために、データベースにクエリを実行する必要があります。

0
Galois

特に認証がある場合は、画像のURLをデータベースに保持し、それをセッション変数に入れるのが最善の方法だと思います。これらの方法では、ページがリロードされるたびにチェックする必要はありません

0
daRula

私は解決策を探してこのページに来ました、そしてfopenがトリックをするかもしれないようです。このコードを使用する場合、見つからないファイルのエラーログを無効にすることができます。

<?php
for ($n=1;$n<100;$n++){
clearstatcache();
$h=@fopen("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>
0
user4064342

Cronjobを実行して定期的に画像のリストを作成し、それらをDB/file/BDB/...に保存できます。

30分ごとに問題ないはずですが、ファイルの追加/削除の場合に備えて、キャッシュをリセットするインターフェースを作成してください。

そして、findを実行するのも簡単です。シェルで-mmin -30 -print0を実行し、新しいファイルを追加します。

0
Antti Rytsölä