¿Es posible acelerar un escaneo recursivo de archivos en PHP?

He intentado replicar Gnu Find (“find.”) En PHP, pero parece imposible acercarme a su velocidad. Las implementaciones de PHP usan al menos el doble de tiempo que Find. ¿Hay formas más rápidas de hacerlo con PHP?

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

EDIT2: cuando se llama a find desde PHP, en realidad era más lento que la implementación nativa de PHP. Creo 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); } 

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

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

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

Antes de comenzar a cambiar algo, perfila tu código .

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

Mi único otro consejo es usar los iteradores del directorio SPL como ya están publicados. Permitir que el código C interno haga el trabajo es casi siempre más rápido.

intenta usar el

ejemplo: RecursiveDirectoryIterator

hombre: RecursiveDirectoryIterator

¿Por qué esperar que el código PHP interpretado sea tan rápido como la versión C comstackda de find? Ser dos veces más lento en realidad 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.

Mantiene abiertas N cadenas de directorios donde N es la profundidad del árbol de directorios. En su lugar, intente leer las entradas de un directorio completo a la vez, y luego iterar sobre las entradas. Por lo menos, maximizará el uso de las cachés de E / S de 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 tan grande que no quieras molestarte con esto tampoco. Considere la posibilidad de amortizar la lentitud de otras maneras. Su segundo bash puede marcarse (por ejemplo) simplemente guardando la stack de directorio en la sesión. Si le está dando al usuario una lista de archivos, simplemente recopile una página llena y guarde el rest del estado en la sesión de la página 2.

Intente usar scandir() para leer todo un directorio a la vez, como ha sugerido Jason Cohen. He basado el siguiente código en el código de los comentarios del manual 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"; }