为什么 Perl 循环中的函数调用这么慢? [英] why are function calls in Perl loops so slow?

查看:63
本文介绍了为什么 Perl 循环中的函数调用这么慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 Perl 编写一个文件解析器,所以不得不遍历文件.文件由固定长度的记录组成,我想创建一个单独的函数来解析给定的记录并在循环中调用该函数.但是,大文件的最终结果变得很慢,我的猜测是我不应该使用外部函数.因此,我在循环中使用和不使用函数调用进行了一些虚拟测试:

I was writing a file parser in Perl, so had to loop through file. File consists of fixed length records and I wanted to make a separate function that parses given record and call that function in a loop. However, final result turned to be slow with big files and my guess was that I shouldn't use external function. So I made some dummy tests with and without function call in a loop:

[A]

foreach (1 .. 10000000) {
$a = &get_string();
}

sub get_string {
return sprintf("%s\n", 'abc');
}

[B]

foreach (1 .. 10000000) {
$a = sprintf "%s\n", 'abc';
}

测量显示 A 代码的运行速度比代码 B 慢 3-4 倍.我事先知道代码 A 应该运行得更慢,但我仍然惊讶于差异如此之大.还尝试使用 Python 和 Java 运行类似的测试.在 Python 代码中,A 等价物比 B 慢约 20%,Java 代码或多或少以相同的速度运行(如预期).将功能从 sprintf 更改为其他功能没有显示出任何显着差异.

Measuring showed that A code runs about 3-4 times slower than code B. I knew beforehand that code A was supposed to run slower but still I was surprised that difference is that big. Also tried to run similar tests with Python and Java. In Python code A equivalent was about 20% slower than B and Java code was runing more or less at the same speed (as expected). Changing function from sprintf to something else didn't show any significant difference.

有什么办法可以帮助 Perl 更快地运行这样的循环?我在这里做错了什么,还是 Perl 的特性导致函数调用的开销如此之大?

Is there any way to help Perl run such loops faster? Am I doing something totaly wrong here or is it Perl's feature that function calls are such overhead?

推荐答案

Perl 函数调用很慢.这很糟糕,因为你想做的事情就是将代码分解成可维护的函数,这正是会减慢程序速度的事情.他们为什么慢?Perl 在进入子例程时会做很多事情,这是因为它非常动态(即在运行时你可能会搞砸很多事情).它必须获取该名称的代码引用,检查它是否是一个代码引用,设置一个新的词法暂存器(存储 my 变量),一个新的动态范围(存储 local 变量),设置 @_ 仅举几例,检查它被调用的上下文并传递返回值.已尝试优化此过程,但尚未得到回报.参见 pp_entersub in pp_hot.c血腥的细节.

Perl function calls are slow. It sucks because the very thing you want to be doing, decomposing your code into maintainable functions, is the very thing that will slow your program down. Why are they slow? Perl does a lot of things when it enters a subroutine, a result of it being extremely dynamic (ie. you can mess with a lot of things at run time). It has to get the code reference for that name, check that it is a code ref, set up a new lexical scratchpad (to store my variables), a new dynamic scope (to store local variables), set up @_ to name a few, check what context it was called in and pass along the return value. Attempts have been made to optimize this process, but they haven't paid out. See pp_entersub in pp_hot.c for the gory details.

在 5.10.0 中也有一个减慢功能的错误.如果您使用的是 5.10.0,请升级.

Also there was a bug in 5.10.0 slowing down functions. If you're using 5.10.0, upgrade.

因此,避免在长循环中一遍又一遍地调用函数.特别是如果它是嵌套的.你能缓存结果吗,也许使用 Memoize?工作必须在循环内完成吗?它是否必须在最内层循环内完成?例如:

As a result, avoid calling functions over and over again in a long loop. Especially if its nested. Can you cache the results, perhaps using Memoize? Does the work have to be done inside the loop? Does it have to be done inside the inner-most loop? For example:

for my $thing (@things) {
    for my $person (@persons) {
        print header($thing);
        print message_for($person);
    }
}

header 的调用可以移出 @persons 循环,从而将调用次数从 @things * @persons 减少到仅@things.

The call to header could be moved out of the @persons loop reducing the number of calls from @things * @persons to just @things.

for my $thing (@things) {
    my $header = header($thing);

    for my $person (@persons) {
        print $header;
        print message_for($person);
    }
}

这篇关于为什么 Perl 循环中的函数调用这么慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