perlperf
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ОПИСАНИЕ
- ОБЗОР
- ОБЩИЕ РЕКОМЕНДАЦИИ
- ТЕСТЫ ПРОИЗВОДИТЕЛЬНОСТИ
- ИНСТРУМЕНТЫ ПРОФИЛИРОВАНИЯ
- СОРТИРОВКА
- ВЕДЕНИЕ ЖУРНАЛОВ
- ДОПОЛНИТЕЛЬНО
- СМОТРИТЕ ТАКЖЕ
- АВТОР
НАЗВАНИЕ
perlperf - Методы повышения производительности и оптимизации Perl
ОПИСАНИЕ
Данное руководство представляет собой введение в методы повышения производительности и оптимизации, которые могут быть применены к программам Perl. Хотя многие разработчики Perl пришли из других языков и могут использовать свои предыдущие знания, многие другие могут извлечь пользу из нескольких специфичных для Perl советов. Если вам нужна сжатая версия, возможно, лучший совет исходит от известного японского самурая Миямото Мусаси, который сказал:
"Do Not Engage in Useless Activity" в 1645 году.
ОБЗОР
Возможно, самой распространённой ошибкой программистов является попытка оптимизировать код до того, как программа выполнит какую-либо полезную работу – это плохая идея. Нет смысла иметь чрезвычайно быструю программу, которая не работает. Первой задачей является заставить программу правильно выполнить какую-либо полезную работу (не говоря уже о проверке работоспособности всего набора тестов), и только после этого следует подумать об оптимизации. Решив оптимизировать существующий работающий код, следует рассмотреть несколько простых, но существенных шагов, которые являются неотъемлемой частью любого процесса оптимизации.
ОДИН ШАГ ВСТОРОНУ
Во-первых, вам необходимо установить базовый временной показатель для существующего кода, который должен быть надёжным и воспроизводимым. Вероятно, для этого шага вам потребуется использовать модули Benchmark или Devel::NYTProf, или что-то подобное, или, возможно, утилиту Unix time, какую из них следует использовать, зависит от конкретной ситуации. Более подробный список модулей для тестирования производительности и профилирования, а также рекомендуемые дальнейшие чтения приведены в конце этого документа.
ОДИН ШАГ ВПЕРЕД
Далее, изучив программу на предмет «узких мест» (мест, где код работает медленно), измените код с намерением ускорить его работу. Использование системы управления версиями, например, subversion, гарантирует, что никакие изменения не будут необратимыми. Очень легко «подокручивать» здесь и там – не меняйте слишком много за один раз, иначе вы можете не обнаружить, какой фрагмент кода действительно является «медленным».
ЕЩЕ ОДИН ШАГ ВСТОРОНУ
Недостаточно сказать: «это ускорит его работу». Вы должны проверить это. Запустите код под управлением модулей для тестирования производительности или профилирования, начиная с первого шага, и убедитесь, что новый код выполняет ту же задачу за меньшее время. Сохраните свою работу и повторите…
ОБЩИЕ РЕКОМЕНДАЦИИ
Важное соображение при рассмотрении производительности заключается в том, что нет такого понятия, как Golden Bullet, поэтому нет правил, только рекомендации.
Очевидно, что встроенный код будет быстрее, чем вызовы подпрограмм или методов, потому что накладные расходы меньше, но этот подход имеет недостаток – он менее поддерживаемый и связан с большим потреблением памяти – нет бесплатного обеда. Если вы ищете элемент в списке, может быть эффективнее хранить данные в структуре хеш-таблицы, а затем просто проверить, определён ли ключ, а не перебирать весь массив, используя 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 v5.36;
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 v5.36;
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 v5.36;
=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 более десяти лет, но был заменён рядом других модулей, которые вернули нас в XXI век. Хотя рекомендуется оценить ваши инструменты из нескольких упомянутых здесь и из списка 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, полнофункциональной Perl-только заменой для Devel::DProf. Использование немного отличается тем, что вместо использования специального флага -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% исчезла. Именно здесь нам необходимо использовать имеющиеся инструменты и распознать их преимущества и недостатки, прежде чем использовать их. Интересно, что количество вызовов каждой подпрограммы идентично в двух отчётах, различаются только проценты. Как пишет автор %%%CODE_BLOCK_77%%:
...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. YMMV.
См. также 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 МБ, и фрагмент его выглядит следующим образом:
# файл журнала
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 строк этого файла по Коду ответа, Запросу, Браузеру, Ссылающейся ссылке и, наконец, Дате. Одним из решений может быть использование следующего кода, который итерирует по файлам, заданным в командной строке.
# sort-apache-log
#!/usr/bin/perl -n
use v5.36;
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 v5.36;
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, и вы увидите, что вы можете сделать…
ВЕДЕНИЕ ЖУРНАЛОВ
Неотъемлемой частью любого хорошего процесса разработки является соответствующая обработка ошибок с информативными сообщениями. Однако существует точка зрения, согласно которой файлы журналов должны быть «богатыми» (chatty), как если бы цепочка непрерывного вывода каким-то образом гарантировала выживание программы. Если скорость является проблемой, этот подход неправилен.
Часто встречается код, похожий на этот:
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 v5.36;
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 v5.36;
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: ноль. Строка, которая должна была быть протестирована, была полностью оптимизирована, и вы не можете получить ничего более эффективного.
POSTSCRIPT
Этот документ предоставляет несколько способов определения узких мест и проверки того, улучшили ли какие-либо изменения время выполнения кода.
В заключение, помните, что в настоящее время невозможно создать полезную программу, которая будет выполняться за ноль или отрицательное время, и этот базовый принцип может быть сформулирован как: «полезные программы медленные» по своему определению. Конечно, можно написать почти мгновенную программу, но она будет делать очень мало. Вот очень эффективная:
$> perl -e 0 Дальнейшая оптимизация – задача для p5p.
См. также
Дополнительную информацию можно найти, используя модули и ссылки ниже.
PERLDOCS
Например: perldoc -f sort.
perlfork, perlfunc, perlretut, perlthrtut.
Страницы руководства
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 URLS
Очень полезные онлайн-справочные материалы:
https://web.archive.org/web/20120515021937/http://www.ccl4.org/~nick/P/Fast_Enough/
https://web.archive.org/web/20050706081718/http://www-106.ibm.com/developerworks/library/l-optperl.html
https://perlbuzz.com/2007/11/14/bind_output_variables_in_dbi_for_speed_and_safety/
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 <richard.foley@rfi.net> Авторское право (c) 2008
© 1993–2023 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.38.0/perlperf