是否可以优化此部分的性能

时间:2014-03-08 19:18:11

标签: performance perl subroutine slowdown

我在Perl中遇到了性能问题。这是代码: http://pastebin.com/jpmhv395

在其他地方也可能有问题,但主要问题在第336行:anagram_hash方法似乎经常被调用。 该方法实际上是在一个不同的模块中,这里是: http://pastebin.com/5NRC4bs8

子程序应该以不同的方式工作,具体取决于是否将整数或字符串作为参数传递。

子程序'anagram_hash'导致性能不佳,或者您是否看到任何可能导致性能下降的其他内容?如果是这样,它怎么可以优化?

1 个答案:

答案 0 :(得分:4)

我猜你可以制作一个256元素的查找表,所以你只需要做

$result += $lookup{$char};

而不是

my $temp = ord($char);
$result += $temp**5;

但你应该真正运行探查器,看看问题首先是什么...... here

EDIT(jm666和ikegami) - 添加了Benchmark示例。正如您可以通过观察power_goodloop和lookup_goodloop的结果看到的,这些结果仅取决于是使用取幂还是使用散列查找,因此取幂速度要快得多。这是一个让你慢下来的糟糕循环。

use strict;
use warnings;
use feature qw( say );
use Benchmark qw(:all);

my @lookup = map { $_ ** 5 } 0..255;
my %lookup = map { chr($_) => $_ ** 5 } 0..255;

my $str = join '', map chr(rand(256)), 1..1000;

say "test of the result";
say anagram_hash1($str);
say anagram_hash2($str);
say anagram_hash3($str);
say anagram_hash4($str);
say anagram_hash5($str);
say "";    
cmpthese(-3, {
    'power_badloop'    => sub { anagram_hash1($str) },
    'hlookup_badloop'  => sub { anagram_hash2($str) },
    'power_goodloop'   => sub { anagram_hash3($str) },
    'hlookup_goodloop' => sub { anagram_hash4($str) },
    'alookup_goodloop' => sub { anagram_hash5($str) },
});


sub anagram_hash1 {
        my $result = 0;
        my $s      = shift;
        my $length = length($s);
        if ( $s =~ /[a-zA-Z]+/ ) {
                for ( my $i = 0 ; $i < $length ; $i++ ) {
                        my $char = substr( $s, $i, 1 );
                        my $temp = ord($char);
                        $result += $temp**5;
                }
        } elsif ( $s =~ /^[\d]+$/ ) {
                my $temp = int($s);
                $result += $temp**5;
        } else {
                die "Invalid parameter passed to method 'anagram_hash'\nExpected: String or Number\nPassed: $s";
        }
        return $result;
}
sub anagram_hash2 {
        my $result = 0;
        my $s      = shift;
        my $length = length($s);
        if ( $s =~ /[a-zA-Z]+/ ) {
                for ( my $i = 0 ; $i < $length ; $i++ ) {
                        my $char = substr( $s, $i, 1 );
                        $result += $lookup{$char};
                }
        } elsif ( $s =~ /^[\d]+$/ ) {
                my $temp = int($s);
                $result += $temp**5;
        } else {
                die "Invalid parameter passed to method 'anagram_hash'\nExpected: String or Number\nPassed: $s";
        }
        return $result;
}

sub anagram_hash3 {
        my $result = 0;
        my $s      = shift;
        if ( $s =~ /[a-zA-Z]/ ) {
                $result += $_ ** 5 for unpack "C*", $s;
        } elsif ( $s =~ /^[\d]+$/ ) {
                $result += int($s) ** 5;
        } else {
                die "Invalid parameter passed to method 'anagram_hash'\nExpected: String or Number\nPassed: $s";
        }
        return $result;
}

sub anagram_hash4 {
        my $result = 0;
        my $s      = shift;
        if ( $s =~ /[a-zA-Z]/ ) {
                $result += $lookup{$_} for unpack "(a)*", $s;
        } elsif ( $s =~ /^[\d]+$/ ) {
                $result += int($s) ** 5;
        } else {
                die "Invalid parameter passed to method 'anagram_hash'\nExpected: String or Number\nPassed: $s";
        }
        return $result;
}

sub anagram_hash5 {
        my $result = 0;
        my $s      = shift;
        if ( $s =~ /[a-zA-Z]/ ) {
                $result += $lookup[$_] for unpack "C*", $s;
        } elsif ( $s =~ /^[\d]+$/ ) {
                $result += int($s) ** 5;
        } else {
                die "Invalid parameter passed to method 'anagram_hash'\nExpected: String or Number\nPassed: $s";
        }
        return $result;
}

输出:

test of the result
171658778879381
171658778879381
171658778879381
171658778879381
171658778879381

                   Rate power_badloop hlookup_badloop hlookup_goodloop power_goodloop alookup_goodloop
power_badloop    2132/s            --            -25%             -35%           -71%             -74%
hlookup_badloop  2826/s           33%              --             -14%           -62%             -66%
hlookup_goodloop 3294/s           55%             17%               --           -56%             -60%
power_goodloop   7446/s          249%            163%             126%             --             -10%
alookup_goodloop 8298/s          289%            194%             152%            11%               --

所以,结果显示:

  • 原始OP的代码是最慢的
  • 第二个是Mark的解决方案(用哈希查找替换ord / exp) - 因此,Mark的解决方案 FASTER 而不是原始OP的代码。

最后,(通常情况下)Ikegami带有3个解决方案,其中很多比以前更快。 :)