这是 perlperf 命令,可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器
程序:
您的姓名
perlperf - Perl 性能和优化技术
商品描述
这是对性能和优化技术的使用的介绍,这些技术可以
用于特别参考 perl 程序。 虽然很多 perl 开发者都来了
来自其他语言,并且可以在适当的情况下使用他们的先验知识,有很多
其他可能从一些 perl 特定指针中受益的人。 如果你想要
精简版,也许最好的建议来自著名的日本武士,
宫本武藏说:
“不要从事无用的活动”
。
产品特点
也许程序员最常犯的错误是试图优化他们的代码
在程序真正做任何有用的事情之前 - 这是一个坏主意。 没有意义
有一个非常快的程序不起作用。 第一项工作是获得一个程序
正确地 做一点事 有用,(更不用说确保测试套件完全
功能),然后才考虑优化它。 决定优化现有
工作代码,有几个简单但必不可少的步骤需要考虑哪些是内在的
到任何优化过程。
一 步骤 侧身
首先,您需要为现有代码建立一个基线时间,该时间需要
可靠且可重复。 您可能想要使用“基准”或
“Devel::NYTProf”模块,或类似的东西,用于这一步,或者可能是 Unix 系统
“时间”实用程序,以适当的为准。 有关更长的列表,请参阅本文档的基础
基准测试和分析模块,并建议进一步阅读。
一 步骤 前锋
接下来,检查了程序 热态 斑点,(代码似乎运行的地方
慢),更改代码以使其运行得更快。 使用版本
控制软件,如“颠覆”,将确保任何更改都是不可逆转的。 它太
很容易在这里摆弄和摆弄那里 - 任何时候都不要改变太多,否则你可能会
没有发现哪一段代码 真 是慢一点。
另一个 步骤 侧身
仅仅说:“这会让它运行得更快”是不够的,你必须检查它。 重新运行
在基准测试或分析模块控制下的代码,从上面的第一步开始,
并检查新代码是否执行了 同 任务 in 减 次. 保存您的工作并
重复...
一般 准则
考虑性能时的关键是记住没有这样的事情
“金子弹”,这就是为什么没有规则,只有指导方针。
很明显,内联代码将比子程序或方法调用更快,
因为开销较少,但是这种方法的缺点是较少
可维护并且以更大的内存使用为代价 - 没有这样的事情
免费午餐。 如果您正在搜索列表中的元素,则可以更有效地
将数据存储在一个hash结构中,然后简单的看一下key是否是
定义,而不是使用循环遍历整个数组 grep() 例如。 子字符串()
可能(很多)比 grep() 但不那么灵活,所以你有另一个权衡
使用权。 您的代码可能包含一行需要 0.01 秒的时间来执行,如果您
调用它 1,000 次,很可能在解析中等大小文件的程序中
例如,您已经有 10 秒的延迟,仅在一个代码位置,并且如果您
调用该行 100,000 次,您的整个程序将慢到无法忍受的爬行。
使用子程序作为排序的一部分是获得您想要的东西的有效方法,
但通常会比内置的慢 拼音 “cmp”和 数字 “<=>”排序
运营商。 可以对数据进行多次传递,构建索引以
使即将到来的排序更有效率,并使用所谓的“OM”(兽人语
机动)来提前缓存排序键。 缓存查找虽然是个好主意,但可以
通过强制对数据进行双重传递 - 一次设置,它本身就是减速的根源
缓存,并一次对数据进行排序。 使用“pack()”提取所需的排序键
成一致的字符串可以是构建单个字符串进行比较的有效方法,
而不是使用多个排序键,这使得可以使用标准,编写
在“c”和fast,perl“sort()”函数上输出,并且是“GRT”的基础
(古特曼罗斯勒变换)。 一些字符串组合可以减慢“GRT”的速度,只需
为了自己的利益而过于简单复杂。
对于使用数据库后端的应用程序,标准的“DBIx”命名空间试图提供帮助
让事情变得轻松,尤其是因为它试图 而不去 查询数据库直到
最新的可能时刻,但请务必阅读您选择的库附带的文档。
在处理数据库的开发人员面临的众多问题中,应该始终注意的是
始终使用“SQL”占位符并考虑在可能的情况下预取数据集
证明是有利的。 通过分配多个进程进行解析来拆分大文件
单个文件,使用“POE”、“线程”或“fork”也是一种有用的优化方式
您对可用“CPU”资源的使用,尽管这种技术充满了
并发问题,需要高度关注细节。
每个案例都有一个特定的应用程序和一个或多个例外,并且没有
代替运行一些测试并找出最适合您的方法
特定环境,这就是为什么编写最佳代码不是一门精确的科学,以及为什么
我们非常喜欢使用 Perl - TMTOWTDI。
基准
这里有几个例子来演示 Perl 的基准测试工具的用法。
分配 和 解除引用 变数
我相信我们中的大多数人都看到过这样的代码(或更糟),如下所示:
if ( $obj->{_ref}->{_myscore} >= $obj->{_ref}->{_yourscore} ) {
...
这类代码读起来真的很碍眼,而且对错别字非常敏感,
并且显式取消引用变量会更清晰。 我们正在回避
使用面向对象的编程技术来封装变量的问题
通过方法访问,只能通过对象访问。 这里我们只讨论
选择的技术实现,以及这是否对性能有影响。 我们可以
通过将比较代码放入,看看这个解引用操作是否有任何开销
一个文件并运行“基准”测试。
# 取消引用
#!/usr/bin/perl
用严格;
使用警告;
使用基准;
我的 $ref = {
'参考' => {
_myscore => '100 + 1',
_yourscore => '102 - 1',
},
};
时间这些(1000000,{
'直接' => 子 {
我的 $x = $ref->{ref}->{_myscore} 。 $ref->{ref}->{_yourscore} ;
},
'取消引用' => 子 {
我的 $ref = $ref->{ref};
我的 $myscore = $ref->{_myscore};
我的 $yourscore = $ref->{_yourscore};
我的 $x = $myscore 。 $你的分数;
},
});
运行任何计时测量足够次数是必不可少的,因此数字
确定一个数字平均值,否则每次运行都会由于
环境中的变化,以减少对“CPU”资源的争用的影响和
例如网络带宽。 将上面的代码运行一百万次迭代,我们可以
看看“基准”模块的报告输出,看看哪种方法是
最有效率。
$> perl 取消引用
基准:计时 1000000 次解引用迭代,直接...
取消引用:2 挂钟秒(1.59 usr + 0.00 sys = 1.59 CPU)@ 628930.82/s (n=1000000)
直接:1 挂钟秒(1.20 usr + 0.00 sys = 1.20 CPU)@ 833333.33/s (n=1000000)
区别很明显,解引用方法更慢。 虽然它管理
在我们的测试期间平均每秒执行 628,930 次,直接方法
不幸的是,它又成功运行了 204,403 次。 不幸的是,因为有
有许多使用多层直接变量访问编写的代码示例,以及
这通常是可怕的。 然而,它的速度要快得多。 问题仍然是是否
微小的收益实际上值得眼睛疲劳或可维护性的损失。
搜索 和 更换 or tr
如果我们有一个需要修改的字符串,而正则表达式几乎总是很多
更灵活,“tr”,一个经常未被充分利用的工具,仍然是一个有用的工具。 一种情况可能是
用另一个字符替换所有元音。 正则表达式解决方案可能如下所示:
$str =~ s/[aeiou]/x/g
“tr”替代方案可能如下所示:
$str =~ tr/aeiou//
我们可以将其放入一个测试文件中,我们可以运行该文件来检查哪种方法最快,
使用全局 $STR 变量分配给“my $str”变量以避免 perl
试图通过注意到它只被分配一次来优化任何工作。
# 正则表达式-音译
#!/usr/bin/perl
用严格;
使用警告;
使用基准;
我的 $STR = "$$-这个和那个";
时间这些(1000000,{
'sr' => sub { 我的 $str = $STR; $str =~ s/[aeiou]/x/g; 返回 $str; },
'tr' => sub { 我的 $str = $STR; $str =~ tr/aeiou//; 返回 $str; },
});
运行代码给出了我们的结果:
$> perl regex-transliterate
基准测试:计时 1000000 次 sr、tr...
sr:2 挂钟秒(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”版本显然是赢家。 一种解决方案是灵活的,另一种是快速的——而且
程序员可以适当地选择使用哪个。
检查“基准”文档以获取更多有用的技术。
剖析 工具
一段稍大的代码将提供分析器可以生成的内容
更广泛的报告统计。 这个例子使用了简单的“wordmatch”程序
它解析给定的输入文件并输出关于内容的简短报告。
# 单词匹配
#!/usr/bin/perl
用严格;
使用警告;
=head1 名字
filewords - 输入文件的词分析
=head1 概要
文件字 -f 输入文件名 [-d]
=head1 描述
这个程序解析给定的文件名,用 C<-f> 指定,并显示一个
对其中发现的单词进行简单分析。 使用 C<-d> 开关启用
调试消息。
=切割
使用文件句柄;
使用 Getopt::Long;
我的 $debug = 0;
我的 $file = '';
我的 $result = GetOptions (
'调试' => \$调试,
'file=s' => \$file,
);
die("invalid args") 除非 $result;
除非(-f $文件){
die("用法:$0 -f 文件名 [-d]");
}
my $FH = FileHandle->new("< $file") or die("unable to open file($file): $!");
我的 $i_LINES = 0;
我的 $i_WORDS = 0;
我的 %count = ();
我的@lines = <$FH>;
foreach 我的 $line ( @lines ) {
$i_LINES++;
$line =~ s/\n//;
我的@words = split(/ +/, $line);
我的 $i_words = 标量(@words);
$i_WORDS = $i_WORDS + $i_words;
调试(“行:$i_LINES 提供 $i_words 词:@words”);
我的 $i_word = 0;
foreach 我的 $word ( @words ) {
$i_word++;
$count{$i_LINES}{spec} += 匹配($i_word, $word, '[^a-zA-Z0-9]');
$count{$i_LINES}{only} += 匹配($i_word, $word, '^[^a-zA-Z0-9]+$');
$count{$i_LINES}{cons} +=matches($i_word, $word, '^[(?i:bcdfghjklmnpqrstvwxyz)]+$');
$count{$i_LINES}{vows} += 匹配($i_word, $word, '^[(?i:aeiou)]+$');
$count{$i_LINES}{caps} += 匹配($i_word, $word, '^[(AZ)]+$');
}
}
打印报告(%count);
子匹配{
我的 $i_wd = shift;
我的 $word = shift;
我的 $regex = shift;
我的 $has = 0;
如果 ( $word =~ /($regex)/ ) {
$has++ 如果 $1;
}
debug("word: $i_wd ".($has ? 'matches' : 'does not match')." chars: /$regex/");
返回 $has;
}
子报告{
我的 %report = @_;
我的 %rep;
foreach 我的 $line ( 键 %report ) {
foreach 我的 $key ( keys %{ $report{$line} } ) {
$rep{$key} += $report{$line}{$key};
}
}
我的 $report = qq|
$file 的 $0 报告:
文件中的行:$i_LINES
文件中的单词:$i_WORDS
带有特殊(非单词)字符的单词:$i_spec
仅包含特殊(非单词)字符的单词:$i_only
只有辅音的词:$i_cons
只有大写字母的单词:$i_caps
只有元音的单词:$i_vows
|;
返回 $report;
}
子调试{
我的 $message = shift;
如果($调试){
打印 STDERR "DBG: $message\n";
}
}
退出 0;
开发::DProf
这个古老的模块已经成为 Perl 代码分析的事实上的标准。
十年,但已被许多其他模块所取代,这些模块使我们回到
21 世纪。 虽然建议您从几个方面评估您的工具
此处和本文档底部的 CPAN 列表中提到,(目前
Devel::NYTProf 似乎是首选武器 - 见下文),我们将快速浏览一下
首先是 Devel::DProf 的输出,为 Perl 分析工具设置基线。 跑过
通过使用命令上的“-d”开关,在“Devel::DProf”控制下的上述程序-
线。
$> perl -d:DProf wordmatch -f perl5db.pl
<...多行剪断...>
perl5db.pl 的词匹配报告:
文件中的行数:9428
文件中的字数:50243
带有特殊(非单词)字符的单词:20480
仅包含特殊(非单词)字符的单词:7790
只有辅音的词:4801
只有大写字母的单词:1316
只有元音的单词:1701
"Devel::DProf" 产生一个特殊的文件,称为 tmon.out 默认情况下,并读取此文件
通过“dprofpp”程序,该程序已作为“Devel::DProf”的一部分安装
分配。 如果您在没有选项的情况下调用“dprofpp”,它将读取 tmon.out 文件
当前目录并生成人类可读的运行统计报告
程序。 请注意,这可能需要一些时间。
$> dprofpp
总经过时间 = 2.951677 秒
用户+系统时间 = 2.871677 秒
独家时间
%Time ExclSec CumulS #Calls sec/call Csec/c 名称
102. 2.945 3.003 251215 0.0000 0.0000 主要::匹配
2.40 0.069 0.069 260643 0.0000 0.0000 主::调试
1.74 0.050 0.050 1 0.0500 0.0500 主要::报告
1.04 0.030 0.049 4 0.0075 0.0123 主要::开始
0.35 0.010 0.010 3 0.0033 0.0033 出口商::as_heavy
0.35 0.010 0.010 7 0.0014 0.0014 IO::文件::开始
0.00 - -0.000 1 - - Getopt::Long::FindOption
0.00 - -0.000 1 - - 符号::开始
0.00 - -0.000 1 - - Fcntl::开始
0.00 - -0.000 1 - - Fcntl::bootstrap
0.00 - -0.000 1 - - 警告::开始
0.00 - -0.000 1 - - IO::引导程序
0.00 - -0.000 1 - - Getopt::Long::ConfigDefaults
0.00 - -0.000 1 - - Getopt::Long::Configure
0.00 - -0.000 1 - - 符号::gensym
“dprofpp”将生成一些关于“wordmatch”活动的非常详细的报告
程序。 挂钟、用户和系统时间位于分析的顶部,之后
这是定义报告的主要列。 检查“dprofpp”文档
它支持的许多选项的详细信息。
另请参阅“Apache::DProf”,它将“Devel::DProf”挂接到“mod_perl”。
开发::分析器
让我们看一下使用不同分析器的同一个程序:“Devel::Profiler”,一个
"Devel::DProf" 的纯 Perl 替代品。 用法略有不同
不是使用特殊的“-d:”标志,而是直接将“Devel::Profiler”作为一个
模块使用“-M”。
$> perl -MDevel::Profiler wordmatch -f perl5db.pl
<...多行剪断...>
perl5db.pl 的词匹配报告:
文件中的行数:9428
文件中的字数:50243
带有特殊(非单词)字符的单词:20480
仅包含特殊(非单词)字符的单词:7790
只有辅音的词:4801
只有大写字母的单词:1316
只有元音的单词:1701
"Devel::Profiler" 生成与 "dprofpp" 兼容的 tmon.out 文件
程序,从而节省了专用统计阅读器程序的构建。 “dprofpp”
因此用法与上述示例相同。
$> dprofpp
总经过时间 = 20.984 秒
用户+系统时间 = 19.981 秒
独家时间
%Time ExclSec CumulS #Calls sec/call Csec/c 名称
49.0 9.792 14.509 251215 0.0000 0.0001 主要::匹配
24.4 4.887 4.887 260643 0.0000 0.0000 主::调试
0.25 0.049 0.049 1 0.0490 0.0490 主要::报告
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::文件::新
0.00 0.000 0.000 1 0.0000 0.0000 IO::句柄::新
0.00 0.000 0.000 1 0.0000 0.0000 符号::gensym
0.00 0.000 0.000 1 0.0000 0.0000 IO::文件::打开
有趣的是,我们得到的结果略有不同,这主要是因为算法
生成报告的方法是不同的,即使据称输出文件格式是
完全相同的。 已用时间、用户时间和系统时间清楚地显示了所需的时间
"Devel::Profiler" 执行自己的运行,但列列表感觉更准确
不知何故,我们早先从“Devel::DProf”中得到的那些。 102% 的数字有
例如消失了。 这是我们必须使用我们可以使用的工具的地方,并且
在使用它们之前,先了解它们的优缺点。 有趣的是,电话号码
两个报告中的每个子程序都相同,只是百分比不同。 作为
“Devel::Proviler”的作者写道:
...在 Devel::DProf 下运行 HTML::Template 的测试套件显示 output()
不花时间,但 Devel::Profiler 显示大约 10% 的时间在 output() 中。
我不知道该相信哪个,但我的直觉告诉我出了什么问题
开发:: DProf。 HTML::Template::output() 是一个很大的例程
每次测试。 无论哪种方式,都需要修复一些东西。
YMMV。
另请参阅“Devel::Apache::Profiler”,它将“Devel::Profiler”挂接到“mod_perl”。
开发::SmallProf
“Devel::SmallProf”分析器检查 Perl 程序的运行时并生成一个
逐行列表显示每行被调用的次数以及每行的长度
采取执行。 它是通过在运行时向 Perl 提供熟悉的“-d”标志来调用的。
$> perl -d:SmallProf wordmatch -f perl5db.pl
<...多行剪断...>
perl5db.pl 的词匹配报告:
文件中的行数:9428
文件中的字数:50243
带有特殊(非单词)字符的单词:20480
仅包含特殊(非单词)字符的单词:7790
只有辅音的词:4801
只有大写字母的单词:1316
只有元音的单词:1701
“Devel::SmallProf”将它的输出写入一个名为 小教授输出, 默认情况下。 这
文件格式如下所示:
:
当程序终止时,可以使用任何标准检查和排序输出
文本过滤实用程序。 类似以下内容可能就足够了:
$> cat smallprof.out | grep \d*: | 排序 -k3 | tac | 头-n20
251215 1.65674 7.68000 75:如果($word =~/($regex)/){
251215 0.03264 4.40000 79:调试(“word:$i_wd”。($has?'matches':
251215 0.02693 4.10000 81:返回 $has;
260643 0.02841 4.07000 128:如果($调试){
260643 0.02601 4.04000 126:我的 $message = shift;
251215 0.02641 3.91000 73:我的 $has = 0;
251215 0.03311 3.71000 70:我的$i_wd = shift;
251215 0.02699 3.69000 72:我的 $regex = shift;
251215 0.02766 3.68000 71:我的$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:我的 $i_cons = 匹配($i_word,$word,
50243 0.48837 0.88000 63:$count{$i_LINES}{caps} =
50243 0.00516 0.88000 58:我的 $i_caps = 匹配($i_word,$word,'^[(A-
50243 0.00631 0.81000 54:我的 $i_spec = 匹配($i_word,$word,'[^a-
50243 0.00496 0.80000 57:我的 $i_vows = 匹配($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:我的 $i_only = 匹配($i_word,$word,'^[^a-
您可以立即看到与子程序分析模块略有不同的重点,
我们开始确切地看到哪一行代码花费的时间最多。 那条正则表达式行
例如,看起来有点可疑。 请记住,这些工具应该是
一起使用,没有单一的最佳方法来分析您的代码,您需要使用最好的
工作的工具。
另请参阅“Apache::SmallProf”,它将“Devel::SmallProf”挂接到“mod_perl”。
开发::FastProf
"Devel::FastProf" 是另一个 Perl 行分析器。 写这篇文章是为了得到
比“Devel::SmallProf”更快的线分析器,因为它是
写成“C”。 要使用“Devel::FastProf”,请向 Perl 提供“-d”参数:
$> perl -d:FastProf wordmatch -f perl5db.pl
<...多行剪断...>
perl5db.pl 的词匹配报告:
文件中的行数:9428
文件中的字数:50243
带有特殊(非单词)字符的单词:20480
仅包含特殊(非单词)字符的单词:7790
只有辅音的词:4801
只有大写字母的单词:1316
只有元音的单词:1701
"Devel::FastProf" 将统计信息写入文件 快速输出 在当前目录中。
可以指定的输出文件可以通过使用“fprofpp”来解释
命令行程序。
$> fprofpp | 头-n20
# fprofpp 输出格式为:
# 文件名:行时间计数:源
单词匹配:75 3.93338 251215:如果($word =~/($regex)/){
wordmatch:79 1.77774 251215: debug("word: $i_wd ".($has ? 'matches' : 'does not match')." chars: /$regex/");
字匹配:81 1.47604 251215:返回 $has;
wordmatch:126 1.43441 260643: 我的 $message = shift;
字匹配:128 1.42156 260643:如果($调试){
wordmatch:70 1.36824 251215: 我的 $i_wd = shift;
wordmatch:71 1.36739 251215: 我的 $word = shift;
wordmatch:72 1.35939 251215: 我的 $regex = shift;
我们可以直接看到每行被调用的次数与
"Devel::SmallProf" 输出,并且序列基于
每行执行时间的顺序,“if ( $debug ) { ”和“my
例如,$message = shift;"。实际记录的时间差异可能在
内部使用的算法,或者可能是由于系统资源限制或
争论。
另请参阅 DBIx::Profile,它将分析在“DBIx::*”下运行的数据库查询
命名空间。
开发::NYTProf
“开发::NYTProf”是 下页 代 Perl 代码分析器,修复了许多缺点
其他工具并实现了许多很酷的功能。 首先,它可以用作
线 分析器,一个 阻止 或者 子程序 分析器,一次完成。 它还可以使用子
提供“clock_gettime()”的系统上的微秒 (100ns) 分辨率。 有可能
甚至由正在分析的程序启动和停止。 这是个人资料的单行条目
“mod_perl”应用程序。 它是用“c”编写的,可能是最快的分析器
可用于 Perl。 酷的清单还在继续。 够了,让我们看看如何
它有效 - 只需使用熟悉的“-d”开关将其插入并运行代码。
$> perl -d:NYTProf wordmatch -f perl5db.pl
perl5db.pl 的词匹配报告:
文件中的行数:9427
文件中的字数:50243
带有特殊(非单词)字符的单词:20480
仅包含特殊(非单词)字符的单词:7790
只有辅音的词:4801
只有大写字母的单词:1316
只有元音的单词:1701
“NYTProf”将生成报告数据库放入文件中 nytprof 输出 默认情况下。 人类
可以使用提供的“nytprofhtml”(HTML
输出)和“nytprofcsv”(CSV 输出)程序。 我们使用了 Unix 系统“html2text”
实用程序来转换 nytprof/index.html 为方便起见,请在此处存档。
$> html2text nytprof/index.html
业绩概况指数
对于字匹配
26 年 13 月 46 日星期五 39:2008:XNUMX 运行
于 26 年 13 月 47 日星期五 23:2008:XNUMX 报告
前 15 个子程序——按独占时间排序
|调用 |P |F |包含|不包含|子程序 |
| | | |时间 |时间 | |
|251215|5 |1 |13.09263 |10.47692 |main:: |匹配 |
|260642|2 |1 |2.71199 |2.71199 |main:: |调试 |
|1 |1 |1 |0.21404 |0.21404 |main:: |报告 |
|2 |2 |2 |0.00511 |0.00511 |XSLoader:: |加载 (xsub) |
|14 |14|7 |0.00304 |0.00298 |出口商:: |进口 |
|3 |1 |1 |0.00265 |0.00254 |出口商:: |as_heavy |
|10 |10|4 |0.00140 |0.00140 |变量:: |导入 |
|13 |13|1 |0.00129 |0.00109 |常数:: |导入 |
|1 |1 |1 |0.00360 |0.00096 |文件句柄:: |导入 |
|3 |3 |3 |0.00086 |0.00074 |警告::注册::|导入 |
|9 |3 |1 |0.00036 |0.00036 |严格:: |位 |
|13 |13|13|0.00032 |0.00029 |严格:: |导入 |
|2 |2 |2 |0.00020 |0.00020 |警告:: |import |
|2 |1 |1 |0.00020 |0.00020 |Getopt::Long:: |ParseOptionSpec|
|7 |7 |6 |0.00043 |0.00020 |严格:: |取消导入 |
有关更多信息,请参阅 189 个子程序的完整列表。
报告的第一部分已经显示了关于哪些关键信息
子程序使用的时间最多。 接下来给出一些关于来源的统计数据
配置文件。
源代码文件——按独占时间和名称排序
|Stmts |独家|平均。 |报告 |源文件 |
| |时间 | | | |
|2699761|15.66654 |6e-06 |行。 堵塞 。 子|字匹配|
|35 |0.02187 |0.00062|行。 堵塞 。 sub|IO/Handle.pm |
|274 |0.01525 |0.00006|行。 堵塞 。 sub|Getopt/Long.pm |
|20 |0.00585 |0.00029|行。 堵塞 。 sub|Fcntl.pm |
|128 |0.00340 |0.00003|行。 堵塞 。 sub|出口商/Heavy.pm |
|42 |0.00332 |0.00008|行。 堵塞 。 sub|IO/File.pm |
|261 |0.00308 |0.00001|行。 堵塞 。 sub|出口商.pm |
|323 |0.00248 |8e-06 |行。 堵塞 。 子|常数.pm |
|12 |0.00246 |0.00021|行。 堵塞 。 sub|文件/规范/Unix.pm |
|191 |0.00240 |0.00001|行。 堵塞 。 子|vars.pm |
|77 |0.00201 |0.00003|行。 堵塞 。 sub|文件句柄.pm |
|12 |0.00198 |0.00016|行。 堵塞 。 sub|鲤鱼.pm |
|14 |0.00175 |0.00013|行。 堵塞 。 sub|符号.pm |
|15 |0.00130 |0.00009|行。 堵塞 。 子|IO.pm |
|22 |0.00120 |0.00005|行。 堵塞 。 sub|IO/Seekable.pm |
|198 |0.00085 |4e-06 |行。 堵塞 。 子|警告/register.pm|
|114 |0.00080 |7e-06 |行。 堵塞 。 sub|strict.pm |
|47 |0.00068 |0.00001|行。 堵塞 。 子|警告.pm |
|27 |0.00054 |0.00002|行。 堵塞 。 子|overload.pm |
|9 |0.00047 |0.00005|行。 堵塞 。 sub|SelectSaver.pm |
|13 |0.00045 |0.00003|行。 堵塞 。 sub|文件/规范.pm |
|2701595|15.73869 | |总计 |
|128647 |0.74946 | |平均 |
| |0.00201 |0.00003|中值 |
| |0.00121 |0.00003|偏差 |
由 Tim Bunce 开发的 NYTProf 2.03 Perl 分析器生成的报告和
亚当·卡普兰。
此时,如果您正在使用 HTML 报告,您可以通过各种链接点击
深入研究每个子程序和每一行代码。 因为我们正在使用文本
在这里报告,并且有一个完整的目录,其中包含为每个源文件构建的报告,
我们将只显示相应的一部分 wordmatch-line.html 文件,足以
给出您可以从这个很酷的工具中获得的输出类型的想法。
$> html2text nytprof/wordmatch-line.html
性能配置文件--块视图-.-线视图-.-子视图-
对于字匹配
26 年 13 月 46 日星期五 39:2008:XNUMX 运行
于 26 年 13 月 47 日星期五 22:2008:XNUMX 报告
文件字匹配
子程序——按独占时间排序
|调用 |P|F|包含|不包含|子程序 |
| | | |时间 |时间 | |
|251215|5|1|13.09263 |10.47692 |main::|matches|
|260642|2|1|2.71199 |2.71199 |main::|调试 |
|1 |1|1|0.21404 |0.21404 |main::|报告 |
|0 |0|0|0 |0 |主::|开始 |
|行|Stmts。|独家|平均。 |代码 |
| | |时间 | | |
|1 | | | |#!/usr/bin/perl |
|2 | | | | |
| | | | |使用严格; |
|3 |3 |0.00086 |0.00029|# 花费了 0.00003 秒对严格进行了 1 次调用:: |
| | | | |进口|
| | | | |使用警告; |
|4 |3 |0.01563 |0.00521|# 花费 0.00012 秒对警告进行 1 次调用:: |
| | | | |进口|
|5 | | | | |
|6 | | | |=head1 名称 |
|7 | | | | |
|8 | | | |filewords - 输入文件的词分析 |
<...剪...>
|62 |1 |0.00445 |0.00445|打印报告( %count ); |
| | | | |# 花费 0.21404 秒对 main::report 进行 1 次调用|
|63 | | | | |
| | | | |# 内花费了 23.56955s (10.47692+2.61571) |
| | | | |main::matches 被调用了 251215 次,|
| | | | |平均 0.00005 秒/调用:# 50243 次 |
| | | | |(2.12134+0.51939s) 在 wordmatch 的第 57 行,avg|
| | | | |0.00005s/call # 50243 次 (2.17735+0.54550s) |
|64 | | | |在 wordmatch 的第 56 行,平均 0.00005s/call # |
| | | | 在 | 的第 50243 行 | 2.10992 次(0.51797+58s)
| | | | |wordmatch, 平均 0.00005s/call # 50243 次 |
| | | | |(2.12696+0.51598s) 在 wordmatch 的第 55 行,avg|
| | | | |0.00005s/call # 50243 次 (1.94134+0.51687s) |
| | | | |在 wordmatch 的第 54 行,平均 0.00005s/call |
| | | | |sub 匹配 { |
<...剪...>
|102 | | | | |
| | | | |# 在 main::debug 中花费了 2.71199 秒,这是 |
| | | | |调用了 260642 次,平均 0.00001 次/调用:# |
| | | | | 251215 次 (2.61571+0s) by main::matches at |
|103 | | | | wordmatch 的第 74 行,平均 0.00001s/call # 9427 |
| | | | |在 wordmatch 的第 0.09628 行的时间 (0+50s), avg|
| | | | |0.00001s/调用 |
| | | | |子调试{ |
|104 |260642|0.58496 |2e-06 |我的 $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|退出 0; |
|112 | | | | |
那里有很多非常有用的信息——这似乎是前进的方向。
另请参阅“Devel::NYTProf::Apache”,它将“Devel::NYTProf”挂接到“mod_perl”。
排序
Perl 模块并不是性能分析师可以使用的唯一工具,系统
不应忽视诸如“时间”之类的工具,如下一个示例所示,我们在其中进行了
快速查看排序。 许多书籍、论文和文章都是关于高效的
排序算法,这里不是重复这种工作的地方,有几个很好的
排序模块也值得一看:“Sort::Maker”、“Sort::Key”跳到
头脑。 但是,仍然可以对某些特定于 Perl 的进行一些观察
对与排序数据集相关的问题的解释,并给出一两个例子
关于对大数据量进行排序如何影响性能。 首先,经常
对大量数据进行排序时忽略的一点,可以尝试减少数据
设置要处理,在许多情况下,“grep()”作为一个简单的过滤器非常有用:
@data = 排序 grep { /$filter/ } @incoming
像这样的命令可以大大减少实际分拣的材料量
首先,不应仅仅根据其
简单。 “KISS”原则经常被忽视——下一个例子使用
简单的系统“时间”实用程序来演示。 我们来看一个实际的例子
对大文件的内容进行排序,一个 apache 日志文件就可以了。 这个有超过一个
百万行的四分之一,大小为 50M,它的一个片段如下所示:
# 日志文件
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(兼容;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(兼容;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-美国;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(兼容;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(兼容;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(兼容;Konqueror/3.4;Linux)KHTML/3.4.0(如 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(兼容;Konqueror/3.4;Linux)KHTML/3.4.0(如 Gecko)”
debora.to.isac.cnr.it - - [08/Feb/2007:13:03:58 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "Mozilla/5.0(兼容;Konqueror/ 3.4; Linux) KHTML/3.4.0 (如 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 .20061206) 壁虎/1.5.0.9 Firefox/XNUMX"
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(兼容;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(兼容;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(兼容;MSIE 6.0;Windows NT 5.1;.NET CLR 1.1.4322) .XNUMX)"
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(兼容;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 。网”
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 配置/CLDC-1.1"
这里的具体任务是将这个文件的 286,525 行按 Response Code、Query、
浏览器,引用 URL,最后是日期。 一种解决方案可能是使用以下代码,
它遍历命令行上给出的文件。
# 排序 apache 日志
#!/usr/bin/perl -n
用严格;
使用警告;
我的@data;
线:
而 ( <> ) {
我的 $line = $_;
如果(
$线 =~ 米/^(
([\w\.\-]+) # 客户端
\s*-\s*-\s*\[
([^]]+) # 日期
\]\s*"\w+\s*
(\S+) # 查询
[^"]+"\s*
(\d+) # 状态
\s+\S+\s+"[^"]*"\s+"
([^"]*) # 浏览器
"
.*
)$/x
){
我的@chunks = split(/ +/, $line);
我的 $ip = $1;
我的 $date = $2;
我的 $query = $3;
我的 $status = $4;
我的 $browser = $5;
推(@data, [$ip, $date, $query, $status, $browser, $line]);
}
}
我的@sorted = 排序{
$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]
} @数据;
foreach 我的 $data ( @sorted ) {
打印 $data->[5];
}
退出 0;
运行此程序时,重定向“STDOUT”以便可以检查输出是否为
从以下测试运行中更正并使用系统“时间”实用程序检查整体
运行。
$> 时间 ./sort-apache-log 日志文件 > out-sort
真正的0m17.371s
用户 0m15.757s
系统 0m0.592s
该程序运行时间仅超过 17 秒。 注意不同的值“时间”
输出,重要的是始终使用相同的输出,并且不要混淆每个输出
手段。
经过的实时时间
调用“时间”与“时间”之间的总体时间或挂钟时间
终止。 已用时间包括用户和系统时间,以及花费的时间
等待系统上的其他用户和进程。 不可避免地,这是最
给出的测量值的近似值。
用户 CPU 时间
用户时间是整个过程代表用户花费的时间
这个系统执行这个程序。
系统 CPU 时间
系统时间是内核本身执行例程所花费的时间,或者
系统调用,代表这个进程的用户。
运行与“Schwarzian 变换”相同的过程,可以消除
用于存储所有数据的输入和输出数组,并直接处理输入
也到了。 否则,代码看起来非常相似:
# 排序-apache-log-schwarzian
#!/usr/bin/perl -n
用严格;
使用警告;
打印
地图 $_->[0] =>
种类 {
$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]
}
地图 [ $_, m/^(
([\w\.\-]+) # 客户端
\s*-\s*-\s*\[
([^]]+) # 日期
\]\s*"\w+\s*
(\S+) # 查询
[^"]+"\s*
(\d+) # 状态
\s+\S+\s+"[^"]*"\s+"
([^"]*) # 浏览器
"
.*
)$/xo]
=> <>;
退出 0;
对同一个日志文件运行新代码,如上,以检查新时间。
$> 时间 ./sort-apache-log-schwarzian 日志文件 > out-schwarz
真正的0m9.664s
用户 0m8.873s
系统 0m0.704s
时间减少了一半,以任何标准衡量,这都是一个可观的速度提升。
当然,重要的是检查输出与第一次程序运行是否一致,
这就是 Unix 系统“cksum”实用程序的用武之地。
$> cksum out-sort out-schwarz
3044173777 52029194 外排
3044173777 52029194 外施瓦茨
顺便提一句。 也要注意来自经理的压力,他们看到您将程序速度提高了 50%
运行一次,仅在一个月后收到再次执行相同操作的请求(真实故事)-
你只需要指出你只是人类,即使你是一个 Perl 程序员,并且
你会看到你能做什么...
记录
任何好的开发过程的一个重要部分是适当的错误处理
适当的信息信息,但是存在一种思想流派
建议日志文件应该是 健谈,好像不间断的输出链不知何故
保证程序的生存。 如果速度有任何问题,这种方法是
错误。
一个常见的景象是如下所示的代码:
logger->debug("通过进程ID的日志消息:$$ INC:" .Dumper(\%INC))
问题是这段代码总是会被解析和执行,即使是在调试的时候
日志配置文件中设置的级别为零。 一旦 调试() 子程序已经
输入,内部$debug变量确认为零,例如消息
已发送的将被丢弃,程序将继续。 在示例中
尽管如此,“\%INC”哈希已经被转储,并且消息字符串
构造,所有这些工作都可以通过语句中的调试变量绕过
水平,像这样:
logger->debug( "A logging message via process-id: $$ INC: " . Dumper(\%INC) ) if $DEBUG;
这种效果可以通过设置具有两种形式的测试脚本来演示,包括
“debug()”子程序来模拟典型的“logger()”功能。
# 如果调试
#!/usr/bin/perl
用严格;
使用警告;
使用基准;
使用数据::倾销者;
我的 $DEBUG = 0;
子调试{
我的 $msg = shift;
如果($调试){
打印“调试:$msg\n”;
}
};
时间这些(100000,{
'调试' => 子 {
调试(“通过进程 ID 的 $0 日志消息:$$”。Dumper(\%INC))
},
'ifdebug' => 子 {
debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) ) if $DEBUG
},
});
让我们看看“基准”是怎么做的:
$> perl ifdebug
基准测试:定时 100000 次常量、子...
ifdebug: 0 wallclock secs (0.01 usr + 0.00 sys = 0.01 CPU) @ 10000000.00/s (n=100000)
(警告:可靠计数的迭代太少)
调试:14 挂钟秒 (13.18 usr + 0.04 sys = 13.22 CPU) @ 7564.30/s (n=100000)
在一种情况下,代码在输出任何
就调试信息而言,也就是说什么都没有,需要 14 秒,并且在
其他情况下,代码需要百分之一秒。 看起来相当确定。 用一个
$DEBUG 变量在调用子程序之前,而不是依赖智能
里面的功能。
记录 if DEBUG (不变)
通过使用编译时“DEBUG”,可以将先前的想法更进一步
不变。
# ifdebug 常量
#!/usr/bin/perl
用严格;
使用警告;
使用基准;
使用数据::倾销者;
使用常量
调试 => 0
;
子调试{
如果(调试){
我的 $msg = shift;
打印“调试:$msg\n”;
}
};
时间这些(100000,{
'调试' => 子 {
调试(“通过进程 ID 的 $0 日志消息:$$”。Dumper(\%INC))
},
'常数' => 子 {
debug( "A $0 logging message via process-id: $$" . Dumper(\%INC) ) if DEBUG
},
});
运行此程序会产生以下输出:
$> perl ifdebug-constant
基准测试:定时 100000 次常量、子...
常数:0 挂钟秒 (-0.00 usr + 0.00 sys = -0.00 CPU) @ -7205759403792793600000.00/s (n=100000)
(警告:可靠计数的迭代太少)
sub:14 秒挂钟 (13.09 usr + 0.00 sys = 13.09 CPU) @ 7639.42/s (n=100000)
“DEBUG”常量甚至用 $debug 变量擦拭地板,以减号计时
零秒,并生成“警告:可靠计数的迭代太少”消息
讨价还价。 看看到底发生了什么,以及为什么我们的迭代次数太少
我们以为我们要求了 100000,我们可以使用非常有用的“B::Deparse”来检查新的
码:
$> perl -MO=Deparse ifdebug-constant
使用基准;
使用数据::倾销者;
使用常量 ('DEBUG', 0);
子调试{
使用警告;
使用严格的“参考”;
0;
}
使用警告;
使用严格的“参考”;
时间这些(100000,{'子',子{
debug "A $0 logging message via process-id: $$" 。 倾销者(\%INC);
}
,'常数',子{
0;
}
});
ifdebug-constant 语法 OK
输出显示 持续的() 我们正在测试的子程序被替换为
“调试”常量:零。 待测线已完全优化掉,并且
没有比这更高效的了。
后记
本文档提供了几种方法来识别热点,并检查
是否有任何修改改进了代码的运行时间。
最后,请记住(在撰写本文时)不可能产生
将在零或负时间运行的有用程序,这个基本原理可以是
写为: 有用 程式 旨在 放慢 根据他们的定义。 这当然是可能的
编写一个几乎即时的程序,但它不会做太多,这里有一个非常
高效一:
$> perl -e 0
进一步优化是“p5p”的工作。
使用 onworks.net 服务在线使用 perlperf