这是 perl_performance 命令,可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器
程序:
您的姓名
makepp_perl_performance -- 如何使 Perl 更快
商品描述
最大的调整收益通常来自算法改进。 但是虽然这些
可能很难找到,你也可以机械地做很多事情。
Makepp 是一个大型的重型程序,速度是必须的。 付出了很多努力
来优化它。 这记录了我们发现的一些一般事物。 目前,
导致这些结果的具体测试大多被丢弃,但我计划
逐渐添加它们。
如果您正在研究如何加速 makepp(超越您放入的 Perl 编程)
makefiles),看看 makepp_speedup。 本页面完全独立于makepp,仅
旨在将我们的结果提供给 Perl 社区。 其中一些措施是
常识,但你有时会忘记它们。 其他人需要测量才能相信他们,所以:
措施, 别 猜
配置您的程序
Makepp 自带一个模块 分析器.pm 在其 cvs 存储库中。 这是第一次作为
在代码的副本(!)上编程,它会检测它。 然后你运行你的副本并
获取每个间隔的可配置统计数据和最频繁的最终总数
被调用的函数和花费在函数上的时间最多(减去子调用)。 两者都是
绝对地和以调用者-被调用者对的形式提供。 (里面的文档。)
这会告诉您哪些函数最有希望进行调整。 它也是
给你一个提示你的算法可能出错的地方,要么出人意料
昂贵的功能,或通过令人惊讶的频繁调用。
为您的解决方案计时
其中之一
perl -Mstrict -MBenchmark -we'my ; timethis -10, sub { }'
时间 perl -Mstrict -we'my ; 对于(0..999_999){ }'
当在您能想到的不同代码变体上运行时,可以给出令人惊讶的结果。
即使是很小的修改也很重要。 小心不要“测量”可以
得到优化,因为你丢弃了结果,或者因为它取决于
常数。
根据您的系统,这会以 kb 为单位告诉您 Perl 有多胖:
perl -Mstrict -we ' ; 系统“ps -ovsz $$”'
下面我们仅将“-e”选项中的代码显示为一行。
正则表达式
使用简单的正则表达式
与“||”组合的几个匹配项比带有“|”的大的更快。
使用预编译的正则表达式
而不是将字符串插入正则表达式(除非字符串永远不会改变
并且您使用“o”修饰符),使用“qr//”预编译正则表达式并对其进行插值。
用 (?:...)
如果您不使用分组匹配的内容,请不要让 Perl 用“(...)”保存它。
字符串开头的锚点
不要让 Perl 查看整个字符串,如果您只想在
开始。
不要在贪婪后锚定
如果你有一个 "*" 或 "+" 匹配到字符串的结尾,不要在后面放一个 "$"
它。
使用 tr///
这在适用时是 s/// 的两倍。
功能
避免面向对象
动态方法查找在任何语言中都较慢,而松散类型的 Perl 可以
永远不要在编译时这样做。 不要使用它,除非你需要它的好处
通过继承实现多态。 以下调用方法从最慢排序
最快:
$o->method( ... ); # 在 $o 及其 @ISA 的类中搜索
类::方法( $o, ... ); # 静态函数,新栈
类::方法 $o, ...; # 静态函数,新堆栈,在编译时检查
&Class::method; # 静态函数,重用栈
如果方法(或普通函数)不带参数,则最后一种形式总是可能的。 如果
它确实需要参数,请注意您不会无意中提供任何可选
那些! 如果您经常使用此表格,最好跟踪最小值和最大值
每个函数可以采用的参数数量。 重用带有额外参数的堆栈是
没问题,他们会被忽略。
不要修改堆栈
即使在 Perl 文档中也经常发现以下错误:
我的 $self = shift;
除非您有相关原因,否则请使用以下命令:
我的($self,$x,$y,@z)=@_;
使用很少的函数和模块
每个函数(而且包括常量)占用了超过 1kb,因为它仅仅是
存在。 每个模块都需要其他模块,其中大部分是您永远不需要的,那
可以加起来。 不要引入一个大模块,只是将两行 Perl 代码替换为一个
单个更优雅的函数调用。
如果你有一个只在一个地方调用的函数,而两者结合起来仍然是
相当短,将它们与适当的评论合并。
不要让一个函数只用相同的参数调用另一个函数。 取而代之的是别名:
*别名 = \&function;
组呼打印
单独调用 print 或使用单独的参数进行打印非常昂贵。 建造
将字符串存储在内存中并一次性打印出来。 如果你可以累积超过3kb,
syswrite 更有效。
perl -MBenchmark -we 'timethis -10, sub { print STDERR $_ for 1..5 }' 2>/dev/null
perl -MBenchmark -we 'timethis -10, sub { print STDERR 1..5 }' 2>/dev/null
perl -MBenchmark -we 'timethis -10, sub { my $str = ""; $str .= $_ 为 1..5; 打印 STDERR $str }' 2>/dev/null
其他
避免散列
Perl 会因许多小散列而变得很慢。 如果你不需要它们,使用一些东西
别的。 面向对象同样适用于数组,除了成员
无法通过名称访问。 但是您可以使用数字常量来命名成员。
为了可比性,我们在这里使用纯数字键:
我的 $i = 0; 我们的 %a = map +($i++, $_), "a".."j"; timethis -10, sub { $b = $a{int rand 10} }
我们的@a = "a".."j"; timethis -10, sub { $b = $a[rand 10] }
我的 $i = 0; 我的 %a = 地图 +($i++, $_), "a".."j"; timethis -10, sub { $b = $a{int rand 10} }
我的@a = "a".."j"; timethis -10, sub { $b = $a[rand 10] }
对引用集使用 int 键
当您需要唯一的参考表示时,例如对于带有散列的 set ops,使用
refs 的整数形式是使用漂亮打印的默认值的三倍
字符串表示。 警告:Perl 的 HP/UX 64bitall 变体,至少高达
5.8.8 有一个错误的“int”函数,它不能可靠地工作。 有一个十六进制形式
仍然比默认字符串快一点。 实际上这甚至可以更快
不是字符串化的 int,这取决于 perl 的版本或配置。 作为
5.8.1 还有等效但希望可靠的 Scalar::Util::refaddr
我的@list = map { bless { $_ => 1 }, "someclass" } 0..9; 我的(%a,%b);
timethis -10, sub { $a{$_} = 1 for @list };
timethis -10, sub { $b{int()} = 1 for @list };
timethis -10, sub { $b{sprintf '%x', $_} = 1 for @list };
timethis -10, sub { $b{refaddr $_} = 1 for @list };
还有 sprintf '%p' 应该输出一个指针,但取决于哪个
表达式导致相同的引用,你得到不同的值,所以它没用。
小心字符串
Perl 总是复制字符串很糟糕,即使你永远不会修改
他们。 这会浪费 CPU 和内存。 在合理可能的情况下尽量避免这种情况。
如果字符串是函数参数并且函数的长度适中,则不要复制
将字符串转换为“我的”变量,使用 $_[0] 访问它并很好地记录该函数。
在其他地方,“for(each)”的别名功能可以提供帮助。 或者只是使用引用
字符串,可以快速复制。 如果您以某种方式确保存储相同的字符串
只有一次,您可以进行相等的数值比较。
避免位操作
如果您有不相交的位模式,您可以添加它们而不是 or`ing 它们。 换挡
可以执行我的乘法或整数除法。 只保留最低的
位可以用模来实现。
单独的布尔哈希成员比将所有内容填充到整数中更快
位操作或转换为带有“vec”的字符串。
布尔运算的使用顺序
如果您只关心表达式的真假,请检查便宜的东西,例如
首先是布尔变量,最后是调用函数。
使用 undef 而不是 0
它占用的内存少了几个百分点,至少作为哈希或列表值。 你还可以
将其作为布尔值查询。
我的 %x; $x{$_} = 0 表示 0..999_999; 系统“ps -ovsz $$”
我的 %x; undef $x{$_} 为 0..999_999; 系统“ps -ovsz $$”
我的@x = (0) x 999_999; 系统“ps -ovsz $$”
我的@x = (undef) x 999_999; 系统“ps -ovsz $$”
选择或地图
这些绝对不是等价的。 根据您的用途(即列表和
代码的复杂性),其中一个可能更快。
我的@l = 0..99;
for( 0..99_999 ) { map $a = " $_ ", @l }
for( 0..99_999 ) { map $a = " $_ ", 0..99 }
for( 0..99_999 ) { $a = " $_ " for @l }
for( 0..99_999 ) { $a = " $_ " for 0..99 }
不要别名 $_
虽然很方便,但也相当昂贵,即使复制合理的字符串也是
快点。 最后一个例子的速度是第一个“for”的两倍。
我的 $x = "abcdefg"; 我的 $b = 0;
for( "$x" ) { $b = 1 - $b if /g/ } # 仅在修改时才需要复制。
for( $x ) { $b = 1 - $b 如果 /g/ }
本地 *_ = \$x; $b = 1 - $b 如果 /g/;
本地 $_ = $x; $b = 1 - $b 如果 /g/; # 复制比别名便宜。
我的 $y = $x; $b = 1 - $b 如果 $y =~ /g/;
使用 onworks.net 服务在线使用 perl_performance