file_exists() is too slow in PHP. Can anyone suggest a faster alternative?

后端 未结 19 1064
迷失自我
迷失自我 2021-02-05 01:18

When displaying images on our website, we check if the file exists with a call to file_exists(). We fall back to a dummy image if the file was missing.

Howe

相关标签:
19条回答
  • 2021-02-05 01:36

    Use absolute paths! Depending on your include_path setting PHP checks all(!) these dirs if you check relative file paths! You might unset include_path temporarily before checking the existence.

    realpath() does the same but I don't know if it is faster.

    But file access I/O is always slow. A hard disk access IS slower than calculating something in the processor, normally.

    0 讨论(0)
  • 2021-02-05 01:36

    Benchmarks with PHP 5.6:

    Existing File:

    0.0012969970 : stream_resolve_include_path + include  
    0.0013520717 : file_exists + include  
    0.0013728141 : @include  
    

    Invalid File:

    0.0000281333 : file_exists + include  
    0.0000319480 : stream_resolve_include_path + include  
    0.0001471042 : @include  
    

    Invalid Folder:

    0.0000281333 : file_exists + include  
    0.0000360012 : stream_resolve_include_path + include  
    0.0001239776 : @include  
    

    Code:

    // 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]);
    

    Command line Execution:

    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'  
    
    0 讨论(0)
  • 2021-02-05 01:37

    file_exists() should be a very inexpensive operation. Note too that file_exists builds its own cache to help with performance.

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

    0 讨论(0)
  • 2021-02-05 01:39

    I don't exactly know what you want to do, but you could just let the client handle it.

    0 讨论(0)
  • 2021-02-05 01:44

    You could do a cronjob to periodically create a list of images and store them in DB/file/BDB/...

    Every half an hour should be fine, but be sure to create an interface to reset cache in case of file addition/delete.

    And then, it's also easy to run find . -mmin -30 -print0 on the shell and add new files.

    0 讨论(0)
  • 2021-02-05 01:45

    I find 1/2ms per call very, very affordable. I don't think there are much faster alternatives around, as the file functions are very close to the lower layers that handle file operations.

    You could however write a wrapper to file_exists() that caches results into a memcache or similar facility. That should reduce the time to next to nothing in everyday use.

    0 讨论(0)
提交回复
热议问题