Question

J'ai essayé de reproduire la recherche de gnou ("rechercher".). en PHP, mais il semble impossible de se rapprocher de sa vitesse. Les implémentations PHP utilisent au moins deux fois le temps de Find. Y at-il des moyens plus rapides de faire cela avec PHP?

EDIT: j'ai ajouté un exemple de code utilisant l'implémentation SPL - ses performances sont égales à l'approche itérative

EDIT2: Lors de l’appel à la recherche depuis PHP, le processus était plus lent que l’implémentation PHP native. Je suppose que je devrais être satisfait de ce que j'ai:)

// 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);
}
Était-ce utile?

La solution

PHP ne peut tout simplement pas fonctionner aussi vite que C, tout simplement.

Autres conseils

Je ne suis pas sûr que les performances soient meilleures, mais vous pouvez utiliser un itérateur de répertoire récursif pour simplifier votre code ... Voir RecursiveDirectoryIterator et 'SplFileInfo` .

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

    echo $file->getPathname();
}

Avant de commencer à modifier quoi que ce soit, profilez votre code .

Utilisez quelque chose comme Xdebug (plus kcachegrind pour un joli graphique) pour savoir où sont les ralentis. . Si vous commencez à changer les choses à l'aveuglette, vous n'irez nulle part.

Mon seul autre conseil est d'utiliser les itérateurs de l'annuaire SPL tels que publiés. Laisser le code C interne faire le travail est presque toujours plus rapide.

Pourquoi vous attendriez-vous à ce que le code PHP interprété soit aussi rapide que la version C compilée de find? Être seulement deux fois plus lent est en fait assez bon.

Le seul conseil que je voudrais ajouter est de faire ob_start () au début et ob_get_contents (), ob_end_clean () à la fin. Cela pourrait accélérer les choses.

Vous conservez N flux de répertoires ouverts, où N est la profondeur de l'arborescence. Au lieu de cela, essayez de lire les entrées d'un répertoire entier à la fois, puis parcourez-les. À tout le moins, vous maximiserez l'utilisation des caches d'E / S du pupitre.

Vous voudrez peut-être envisager sérieusement d’utiliser simplement GNU Find. S'il est disponible et que le mode sans échec n'est pas activé, vous apprécierez sans aucun doute les résultats:

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

Cependant, il se peut qu’un répertoire soit si volumineux que vous ne voudrez plus vous en soucier. Pensez à amortir la lenteur d'une autre manière. Votre deuxième essai peut être contrôlé (par exemple) en enregistrant simplement la pile de répertoires dans la session. Si vous fournissez à l'utilisateur une liste de fichiers, collectez simplement une page puis enregistrez le reste de l'état dans la session pour la page 2.

Essayez d’utiliser scandir () pour lire un répertoire entier à la fois, comme l’a suggéré Jason Cohen. J'ai basé le code suivant sur le code des commentaires du manuel php pour 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";
 }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top