【发布时间】:2010-11-01 22:04:42
【问题描述】:
我有时会像这样访问哈希:
if(exists $ids{$name}){
$id = $ids{$name};
}
这是好的做法吗?我有点担心它包含两个真正应该完成的查找。有没有更好的方法来检查存在并赋值?
【问题讨论】:
标签: performance perl hash lookup exists
我有时会像这样访问哈希:
if(exists $ids{$name}){
$id = $ids{$name};
}
这是好的做法吗?我有点担心它包含两个真正应该完成的查找。有没有更好的方法来检查存在并赋值?
【问题讨论】:
标签: performance perl hash lookup exists
您可以通过这样的查找来做到这一点:
$tmp = $ids{$name};
$id = $tmp if (defined $tmp);
但是,除非我看到这是一个瓶颈,否则我不会打扰
【讨论】:
exists 检查是否有一个值(可能是 undef),而定义检查是否有一个值并且它不是 undef。
defined 检查。
通过检查exists,您可以防止自动复活。见Autovivification : What is it and why do I care?。
更新:正如trendels 在下面指出的那样,在您发布的示例中,自动激活不会发挥作用。我假设实际代码涉及多级哈希。
这是一个插图:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my (%hash, $x);
if ( exists $hash{test}->{vivify} ) {
$x = $hash{test}->{vivify}->{now};
}
print Dumper \%hash;
$x = $hash{test}->{vivify}->{now};
print Dumper \%hash;
__END__
C:\Temp> t
$VAR1 = {
'test' => {}
};
$VAR1 = {
'test' => {
'vivify' => {}
}
};
【讨论】:
exists 是否比实际检索价值便宜?毕竟,它发现碰撞时不必遵循链表。
如果不是多级哈希,您可以这样做:
$id = $ids{$name} || 'foo';
或者如果 $id 已经有一个值:
$id ||= $ids{$name};
其中 'foo' 是默认值或失败值。如果它是多级散列,您将使用“存在”来避免线程中前面讨论的自动激活,或者如果自动激活不会成为问题,则不使用它。
【讨论】:
您可以使用将Hash::Util 的lock_keys 应用于哈希。然后在 eval 中执行你的任务。
#!/usr/bin/perl
use Hash::Util qw/lock_keys/;
my %a = (
1 => 'one',
2 => 'two'
);
lock_keys(%a);
eval {$val = $a{2}}; # this assignment completes
eval {$val = $a{3}}; # this assignment aborts
print "val=$val\n"; # has value 'two'
【讨论】:
如果我想要高性能,我习惯于在想要创建哈希集时写这个成语:
my %h;
for my $key (@some_vals) {
...
$h{$key} = undef unless exists $h{$key};
...
}
return keys %h;
此代码比常用的$h{$key}++ 快一点。 exists 避免无用的分配,undef 避免分配价值。对你来说最好的答案是:基准测试!我猜exists $ids{$name} 比$id=$ids{$name} 快一点,如果你有很大的未命中率,你的版本可能会比分配和测试之后更快。
例如,如果我想要快速设置交集,我会写这样的内容。
sub intersect {
my $h;
@$h{@{shift()}} = ();
my $i;
for (@_) {
return unless %$h;
$i = {};
@$i{grep exists $h->{$_}, @$_} = ();
$h = $i;
}
return keys %$h;
}
【讨论】:
在这种情况下,性能并不重要,请参阅“Devel::NYTProf”。 但要回答你的问题:
如果哈希中的值不存在,“存在”很快
if(exists $ids{$name}){
$id = $ids{$name};
}
但如果确实存在,则会进行第二次查找。 如果该值可能存在,那么只查找一次会更快
$id = $ids{$name};
if($id){
#....
}
从 perl 邮件列表中查看这个小基准。
#!/usr/bin/perl -w
use strict;
use Benchmark qw( timethese );
use vars qw( %hash );
@hash{ 'A' .. 'Z', 'a' .. 'z' } = (1) x 52;
my $key = 'xx';
timethese 10000000, {
'defined' => sub {
if (defined $hash{$key}) { my $x = $hash{$key}; return $x; };
return 0;
},
'defined_smart' => sub {
my $x = $hash{$key};
if (defined $x) {
return $x;
};
return 0;
},
'exists' => sub {
if (exists $hash{$key}) { my $x = $hash{$key}; return $x; };
return 0;
},
'as is' => sub {
if ($hash{$key}) { my $x = $hash{$key}; return $x; };
return 0;
},
'as is_smart' => sub {
my $x = $hash{$key};
if ($x) { return $x; };
return 0;
},
};
使用不存在的key('xx') 表明'exists' 是赢家。
Benchmark: timing 10000000 iterations of as is, as is_smart, defined, defined_smart, exists...
as is: 1 wallclock secs ( 1.52 usr + 0.00 sys = 1.52 CPU) @ 6578947.37/s (n=10000000)
as is_smart: 3 wallclock secs ( 2.67 usr + 0.00 sys = 2.67 CPU) @ 3745318.35/s (n=10000000)
defined: 3 wallclock secs ( 1.53 usr + 0.00 sys = 1.53 CPU) @ 6535947.71/s (n=10000000)
defined_smart: 3 wallclock secs ( 2.17 usr + 0.00 sys = 2.17 CPU) @ 4608294.93/s (n=10000000)
exists: 1 wallclock secs ( 1.33 usr + 0.00 sys = 1.33 CPU) @ 7518796.99/s (n=10000000)
使用确实存在的 key('x') 表明 'as is_smart' 是赢家。
Benchmark: timing 10000000 iterations of as is, as is_smart, defined, defined_smart, exists...
as is: 3 wallclock secs ( 2.76 usr + 0.00 sys = 2.76 CPU) @ 3623188.41/s (n=10000000)
as is_smart: 3 wallclock secs ( 1.81 usr + 0.00 sys = 1.81 CPU) @ 5524861.88/s (n=10000000)
defined: 3 wallclock secs ( 3.42 usr + 0.00 sys = 3.42 CPU) @ 2923976.61/s (n=10000000)
defined_smart: 2 wallclock secs ( 2.32 usr + 0.00 sys = 2.32 CPU) @ 4310344.83/s (n=10000000)
exists: 3 wallclock secs ( 2.83 usr + 0.00 sys = 2.83 CPU) @ 3533568.90/s (n=10000000)
【讨论】: