2009-03-08 13 views
9

He intentado replicar Gnu Find ("buscar") en PHP, pero parece imposible acercarse 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?¿Es posible acelerar un escaneo recursivo de archivos en PHP?

EDIT: He añadido un ejemplo de código usando la aplicación SPL - su rendimiento se corresponde con el enfoque iterativo

Edit2: Al llamar al hallazgo de PHP en realidad era más lenta que la aplicació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); 
} 
+0

Probablemente no. PHP es de alto nivel y probablemente nunca coincida con algo como Gnu Find (que probablemente está escrito en C) en términos de velocidad. Además, es notoriamente lento en la recursión. –

+1

+1 por ejemplo código – buggedcom

Respuesta

3

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

1

Estás directorio N mantener los flujos abiertos 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.

4

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

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

    echo $file->getPathname(); 
} 
+0

Un buen consejo (aunque no funcionó mejor). – neu242

2

¿Por qué esperar que el código PHP interpretado sea tan rápido como la versión C compilada 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.

+0

Sí, igual de bueno sería muy optimista :) El almacenamiento en búfer de salida no ayudó, por cierto ... – neu242

4

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

Use algo como Xdebug (más kcachegrind para obtener un gráfico bonito) para saber 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.

0

Es posible que desee considerar seriamente el solo uso de GNU find. Si está disponible, y el modo seguro no se enciende, es probable que al igual que los resultados apenas muy bien:

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 podría ser algún directorio que es tan grande, que no vas a querer molestar con esto tampoco Considera amortizar la lentitud de otras maneras. Su segundo intento puede marcarse (por ejemplo) simplemente guardando la pila 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 resto del estado en la sesión de la página 2.

+0

Al incrustar la utilidad de búsqueda de esta manera, el rendimiento es en realidad peor que el rendimiento de PHP. Supongo que debería estar satisfecho :) – neu242

+0

escapeshellarg y shell_exec – troelskn

0

Pruebe usar scandir() para leer todo un directorio a la vez, como Jason Cohen ha sugerido.He basado el código siguiente 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"; 
} 
Cuestiones relacionadas