file_exists() es demasiado lento en PHP. ¿Alguien puede sugerir una alternativa más rápida?

6 minutos de lectura

Al mostrar imágenes en nuestro sitio web, verificamos si el archivo existe con una llamada a file_exists(). Recurrimos a una imagen ficticia si falta el archivo.

Sin embargo, la creación de perfiles ha demostrado que esta es la parte más lenta de generar nuestras páginas con file_exists() tomando hasta 1/2 ms por archivo. Solo estamos probando alrededor de 40 archivos, pero esto aún empuja 20ms en el tiempo de carga de la página.

¿Alguien puede sugerir una manera de hacer que esto vaya más rápido? ¿Hay una mejor manera de probar si el archivo está presente? Si construyo un caché de algún tipo, ¿cómo debo mantenerlo sincronizado?

  • Si el parte más lenta en su código solo agrega 20 ms en el tiempo de carga total, debe salir y disfrutar de una cerveza, en lugar de preocuparse tanto, está publicando una pregunta en SO 😉

    – Duroth

    10 de noviembre de 2009 a las 15:27

  • ¿Qué sistema de archivos estás usando? – La velocidad de file_Exists() debería depender principalmente de la velocidad de la llamada al sistema stat(). ¿Cuántos archivos hay en el directorio? (Dependiendo del sistema de archivos, la cantidad de archivos tiene un impacto en la velocidad de stat())

    – johannes

    10 de noviembre de 2009 a las 15:33

  • A 1/2 ms cada uno, podría hacer 2000 file_exists en un segundo

    – Adam Hopkinson

    10 de noviembre de 2009 a las 15:35

  • Oh, citando a Wikipedia… La duración promedio de un parpadeo es de 300 a 400 milisegundos. No estoy seguro de por qué, pero me pareció apropiado compartirlo contigo.

    – Duroth

    10 de noviembre de 2009 a las 15:36

  • De hecho, probé esto una vez, mi función tomó 11 veces el tiempo de ejecución de file_exists(), por lo que mi mejor opción es usar mejor el almacenamiento en caché o encontrar otro método.

    –Peter Lindqvist

    10 de noviembre de 2009 a las 16:12

avatar de usuario
RC.

file_exists() debe ser una operación muy económica. Tenga en cuenta también que file_exists construye su propio caché para ayudar con el rendimiento.

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

  • Supongo que debería aceptar que el rendimiento está bien y dejarlo como está. Sin embargo, podría dividir los archivos en más carpetas, ya que esto probablemente ayude.

    –Rik Heywood

    10 de noviembre de 2009 a las 16:12

  • De acuerdo con la documentación, el almacenamiento en caché solo ocurrirá si file_exists() devuelve verdadero. Entonces, si verifica archivos inexistentes, la función lo verificará cada vez. Puede crear un enlace simbólico a la imagen ficticia cuando file_exists() devuelve falso para que las llamadas posteriores se almacenen en caché. (esto podría causar otros problemas)

    – Patricio Olvídalo

    3 de julio de 2013 a las 15:34

avatar de usuario
powtac

¡Usa rutas absolutas! Depende de tu include_path ¡La configuración de PHP verifica todos (!) Estos directorios si verifica las rutas de archivo relativas! Podrías desarmar include_path temporalmente antes de comprobar la existencia.

realpath() hace lo mismo pero no sé si es más rápido.

Pero la E/S de acceso a archivos siempre es lenta. Un acceso al disco duro ES más lento que calcular algo en el procesador, normalmente.

  • Buen consejo. Sin embargo, ya proporcioné un nombre de ruta completo al archivo (principalmente para evitar la naturaleza poco confiable de la configuración de ruta de inclusión).

    –Rik Heywood

    10 de noviembre de 2009 a las 16:00

  • Un hilo sobre este problema y un script para probar: bytes.com/topic/php/answers/…

    – powtac

    10 de noviembre de 2009 a las 16:19

  • Podría estar equivocado, pero saber si existe un archivo requiere una verificación en la tabla de índice FS, por lo que no debería ser una operación IO real que espera una operación de “lectura” o “escritura” de archivo en el disco.

    – TechNyquist

    16 mayo 2016 a las 12:59

avatar de usuario
Alejandro Yancharuk

La forma más rápida de verificar la existencia de un archivo local es stream_resolve_include_path():

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

Resultados de rendimiento stream_resolve_include_path() contra El archivo existe():

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

En la prueba se utilizaron rutas absolutas. La fuente de prueba es aquí. Versión PHP:

PHP 5.4.23-1~dotdeb.1 (cli) (construido: 13 de diciembre de 2013 21:53:21)

Copyright (c) 1997-2013 El Grupo PHP
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

Volvemos a una imagen ficticia si falta el archivo

Si solo está interesado en recurrir a esta imagen ficticia, puede considerar dejar que el cliente negocie con el servidor mediante una redirección (a la imagen ficticia) en el archivo no encontrado.

De esa manera, solo tendrá una pequeña sobrecarga de redirección y un retraso imperceptible en el lado del cliente. Al menos te desharás de la llamada “cara” (que no lo es, lo sé) a file_exists.

Solo un pensamiento.

Puntos de referencia con PHP 5.6:

Archivo existente:

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

Archivo inválido:

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

Carpeta no válida:

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

Código:

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

Línea de comando Ejecución:

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'  

avatar de usuario
Hassan

Cree una rutina hash para fragmentar los archivos en varios subdirectorios.

nombre de archivo.jpg -> 012345 -> /01/23/45.jpg

Además, puede usar mod_rewrite para devolver su imagen de marcador de posición para solicitudes a su directorio de imágenes que 404.

avatar de usuario
Comunidad

file_exists() se almacena automáticamente en caché por PHP. No creo que encuentre una función más rápida en PHP para verificar la existencia de un archivo.

Ver este hilo.

¿Ha sido útil esta solución?