Spec-Zone.ru › Perl 5.28

perlperf

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
  • ОБЗОР
    • ОДИН ШАГ ВСТОРОНУ
    • ОДИН ШАГ ВПЕРЕД
    • ЕЩЕ ОДИН ШАГ ВСТОРОНУ
  • ОБЩИЕ РЕКОМЕНДАЦИИ
  • БЕНЧМАРКИ
    • Присваивание и разыменование переменных.
    • Поиск и замена или tr
  • ИНСТРУМЕНТЫ ПРОФИЛИРОВАНИЯ
    • Devel::DProf
    • Devel::Profiler
    • Devel::SmallProf
    • Devel::FastProf
    • Devel::NYTProf
  • СОРТИРОВКА
  • ВЕДЕНИЕ ЖУРНАЛА
    • Ведение журнала, если DEBUG (константа)
  • POSTSCRIPT
  • СМОТРИТЕ ТАКЖЕ
    • PERLDOCS
    • СТРАНИЦЫ MAN
    • МОДУЛИ
    • URL
  • АВТОР

ИМЯ

perlperf - Perl-техники производительности и оптимизации

ОПИСАНИЕ

Это введение в использование техник производительности и оптимизации, которые могут быть применены к программам Perl. Хотя многие разработчики Perl пришли из других языков и могут использовать свои предыдущие знания по мере необходимости, многие другие люди могут извлечь пользу из нескольких специфических для Perl советов. Если вам нужна сжатая версия, пожалуй, лучший совет исходит от известного японского самурая Миямото Мусаси, который сказал:

"Do Not Engage in Useless Activity"

в 1645 году.

ОБЗОР

Возможно, самая распространённая ошибка программистов заключается в том, что они пытаются оптимизировать свой код до того, как программа сделает что-то полезное — это плохая идея. Нет смысла иметь чрезвычайно быструю программу, которая не работает. Первая задача — заставить программу *правильно* делать что-то *полезное* (не говоря уже о том, чтобы убедиться, что пакет тестов полностью функционален), и только тогда стоит подумать об оптимизации. Решив оптимизировать существующий рабочий код, следует рассмотреть несколько простых, но важных шагов, которые являются неотъемлемой частью любого процесса оптимизации.

ОДИН ШАГ ВСТОРОНУ

Во-первых, необходимо установить базовый временной интервал для существующего кода, который должен быть надёжным и воспроизводимым. Вероятно, вы захотите использовать модули Benchmark или Devel::NYTProf, или что-то подобное, для этого этапа, или, возможно, утилиту Unix time, что более подходит. Смотрите в начале этого документа более подробный список модулей для бенчмаркинга и профилирования, а также рекомендуемые дальнейшие материалы.

ОДИН ШАГ ВПЕРЕД

Далее, изучив программу на предмет *узких мест* (места, где код, похоже, работает медленно), измените код с намерением ускорить его выполнение. Использование системы контроля версий, такой как subversion, обеспечит, что никакие изменения не будут необратимыми. Очень легко что-то поправить здесь и там — не меняйте слишком много за один раз, иначе вы можете не обнаружить, какой именно фрагмент кода *действительно* был медленным.

ЕЩЕ ОДИН ШАГ ВСТОРОНУ

Недостаточно сказать: «это ускорит его работу». Нужно проверить это. Запустите код под управлением модулей для бенчмаркинга или профилирования, которые были указаны на предыдущем шаге, и убедитесь, что новый код выполнил *ту же задачу* за *меньшее время*. Сохраните свою работу и повторите...

ОБЩИЕ РЕКОМЕНДАЦИИ

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

Очевидно, что встроенный код будет быстрее, чем вызовы подпрограмм или методов, потому что накладные расходы меньше, но этот подход имеет недостаток меньшей поддерживаемости и влечёт за собой увеличение использования памяти — нет ничего даром. Если вы ищете элемент в списке, может быть эффективнее хранить данные в структуре хеша, а затем просто проверить, определён ли ключ, вместо того, чтобы перебирать весь массив, используя grep() или аналогичные методы. substr() может быть (намного) быстрее, чем grep(), но не таким гибким, поэтому вы имеете ещё один компромисс в доступе. Ваш код может содержать строку, которая выполняется за 0,01 секунды, но если вы вызываете её 1000 раз, скорее всего, при обработке даже средних по размеру файлов, у вас уже будет задержка в 10 секунд только в одной строке кода. Если вы вызовете эту строку 100 000 раз, вся программа замедлится до неприемлемого уровня.

Использование подпрограммы в качестве части вашей сортировки — мощный способ получить именно то, что нужно, но обычно медленнее, чем встроенные операторы сортировки по *алфавиту* cmp и *числу* <=>. Возможно создание нескольких проходов по данным, построение индексов для повышения эффективности последующей сортировки и использование так называемого OM (оркинг манёвр) для кеширования ключей сортировки заранее. Поиск в кеше, хотя и хорошая идея, может сам по себе стать источником замедления, выполняя двойной проход по данным — один для настройки кеша и один для сортировки данных. Использование pack() для извлечения необходимого ключа сортировки в согласованную строку может быть эффективным способом построения одной строки для сравнения вместо использования нескольких ключей сортировки, что позволяет использовать стандартную, написанную на c и быструю, функцию Perl sort() для вывода и является основой GRT (преобразование Гутмана-Росслера). Некоторые комбинации строк могут замедлить GRT просто по причине избыточной сложности.

Для приложений, использующих базы данных, стандартный DBIx имеет средства для ускорения работы, не в последнюю очередь благодаря тому, что он пытается *не* запрашивать базу данных до последнего возможного момента, но всегда читайте документацию, прилагаемую к вашему выбору библиотек. Среди множества проблем, с которыми сталкиваются разработчики, работающие с базами данных, следует помнить о том, чтобы всегда использовать SQL и рассматривать предварительную выборку наборов данных, когда это может оказаться целесообразным. Разделение большого файла путём назначения нескольких процессов для обработки одного файла, используя, например, POE, threads или fork, также может быть полезным способом оптимизации использования доступных CPU ресурсов, хотя эта техника полна проблем с конкурентностью и требует пристального внимания к деталям.

