Pregunta

He estado intentando replicar Gnu Find (" find. ") en PHP, pero parece imposible acercarse a su velocidad. Las implementaciones de PHP utilizan al menos dos veces el tiempo de Buscar. ¿Hay formas más rápidas de hacer esto con PHP?

EDITAR: Agregué un ejemplo de código utilizando la implementación de SPL: su rendimiento es igual al enfoque iterativo

EDIT2: Al llamar a find desde PHP, en realidad era más lento que la implementación nativa de PHP. Supongo que debería estar satisfecho con lo que tengo :)

// measured to 317% of gnu find's speed when run directly from a shell
function list_recursive($dir) { 
  if ($dh = opendir($dir)) {
    while (false !== ($entry = readdir($dh))) {
      if ($entry == '.' || $entry == '..') continue;

      $path = "$dir/$entry";
      echo "$path\n";
      if (is_dir($path)) list_recursive($path);       
    }
    closedir($d);
  }
}

// measured to 315% of gnu find's speed when run directly from a shell
function list_iterative($from) {
  $dirs = array($from);  
  while (NULL !== ($dir = array_pop($dirs))) {  
    if ($dh = opendir($dir)) {    
      while (false !== ($entry = readdir($dh))) {      
        if ($entry == '.' || $entry == '..') continue;        

        $path = "$dir/$entry";        
        echo "$path\n";        
        if (is_dir($path)) $dirs[] = $path;        
      }      
      closedir($dh);      
    }    
  }  
}

// measured to 315% of gnu find's speed when run directly from a shell
function list_recursivedirectoryiterator($path) {
  $it = new RecursiveDirectoryIterator($path);
  foreach ($it as $file) {
    if ($file->isDot()) continue;

    echo $file->getPathname();
  }
}

// measured to 390% of gnu find's speed when run directly from a shell
function list_gnufind($dir) { 
  $dir = escapeshellcmd($dir);
  $h = popen("/usr/bin/find $dir", "r");
  while ('' != ($s = fread($h, 2048))) {
    echo $s;
  }
  pclose($h);
}
¿Fue útil?

Solución

PHP simplemente no puede funcionar tan rápido como C, simple y simple.

Otros consejos

No estoy seguro de si el rendimiento es mejor, pero podría usar un iterador de directorio recursivo para simplificar su código ... Ver RecursiveDirectoryIterator y 'SplFileInfo` .

$it = new RecursiveDirectoryIterator($from);
foreach ($it as $file)
{
    if ($file->isDot())
        continue;

    echo $file->getPathname();
}

Antes de comenzar a cambiar cualquier cosa, crea un perfil de tu código .

Use algo como Xdebug (más kcachegrind para un gráfico bonito) para averiguar dónde están las partes lentas . Si empiezas a cambiar las cosas a ciegas, no llegarás a ningún lado.

Mi único otro consejo es usar los iteradores de directorio SPL como ya se han publicado. Dejar que el código C interno haga el trabajo es casi siempre más rápido.

¿Por qué esperarías que el código PHP interpretado sea tan rápido como la versión compilada de C de find? Ser solo el doble de lento es bastante bueno.

El único consejo que agregaría es hacer un ob_start () al principio y ob_get_contents (), ob_end_clean () al final. Eso podría acelerar las cosas.

Mantienes abiertas N secuencias de directorios donde N es la profundidad del árbol de directorios. En su lugar, intente leer el valor de un directorio completo de entradas a la vez, y luego itere sobre las entradas. Como mínimo, maximizará el uso de los cachés de E / S del escritorio.

Es posible que desee considerar seriamente el uso de GNU find. Si está disponible y el modo seguro no está activado, probablemente te gusten los resultados:

function list_recursive($dir) { 
  $dir=escapeshellcmd($dir);
  $h = popen("/usr/bin/find $dir -type f", "r")
  while ($s = fgets($h,1024)) { 
    echo $s;
  }
  pclose($h);
}

Sin embargo, puede que haya un directorio que sea tan grande, que tampoco querrás preocuparte por esto. Considere la posibilidad de amortizar la lentitud de otras maneras. Su segundo intento se puede controlar (por ejemplo) simplemente guardando la pila de directorios en la sesión. Si le está dando al usuario una lista de archivos, simplemente recopile una página y luego guarde el resto del estado en la sesión para la página 2.

Intente usar scandir () para leer un directorio completo a la vez, como ha sugerido Jason Cohen. He basado el siguiente código en el código de los comentarios del manual de php para scandir()

 function scan( $dir ){
        $dirs = array_diff( scandir( $dir ), Array( ".", ".." ));
        $dir_array = Array();
        foreach( $dirs as $d )
            $dir_array[ $d ] = is_dir($dir."/".$d) ? scan( $dir."/".$d) : print $dir."/".$d."\n";
 }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top