如何加速我的Perl程序?

时间:2008-10-07 03:23:25

标签: perl optimization performance

这实际上是两个问题,但它们非常相似,为了保持简单,我想我只是把它们放在一起:

  • 首先:鉴于已经建立的Perl项目,除了简单的代码内优化之外,还有什么方法可以加快速度?

  • 其次:在Perl中从头开始编写程序时,有哪些好方法可以大大提高性能?

对于第一个问题,假设您获得了一个写得很好的项目,并且您需要提高性能,但您似乎无法通过重构/优化获得很多收益。在这种情况下,你会做些什么来加速它,而不是用C语言重写它?

请远离常规优化技术,除非它们 Perl特定

我之前问过Python这个问题,我认为对其他语言这样做可能会很好(如果有psychopyrex的推论,我会特别好奇。 Perl的)。

11 个答案:

答案 0 :(得分:153)

请记住优化俱乐部的规则:

  1. 优化俱乐部的第一条规则 是的,你没有优化。
  2. 优化俱乐部的第二条规则是,如果不进行测量,则不进行优化。
  3. 如果您的应用运行速度比基础传输协议快,则优化已结束。
  4. 一次一个因素。
  5. 没有marketroids,没有marketroid时间表。
  6. 只要必要,测试就会继续进行。
  7. 如果这是您在优化俱乐部的第一个晚上,您必须编写测试用例。
  8. 因此,假设您确实有工作代码,请在Devel::NYTProf下运行您的程序。

    找出瓶颈。然后回到这里告诉我们它们是什么。

    如果您拥有正常工作的代码,请先让它运行。您将进行的最大优化是从非工作到工作。

答案 1 :(得分:34)

安迪已经提到了Devel::NYTProf。这很棒。真的,非常棒。使用它。

如果出于某种原因你无法使用Devel::NYTProf,那么你可以回到古老的Devel::DProf,这已经成为Perl的标准了很长一段时间了。如果您有 true 函数(在数学意义上)需要很长时间来计算(例如,Fibonacci数),那么您可能会发现Memoize提供了一些速度提升。

许多糟糕的性能来自不适当的数据结构和算法。计算机科学的好课程可以在这里帮助很大。如果您有两种方法,并希望比较它们的性能,那么Benchmark模块也可以证明是有用的。

以下Perl Tips在这里也可能有用:

免责声明:我写了一些上面的资源,所以我可能会偏向他们。

答案 2 :(得分:31)

你可能会改进很多事情,所以你首先必须弄清楚什么是缓慢的。其他人已经回答了这个问题。我在Mastering Perl中也谈到了这一点。

