Pergunta

Eu tenho tentado replicar Gnu Localizar ( "encontrar".) Em PHP , mas parece impossível chegar nem perto de sua velocidade. As implementações de PHP usar pelo menos o dobro do tempo de Find. Há mais rápido maneiras de fazer isso com PHP?

EDIT: Eu adicionei um exemplo de código usando a implementação SPL - seu desempenho é igual à abordagem iterativa

EDIT2: Ao ligar para encontrar a partir de PHP era realmente mais lenta do que a implementação PHP nativo. Acho que eu deveria estar satisfeito com o que eu tenho:)

// 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);
}
Foi útil?

Solução

PHP simplesmente não pode executar tão rápido quanto C, pura e simples.

Outras dicas

Eu não tenho certeza se o desempenho é melhor, mas você poderia usar um diretório recursiva iterador para tornar o código mais simples ... Ver RecursiveDirectoryIterator e 'SplFileInfo` .

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

    echo $file->getPathname();
}

Antes de começar a mudar alguma coisa, Perfil seu código .

Use algo como Xdebug (mais kcachegrind para um gráfico muito) para descobrir onde as peças são lentos . Se você começar a mudar as coisas cegamente, você não vai chegar a lugar nenhum.

O meu único outro conselho é usar os iteradores diretório SPL como já postou. Deixando o código C interna fazer o trabalho é quase sempre mais rápido.

Por que você espera que o código PHP interpretada para ser tão rápido quanto a versão compilada C de encontrar? Sendo apenas duas vezes mais lenta é realmente muito bom.

Sobre o único conselho que eu gostaria de acrescentar é fazer uma ob_start () no início e ob_get_contents (), ob_end_clean () no final. Que força acelerar as coisas.

Você está mantendo diretório N córregos aberto, onde N é a profundidade da árvore de diretórios. Em vez disso, tente ler o valor de um diretório inteiro de entradas de uma vez, e, em seguida, iterar sobre as entradas. No mínimo você vai maximizar o uso da mesa de I / O caches.

Você pode querer considerar seriamente apenas usando GNU encontrar. Se estiver disponível, e modo de segurança não estiver ativada, você provavelmente vai gostar dos resultados bem:

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

No entanto, há poderia ser algum diretório que é tão grande, você não vai querer se preocupar com isso também. Considere amortizando a lentidão de outras maneiras. Sua segunda tentativa pode ser checkpointed (por exemplo), basta salvar a pilha de diretórios na sessão. Se você está dando ao usuário uma lista de arquivos, simplesmente recolher um pageful depois salvar o resto do estado na sessão para a página 2.

Tente usar scandir() para ler um diretório inteiro de uma vez, como Jason Cohen sugeriu. Eu base o seguinte código no código do php comentários manuais 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top