2016-05-24 1 views
3

У меня есть сценарий Perl, который использует opendir прочитать содержимое каталога:Почему работает opendir, readdir, stat так медленно по сравнению с командой dir для Windows?

opendir (DIR, $path) or next; 
    while (my $file = readdir DIR) { 

Тогда я делаю:

  • -s $file, чтобы получить размер каждого файла
  • (stat($file))[9] для получения измененного времени каждого файла

Я запускаю это с Windows-машины и доступ к Samba на Ubuntu 14.04.

Все это работает нормально, но процесс работает очень медленно по сравнению с тем, когда я запускаю список в той же папке.

Кто-нибудь знает, почему использование opendir занимает гораздо больше времени, чем список dir, и если есть какой-либо способ, я могу изменить свой сценарий, чтобы ускорить его?

+1

Пока вы звоните 'stat', получить размер файла с' (стат ($ файл)) [7] ', – mob

+1

@mob может вам скажите мне разницу между использованием stat и -s? Спасибо –

+0

@ A-Kay Операторы тестовых файлов, такие как '-s', фактически вызывают системный вызов' stat' за кулисами, поэтому, если вы вызываете '-s', за которым следует' stat', вы делаете два системных вызова, когда только один необходим. – ThisSuitIsBlackNot

ответ

2

По perlport:

На Win32 stat() необходимо открыть файл, чтобы определить количество ссылок и обновления атрибутов, которые могут быть изменены с помощью жестких ссылок. Установка ${^WIN32_SLOPPY_STAT} на истинное значение ускоряет работу stat(), не выполняя эту операцию.

Поскольку файлы, к которым вы обращаетесь, находятся на общей раздаче Samba, открытие их, вероятно, занимает довольно много времени.Кроме того, -s делает системный вызов за кулисами, поэтому звонок -s, за которым следует stat.

Следующие должны быть быстрее:

local ${^WIN32_SLOPPY_STAT} = 1; 

opendir my $dh, $path or die "Failed to opendir '$path': $!"; 

while (my $file = readdir $dh) { 
    my ($size, $mtime) = (stat $file)[7, 9]; 

    say join "\t", $file, $size, $mtime; 
} 
+0

Как насчет '-s $ file', за которым следует' -M _'? Согласно [docs] (http://perldoc.perl.org/functions/-X.html), '-M _' не будет выполнять 2-й' stat() ', а использовать последний' stat' результаты, т. е. только _one_ вызывает 'stat' тоже. – PerlDuck

+1

@PerlDog Это делает только один вызов 'stat', но даст другой результат. '-M' возвращает время начала скрипта * минус * файл mtime, в днях; 'stat' возвращает mtime в секундах секунд. – ThisSuitIsBlackNot

+0

Правда, но добавление '$^T' не повредит. – PerlDuck

0

Dir будет намного быстрее, так как это двоичный код, который, как я подозреваю, очень оптимизирован, поэтому он может быстро получать и форматировать информацию.

В вашем скрипте кажется, что вы выполняете несколько вызовов, которые нужно интерпретировать, одно для времени и другое для размера. Даже если более низкие вызовы в Perl являются двоичным кодом, чтобы получить информацию, она, вероятно, должна пройти через несколько уровней. Вы можете уменьшить количество вызовов по предложению @mob, сохранив возвращаемые значения stat и получив доступ к необходимым вам частям. Например:

@items = stat($file); 
$size = $items[7]; 
$modified = $items[9]; 

, который сохранит один из вызовов и, возможно, ускорит выполнение сценария.

Если вам нужны все файлы, вы можете сделать системный вызов для выполнения команды каталога и перенаправить вывод в файл, после чего вы можете проанализировать файл, чтобы получить информацию о времени и размере. Это может быть немного быстрее в зависимости от количества файлов. (/ 4 будет 4 цифры года,/т: ж будет, когда она была последней записью/модифицированы и/с избавятся от запятых в размере)

system("dir /4 /t:w /-c $path > tempList.txt"); 

Затем откройте и разобрать перенаправлен файл информацию, которую вы желаете.

open my $in,"tempList.txt" die "Unable to open file tempList.txt"; 
my @lines = <$in>; 
close($in); 
chomp(@lines); 

foreach (@lines) 
{ 
    next if (! (m/^\d{4}\/\d{2}\/\d{2}\s+); # Not a line with a file 
    @parts = split('\s+'); 
    # Get the parts you need (time and size, where you may have to some other 
    # work to get it in the desired format 
    #..... 
} 

Возможно добавить регулярное выражение, чтобы сделать согласование и вытащить детали, как вам нужно их при тестировании, если вы хотите обработать строку. Это может сэкономить время и силы.

+0

Спасибо @Glenn. Основная проблема здесь заключается в том, что dir не дает мне секунд, поэтому я не могу сравнивать его с текущей эпохой, которую я уже получаю от stat. Я нашел обходное решение, которое дает мне секунды, используя: 'forfiles/c" cmd/c echo @file @ftime ", однако это не работает на UNC-путях (которые мои). Является ли использование dir единственным для вас способом? Я вижу около 20-кратного снижения скорости, используя мой текущий метод по сравнению с –

+0

. Ваш очень приветствуемый @ A-Kay. Если вам нужно использовать UNC-пути, я бы поискал команду net, в которой вы бы использовали подкоманду net, чтобы монтировать UNC-путь в качестве диска. Это может быть полезно для того, чего вы пытаетесь достичь. Если вам нужна скорость передачи данных, возможно ли, что просто использование минут для сравнений было бы достаточной детализацией для ваших усилий? – Glenn

+0

@Glenn: Не будет ли это также работать, чтобы получить файлы по одному файлу в строке? '@ filelist = \' ls -1 \ '' Мне просто проще прокручивать массив, особенно когда я должен использовать отладчик, чтобы узнать, что находится в массиве. – Bulrush

Смежные вопросы