9

我一直在尝试在 PHP 中复制Gnu Find(“find .”),但似乎无法接近它的速度。PHP 实现使用至少两倍于 Find 的时间。有没有更快的方法用 PHP 做到这一点?

编辑:我添加了一个使用 SPL 实现的代码示例——它的性能等于迭代方法

EDIT2:当从 PHP 调用 find 时,它实际上比原生 PHP 实现慢。我想我应该对我所拥有的感到满意:)

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

7 回答 7

4

我不确定性能是否更好,但您可以使用递归目录迭代器来简化您的代码...参见RecursiveDirectoryIterator'SplFileInfo`

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

    echo $file->getPathname();
}
于 2009-03-08T19:26:18.013 回答
4

在开始更改任何内容之前,请分析您的代码

使用Xdebug(加上 kcachegrind 以获得漂亮的图表)之类的东西来找出慢速部分在哪里。如果你开始盲目地改变事情,你将一事无成。

我唯一的其他建议是使用已经发布的 SPL 目录迭代器。让内部 C 代码完成工作几乎总是更快。

于 2009-03-08T19:40:29.307 回答
3

PHP 只是不能像 C 一样快,简单明了。

于 2009-03-08T20:17:44.647 回答
2

为什么您希望解释的 PHP 代码与编译的 C 版本的 find 一样快?只慢两倍实际上是相当不错的。

关于我要添加的唯一建议是在开始时执行 ob_start() 并在末尾执行 ob_get_contents()、ob_end_clean()。这可能会加快速度。

于 2009-03-08T19:30:33.010 回答
1

您保持打开 N 个目录流,其中 N 是目录树的深度。相反,尝试一次读取整个目录的条目,然后遍历这些条目。至少您将最大限度地利用桌面 I/O 缓存。

于 2009-03-08T19:25:46.470 回答
0

您可能要认真考虑只使用 GNU find。如果它可用,并且未打开安全模式,您可能会喜欢结果:

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

但是,可能会有一些目录太大,您也不想为此烦恼。考虑以其他方式摊销缓慢。您的第二次尝试可以通过简单地将目录堆栈保存在会话中来设置检查点(例如)。如果您要为用户提供文件列表,只需收集一个页面,然后将其余状态保存在第 2 页的会话中。

于 2009-03-08T19:57:48.593 回答
0

正如 Jason Cohen 所建议的那样,尝试使用scandir()一次读取整个目录。我将以下代码基于 php 手册注释中的代码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";
 }
于 2009-10-28T15:33:04.767 回答