这真的是两个问题,但是它们非常相似,为了保持简单,我想我只是把它们放在一起:
首先:鉴于已经建立的Perl项目,除了简单的代码内优化之外,有什么方法可以加快速度?
其次:在Perl中从头开始编写程序时,有哪些好方法可以大大提高性能?
对于第一个问题,想象一下你得到了一个写得很好的项目,你需要提高性能,但你似乎无法通过重构/优化获得很多收益.在这种情况下你会做些什么来加速它,而不是像C那样重写它?
请远离一般优化技术,除非它们是Perl特定的.
我之前曾经问过这个关于Python的问题,我觉得用其他语言做这件事可能会很好(我特别好奇,如果有心理学和pyrex的推论用于Perl).
请记住优化俱乐部的规则:
优化俱乐部的第一条规则是,您不优化.
优化俱乐部的第二条规则是,如果不进行测量,则不进行优化.
如果您的应用程序运行速度超过底层传输协议,则优化结束.
一次一个因素.
没有marketroids,没有marketroid时间表.
只要必要,测试就会继续进行.
如果这是您在优化俱乐部的第一个晚上,您必须编写测试用例.
因此,假设您确实有工作代码,请在Devel :: NYTProf下运行您的程序.
找到瓶颈.然后回到这里告诉我们它们是什么.
如果您没有正常工作的代码,请先让它工作.您将进行的最大优化是从非工作到工作.
安迪已经提到了Devel :: NYTProf.这很棒.真的,非常棒.用它.
如果由于某种原因你无法使用Devel::NYTProf
,那么你可以回到过去很好的旧版Devel :: DProf,它现在已经成为Perl的标准配置.如果你有真正的函数(在数学意义上)需要很长时间来计算(例如,斐波纳契数),那么你可能会发现Memoize提供了一些速度提升.
很多糟糕的性能来自不适当的数据结构和算法.计算机科学的好课程可以在这里帮助很大.如果您有两种做事方式,并希望比较它们的性能,那么Benchmark模块也可以证明是有用的.
以下Perl提示在这里也可能有用:
用昂贵的比较排序
使用Devel :: DProf进行分析
Big-O表示法和算法复杂性
搜索大型列表中的项目
基准测试基础知识
Memoizing
免责声明:我写了一些上面的资源,所以我可能会偏向他们.
有很多事情你可以改进,所以你首先必须弄清楚什么是缓慢的.其他人已经回答了这个问题.我在Mastering Perl中也谈到了这一点.
在编写新代码时要考虑的事项列表不完整:
使用Devel :: NYTProf之类的配置文件查看您在代码中花费大部分时间的位置.有时,这是令人惊讶和容易修复.掌握Perl有很多建议.
Perl每次都必须编译源代码,编译速度很慢.它必须找到所有文件等.例如,参见Jean-Louis Leroy撰写的"及时开始",他通过优化模块位置来加速一切@INC
.如果您的启动成本昂贵且不可避免,您还可以查看持久性perls,如pperl,mod_perl等.
看看你使用的一些模块.他们有很长的依赖链只是为了做简单的事情吗?当然,我们不喜欢重新发明,但如果您想要放在车上的车轮还配有三艘船,五只山羊和一个芝士汉堡,也许您想要建造自己的车轮(或找一个不同的车轮) .
方法调用可能很昂贵.例如,在Perl :: Critic测试套件中,它的调用isa
减慢了速度.在所有情况下,这都不是你可以真正避免的,但要记住这一点.有人有一个很好的引用,就像"没有人介意放弃因子2;当你有十个人做这件事时,这是坏事." :) Perl v5.22对此有一些性能改进.
如果你一遍又一遍地调用同样昂贵的方法,但得到相同的答案,像Memoize这样的东西可能适合你.它是方法调用的代理.如果它真的是一个函数(意思是,相同的输入给出相同的输出而没有副作用),你实际上不需要重复调用它.
诸如Apache :: DBI之类的模块可以为您重用数据库句柄,以避免昂贵的数据库连接打开.这是非常简单的代码,所以即使你没有使用Apache,查看内部也可以告诉你如何做到这一点.
Perl不会为你做尾递归优化,所以不要过来Lisp认为你将要制作这些超快速递归算法.您可以轻松地将它们转换为迭代解决方案(我们在中级Perl中讨论它).
看看你的正则表达式.许多开放式量词(例如.*
)可能导致大量回溯.查看Jeffrey Freidl的掌握正则表达式,了解所有血腥细节(以及多种语言).另请查看他的正则表达式网站.
知道如何编译perl.你真的需要线程DDEBUGGING
吗?那些让你慢下来.查看perlbench实用程序以比较不同的perl二进制文件.
针对不同的Perls评估您的应用程序.一些较新的版本具有加速,但是对于有限的操作集,一些旧版本可以更快.我没有特别的建议,因为我不知道你在做什么.
分散工作.你可以在其他进程或远程计算机上做一些异步工作吗?当其他人找出一些子问题时,让你的程序处理其他事情.Perl有几个异步和负载转移模块.但是要注意,做好这些东西的脚手架可能会失去任何好处.
无需重写大块,您可以使用Inline :: C将任何单个慢速子例程转换为C.或直接使用XS.也可以使用XS逐步转换潜艇.例如,PPI/PPI :: XS就是这样做的.
但转向另一种语言始终是最后的选择.也许你应该让专业的Perl程序员来查看你的代码?更有可能的是,(s)他发现了一些严重损害你表现的特性.除此之外,您的代码配置文件.记住,没有银弹.
关于psyco和pyrex:不,Perl没有相应的东西.
这只有一半与你的问题有关 - 但为了文件的目的,我会在这里发布.
最近的CentOS/Perl错误修正使我们的应用程序的速度提高了两倍多.对于运行CentOS Perl并使用bless/overload功能的人来说,这是必须的.
描述您的应用程序 - 例如,使用上面提到的分析器.然后,您将看到时间的流逝
如果花费时间用于CPU使用以外的其他事情,则需要先减少这些时间 - CPU很容易扩展,其他情况则不然.
一些操作特别慢,我发现:
keys()
在一个大哈希是非常糟糕的
使用的Data::Dumper
调试.在大型结构上使用此功能非常慢.如果可以,请避免使用它.我们已经看到了代码:
use Data::Dumper; $debugstr = Dumper(\%bighash); if ($debugflag_mostlyoff) { log($debugstr); }
大多数模块都有不同性能特征的替代品 - 有些确实非常糟糕.
一些正则表达式可能非常慢(很多.*等),并且可以被更快的等效表达式替换.正则表达式非常容易进行单元测试和性能测试(只需编写一个程序,在一个大型模拟数据集的循环中运行它).最好的正则表达式从可以非常快速地测试的东西开始,例如文字字符串.有时候最好先不要先寻找你想要的东西,并做一个"向后看"来检查它是否真的是你正在寻找的东西.优化regexp确实是一种黑色艺术,我不是很好.
除非作为最后的手段,否则不要考虑在C中重写某些内容.从Perl调用C(反之亦然)具有相对较大的开销.如果你能快速实现Perl,那就更好了.
如果你在C中重写某些内容,尝试以最小化调用开销的方式执行它,并调用perl运行时(SV*函数例如主要是复制字符串).实现这一目标的一种方法是创建一个C函数,它可以执行更多操作并将其调用次数更少.在内存中复制字符串并不酷.
另一方面,在C中重写某些内容会带来很大的风险,因为您可能会引入新的失败模式,例如内存泄漏,崩溃,安全问题.
一篇关于这个主题非常值得一读的文章是尼古拉斯·克拉克的讲话当perl不够快时(PDF).有些要点略显过时,例如对Devel :: DProf的引用,但请记住它是在2002年编写的.
尽管如此,所涵盖的大部分材料仍然具有相
Perl中的方法和子程序调用不是免费的.它们相对昂贵.因此,如果您的分析结果表明您在小型访问器方法中花费了相当大的运行时间,那么这可能是值得关注的微优化.
但是,你应该什么不能做的是更换存取如get_color()在这里:
package Car; # sub new {...} sub get_color { my $self = shift; return $self->{color}; } package main; #... my $color = $car->get_color();
封装破坏直接访问:
my $color = $car->{color};
人们会认为这是不言而喻的,但人们也会看到这一切都在这个地方完成.以下是使用Class :: XSAccessor可以执行的操作
package Car; # sub new {...} use Class::XSAccessor getters => { get_color => 'color', }, setters => { set_color => 'color', };
这将创建新方法get-和set_color(),这些方法在XS中实现,因此速度大约是手动滚动版本的两倍.Mutators(即"$ car-> color('red')")也是可用的,链式方法也是如此.
根据您的应用程序,这可能会给您一个非常小的(但基本上是免费的)提升.除非你做一些奇特的事情,否则不要指望超过1-2%.
使程序运行得更快的最佳方法是让程序减少工作量.为工作选择正确的算法.我见过很多缓慢的应用程序,因为他们在代码的某些区域选择了一个愚蠢的算法,这个算法被调用了数百万次.当你进行一百万*百万次操作而不是一百万次操作时,你的程序运行速度会慢一百万次.从字面上看.
例如,这里是我看到的一些代码,它将元素插入到排序列表中:
while(my $new_item = <>){ push @list, $new_item; @list = sort @list; ... use sorted list }
sort是O(n log n).插入到排序列表中的是O(log n).
修复算法.