代码之家  ›  专栏  ›  技术社区  ›  akdom

如何加快Perl程序的速度?

  •  28
  • akdom  · 技术社区  · 17 年前

    这确实是两个问题,但它们非常相似,为了简单起见,我想我应该把它们放在一起:

    • 首先 :给定一个已建立的Perl项目,除了简单的代码优化之外,还有什么好的方法可以加速它?

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

    对于第一个问题,假设你得到了一个写得很好的项目,你需要提高性能,但是你似乎无法通过重构/优化获得太多好处。在这种情况下,如果不用C这样的语言重写它,你会怎么做来加快速度呢?

    请远离一般的优化技术,除非它们是 Perl特定 .

    我问过这个 Python 早些时候,我认为对其他语言来说这样做可能是好的(我特别好奇 psycho pyrex 对于Perl)。

    12 回复  |  直到 9 年前
        1
  •  155
  •   szabgab Brandon Fosdick    12 年前

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

    1. 优化俱乐部的第一条规则 就是,你没有优化。
    2. 优化俱乐部的第二条规则是,没有度量就不能优化。
    3. 如果你的应用运行速度比底层传输协议快,那么优化就结束了。
    4. 一次一个因素。
    5. 没有marketroid,没有marketroid时间表。
    6. 只要有必要,测试将继续进行。
    7. 如果这是你在优化俱乐部的第一晚,你必须写一个测试用例。

    所以,假设你有工作代码,运行你的程序 Devel::NYTProf .

    找出瓶颈。然后回来告诉我们他们是什么。

    如果你 不要 有工作代码,先让它工作。你唯一能做的最大的优化就是从不工作到工作。

        2
  •  34
  •   szabgab Brandon Fosdick    12 年前

    安迪已经提到 Devel::NYTProf . 太棒了。真的,真的很棒。使用它。

    如果因为某种原因你不能使用 Devel::NYTProf ,然后你就可以回到过去 Devel::DProf ,它已经成为Perl的标准配置很长一段时间了。如果你有 真的 函数(在数学意义上)需要很长时间来计算(如斐波那契数),然后你可以发现 Memoize 提高了一些速度。

    很多糟糕的性能来自不适当的数据结构和算法。一门好的计算机科学课程对这里有很大的帮助。如果你有两种做事方式,并且想比较他们的表现,那么 Benchmark 模块也可以证明是有用的。

    以下 Perl Tips 在这里也可能被证明是有用的:

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

        3
  •  31
  •   brian d foy    11 年前

    有很多事情你可以改进,所以你首先要弄清楚什么是慢的。其他人已经回答了这个问题。我有点在说这个 Mastering Perl 我也是。

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

    • 像这样的资料 Devel::NYTProf 看看你大部分时间都在代码中花在哪里。有时这是令人惊讶的,而且很容易解决。 掌握Perl 对此有很多建议。

    • Perl每次都必须编译源代码,编译可能会很慢。它必须找到所有的文件等等。比如说, "A Timely Start" ,作者Jean-Louis Leroy,他通过优化模块在 @INC . 如果您的启动成本昂贵且不可避免,那么您还可以查看持久perl,如pperl、mod_perl等。

    • 看看你使用的一些模块。他们有很长的依赖链仅仅是为了做简单的事情吗?当然,我们不喜欢再发明,但是如果你想装在车上的轮子也有三条船,五只山羊和一个奶酪汉堡,也许你想自己制造轮子(或者找一个不同的)。

    • 方法调用可能很昂贵。例如,在Perl::Critic测试套件中,它调用 isa 放慢速度。这不是你在任何情况下都能避免的,但要记住。有人引用了一句很好的名言,比如“没有人会介意放弃系数2;只有当你有10个人在做这件事时,它才是坏的。”:)PerlV5.22对此有一些性能改进。

    • 如果你一次又一次地调用同样昂贵的方法,却得到了同样的答案,比如 Memoize 可能是给你的。它是方法调用的代理。如果它是一个真正的函数(也就是说,相同的输入提供相同的输出,没有副作用),您不需要反复调用它。

    • 模块,如 Apache::DBI 可以为您重用数据库句柄,以避免昂贵的数据库连接打开。这是一个非常简单的代码,所以即使您不使用Apache,也可以从内部查看如何做到这一点。

    • Perl不会为您进行尾部递归优化,所以不要从Lisp中过来,以为您会生成这些超快速递归算法。你可以很容易地把它们转化为迭代解(我们在 Intermediate Perl .

    • 看看你的正则表达式。很多开放式量词(例如。 .* )会导致很多回溯。看看杰弗里弗雷德的 Mastering Regular Expressions 所有血腥的细节(以及跨语言)。也可以退房 his regex website .

    • 了解perl是如何编译的。你真的需要穿线和 DDEBUGGING ? 那些会让你慢一点。查看perlbench实用程序以比较不同的perl二进制文件。

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

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

        4
  •  14
  •   szabgab Brandon Fosdick    12 年前

    不必重写大块,您可以使用 Inline::C 将任何单一的、慢的子程序转换为C。或直接使用XS。也可以用XS增量转换sub。 PPI/PPI::XS 例如,这样做。

    但换一种语言总是万不得已。也许你应该找一个专业的Perl程序员来看看你的代码?他很可能会发现一些严重影响你表演的怪癖。除此之外,请分析您的代码。记住,没有银弹。

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

        5
  •  9
  •   Grey Panther    17 年前

    这只涉及到你的问题的一半-但是为了文档的利益,我会把它贴在这里。

    最近的 CentOS/Perl bugfix 我们的应用程序速度提高了两倍多。任何运行CentOS Perl并使用bless/overload函数的人都必须这样做。

        6
  •  9
  •   Grzegorz Rożniecki    13 年前

    分析您的应用程序-例如,使用上面提到的分析器。到时候你就会知道时间的走向了

    如果把时间花在做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语言重写某些内容会带来很大的风险,因为您可能会引入新的故障模式,例如内存泄漏、崩溃、安全问题。

        7
  •  9
  •   Lizardx    8 年前

    尼古拉斯·克拉克的演讲是一篇值得一读的关于这个问题的文章 When perl is not quite fast enough (PDF格式)。有些观点有点过时了,比如对Devel::DProf的引用,但是请记住它是在2002年编写的。

    然而,所涉及的许多材料仍然是相关的。

        8
  •  8
  •   szabgab Brandon Fosdick    12 年前

    方法和子例程调用在Perl中不是免费的。它们比较贵。所以,如果您的分析结果显示您在小访问器方法中花费了相当大的运行时间,那么这可能是一个值得关注的微观优化。

    但是,你应该 do正在此处替换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-and-set_color()方法,这些方法在XS中实现,因此速度大约是手动版本的两倍。变异器(即“$car->color('red')”)和链式方法也可用。

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

        9
  •  6
  •   jrockway    17 年前

    使程序运行得更快的最好方法是使程序减少工作。为作业选择正确的算法。我见过很多速度慢的应用程序,因为它们在代码的某个区域选择了一个愚蠢的算法,这个算法被调用了数百万次。当你执行一百万次操作而不是一百万次操作时,你的程序运行速度会慢一百万倍。真的。

    例如,下面是我看到的将元素插入排序列表的一些代码:

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

    排序为O(n logn)。插入到排序列表中是O(log n)。

    修正算法。

        10
  •  2
  •   Peter Mortensen Pieter Jan Bonestroo    16 年前

    最具成本效益的方法可能是,考虑更快的硬件(=>适当的硬件架构)。我说的不是更快的CPU,而是更快的磁盘和更快的网络。。更快的任何东西,真的,那会加速I/O。

    多年前,当我们将一个基于XML解析的应用程序(当时的前沿技术<g>)从一个(快速可靠的!)Windows服务器到一个专用的,虽然有些过时,SUN平台与更快的I/O周围。

    一如既往,考虑一下

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

    在大多数地方提高(成本!)对手头的问题有效。。。

        11
  •  1
  •   EvdB loudej    17 年前

    如果您的代码需要加速,那么您的测试套件也可能需要加速。本次演讲涉及以下要点:

    Turbo Charged Test Suites