给Perl的打印列表或串联字符串更快吗?

时间:2009-09-17 18:19:21

标签: performance perl printing

选项A:

print $fh $hr->{'something'}, "|", $hr->{'somethingelse'}, "\n";

选项B:

print $fh $hr->{'something'} . "|" . $hr->{'somethingelse'} . "\n";

6 个答案:

答案 0 :(得分:13)

除非您执行数百万条这样的陈述,否则性能差异无关紧要。我真的建议专注于它们确实存在的性能问题 - 找到它的唯一方法是分析你的应用程序。

过早的优化是乔尔和杰夫多年来播客和抱怨的事情。尝试优化某些东西只是浪费时间,直到你知道它很慢。

答案 1 :(得分:13)

答案很简单,没关系。正如许多人所指出的那样,这不会是你程序的瓶颈。即时发生优化甚至不会对您的表现产生任何影响。你必须先发布,否则你只是在猜测并浪费你的时间。

如果我们要浪费时间,让我们至少做正确的事。下面是做一个现实基准的代码。它实际上执行打印并将基准信息发送到STDERR。您将其作为perl benchmark.plx > /dev/null运行,以防止输出充斥您的屏幕。

这里有500万次迭代写入STDOUT。通过同时使用timethese()cmpthese(),我们可以获得所有基准数据。

$ perl ~/tmp/bench.plx 5000000 > /dev/null
Benchmark: timing 5000000 iterations of concat, list...
    concat:  3 wallclock secs ( 3.84 usr +  0.12 sys =  3.96 CPU) @ 1262626.26/s (n=5000000)
      list:  4 wallclock secs ( 3.57 usr +  0.12 sys =  3.69 CPU) @ 1355013.55/s (n=5000000)
            Rate concat   list
concat 1262626/s     --    -7%
list   1355014/s     7%     --

这里有500万写入临时文件

$ perl ~/tmp/bench.plx 5000000
Benchmark: timing 5000000 iterations of concat, list...
    concat:  6 wallclock secs ( 3.94 usr +  1.05 sys =  4.99 CPU) @ 1002004.01/s (n=5000000)
      list:  7 wallclock secs ( 3.64 usr +  1.06 sys =  4.70 CPU) @ 1063829.79/s (n=5000000)
            Rate concat   list
concat 1002004/s     --    -6%
list   1063830/s     6%     --

请注意额外的wallclock和sys时间,强调您打印的方式与打印的内容一样重要。

列表版本的速度提高了约5%(请注意,这与Pavel的逻辑相反,强调了试图仅仅考虑这些内容是徒劳的)。你说你做了成千上万的这些吗?让我们看看......我的笔记本电脑上有100毫秒的挂钟时间(有蹩脚的I / O),所以你在这里做的最好的事情就是剃掉大约7毫秒。恭喜。如果你花了一分钟考虑这个问题,那么在你弥补这段时间之前,你需要花费40k次迭代代码。更不用说机会成本,在那一刻你可以优化更重要的东西。

现在,有人会说“现在我们知道哪种方式更快,我们应该快速编写它,并在我们编写的每个程序中节省时间,使整个练习变得有价值!”不会。它仍然会占到程序运行时间的一小部分,远低于测量单个语句的5%。其次,这样的逻辑会使您优先考虑微优化而不是可维护性。

哦,和5.8.8中的不同,如5.10.0。

$ perl5.8.8 ~/tmp/bench.plx 5000000 > /dev/null
Benchmark: timing 5000000 iterations of concat, list...
    concat:  3 wallclock secs ( 3.69 usr +  0.04 sys =  3.73 CPU) @ 1340482.57/s (n=5000000)
      list:  5 wallclock secs ( 3.97 usr +  0.06 sys =  4.03 CPU) @ 1240694.79/s (n=5000000)
            Rate   list concat
list   1240695/s     --    -7%
concat 1340483/s     8%     --

它甚至可能会改变,具体取决于您使用的Perl I / O层和操作系统。所以整个练习都是徒劳的。

微优化是一个傻瓜的游戏。始终首先进行配置并寻求优化算法。 Devel::NYTProf是一位出色的导演。

#!/usr/bin/perl -w

use strict;
use warnings;
use Benchmark qw(timethese cmpthese);

#open my $fh, ">", "/tmp/test.out" or die $!;
#open my $fh, ">", "/dev/null" or die $!;
my $fh = *STDOUT;
my $hash = {
    foo => "something and stuff",
    bar => "and some other stuff"
};

select *STDERR;
my $r = timethese(shift || -3, {
    list => sub {
        print $fh $hash->{foo}, "|", $hash->{bar};
    },
    concat => sub {
        print $fh $hash->{foo}. "|". $hash->{bar};
    },
});
cmpthese($r);

答案 2 :(得分:4)

Perl是一种高级语言,因此您在源代码中看到的语句不会直接映射到计算机实际要执行的操作。您可能会发现perl的特定实现比另一个更快,但这并不能保证另一个实现可能会剥夺优势(尽管它们不会让事情变慢)。

如果你担心I / O速度,那么在你开始担心逗号和句号之前,还有很多有趣且有用的东西需要调整。例如,请参阅Perl write speed mystery下的讨论。

答案 3 :(得分:3)

<强>更新: 我刚开始自己​​的测试。

每个版本的1,000,000次迭代使每个&lt; 1秒。

对于列表版本,每个版本的10mm迭代平均花费2.35秒而对于字符串concat版本则为2.1秒

答案 4 :(得分:3)

你真的尝试过分析这个吗?只需几秒钟。

在我的机器上,看起来B更快。但是,你应该看看Pareto Analysis。你已经浪费了很多时间来考虑这个问题,然后你就可以在任何程序运行中保存。对于像这样微不足道的问题(字符替换!),你应该等到你真正遇到问题为止。

答案 5 :(得分:0)

在这三个选项中,我可能首先选择字符串插值并切换到无法插值的表达式的逗号。这个,幽默地说,意味着我的默认选择是最慢的,但鉴于它们在速度上都非常接近,并且磁盘速度可能比其他任何东西慢,我不相信改变该方法具有任何实际的性能优势。

正如其他人所说,编写代码,然后分析代码,然后检查您选择的算法和数据结构,这些算法和数据结构位于代码的缓慢部分,最后,查看算法和数据的实现结构。其他任何事情都是愚蠢的微观优化,浪费的时间多于节省的时间。

您可能还想阅读perldoc perlperf

           Rate string concat  comma
string 803887/s     --    -0%    -7%
concat 803888/s     0%     --    -7%
comma  865570/s     8%     8%     --
#!/usr/bin/perl

use strict;
use warnings;

use Carp;
use List::Util qw/first/;
use Benchmark;

sub benchmark {
    my $subs = shift;

    my ($k, $sub) = each %$subs;
    my $value = $sub->();
    croak "bad" if first { $value ne $_->() and print "$value\n", $_->(), "\n" } values %$subs;

    Benchmark::cmpthese -1, $subs;
}

sub fake_print {
    #this is, plus writing output to the screen is what print does
    no warnings;
    my $output = join $,, @_;
    return $output;
}

my ($x, $y) = ("a", "b");
benchmark {
    comma  => sub { return fake_print $x, "|", $y, "\n"     },
    concat => sub { return fake_print $x .  "|" . $y . "\n" },
    string => sub { return fake_print "$x|$y\n"             },
};