在Perl脚本中实现更好性能的一些基本经验法则是什么?

时间:2010-11-04 12:53:38

标签: perl performance optimization

我想知道您是否有任何提高Perl脚本性能性能的基本技巧(如记忆确定函数)?

7 个答案:

答案 0 :(得分:12)

  1. 安装Devel::NYTProf
  2. 用它运行你的脚本:perl -d:NYTProf some_perl.pl
  3. 将输出文件转换为精美的报告:nytprofhtml -f nytprof
  4. 在网络浏览器中打开报告:firefox nytprof/index.html
  5. 寻找花费最多时间的东西
  6. 确定这是否是该作业的正确算法(例如,您使用O(n 2 )算法,其中O(n)算法也可以吗?)
  7. 将慢速代码解压缩到可以自行运行的单独脚本
  8. 编写慢速代码的第二个(或更多)版本
  9. 使用Benchmark来比较它们(请记住使用您希望在野外看到的数据,某些算法对于少量项目会很好,但是当项目数量增加时会很糟糕)
  10. 当您证明自己拥有更快的代码时,请修改原始内容并返回第2步,直到您的代码足够快或者您无法再对其进行改进
  11. 如果它仍然太慢,请问如何在这里更快地做X(尽可能详细了解X)

答案 1 :(得分:7)

你应该先做个人资料。规则被打破。

一些非常基本的提示:

  • 避免啜饮。在某些情况下,不仅仅有一条线路是合理的,但是到处乱窜整个文件都会付出代价。
  • 避免将大型列表传递给子例程。
  • 如果您有多级哈希或数组,请避免重复解除引用多个级别。在词法变量中存储对最深适用级别的引用并使用它。
  • 在尽可能小的范围内声明您的变量。我不确定这是否是一种优化,但通常会让您看得更清楚,这对提高性能至关重要。

答案 2 :(得分:6)

提高Perl脚本性能的基本技巧包括适用于所有地方的一般方法,以及一些特定于Perl的内容。

类似的事情:

  • 衡量,不要猜测,只针对昂贵的区域。您的投资回报率会更高。
  • 如果您要稍微使用它们,请缓存不变的昂贵结果。
  • 当有一个能够为你完成的功能时,不要手动执行某些操作。这是解释和编译速度之间的差异。
  • 使用CPAN,有一个所有的模块,通常由比我们凡人更了解Perl的人编写。

这不是一个很容易被简单回答的东西。

我会给一条一条建议。提高代码性能的最佳方法是在SO上发布它的特定位,并等待brian d foy之类的内容找到它: - )

答案 3 :(得分:4)

我最近发现分析非常有用,并且Devel::NYTProf非常擅长。分析会阻止您进行不必要的优化,并帮助您首先解决最大的瓶颈。

当然,了解要做什么有帮助,但猜测那不是特定的perl ..

虽然常见的perl性能问题列表很不错:)

<强>更新

这里有一个:如果你不另外说明,glob()会排序:|

不要复制大量数据,请使用引用(例如:Use scalar references to pass large data without copying.

重复: How can I speed up my Perl program?

答案 4 :(得分:4)

这里有许多好的建议,这里还有一些:

  • 避免重复打包和打开数组,特别是当它们很大时
  • packunpack非常快,但split有时更快
  • 内联小部分代码,而不是将所有内容都分解为子程序(你可以轻松地对此进行过多的分析,以便查找热点)。
  • 如果您想要一组别名,知道您的数据是可变的,或承诺不更改值,使用sub{\@_}->(retuns_a_list())[returns_a_list()]快40%左右(您不必内联子程序,我通常称之为sub cap {\@_}(捕获的简称)

如果你真的需要提高某些对象的方法调用速度,你可以使用基于闭包的对象。以下是我的模块List::Gen的一段摘录,讨论了创建闭包对象的curse方法:

  • 诅咒HASHREF PACKAGE

    此程序包中的许多函数都使用闭包对象,以避免在每次访问期间解除引用其对象中字段的速度。诅咒类似于祝福这些物品,虽然祝福引用现有包的成员,但是诅咒会让人想起一个新的包来进行参考的出价

    package Closure::Object;
        sub new {
            my ($class, $name, $value) = @_;
            curse {
                get  => sub {$value},
                set  => sub {$value = $_[1]},
                name => sub {$name},
            } => $class
        }
    

    Closure :: Object在功能上等同于以下普通的perl对象,但是由于没有哈希查找或其他解引用(对于短getter / setter类型方法,快40-50%),所以方法调用更快。

    package Normal::Object;
        sub new {
            my ($class, $name, $value) = @_;
            bless {
                name  => $name,
                value => $value,
            } => $class
        }
        sub get  {$_[0]{value}}
        sub set  {$_[0]{value} = $_[1]}
        sub name {$_[0]{name}}
    
    折扣是在创造时间/记忆中,因为任何好的诅咒都需要在无辜的包裹的血液中画出至少几个五角星。返回的对象被祝福到变形包中,该变换包继承自提供的PACKAGE。

    当fast is不够快时,由于大多数被诅咒的方法不需要传递给它们的对象,所以调用该方法的最快方法是:

    my $obj = Closure::Object->new('tim', 3);
    my $set = $obj->{set};                  # fetch the closure
         # or $obj->can('set')
    
    
    $set->(undef, $_) for 1 .. 1_000_000;   # call without first arg
    

    比使用简短的getter / setter方法从普通对象预缓存方法快约70%。

答案 5 :(得分:3)

这个问题让我想起advice of Tim BunceDevel::NYTProf的维护者。

总结一下,除非你真的需要,否则不要这样做。

我最喜欢引用他的演讲:

  

“计划的第一条规则”   优化:不要这样做。

     

程序优化的第二条规则(仅供专家使用!):不要尚未。“

     

- Michael A. Jackson

     

[强调添加]

答案 6 :(得分:2)

以下是一些尚未提及的具体事项:

  • 尽可能使用tr///代替s///
  • 使用index()来匹配确切的子字符串而不是正则表达式
  • 永远不会使用$^$&$-。这些是可怕的正则表达式匹配变量,可以减慢所有正则表达式。有些替代方案不会受到惩罚。
  • 使用English模块时,匹配变量的复仇排除匹配变量别名。也就是说,use English '-no_match_vars';而不仅仅是use English;

可在perlopperlfuncperlvar找到更多信息。