如何才能提高速度的通用程序?

时间:2010-02-05 10:45:22

标签: perl formatting

我需要一个有效的commify过滤器或例程来与Template :: Toolkit一起使用。它将在页面上多次使用。它应该支持小数。

这个可以在The Perl Cookbook找到:

sub commify {
    my $text = reverse $_[0];
    $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
    return scalar reverse $text;
}

有更有效的方法吗?

6 个答案:

答案 0 :(得分:8)

在尝试优化任何内容之前,请确保它确实存在问题。使用分析器查找代码中的问题区域并关注这些区域。

commify解决方案与您可以获得的一样好,但如果您需要绕过它,还有其他一些事情可以做:

  • 如果要重复汇总相同的数字,请使用Memoize之类的内容来缓存结果
  • 如果不太可能改变,请预先计算所有数字。
  • 可以
  • 缓存已处理的模板
  • 对您的网络服务器使用反向代理设置,将大量处理交给后端服务器。

答案 1 :(得分:4)

我认为,如果你担心速度这个问题 - 你会严重错误地担心你的担忧。

commify功能在我的桌面上每秒运行130000次,如:“31243245356.4432”。

这意味着如果您的页面上有10000个数字,那么它的通信将需要76毫秒。并且页面的模板工具包处理可能需要2-3倍的时间。

答案 2 :(得分:4)

另一种选择是:

sub commify {
    my $text = shift;
    1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
    return $text;
}

当决定哪个更快时,Benchmark模块非常有用。

#!/usr/bin/perl

use strict;
use warnings;
use Benchmark;

sub your_commify {
    my $text = reverse 100000000;
    $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
    return scalar reverse $text;
}

sub my_commify {
    my $text = 100000000;
    1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
    return $text;
}

timethese(
    -10,
    {
        'yours' => \&your_commify,
        'mine'  => \&my_commify,
    }
);

运行此命令:

~$ ./benchmark.pl
Benchmark: running mine, yours for at least 10 CPU seconds...
mine: 10 wallclock secs (10.01 usr +  0.01 sys = 10.02 CPU) @ 111456.89/s (n=1116798)
yours: 11 wallclock secs (10.04 usr +  0.00 sys = 10.04 CPU) @ 250092.33/s (n=2510927)

看起来你的速度快〜2.25倍! (当使用“至少10 CPU秒”模式时,您必须检查使用的“n”值。)

所以看起来你必须继续搜索......但请记住使用Benchmark!

答案 3 :(得分:3)

sub commify {
    my $n = $_[0];

    my $s = abs($n) != $n;
    my $x = index($n, '.');
    $x = length($n) if $x == -1;
    substr($n, $x, 0, ',') while ($x -= 3) > $s;
    return $n;
}

答案 4 :(得分:2)

我同意brian和depesz:从实际的角度来看,如果你试图提高应用程序的性能,这个功能可能不是开始的地方。也就是说,可以编写更快的commify函数。一种方法是避免正则表达式。

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

sub commify_regex {
    my $text = reverse $_[0];
    $text =~ s{(\d\d\d)(?=\d)(?!\d*\.)}{$1,}g;
    return scalar reverse $text;
}

sub commify_substr {
    my $v = $_[0];
    my $len = length $v;
    my $dec = index($v, '.');
    my $i = 3 + ($dec < 0 ? 0 : $len - $dec);
    $len -- unless $v == abs($v);
    while ($i < $len ++){
        substr($v, -$i, 0, ',');
        $i += 4;    
    }
    return $v;
}

my @tests = qw(
    1 12 123 1234 12345 123456 1234567 
    12345678 123456789 1234567890 12345678901 
    123456789012 1234567890123
);
push @tests, map "$_.$_", @tests;
push @tests, map - $_,    @tests;

for my $t (@tests){
    print "Incorrect for: ", $t, "\n"
        unless commify_substr($t) eq commify_regex($t);
}

cmpthese( -2, {
    regex  => sub { commify_regex($_)  for @tests },
    substr => sub { commify_substr($_) for @tests },
});

# Output on my Windows machine.

         Rate  regex substr
regex  3277/s     --   -54%
substr 7109/s   117%     --

答案 5 :(得分:2)

这可以使用单个正则表达式完成:

$number =~ s/(?<=\d)(?=(?:\d\d\d)+(?!\d))/,/g;