我有哈希哈希%signal_db
。典型的元素是:$signal_db{$cycle}{$key}
。有10,000个信号和10,000个密钥。
有没有办法优化(按时间)这段代码:
foreach my $cycle (sort numerically keys %signal_db) {
foreach my $key (sort keys %{$signal_db{$cycle}}) {
print $signal_db{$cycle}{$key}.$key."\n";
}
}
必须按照与我的代码相同的顺序打印元素。
答案 0 :(得分:7)
两个微优化:映射内部哈希而不是常量解除引用和缓冲而不是常量打印。可以使用替代存储格式摆脱排序,测试两种变体。结果:
Rate original try3 alternative alternative2
original 46.1/s -- -12% -21% -32%
try3 52.6/s 14% -- -10% -22%
alternative 58.6/s 27% 11% -- -13%
alternative2 67.5/s 46% 28% 15% --
结论:
最好使用预先存储的存储格式,但没有C win可能会在100%之内(在我的测试数据集上)。提供的有关数据的信息表明,外部哈希中的键几乎是连续的数字,所以这就要求数组。
脚本:
#!/usr/bin/env perl
use strict; use warnings;
use Benchmark qw/timethese cmpthese/;
my %signal_db = map { $_ => {} } 1..1000;
%$_ = map { $_ => $_ } 'a'..'z' foreach values %signal_db;
my @signal_db = map { { cycle => $_ } } 1..1000;
$_->{'samples'} = { map { $_ => $_ } 'a'..'z' } foreach @signal_db;
my @signal_db1 = map { $_ => [] } 1..1000;
@$_ = map { $_ => $_ } 'a'..'z' foreach grep ref $_, @signal_db1;
use Sort::Key qw(nsort);
sub numerically { $a <=> $b }
my $result = cmpthese( -2, {
'original' => sub {
open my $out, '>', 'tmp.out';
foreach my $cycle (sort numerically keys %signal_db) {
foreach my $key (sort keys %{$signal_db{$cycle}}) {
print $out $signal_db{$cycle}{$key}.$key."\n";
}
}
},
'try3' => sub {
open my $out, '>', 'tmp.out';
foreach my $cycle (map $signal_db{$_}, sort numerically keys %signal_db) {
my $tmp = '';
foreach my $key (sort keys %$cycle) {
$tmp .= $cycle->{$key}.$key."\n";
}
print $out $tmp;
}
},
'alternative' => sub {
open my $out, '>', 'tmp.out';
foreach my $cycle (map $_->{'samples'}, @signal_db) {
my $tmp = '';
foreach my $key (sort keys %$cycle) {
$tmp .= $cycle->{$key}.$key."\n";
}
print $out $tmp;
}
},
'alternative2' => sub {
open my $out, '>', 'tmp.out';
foreach my $cycle (grep ref $_, @signal_db1) {
my $tmp = '';
foreach (my $i = 0; $i < @$cycle; $i+=2) {
$tmp .= $cycle->[$i+1].$cycle->[$i]."\n";
}
print $out $tmp;
}
},
} );
答案 1 :(得分:4)
my %signal_db = map {$_ => {1 .. 1000}} 1 .. 1000;
sub numerically {$a <=> $b}
sub orig {
my $x;
foreach my $cycle (sort numerically keys %signal_db) {
foreach my $key (sort keys %{$signal_db{$cycle}}) {
$x += length $signal_db{$cycle}{$key}.$key."\n";
}
}
}
sub faster {
my $x;
our ($cycle, $key, %hash); # move allocation out of the loop
local *hash; # and use package variables which are faster to alias into
foreach $cycle (sort {$a <=> $b} # the {$a <=> $b} literal is optimized
keys %signal_db) {
*hash = $signal_db{$cycle}; # alias into %hash
foreach $key (sort keys %hash) {
$x += length $hash{$key}.$key."\n"; # simplify the lookup
}
}
}
use Benchmark 'cmpthese';
cmpthese -5 => {
orig => \&orig,
faster => \&faster,
};
得到:
Rate orig faster orig 2.56/s -- -15% faster 3.03/s 18% --
不是一个巨大的收获,但它是一个东西。在不改变数据结构的情况下,您可以优化以使用预分类数组。 (或在XS中写下整件事)
切换foreach
循环以使用外部包变量可节省一点时间,因为perl不必在循环中创建词法。包变量似乎也有点快了别名。将内部查找减少到单个级别也有帮助。
我假设您要打印到STDOUT
然后将输出重定向到文件?如果是这样,使用Perl直接打开输出文件然后打印到该句柄可以允许改进文件IO性能。另一个微优化可能是尝试不同的记录大小。例如,它是否节省了在内部循环中构建数组的任何时间,然后在外部循环的底部连接/打印它?但这是相当依赖于设备的东西(并且由于其他IO缓存层可能毫无意义),所以我将把测试留给你。
答案 2 :(得分:3)
我首先尝试使用Sort::Key
module,因为排序比简单的循环和打印需要更长的时间。此外,如果内部哈希键(大多数)相同,那么你应该简单地预先分配它们,但我会假设情况并非如此,否则你就已经这样做了。
您显然应该尝试将$ signal_db {$ cycle}分配给引用。您可能会发现each
比keys
加快检索速度更快,尤其是与Sort::Key
一起使用时。我会检查地图是否也比foreach
运行得快,可能是相同的,但是谁知道。如果您将列表传递给列表或多次调用它,您可能会发现print
运行得更快。
我没有尝试过这段代码,但除了each
之外,所有这些想法都归结为:
foreach my $cycle (nsort keys %signal_db) {
my $r = $signal_db{$cycle};
map { print ($r->{$_},$_,"\n"); } (nsort keys %$r);
}
有一篇关于在perl here中进行排序的文章,如果您希望了解如何使用each
,请查看Schwartzian变换。
如果您的代码不需要具有安全意识,那么您可以设想通过设置algorithmic complexity attacks或相关变量和/或使用更改的设置重新编译Perl来禁用Perl对PERL_HASH_SEED的保护,以便perl的{{1 }}和keys
命令已经按排序顺序返回了键或值,从而节省了大量时间对它们进行排序。但请在执行此操作之前先查看this 28C3 talk。我不知道如果这甚至可以工作,你需要阅读Perl源代码的这一部分,也许更容易在C中实现你的循环。