在编写新代码时要考虑的事项列表不完整:

  • 使用Devel::NYTProf之类的个人资料查看您在代码中花费大部分时间的位置。有时,这是令人惊讶和容易修复。 Mastering Perl对此有很多建议。

  • Perl每次都必须编译源代码,编译速度可能会很慢。它必须找到所有文件等。例如,参见Jean-Louis Leroy撰写的"A Timely Start",他通过优化@INC中的模块位置来加速一切。如果您的启动成本昂贵且不可避免,您还可以查看持久性perls,如pperl,mod_perl等。

  • 查看您使用的一些模块。他们有很长的依赖链只是为了做简单的事情吗?当然,我们不喜欢重新发明,但如果您想要放在车上的车轮还配有三艘船,五只山羊和一个芝士汉堡,也许您想要建造自己的车轮(或找一个不同的车轮)

  • 方法调用可能很昂贵。例如,在Perl :: Critic测试套件中,它对isa的调用减慢了速度。在所有情况下,这都不是你可以真正避免的,但要记住这一点。有人有一个很好的引用,就像“没有人介意放弃因子2;当你有十个人做这件事时,这是坏事。” :) Perl v5.22对此有一些性能改进。

  • 如果您一遍又一遍地使用相同的昂贵方法,但得到相同的答案,Memoize之类的内容可能适合您。它是方法调用的代理。如果它真的是一个函数(意思是,相同的输入给出相同的输出而没有副作用),你实际上不需要重复调​​用它。

  • Apache::DBI之类的模块可以为您重用数据库句柄,以避免昂贵的数据库连接打开。这是非常简单的代码,因此即使你没有使用Apache,查看内部也可以告诉你如何做到这一点。

  • Perl不会为你做尾递归优化,所以不要过来Lisp认为你会做出这些超快递归算法。您可以轻松地将这些转换为迭代解决方案(我们在Intermediate Perl中讨论这个问题。

  • 看看你的正则表达式。许多开放式量词(例如.*)可能导致大量回溯。查看Jeffrey Freidl的Mastering Regular Expressions了解所有血腥细节(以及多种语言)。另请查看his regex website

  • 知道如何编译perl。你真的需要线程和DDEBUGGING吗?那些让你慢下来。查看perlbench实用程序以比较不同的perl二进制文件。

  • 针对不同的Perls对您的应用程序进行基准测试。一些较新的版本具有加速,但是对于有限的操作集,一些旧版本可以更快。我没有特别的建议,因为我不知道你在做什么。

  • 展开工作。你可以在其他进程或远程计算机上做一些异步工作吗?当其他人找出一些子问题时,让你的程序处理其他事情。 Perl有几个异步和负载转移模块。但要注意,做好这些事情的脚手架可能会失去任何好处。

答案 3 :(得分:14)

无需重写大块,您可以使用Inline::C将任何单个慢速子例程转换为C.或直接使用XS。也可以使用XS逐步转换潜艇。例如,PPI/PPI::XS就是这样做的。

但转向另一种语言始终是最后的选择。也许你应该让专业的Perl程序员来查看你的代码?更有可能的是,(s)他发现了一些严重损害你表现的特殊性。除此之外,您的代码配置文件。记住,没有银弹。

关于psyco和pyrex:不,Perl没有等价物。

答案 4 :(得分:9)

这只有一半与你的问题有关 - 但为了文件的目的,我会在这里发布。

最近CentOS/Perl bugfix将我们的申请速度提高了两倍以上。对于运行CentOS Perl并使用bless / overload函数的人来说,这是必须的。

答案 5 :(得分:9)

描述您的应用程序 - 例如,使用上面提到的探查器。然后,您将看到时间在哪里

如果花费时间用于CPU使用以外的其他事情,则需要先减少这些时间 - CPU很容易扩展,其他情况则不然。

一些操作特别慢,我发现:

  • keys()对大哈希非常糟糕
  • 使用Data::Dumper进行调试。在大型结构上使用此功能非常慢。如果可以,请避免使用它。我们已经看到了代码:

    use Data::Dumper; 
    $debugstr = Dumper(\%bighash); 
    if ($debugflag_mostlyoff) { log($debugstr); } 
    
  • 大多数模块都有不同性能特征的替代品 - 实际上有些非常糟糕。

  • 一些正则表达式可能非常慢(很多。*等),可以用更快的等效表达式替换。正则表达式非常容易进行单元测试和性能测试(只需编写一个程序,在一个大型模拟数据集的循环中运行它)。最好的正则表达式从可以非常快速地测试的东西开始,例如文字字符串。有时候最好先不要先寻找你想要的东西,并做一个“向后看”来检查它是否真的是你正在寻找的东西。优化正则表达式确实是一种黑色艺术,我不是很好。

除非作为最后的手段,否则不要考虑在C中重写某些内容。从Perl调用C(反之亦然)具有相对较大的开销。如果你能快速实现Perl,那就更好了。

如果你在C中重写某些内容,请尝试以最小化调用开销的方式执行此操作,并调用perl运行时(SV *函数例如主要是复制字符串)。实现这一目标的一种方法是创建一个C函数,它可以执行更多操作并将其调用次数更少。在内存中复制字符串并不酷。

另一方面,在C中重写某些内容会带来很大的风险,因为您可以引入新的失败模式,例如:内存泄漏,崩溃,安全问题。

答案 6 :(得分:9)

一篇非常值得一读的关于这个主题的文章是尼古拉斯·克拉克的演讲When perl is not quite fast enough(PDF)。有些要点略显陈旧,例如对Devel :: DProf的引用,但要记住它是在2002年写的。

尽管如此,所涉及的大部分材料仍然具有相关性。

答案 7 :(得分:8)

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中实现,因此速度大约是手动滚动版本的两倍。突变者(即“$ car-> color('red')”)也是可用的,链式方法也是如此。

根据您的应用程序,这可能会给您一个非常小的(但基本上是免费的)提升。除非你做一些特殊的事情,否则不要指望超过1-2%。

答案 8 :(得分:6)

让程序运行得更快的最佳方法是让您的程序减少工作量。为工作选择正确的算法。我见过很多缓慢的应用程序,因为他们在代码的某些区域选择了一个愚蠢的算法,这个算法被调用了数百万次。当你进行一百万*百万次操作而不是一百万次操作时,你的程序运行速度会慢一百万次。字面上。

例如,这里是我看到的一些代码,它将元素插入到排序列表中:

while(my $new_item = <>){
    push @list, $new_item;
    @list = sort @list;
    ... use sorted list
}

sort是O(n log n)。插入到排序列表中的是O(log n)。

修复算法。

答案 9 :(得分:2)

最经济有效的方法可能是考虑更快的硬件(=&gt;适当的硬件架构)。我不是说更快的CPU,而是更快的磁盘,更快的网络..更快的任何东西,真的,加速I / O.

多年前我经历过这种情况,当时我们将一个基于XML解析的应用程序(当时的前沿技术&lt; g&gt;)从一个(快速可靠的!)Windows Server转移到一个专用的,虽然有点过时的SUN平台更快的I / O。

一如既往,请考虑

  • 开发人员的表现(代码需要多长时间,问题有多复杂,结果是可维护的),
  • 硬件性能,
  • 软件性能

并改善大多数(成本!)对手头问题有效的地方......

答案 10 :(得分:1)

如果您的代码需要加速,那么您的测试套件也可能会加速。这个谈话涉及关键点:

Turbo Charged Test Suites