В каждом случае есть специфическое применение и один или несколько исключений, и нет замены для проведения нескольких тестов и выяснения того, какой метод лучше всего подходит для вашей конкретной среды. Именно поэтому написание оптимального кода — не точная наука и почему нам так нравится использовать Perl — TMTOWTDI.

БЕНЧМАРКИ

Вот несколько примеров, демонстрирующих использование средств бенчмаркинга Perl.

Присваивание и разыменование переменных.

Я уверен, что большинство из нас видели код, который выглядит так (или хуже):

if ( $obj->{_ref}->{_myscore} >= $obj->{_ref}->{_yourscore} ) {
    ...

Такой код может быть действительно неприятным для чтения, а также очень чувствительным к опечаткам, и гораздо яснее разыменовывать переменную явно. Мы обходим проблему работы с методами объектно-ориентированного программирования для инкапсуляции доступа к переменным через методы, доступные только через объект. Здесь мы просто обсуждаем техническое решение, подходящее по умолчанию, и влияет ли это на производительность. Мы можем проверить, есть ли накладные расходы у этой операции разыменования, разместив сравнительный код в файле и выполнив тест Benchmark.

# разыменование

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;

my $ref = {
        'ref'   => {
            _myscore    => '100 + 1',
            _yourscore  => '102 - 1',
        },
};

timethese(1000000, {
        'direct'       => sub {
          my $x = $ref->{ref}->{_myscore} . $ref->{ref}->{_yourscore} ;
        },
        'dereference'  => sub {
            my $ref  = $ref->{ref};
            my $myscore = $ref->{_myscore};
            my $yourscore = $ref->{_yourscore};
            my $x = $myscore . $yourscore;
        },
});

Необходимо запускать любые измерения времени достаточное количество раз, чтобы получить среднее числовое значение, иначе каждое выполнение будет естественным образом варьироваться из-за колебаний среды, чтобы уменьшить влияние конкуренции за CPU ресурсы и пропускную способность сети, например. Запустив указанный выше код для одного миллиона итераций, мы можем взглянуть на вывод отчёта модуля Benchmark, чтобы увидеть, какой подход является наиболее эффективным.

$> perl dereference

Benchmark: timing 1000000 iterations of dereference, direct...
dereference:  2 wallclock secs ( 1.59 usr +  0.00 sys =  1.59 CPU) @ 628930.82/s (n=1000000)
    direct:  1 wallclock secs ( 1.20 usr +  0.00 sys =  1.20 CPU) @ 833333.33/s (n=1000000)

Разница хорошо видна, и подход с разыменованием медленнее. Хотя ему удалось выполнить в среднем 628 930 раз в секунду во время нашего теста, прямому подходу удалось выполнить ещё 204 403 раза, к сожалению. К сожалению, так как существует множество примеров кода, написанного с использованием многоуровневого прямого доступа к переменным, и он обычно ужасен. Тем не менее, он незначительно быстрее. Вопрос в том, стоит ли незначительный выигрыш напряжения глаз или потери поддерживаемости.

Поиск и замена или tr

Если нам нужно изменить строку, хотя регулярное выражение почти всегда будет намного гибче, tr, часто недооцениваемый инструмент, по-прежнему может быть полезным. Одна из ситуаций — замена всех гласных на другой символ. Решение с регулярным выражением может выглядеть так:

$str =~ s/[aeiou]/x/g

Альтернативный вариант с tr может выглядеть так:

$str =~ tr/aeiou/xxxxx/

Мы можем поместить это в тестовый файл, который мы можем запустить, чтобы проверить, какой подход является самым быстрым, используя глобальную $STR переменную для присвоения переменной my $str, чтобы избежать попыток Perl оптимизировать работу, заметив, что она назначена только один раз.

# regex-transliterate

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;

my $STR = "$$-this and that";

timethese( 1000000, {
'sr'  => sub { my $str = $STR; $str =~ s/[aeiou]/x/g; return $str; },
'tr'  => sub { my $str = $STR; $str =~ tr/aeiou/xxxxx/; return $str; },
});

Запустив код, мы получим результаты:

$> perl regex-transliterate

Benchmark: timing 1000000 iterations of sr, tr...
        sr:  2 wallclock secs ( 1.19 usr +  0.00 sys =  1.19 CPU) @ 840336.13/s (n=1000000)
        tr:  0 wallclock secs ( 0.49 usr +  0.00 sys =  0.49 CPU) @ 2040816.33/s (n=1000000)

Версия с tr — явный победитель. Одно решение гибкое, другое — быстрое, и разработчик вправе выбрать, какое использовать.

Обратитесь к документации Benchmark для получения дополнительных полезных техник.

ИНСТРУМЕНТЫ ПРОФИЛИРОВАНИЯ

Несколько более крупный фрагмент кода предоставит что-то, на чём профилировщик может составить более подробный отчёт о статистических данных. Этот пример использует простую программу wordmatch, которая анализирует указанный входной файл и выводит короткий отчёт о содержимом.

# wordmatch

#!/usr/bin/perl

use strict;
use warnings;

=head1 NAME

filewords - word analysis of input file

=head1 SYNOPSIS

    filewords -f inputfilename [-d]

=head1 DESCRIPTION

This program parses the given filename, specified with C<-f>, and
displays a simple analysis of the words found therein.  Use the C<-d>
switch to enable debugging messages.

=cut

use FileHandle;
use Getopt::Long;

my $debug   =  0;
my $file    = '';

my $result = GetOptions (
    'debug'         => \$debug,
    'file=s'        => \$file,
);
die("invalid args") unless $result;

unless ( -f $file ) {
    die("Usage: $0 -f filename [-d]");
}
my $FH = FileHandle->new("< $file")
                              or die("unable to open file($file): $!");

my $i_LINES = 0;
my $i_WORDS = 0;
my %count   = ();

my @lines = <$FH>;
foreach my $line ( @lines ) {
    $i_LINES++;
    $line =~ s/\n//;
    my @words = split(/ +/, $line);
    my $i_words = scalar(@words);
    $i_WORDS = $i_WORDS + $i_words;
    debug("line: $i_LINES supplying $i_words words: @words");
    my $i_word = 0;
    foreach my $word ( @words ) {
        $i_word++;
        $count{$i_LINES}{spec} += matches($i_word, $word,
                                          '[^a-zA-Z0-9]');
        $count{$i_LINES}{only} += matches($i_word, $word,
                                          '^[^a-zA-Z0-9]+$');
        $count{$i_LINES}{cons} += matches($i_word, $word,
                                    '^[(?i:bcdfghjklmnpqrstvwxyz)]+$');
        $count{$i_LINES}{vows} += matches($i_word, $word,
                                          '^[(?i:aeiou)]+$');
        $count{$i_LINES}{caps} += matches($i_word, $word,
                                          '^[(A-Z)]+$');
    }
}

print report( %count );

sub matches {
    my $i_wd  = shift;
    my $word  = shift;
    my $regex = shift;
    my $has = 0;

    if ( $word =~ /($regex)/ ) {
        $has++ if $1;
    }

    debug( "word: $i_wd "
          . ($has ? 'matches' : 'does not match')
          . " chars: /$regex/");

    return $has;
}

sub report {
    my %report = @_;
    my %rep;

    foreach my $line ( keys %report ) {
        foreach my $key ( keys %{ $report{$line} } ) {
            $rep{$key} += $report{$line}{$key};
        }
    }

    my $report = qq|
$0 report for $file:
lines in file: $i_LINES
words in file: $i_WORDS
words with special (non-word) characters: $i_spec
words with only special (non-word) characters: $i_only
words with only consonants: $i_cons
words with only capital letters: $i_caps
words with only vowels: $i_vows
|;

    return $report;
}

sub debug {
    my $message = shift;

    if ( $debug ) {
        print STDERR "DBG: $message\n";
    }
}

exit 0;

Devel::DProf

Этот уважаемый модуль был фактическим стандартом профилирования кода Perl более десяти лет, но был заменён рядом других модулей, которые вернули нас в 21 век. Хотя рекомендуется оценить ваш инструмент из нескольких упомянутых здесь и из списка CPAN внизу этого документа, (и в настоящее время Devel::NYTProf кажется предпочтительным оружием - см. ниже), мы сначала кратко рассмотрим вывод из Devel::DProf, чтобы установить базовый уровень для инструментов профилирования Perl. Запустите вышеприведённую программу под управлением Devel::DProf с помощью переключателя -d в командной строке.

$> perl -d:DProf wordmatch -f perl5db.pl

<...multiple lines snipped...>

wordmatch report for perl5db.pl:
lines in file: 9428
words in file: 50243
words with special (non-word) characters: 20480
words with only special (non-word) characters: 7790
words with only consonants: 4801
words with only capital letters: 1316
words with only vowels: 1701

Devel::DProf генерирует специальный файл, по умолчанию названный tmon.out, и этот файл считывается программой dprofpp, которая уже установлена в составе дистрибутива Devel::DProf. Если вы вызовете dprofpp без опций, она прочитает файл tmon.out в текущем каталоге и создаст удобочитаемый отчёт о статистике выполнения вашей программы. Обратите внимание, что это может занять некоторое время.

$> dprofpp

Total Elapsed Time = 2.951677 Seconds
  User+System Time = 2.871677 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 102.   2.945  3.003 251215   0.0000 0.0000  main::matches
 2.40   0.069  0.069 260643   0.0000 0.0000  main::debug
 1.74   0.050  0.050      1   0.0500 0.0500  main::report
 1.04   0.030  0.049      4   0.0075 0.0123  main::BEGIN
 0.35   0.010  0.010      3   0.0033 0.0033  Exporter::as_heavy
 0.35   0.010  0.010      7   0.0014 0.0014  IO::File::BEGIN
 0.00       - -0.000      1        -      -  Getopt::Long::FindOption
 0.00       - -0.000      1        -      -  Symbol::BEGIN
 0.00       - -0.000      1        -      -  Fcntl::BEGIN
 0.00       - -0.000      1        -      -  Fcntl::bootstrap
 0.00       - -0.000      1        -      -  warnings::BEGIN
 0.00       - -0.000      1        -      -  IO::bootstrap
 0.00       - -0.000      1        -      -  Getopt::Long::ConfigDefaults
 0.00       - -0.000      1        -      -  Getopt::Long::Configure
 0.00       - -0.000      1        -      -  Symbol::gensym

dprofpp создаст довольно подробный отчёт о деятельности программы wordmatch. Вверху анализа указаны время, затраченное на выполнение, время пользователя и время системы, а после них указаны основные столбцы, определяющие отчёт. См. документацию dprofpp для получения подробностей о поддерживаемых опциях.

См. также Apache::DProf, который подключает Devel::DProf к mod_perl.

Devel::Profiler

Давайте рассмотрим ту же программу с помощью другого профилировщика: Devel::Profiler, прямой заменой для Devel::DProf только для Perl. Использование немного отличается, так как вместо использования специального флага -d:, вы подключаете Devel::Profiler напрямую как модуль с помощью -M.

$> perl -MDevel::Profiler wordmatch -f perl5db.pl

<...multiple lines snipped...>

wordmatch report for perl5db.pl:
lines in file: 9428
words in file: 50243
words with special (non-word) characters: 20480
words with only special (non-word) characters: 7790
words with only consonants: 4801
words with only capital letters: 1316
words with only vowels: 1701

Devel::Profiler генерирует файл tmon.out, совместимый с программой dprofpp, тем самым экономя создание отдельной программы-читателя статистики. dprofpp использование, следовательно, идентично вышеприведённому примеру.

$> dprofpp

Total Elapsed Time =   20.984 Seconds
  User+System Time =   19.981 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 49.0   9.792 14.509 251215   0.0000 0.0001  main::matches
 24.4   4.887  4.887 260643   0.0000 0.0000  main::debug
 0.25   0.049  0.049      1   0.0490 0.0490  main::report
 0.00   0.000  0.000      1   0.0000 0.0000  Getopt::Long::GetOptions
 0.00   0.000  0.000      2   0.0000 0.0000  Getopt::Long::ParseOptionSpec
 0.00   0.000  0.000      1   0.0000 0.0000  Getopt::Long::FindOption
 0.00   0.000  0.000      1   0.0000 0.0000  IO::File::new
 0.00   0.000  0.000      1   0.0000 0.0000  IO::Handle::new
 0.00   0.000  0.000      1   0.0000 0.0000  Symbol::gensym
 0.00   0.000  0.000      1   0.0000 0.0000  IO::File::open

Интересно, что мы получаем немного разные результаты, что в основном из-за того, что алгоритм, генерирующий отчёт, отличается, даже если формат выходного файла, предположительно, идентичен. Время выполнения, время пользователя и время системы чётко показывают время, необходимое для выполнения Devel::Profiler своей работы, но перечень столбцов, как будто, более точны, чем те, что были раньше от Devel::DProf. Например, цифра 102% исчезла. Здесь нам нужно использовать имеющиеся инструменты и понимать их плюсы и минусы, прежде чем использовать их. Интересно, что количество вызовов для каждой подпрограммы идентично в двух отчётах, отличаются только проценты. Как пишет автор Devel::Proviler:

...running HTML::Template's test suite under Devel::DProf shows
output() taking NO time but Devel::Profiler shows around 10% of the
time is in output().  I don't know which to trust but my gut tells me
something is wrong with Devel::DProf.  HTML::Template::output() is a
big routine that's called for every test. Either way, something needs
fixing.

Результат может варьироваться.

См. также Devel::Apache::Profiler, который подключает Devel::Profiler к mod_perl.

Devel::SmallProf

Профилировщик Devel::SmallProf исследует время выполнения вашей программы Perl и генерирует построчное представление, чтобы показать, сколько раз каждая строка вызывалась и сколько времени потребовалось для выполнения каждой строки. Он вызывается путём указания знакомого флага -d для Perl во время выполнения.

$> perl -d:SmallProf wordmatch -f perl5db.pl

<...multiple lines snipped...>

wordmatch report for perl5db.pl:
lines in file: 9428
words in file: 50243
words with special (non-word) characters: 20480
words with only special (non-word) characters: 7790
words with only consonants: 4801
words with only capital letters: 1316
words with only vowels: 1701

Devel::SmallProf записывает свой вывод в файл smallprof.out по умолчанию. Формат файла выглядит следующим образом:

<num> <time> <ctime> <line>:<text>

По завершении программы вывод можно просмотреть и отсортировать с помощью стандартных инструментов фильтрации текста. Что-то вроде следующего может быть достаточно:

$> cat smallprof.out | grep \d*: | sort -k3 | tac | head -n20

251215   1.65674   7.68000    75: if ( $word =~ /($regex)/ ) {
251215   0.03264   4.40000    79: debug("word: $i_wd ".($has ? 'matches' :
251215   0.02693   4.10000    81: return $has;
260643   0.02841   4.07000   128: if ( $debug ) {
260643   0.02601   4.04000   126: my $message = shift;
251215   0.02641   3.91000    73: my $has = 0;
251215   0.03311   3.71000    70: my $i_wd  = shift;
251215   0.02699   3.69000    72: my $regex = shift;
251215   0.02766   3.68000    71: my $word  = shift;
 50243   0.59726   1.00000    59:  $count{$i_LINES}{cons} =
 50243   0.48175   0.92000    61:  $count{$i_LINES}{spec} =
 50243   0.00644   0.89000    56:  my $i_cons = matches($i_word, $word,
 50243   0.48837   0.88000    63:  $count{$i_LINES}{caps} =
 50243   0.00516   0.88000    58:  my $i_caps = matches($i_word, $word, '^[(A-
 50243   0.00631   0.81000    54:  my $i_spec = matches($i_word, $word, '[^a-
 50243   0.00496   0.80000    57:  my $i_vows = matches($i_word, $word,
 50243   0.00688   0.80000    53:  $i_word++;
 50243   0.48469   0.79000    62:  $count{$i_LINES}{only} =
 50243   0.48928   0.77000    60:  $count{$i_LINES}{vows} =
 50243   0.00683   0.75000    55:  my $i_only = matches($i_word, $word, '^[^a-

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

См. также Apache::SmallProf , который подключает Devel::SmallProf к mod_perl.

Devel::FastProf

Devel::FastProf — ещё один профилировщик строк Perl. Он был написан с целью получения более быстрого профилировщика строк, чем, например, Devel::SmallProf, потому что он написан на C Для использования Devel::FastProf, укажите аргумент -d для Perl:

$> perl -d:FastProf wordmatch -f perl5db.pl

<...multiple lines snipped...>

wordmatch report for perl5db.pl:
lines in file: 9428
words in file: 50243
words with special (non-word) characters: 20480
words with only special (non-word) characters: 7790
words with only consonants: 4801
words with only capital letters: 1316
words with only vowels: 1701

Devel::FastProf записывает статистику в файл fastprof.out в текущем каталоге. Файл вывода, который можно указать, может быть интерпретирован с помощью программы командной строки fprofpp.

$> fprofpp | head -n20

# fprofpp output format is:
# filename:line time count: source
wordmatch:75 3.93338 251215: if ( $word =~ /($regex)/ ) {
wordmatch:79 1.77774 251215: debug("word: $i_wd ".($has ? 'matches' : 'does not match')." chars: /$regex/");
wordmatch:81 1.47604 251215: return $has;
wordmatch:126 1.43441 260643: my $message = shift;
wordmatch:128 1.42156 260643: if ( $debug ) {
wordmatch:70 1.36824 251215: my $i_wd  = shift;
wordmatch:71 1.36739 251215: my $word  = shift;
wordmatch:72 1.35939 251215: my $regex = shift;

Сразу же мы можем увидеть, что количество вызовов каждой строки идентично выводу Devel::SmallProf, а последовательность отличается лишь незначительно в зависимости от порядка затраченного времени на выполнение каждой строки, if ( $debug ) { и my $message = shift;, например. Различия в фактических записываемых временах могут быть связаны с используемым внутренним алгоритмом, либо с ограничениями или конфликтами системных ресурсов.

См. также DBIx::Profile, который будет профилировать запросы к базе данных, выполняемые в пространстве имён DBIx::*.

Devel::NYTProf

Devel::NYTProf — это следующее поколение профилировщика кода Perl, исправляющее многие недостатки других инструментов и реализующее множество интересных функций. Во-первых, он может использоваться как профилировщик строк, блоков или подпрограмм одновременно. Он также может использовать разрешение в доли микросекунды (100 нс) на системах, которые предоставляют clock_gettime() Он может запускаться и останавливаться даже программой, которая профилируется. Это однострочная запись для профилирования mod_perl приложений. Он написан на c и, вероятно, является самым быстрым доступным профилировщиком для Perl. Список преимуществ просто продолжается. Достаточно об этом, давайте посмотрим, как это работает — просто используйте знакомый переключатель -d для подключения и запуска кода.

$> perl -d:NYTProf wordmatch -f perl5db.pl

wordmatch report for perl5db.pl:
lines in file: 9427
words in file: 50243
words with special (non-word) characters: 20480
words with only special (non-word) characters: 7790
words with only consonants: 4801
words with only capital letters: 1316
words with only vowels: 1701

NYTProf сгенерирует базу отчётов в файл nytprof.out по умолчанию. Удобные удобочитаемые отчёты можно сгенерировать отсюда, используя предоставленные программы nytprofhtml (HTML вывод) и nytprofcsv (CSV вывод). Здесь мы использовали утилиту Unix html2text для преобразования файла nytprof/index.html для удобства.

$> html2text nytprof/index.html

Performance Profile Index
For wordmatch
  Run on Fri Sep 26 13:46:39 2008
Reported on Fri Sep 26 13:47:23 2008

         Top 15 Subroutines -- ordered by exclusive time
|Calls |P |F |Inclusive|Exclusive|Subroutine                          |
|      |  |  |Time     |Time     |                                    |
|251215|5 |1 |13.09263 |10.47692 |main::              |matches        |
|260642|2 |1 |2.71199  |2.71199  |main::              |debug          |
|1     |1 |1 |0.21404  |0.21404  |main::              |report         |
|2     |2 |2 |0.00511  |0.00511  |XSLoader::          |load (xsub)    |
|14    |14|7 |0.00304  |0.00298  |Exporter::          |import         |
|3     |1 |1 |0.00265  |0.00254  |Exporter::          |as_heavy       |
|10    |10|4 |0.00140  |0.00140  |vars::              |import         |
|13    |13|1 |0.00129  |0.00109  |constant::          |import         |
|1     |1 |1 |0.00360  |0.00096  |FileHandle::        |import         |
|3     |3 |3 |0.00086  |0.00074  |warnings::register::|import         |
|9     |3 |1 |0.00036  |0.00036  |strict::            |bits           |
|13    |13|13|0.00032  |0.00029  |strict::            |import         |
|2     |2 |2 |0.00020  |0.00020  |warnings::          |import         |
|2     |1 |1 |0.00020  |0.00020  |Getopt::Long::      |ParseOptionSpec|
|7     |7 |6 |0.00043  |0.00020  |strict::            |unimport       |

For more information see the full list of 189 subroutines.

Первая часть отчёта уже показывает важную информацию о том, какие подпрограммы используют больше всего времени. Следующая предоставляет статистику о профилированных исходных файлах.

        Source Code Files -- ordered by exclusive time then name
|Stmts  |Exclusive|Avg.   |Reports                     |Source File         |
|       |Time     |       |                            |                    |
|2699761|15.66654 |6e-06  |line   .    block   .    sub|wordmatch           |
|35     |0.02187  |0.00062|line   .    block   .    sub|IO/Handle.pm        |
|274    |0.01525  |0.00006|line   .    block   .    sub|Getopt/Long.pm      |
|20     |0.00585  |0.00029|line   .    block   .    sub|Fcntl.pm            |
|128    |0.00340  |0.00003|line   .    block   .    sub|Exporter/Heavy.pm   |
|42     |0.00332  |0.00008|line   .    block   .    sub|IO/File.pm          |
|261    |0.00308  |0.00001|line   .    block   .    sub|Exporter.pm         |
|323    |0.00248  |8e-06  |line   .    block   .    sub|constant.pm         |
|12     |0.00246  |0.00021|line   .    block   .    sub|File/Spec/Unix.pm   |
|191    |0.00240  |0.00001|line   .    block   .    sub|vars.pm             |
|77     |0.00201  |0.00003|line   .    block   .    sub|FileHandle.pm       |
|12     |0.00198  |0.00016|line   .    block   .    sub|Carp.pm             |
|14     |0.00175  |0.00013|line   .    block   .    sub|Symbol.pm           |
|15     |0.00130  |0.00009|line   .    block   .    sub|IO.pm               |
|22     |0.00120  |0.00005|line   .    block   .    sub|IO/Seekable.pm      |
|198    |0.00085  |4e-06  |line   .    block   .    sub|warnings/register.pm|
|114    |0.00080  |7e-06  |line   .    block   .    sub|strict.pm           |
|47     |0.00068  |0.00001|line   .    block   .    sub|warnings.pm         |
|27     |0.00054  |0.00002|line   .    block   .    sub|overload.pm         |
|9      |0.00047  |0.00005|line   .    block   .    sub|SelectSaver.pm      |
|13     |0.00045  |0.00003|line   .    block   .    sub|File/Spec.pm        |
|2701595|15.73869 |       |Total                       |
|128647 |0.74946  |       |Average                     |
|       |0.00201  |0.00003|Median                      |
|       |0.00121  |0.00003|Deviation                   |

Report produced by the NYTProf 2.03 Perl profiler, developed by Tim Bunce and
Adam Kaplan.

На этом этапе, если вы используете отчёт в формате html, вы можете переходить по различным ссылкам, чтобы углубиться в каждую подпрограмму и каждую строку кода. Поскольку мы используем здесь текстовый отчёт и существует целая директория отчётов, сгенерированных для каждого исходного файла, мы просто отобразим часть соответствующего файла wordmatch-line.html, чтобы дать представление о типе вывода, который вы можете ожидать от этого отличного инструмента.

$> html2text nytprof/wordmatch-line.html

Performance Profile -- -block view-.-line view-.-sub view-
For wordmatch
Run on Fri Sep 26 13:46:39 2008
Reported on Fri Sep 26 13:47:22 2008

File wordmatch

 Subroutines -- ordered by exclusive time
|Calls |P|F|Inclusive|Exclusive|Subroutine    |
|      | | |Time     |Time     |              |
|251215|5|1|13.09263 |10.47692 |main::|matches|
|260642|2|1|2.71199  |2.71199  |main::|debug  |
|1     |1|1|0.21404  |0.21404  |main::|report |
|0     |0|0|0        |0        |main::|BEGIN  |


|Line|Stmts.|Exclusive|Avg.   |Code                                           |
|    |      |Time     |       |                                               |
|1   |      |         |       |#!/usr/bin/perl                                |
|2   |      |         |       |                                               |
|    |      |         |       |use strict;                                    |
|3   |3     |0.00086  |0.00029|# spent 0.00003s making 1 calls to strict::    |
|    |      |         |       |import                                         |
|    |      |         |       |use warnings;                                  |
|4   |3     |0.01563  |0.00521|# spent 0.00012s making 1 calls to warnings::  |
|    |      |         |       |import                                         |
|5   |      |         |       |                                               |
|6   |      |         |       |=head1 NAME                                    |
|7   |      |         |       |                                               |
|8   |      |         |       |filewords - word analysis of input file        |
<...snip...>
|62  |1     |0.00445  |0.00445|print report( %count );                        |
|    |      |         |       |# spent 0.21404s making 1 calls to main::report|
|63  |      |         |       |                                               |
|    |      |         |       |# spent 23.56955s (10.47692+2.61571) within    |
|    |      |         |       |main::matches which was called 251215 times,   |
|    |      |         |       |avg 0.00005s/call: # 50243 times               |
|    |      |         |       |(2.12134+0.51939s) at line 57 of wordmatch, avg|
|    |      |         |       |0.00005s/call # 50243 times (2.17735+0.54550s) |
|64  |      |         |       |at line 56 of wordmatch, avg 0.00005s/call #   |
|    |      |         |       |50243 times (2.10992+0.51797s) at line 58 of   |
|    |      |         |       |wordmatch, avg 0.00005s/call # 50243 times     |
|    |      |         |       |(2.12696+0.51598s) at line 55 of wordmatch, avg|
|    |      |         |       |0.00005s/call # 50243 times (1.94134+0.51687s) |
|    |      |         |       |at line 54 of wordmatch, avg 0.00005s/call     |
|    |      |         |       |sub matches {                                  |
<...snip...>
|102 |      |         |       |                                               |
|    |      |         |       |# spent 2.71199s within main::debug which was  |
|    |      |         |       |called 260642 times, avg 0.00001s/call: #      |
|    |      |         |       |251215 times (2.61571+0s) by main::matches at  |
|103 |      |         |       |line 74 of wordmatch, avg 0.00001s/call # 9427 |
|    |      |         |       |times (0.09628+0s) at line 50 of wordmatch, avg|
|    |      |         |       |0.00001s/call                                  |
|    |      |         |       |sub debug {                                    |
|104 |260642|0.58496  |2e-06  |my $message = shift;                           |
|105 |      |         |       |                                               |
|106 |260642|1.09917  |4e-06  |if ( $debug ) {                                |
|107 |      |         |       |print STDERR "DBG: $message\n";                |
|108 |      |         |       |}                                              |
|109 |      |         |       |}                                              |
|110 |      |         |       |                                               |
|111 |1     |0.01501  |0.01501|exit 0;                                        |
|112 |      |         |       |                                               |

В нём содержится множество очень полезной информации — это, похоже, верный путь вперёд.

См. также Devel::NYTProf::Apache который подключает Devel::NYTProf к mod_perl.

СОРТИРОВКА

Модули Perl не являются единственными инструментами, которые есть у специалиста по производительности. Не следует игнорировать системные инструменты, как time , как показывает следующий пример, где мы кратко рассмотрим сортировку. Много книг, диссертаций и статей было написано об эффективных алгоритмах сортировки, и это не место для повторения такой работы. Есть несколько хороших модулей сортировки, которые тоже заслуживают внимания: Sort::Maker, Sort::Key приходят на ум. Однако всё ещё возможно сделать некоторые наблюдения относительно определённых интерпретаций Perl, касающихся задач сортировки наборов данных, и привести один или два примера того, как сортировка больших объёмов данных может повлиять на производительность. Во-первых, часто упускается из виду момент, когда сортируются большие объёмы данных, можно попытаться сократить обрабатываемый набор данных, и во многих случаях grep() может быть довольно полезным как простой фильтр:

@data = sort grep { /$filter/ } @incoming

Такая команда может значительно уменьшить объём материала, который нужно сортировать в первую очередь, и её не следует игнорировать из-за простоты. Принцип KISS слишком часто упускается из виду — следующий пример использует простую системную утилиту time для демонстрации. Давайте рассмотрим фактический пример сортировки содержимого большого файла, например, файла журнала apache. Этот файл содержит более четверти миллиона строк, имеет размер 50 МБ, а фрагмент его содержимого выглядит так:

# logfile

188.209-65-87.adsl-dyn.isp.belgacom.be - - [08/Feb/2007:12:57:16 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
188.209-65-87.adsl-dyn.isp.belgacom.be - - [08/Feb/2007:12:57:16 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
151.56.71.198 - - [08/Feb/2007:12:57:41 +0000] "GET /suse-on-vaio.html HTTP/1.1" 200 2858 "http://www.linux-on-laptops.com/sony.html" "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"
151.56.71.198 - - [08/Feb/2007:12:57:42 +0000] "GET /data/css HTTP/1.1" 404 206 "http://www.rfi.net/suse-on-vaio.html" "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"
151.56.71.198 - - [08/Feb/2007:12:57:43 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"
217.113.68.60 - - [08/Feb/2007:13:02:15 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
217.113.68.60 - - [08/Feb/2007:13:02:16 +0000] "GET /data/css HTTP/1.1" 404 206 "http://www.rfi.net/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
debora.to.isac.cnr.it - - [08/Feb/2007:13:03:58 +0000] "GET /suse-on-vaio.html HTTP/1.1" 200 2858 "http://www.linux-on-laptops.com/sony.html" "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.0 (like Gecko)"
debora.to.isac.cnr.it - - [08/Feb/2007:13:03:58 +0000] "GET /data/css HTTP/1.1" 404 206 "http://www.rfi.net/suse-on-vaio.html" "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.0 (like Gecko)"
debora.to.isac.cnr.it - - [08/Feb/2007:13:03:58 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.0 (like Gecko)"
195.24.196.99 - - [08/Feb/2007:13:26:48 +0000] "GET / HTTP/1.0" 200 3309 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9"
195.24.196.99 - - [08/Feb/2007:13:26:58 +0000] "GET /data/css HTTP/1.0" 404 206 "http://www.rfi.net/" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9"
195.24.196.99 - - [08/Feb/2007:13:26:59 +0000] "GET /favicon.ico HTTP/1.0" 404 209 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9"
crawl1.cosmixcorp.com - - [08/Feb/2007:13:27:57 +0000] "GET /robots.txt HTTP/1.0" 200 179 "-" "voyager/1.0"
crawl1.cosmixcorp.com - - [08/Feb/2007:13:28:25 +0000] "GET /links.html HTTP/1.0" 200 3413 "-" "voyager/1.0"
fhm226.internetdsl.tpnet.pl - - [08/Feb/2007:13:37:32 +0000] "GET /suse-on-vaio.html HTTP/1.1" 200 2858 "http://www.linux-on-laptops.com/sony.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
fhm226.internetdsl.tpnet.pl - - [08/Feb/2007:13:37:34 +0000] "GET /data/css HTTP/1.1" 404 206 "http://www.rfi.net/suse-on-vaio.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
80.247.140.134 - - [08/Feb/2007:13:57:35 +0000] "GET / HTTP/1.1" 200 3309 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)"
80.247.140.134 - - [08/Feb/2007:13:57:37 +0000] "GET /data/css HTTP/1.1" 404 206 "http://www.rfi.net" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)"
pop.compuscan.co.za - - [08/Feb/2007:14:10:43 +0000] "GET / HTTP/1.1" 200 3309 "-" "www.clamav.net"
livebot-207-46-98-57.search.live.com - - [08/Feb/2007:14:12:04 +0000] "GET /robots.txt HTTP/1.0" 200 179 "-" "msnbot/1.0 (+http://search.msn.com/msnbot.htm)"
livebot-207-46-98-57.search.live.com - - [08/Feb/2007:14:12:04 +0000] "GET /html/oracle.html HTTP/1.0" 404 214 "-" "msnbot/1.0 (+http://search.msn.com/msnbot.htm)"
dslb-088-064-005-154.pools.arcor-ip.net - - [08/Feb/2007:14:12:15 +0000] "GET / HTTP/1.1" 200 3309 "-" "www.clamav.net"
196.201.92.41 - - [08/Feb/2007:14:15:01 +0000] "GET / HTTP/1.1" 200 3309 "-" "MOT-L7/08.B7.DCR MIB/2.2.1 Profile/MIDP-2.0 Configuration/CLDC-1.1"

Задача заключается в сортировке 286 525 строк этого файла по коду ответа, запросу, браузеру, URL-адресу обратной ссылки и, наконец, дате. Одним из решений может быть использование следующего кода, который перебирает файлы, указанные в командной строке.

# sort-apache-log

#!/usr/bin/perl -n

use strict;
use warnings;

my @data;

LINE:
while ( <> ) {
    my $line = $_;
    if (
        $line =~ m/^(
            ([\w\.\-]+)             # client
            \s*-\s*-\s*\[
            ([^]]+)                 # date
            \]\s*"\w+\s*
            (\S+)                   # query
            [^"]+"\s*
            (\d+)                   # status
            \s+\S+\s+"[^"]*"\s+"
            ([^"]*)                 # browser
            "
            .*
        )$/x
    ) {
        my @chunks = split(/ +/, $line);
        my $ip      = $1;
        my $date    = $2;
        my $query   = $3;
        my $status  = $4;
        my $browser = $5;

        push(@data, [$ip, $date, $query, $status, $browser, $line]);
    }
}

my @sorted = sort {
    $a->[3] cmp $b->[3]
            ||
    $a->[2] cmp $b->[2]
            ||
    $a->[0] cmp $b->[0]
            ||
    $a->[1] cmp $b->[1]
            ||
    $a->[4] cmp $b->[4]
} @data;

foreach my $data ( @sorted ) {
    print $data->[5];
}

exit 0;

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

$> time ./sort-apache-log logfile > out-sort

real    0m17.371s
user    0m15.757s
sys     0m0.592s

Программа заняла чуть более 17 секунд реального времени. Обратите внимание на разные значения, которые выводит time , важно всегда использовать одно и то же значение и не путать, что оно означает.

Время выполнения

Общее или реальное время между вызовом time и завершением. Время выполнения включает время пользователя и время системы, а также время ожидания других пользователей и процессов в системе. Неизбежно, это наиболее приблизительное из приведённых измерений.

Время работы процессора пользователя

Время пользователя — это время, которое весь процесс провёл от имени пользователя в этой системе, выполняя эту программу.

Время работы процессора системы

Время системы — это время, которое сам ядро потратил на выполнение процедур или системных вызовов от имени пользователя этого процесса.

Выполнение этой же задачи как Schwarzian Transform позволяет исключить массивы ввода и вывода для хранения всех данных и работать с вводом непосредственно по мере его поступления. В остальном код выглядит довольно похожим:

# sort-apache-log-schwarzian

#!/usr/bin/perl -n

use strict;
use warnings;

print

    map $_->[0] =>

    sort {
        $a->[4] cmp $b->[4]
                ||
        $a->[3] cmp $b->[3]
                ||
        $a->[1] cmp $b->[1]
                ||
        $a->[2] cmp $b->[2]
                ||
        $a->[5] cmp $b->[5]
    }
    map  [ $_, m/^(
        ([\w\.\-]+)             # client
        \s*-\s*-\s*\[
        ([^]]+)                 # date
        \]\s*"\w+\s*
        (\S+)                   # query
        [^"]+"\s*
        (\d+)                   # status
        \s+\S+\s+"[^"]*"\s+"
        ([^"]*)                 # browser
        "
        .*
    )$/xo ]

    => <>;

exit 0;

Запустите новый код на том же файле журнала, что и выше, чтобы проверить новое время.

$> time ./sort-apache-log-schwarzian logfile > out-schwarz

real    0m9.664s
user    0m8.873s
sys     0m0.704s

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

$> cksum out-sort out-schwarz
3044173777 52029194 out-sort
3044173777 52029194 out-schwarz

Кстати. Также будьте внимательны к давлению со стороны менеджеров, которые видят, как вы ускорили программу на 50% от времени выполнения один раз, только чтобы получить запрос через месяц сделать то же самое снова (история из жизни) – вам просто нужно будет указать, что вы только человек, даже если вы программист Perl, и вы увидите, что вы можете сделать...

ВЕДЕНИЕ ЖУРНАЛА

Неотъемлемой частью любого хорошего процесса разработки является соответствующая обработка ошибок с надлежащими информативными сообщениями, однако существует школа мысли, которая предполагает, что файлы журнала должны быть разговорчивыми, как если бы цепочка непрерывного вывода каким-то образом гарантировала выживание программы. Если скорость является проблемой, этот подход неверен.

Часто встречается код, который выглядит примерно так:

logger->debug( "A logging message via process-id: $$ INC: "
                                                      . Dumper(\%INC) )

Проблема в том, что этот код всегда будет парситься и выполняться, даже когда уровень отладки, заданный в файле конфигурации ведения журнала, равен нулю. Как только подпрограмма debug() будет запущена, и внутренняя переменная $debug будет подтверждена как нулевая, например, сообщение, которое было отправлено, будет отброшено, и программа продолжит работу. Однако в приведенном примере хеш \%INC уже будет выведен, а строка сообщения построена, все это можно обойти с помощью переменной отладки на уровне оператора, как в этом примере:

logger->debug( "A logging message via process-id: $$ INC: "
                                           . Dumper(\%INC) ) if $DEBUG;

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

# ifdebug

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;
use Data::Dumper;
my $DEBUG = 0;

sub debug {
    my $msg = shift;

    if ( $DEBUG ) {
        print "DEBUG: $msg\n";
    }
};

timethese(100000, {
        'debug'       => sub {
            debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) )
        },
        'ifdebug'  => sub {
            debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) ) if $DEBUG
        },
});

Давайте посмотрим, что Benchmark сделает с этим:

$> perl ifdebug
Benchmark: timing 100000 iterations of constant, sub...
   ifdebug:  0 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU) @ 10000000.00/s (n=100000)
            (warning: too few iterations for a reliable count)
     debug: 14 wallclock secs (13.18 usr +  0.04 sys = 13.22 CPU) @ 7564.30/s (n=100000)

В одном случае код, который делает ровно то же самое, что касается вывода любой отладочной информации, то есть ничего, занимает 14 секунд, а в другом случае код занимает одну сотую секунды. Похоже, это достаточно определённо. Используйте переменную $DEBUG ПЕРЕД вызовом подпрограммы, а не полагайтесь на умную функциональность внутри неё.

Ведение журнала, если DEBUG (константа)

Можно немного продвинуться дальше, используя константу времени компиляции DEBUG.

# ifdebug-constant

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark;
use Data::Dumper;
use constant
    DEBUG => 0
;

sub debug {
    if ( DEBUG ) {
        my $msg = shift;
        print "DEBUG: $msg\n";
    }
};

timethese(100000, {
        'debug'       => sub {
            debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) )
        },
        'constant'  => sub {
            debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) ) if DEBUG
        },
});

Запуск этой программы даёт следующий вывод:

$> perl ifdebug-constant
Benchmark: timing 100000 iterations of constant, sub...
  constant:  0 wallclock secs (-0.00 usr +  0.00 sys = -0.00 CPU) @ -7205759403792793600000.00/s (n=100000)
            (warning: too few iterations for a reliable count)
       sub: 14 wallclock secs (13.09 usr +  0.00 sys = 13.09 CPU) @ 7639.42/s (n=100000)

Константа DEBUG превосходит даже переменную $debug, показывая время выполнения минус ноль секунд и, к тому же, генерирует сообщение "предупреждение: слишком мало итераций для надёжного подсчёта". Чтобы увидеть, что происходит на самом деле, и почему у нас было слишком мало итераций, когда мы думали, что запросили 100000, мы можем использовать очень полезную функцию B::Deparse для проверки нового кода:

$> perl -MO=Deparse ifdebug-constant

use Benchmark;
use Data::Dumper;
use constant ('DEBUG', 0);
sub debug {
    use warnings;
    use strict 'refs';
    0;
}
use warnings;
use strict 'refs';
timethese(100000, {'sub', sub {
    debug "A $0 logging message via process-id: $$" . Dumper(\%INC);
}
, 'constant', sub {
    0;
}
});
ifdebug-constant syntax OK

Вывод показывает, что подпрограмма constant(), которую мы тестируем, заменяется значением константы DEBUG: ноль. Строка, подлежащая проверке, была полностью оптимизирована, и вы не можете получить ничего более эффективного.

ЗАКЛЮЧИТЕЛЬНЫЕ ЗАМЕЧАНИЯ

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

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

$> perl -e 0

Дальнейшая оптимизация – задача для p5p.

СМОТРИТЕ ТАКЖЕ

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

PERLDOCS

Например: perldoc -f sort.

perlfaq4.

perlfork, perlfunc, perlretut, perlthrtut.

threads.

СТРАНИЦЫ СПРАВКИ

time.

МОДУЛИ

Конечно, здесь невозможно индивидуально продемонстрировать весь код, связанный с производительностью Perl, но вот короткий список модулей из CPAN, которые заслуживают дальнейшего внимания.

Apache::DProf
Apache::SmallProf
Benchmark
DBIx::Profile
Devel::AutoProfiler
Devel::DProf
Devel::DProfLB
Devel::FastProf
Devel::GraphVizProf
Devel::NYTProf
Devel::NYTProf::Apache
Devel::Profiler
Devel::Profile
Devel::Profit
Devel::SmallProf
Devel::WxProf
POE::Devel::Profiler
Sort::Key
Sort::Maker

URL

Очень полезные онлайн-справочные материалы:

http://www.ccl4.org/~nick/P/Fast_Enough/

http://www-128.ibm.com/developerworks/library/l-optperl.html

http://perlbuzz.com/2007/11/bind-output-variables-in-dbi-for-speed-and-safety.html

http://en.wikipedia.org/wiki/Performance_analysis

http://apache.perl.org/docs/1.0/guide/performance.html

http://perlgolf.sourceforge.net/

http://www.sysarch.com/Perl/sort_paper.html

АВТОР

Ричард Фоли <richard.foley@rfi.net> Авторское право (c) 2008

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlperf

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API