Я пытался копировать Gnu Find («find.») В PHP, но, похоже, невозможно приблизиться к его скорости. В реализациях PHP используется как минимум в два раза больше времени поиска. Существуют ли более быстрые способы сделать это с помощью PHP?
EDIT: я добавил пример кода, используя реализацию SPL – его производительность равна итерационному подходу
EDIT2: При вызове find из PHP он был фактически медленнее, чем встроенная реализация 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); }
PHP просто не может работать так же быстро, как C, простой и простой.
Я не уверен, что производительность лучше, но вы можете использовать рекурсивный итератор каталогов, чтобы сделать ваш код проще … См. RecursiveDirectoryIterator
и «SplFileInfo» .
$it = new RecursiveDirectoryIterator($from); foreach ($it as $file) { if ($file->isDot()) continue; echo $file->getPathname(); }
Прежде чем начать что-либо менять, профаймируйте свой код .
Используйте что-то вроде Xdebug (плюс kcachegrind для красивого графика), чтобы узнать, где находятся медленные части. Если вы начнете менять вещи слепо, вы никуда не пойдете.
Мой единственный совет – использовать итераторы каталогов SPL, как уже было опубликовано. Предоставление внутреннего кода C делает работу почти всегда быстрее.
попробуйте использовать
Пример: RecursiveDirectoryIterator
man: RecursiveDirectoryIterator
Почему вы ожидаете, что интерпретируемый PHP-код будет таким же быстрым, как скомпилированная версия C find? На самом деле, это всего лишь два раза медленнее.
О единственном совете, который я бы добавил, это сделать ob_start () в начале и ob_get_contents (), ob_end_clean () в конце. Это может ускорить процесс.
Вы сохраняете N потоков каталогов, где N – глубина дерева каталогов. Вместо этого попробуйте сразу прочитать целые записи за целый каталог, а затем перебирайте записи. По крайней мере, вы будете максимально использовать кеширование ввода-вывода.
Возможно, вы захотите серьезно рассмотреть возможность использования 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.
Попробуйте использовать 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"; }