是否可以在 Perl 中保留哈希表的大小?

Posted

技术标签:

【中文标题】是否可以在 Perl 中保留哈希表的大小?【英文标题】:Is it possible to reserve the size of a hash table in Perl? 【发布时间】:2011-09-30 22:57:35 【问题描述】:

我正在 Perl 中创建一个大小未知的哈希表。

哈希表将字符串映射到对数组的引用。

我的应用程序的主循环在每次迭代中向哈希表添加 5-10 个元素。随着哈希表填满,事情开始急剧放缓。根据观察,当哈希表中有大约 50k 个键时,添加键的速度会降低 20 倍。

我假设哈希表已满,并且正在发生冲突。我想“保留”哈希表的大小,但我不确定如何。


有问题的哈希是 hNgramsToWord。

对于每个单词,该单词的 1-len-gram 作为键添加,并引用包含该 ngram 的单词数组。

例如:

AddToNgramHash("你好");

[h, e, l, l, o, he, el, ll, lo, hel, llo, hell, ello, hello ] 都作为键添加,映射到“hello”

sub AddToNgramHash($) 
    my $word = shift;
    my @aNgrams = MakeNgrams($word);
    foreach my $ngram (@aNgrams) 
       my @aWords;
       if(defined($hNgramsToWord$ngram)) 
          @aWords = @$hNgramsToWord$ngram;
       
       push (@aWords, $word);
       $hNgramsToWord$ngram = \@aWords;
    
    return scalar keys %hNgramsToWord;


sub MakeNgrams($) 
    my $word = shift;
    my $len = length($word);
    my @aNgrams;
    for(1..$len) 
       my $ngs = $_;
          for(0..$len-$ngs) 
           my $ngram = substr($word, $_, $ngs);
           push (@aNgrams, $ngram);
       
    
    return @aNgrams;

【问题讨论】:

我的猜测是 perl 根本就没有考虑到类似的东西(那是很多键)。据我所知,在这样的实现中没有任何低级别的访问权限。 @crimson_penguin:不是真的,反正 50k 也不是很多钥匙 【参考方案1】:

您可以像这样设置哈希的桶数:

keys(%hash) = 128;

数字将四舍五入为 2 的幂。

也就是说,您看到的减速不太可能是由于过多的哈希冲突造成的,因为 Perl 会根据需要动态扩展存储桶的数量。从 5.8.2 开始,它甚至会检测导致给定存储桶被过度使用的病态数据,并为该哈希重新配置哈希函数。

显示您的代码,我们可能会帮助您找到真正的问题。

大量哈希键的演示(不要让它继续直到你内存不足......):

use strict;
use warnings;
my $start = time();
my %hash;
$SIGALRM = sub 
    alarm 1;
    printf(
        "%.0f keys/s; %d keys, %s buckets used\n",
        keys(%hash) / (time() - $start),
        scalar(keys(%hash)),
        scalar(%hash)
    );
;
alarm 1;
$hashrand()++ while 1;

一旦有很多键,当它需要扩展存储桶的数量时,您会注意到明显的减速,但它仍然保持相当均匀的速度。

查看您的代码,加载的单词越多,每个单词要做的工作就越多。

你可以通过改变这个来修复它:

   my @aWords;
   if(defined($hNgramsToWord$ngram)) 
      @aWords = @$hNgramsToWord$ngram;
   
   push (@aWords, $word);
   $hNgramsToWord$ngram = \@aWords;

到这里:

   push @ $hNgramsToWord$ngram , $word;

无需复制数组两次。无需检查 ngram 是否已经有条目 - 它会为您自动激活数组引用。

【讨论】:

有趣。我已经添加了上面的代码,并将通过设置散列大小进行测试。谢谢。 @user756079:是的,不是哈希冲突的问题 哇。它刚刚运行并在大约 5 秒内完成。是的,你是一位绅士和学者。 @user756079:这会加快速度吗? sub MakeNgrams map $_[0] =~ /(?=(.$_))/g, 1..length $_[0]

以上是关于是否可以在 Perl 中保留哈希表的大小?的主要内容,如果未能解决你的问题,请参考以下文章

在 Perl 中,我如何处理整个哈希?

无法在 Perl 循环中访问哈希查找表

C 无法调整哈希表的大小

哈希表的性能,为啥C++最慢?

有哪些算法可用于调整哈希表的大小?

23-Perl 面向对象