Я написал довольно большой (400 строк) скрипт, который выполняет одно задание: читать и анализировать (используя регулярные выражения) полезные строки в очень большом файле журнала, к которому непрерывно добавляются новые строки (записано примерно 40 ГБ) каждый день).
Основной l oop выглядит следующим образом:
#Main loop through the log file. Runs the loop until '<information_type' is found.
if ($input_type eq 'clear') {
while (my $line = <$inputfh>) {
next unless ($line =~ /$xml_entry/);
load_XML($line, $inputfh);
}
}
elsif ($input_type eq 'zip') {
#Same as load_XML but with print $localfh $line.
$inputfh->getline();
my @tmp;
while (my $line = <$inputfh>) {
print ($localfh $line);
next unless ($line =~ /$xml_entry/);
push @tmp, $.;
push @tmp, tell($localfh);
push @tmp, $line;
while ($line !~ /$xml_exit/)
{
$line = $inputfh->getline();
print $localfh $line;
push @tmp, $line;
}
cross_grep(\@tmp);
@tmp = ();
}
close $localfh;
}
sub load_XML {
#This is a very odd construct, but a zipped file apparently cannot provide a trustworthy tell.
#Tellhandle is the inputfh in the case of an unzipped and the outputfh in the case of a zipped.
my ($line, $tellhandle) = @_;
my @tmp;
push @tmp, $.;
push @tmp, tell($tellhandle);
push @tmp, $line;
while ($line !~ /$xml_exit/)
{
$line = $inputfh->getline();
print $localfh $line if $localfh;
push @tmp, $line;
}
#Cross grep for the target words given as argument. If all words are found, the array is valid and printed.
cross_grep(\@tmp);
return;
}
Скрипт использует множество обходных путей, чтобы охватить множество вариантов использования, которые мы можем иметь, но по существу it:
- Открывает файл, который может быть либо простым текстом, либо zip-файлом
- В случае zip-файла использует PerlIO :: gzip, чтобы открыть его и прочитать как обычный дескриптор файла
- В случае открытого текста использует простое открывание ("$ filehandle" "<$ file") </li>
- Передает строки через регулярное выражение, которое ищет точку входа в XML
- Записывает и печатает строки этого XML до тех пор, пока не найдет свою точку выхода
- Поиск в строках указанных c слов (функция cross_grep ())
- Имеет много маленьких распечаток с бухгалтерией Tell ($ filehandle) и печать номера строки
Проблема заключается в том, что в настоящее время этот сценарий распаковывает, читает и анализирует сжатый 10 ГБ журнал примерно за 3 минуты, , в то время как он совершенно не способен читать и анализировать непрерывно добавляемый открытый текст регистрируйте даже 1/5 от этой скорости.
Когда я использую top на сервере, на котором находятся скрипты, он говорит, что загрузка ЦП составляет около 65% для zip-файл, но никогда не превышает 25% для открытого текста и обычно составляет около 13%. Помимо PerlIO :: gzip, используемого для файлов журналов, нет разницы в потоке, регулярные выражения одинаковы, функциональные возможности или строки не меняются ни на один бит.
На самом деле, сжатые журналы принимают время записать все разархивированные строки в другой текстовый файл. И все же я могу извлекать журналы только в течение 20 минут из живого журнала (постоянно добавляя новые строки), в то время как я могу получить 5 часов заархивированного файла в одно и то же время.
Я повернул голову вокруг это 100 раз, но мне кажется, что это невозможно с вычислительной точки зрения. Если проблема в том, что текстовый файл постоянно добавляется и это как-то мешает Perl читать, то почему это не так, когда я выполняю поиск grep или меньше в этом файле?
Мне кажется, что Perl естественным образом ограничивает чтение файла во время его добавления (что происходит буквально каждые 0,05 секунды), но мне это нужно, чтобы не делать и использовать как можно больше ресурсов процессора и быстро завершить поиск sh, не останавливая при этом все время.
Является ли Perl удушающим показанием для добавленного открытого текста? Если да, могу ли я как-то заставить его использовать как можно больше энергии? Или есть другая причина, которую я не вижу?
Как распаковка, чтение и запись в открытом тексте может быть быстрее, чем просто чтение приложенного файла в открытом тексте?