Domanda

Ho provato a replicare Gnu Find (" find. ") in PHP, ma sembra impossibile avvicinarsi alla sua velocità. Le implementazioni di PHP utilizzano almeno il doppio del tempo di Find. Ci sono modi più veloci per farlo con PHP?

EDIT: ho aggiunto un esempio di codice usando l'implementazione SPL - le sue prestazioni sono uguali all'approccio iterativo

EDIT2: quando si chiamava find da PHP era effettivamente più lento dell'implementazione nativa di PHP. Immagino che dovrei essere soddisfatto di quello che ho :)

// 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);
}
È stato utile?

Soluzione

PHP non può essere veloce quanto C, chiaro e semplice.

Altri suggerimenti

Non sono sicuro che le prestazioni siano migliori, ma potresti usare un iteratore di directory ricorsivo per semplificare il tuo codice ... Vedi RecursiveDirectoryIterator e 'SplFileInfo` .

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

    echo $file->getPathname();
}

Prima di iniziare a modificare qualsiasi cosa, profila il tuo codice .

Usa qualcosa come Xdebug (più kcachegrind per un bel grafico) per scoprire dove sono le parti lente . Se inizi a cambiare le cose alla cieca, non arriverai da nessuna parte.

Il mio unico consiglio è di usare gli iteratori della directory SPL già pubblicati. Lasciare che il codice C interno faccia il lavoro è quasi sempre più veloce.

Perché dovresti aspettarti che il codice PHP interpretato sia veloce come la versione C compilata di find? Essere solo due volte più lenti è in realtà abbastanza buono.

L'unico consiglio che vorrei aggiungere è di fare un ob_start () all'inizio e ob_get_contents (), ob_end_clean () alla fine. Che potrebbe velocizzare le cose.

Stai mantenendo aperti gli stream di directory N dove N è la profondità dell'albero di directory. Invece, prova a leggere le voci di un'intera directory in una sola volta, quindi esegui l'iterazione delle voci. Perlomeno massimizzerete l'uso delle cache di I / O della scrivania.

Potresti prendere in seria considerazione l'idea di usare GNU find. Se è disponibile e la modalità provvisoria non è attiva, probabilmente ti piaceranno i risultati:

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);
}

Tuttavia potrebbe esserci una directory così grande, non vorrai nemmeno preoccuparti di questo. Valuta di ammortizzare la lentezza in altri modi. Il tuo secondo tentativo può essere controllato (ad esempio) semplicemente salvando lo stack di directory nella sessione. Se stai fornendo all'utente un elenco di file, raccogli semplicemente un pageful quindi salva il resto dello stato nella sessione per pagina 2.

Prova a usare scandir () per leggere contemporaneamente un'intera directory, come ha suggerito Jason Cohen. Ho basato il seguente codice sul codice dai commenti del manuale php per 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";
 }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top