【问题标题】:very fast look up's in Perl : re-loading hash values possible?在 Perl 中查找速度非常快:可以重新加载哈希值吗?
【发布时间】:2014-09-24 00:12:19
【问题描述】:

我有大约 1 亿行,例如:

A : value of A
B : value of B
|
|
|
Z : value of Z  upto 100 million unique entries

目前,每次运行程序时,我都会将整个文件作为哈希加载,这需要一些时间。在运行时,我需要访问 A,B 的值,因为我知道 A,B 等。

我想知道是否可以进行一次哈希并将其存储为二进制数据结构或索引文件。用最少的编程在 perl 中可能发生什么。

谢谢! -阿比

【问题讨论】:

  • perl -e'++$h{$_} for 1..100_000_000;' 对我来说需要 7 秒。这实际上相当快。我认为您的代码可能对您来说很慢,因为您的内存不足,因此求助于使用虚拟内存。最适合您的解决方案可能是不需要您将整个数据集加载到内存中的解决方案(例如数据库解决方案)。
  • 你是对的,在使用 Storable 模块时我发现我正在达到内存限制。在生产数据集中,我有 500 行。我想我需要使用基于数据库的解决方案。

标签: perl hash


【解决方案1】:

我建议使用磁盘键/值数据库。由于 Perl 的tie function,它们可以与普通的内存哈希一样使用。如果您的哈希非常大,它们的读取/写入速度将比 Perl 的哈希更快,并且它们支持自动保存/加载到磁盘。

BerkeleyDB 是一个老最爱:

use BerkeleyDB;
# Make %db an on-disk database stored in database.dbm. Create file if needed
tie my %db, 'BerkeleyDB::Hash', -Filename => "database.dbm", -Flags => DB_CREATE
    or die "Couldn't tie database: $BerkeleyDB::Error";

$db{foo} = 1;            # get value
print $db{foo}, "\n";    # set value
for my $key (keys %db) {
    print "$key -> $db{$key}\n";  # iterate values
}

%db = ();  # wipe

对数据库的更改会自动保存到磁盘,并会在多次调用脚本后持续存在。

检查 perldoc 中的选项,但最重要的是:

# Increase memory allocation for database (increases performance), e.g. 640 MB
tie my %db, 'BerkeleyDB::Hash', -Filename => $filename, -CacheSize => 640*1024*1024;

# Open database in readonly mode
tie my %db, 'BerkeleyDB::Hash', -Filename => $filename, -Flags => DB_RDONLY;

更复杂但速度更快的数据库库是Tokyo Cabinet,当然还有很多其他选择(毕竟这是 Perl...)

【讨论】:

  • 我想我将不得不使用基于数据库的方法,因为内存。我试图初始化 BerkeleyDB 环境,但脚本给了我错误:无法打开环境没有这样的文件或目录:这里的代码 $berkeleyDB_env = new BerkeleyDB::Env -Cachesize =>$cacheSize or die "cannot open environment $BerkeleyDB::Error \n"; 我不确定为什么代码没有被格式化......对此感到抱歉
  • @rjh :我也可以将哈希引用绑定到 BerkeleyDB。我想对我的数据结构更加花哨。想要遵循 $a->{$b}->{$c}->{$d}=e 之类的东西。我厌倦了使用 BerkeleyDB::Btree but i am getting an error Can't locate object method "TIESCALAR" via package "BerkeleyDB::Btree"`
  • Abhi:不知道你为什么要创建一个BerkeleyDB::Env 对象?但是如果你添加-Flags => DB_CREATE,它就会起作用。对于您的另一个问题-这些数据库仅支持键和值的字符串,因此您需要在将它们放入数据库之前使用Storablefreeze 数据结构,然后在将它们拉出时使用thaw 它们。
  • 在磁盘上创建 BerkeleyDB 文件与加载内存中的数据。这听起来很现实吗?
【解决方案2】:

看看Storable - 它应该做你想做的事,而且使用起来非常简单:

use Storable;
store \%table, 'file';
$hashref = retrieve('file');

当然,这仅在您的程序实际上受 CPU 速度限制时才有用。由于您的数据结构非常简单,因此您解析它的速度可能比从磁盘读取它的速度要快。在这种情况下,Storable 不会对您有太大帮助。

【讨论】:

  • 值得注意的是,这是一个核心模块,即它包含在 Perl 中。
  • 很酷,谢谢.. 我确实有一些解析开销,我力图将其包含在此处。可储存很棒
  • 那如何节省时间?一个 100,000,000 个条目的哈希将花费相同的时间来创建无论是 perl 还是 Storable 创建它。
  • 在创建哈希时,我正在做很多字符串操作,从另一个文本文件中提取数据,因此我可以在 Storable 的帮助下使用持久性 DS,我可以重复使用它。希望重新加载哈希将花费更少的时间。但是,就我而言,正如我提到的,由于内存问题,我不得不求助于基于数据库的解决方案。
  • @ikegami:是什么让你这么想? Storable 的核心是用 C 写的,为什么不比纯 Perl 解决方案更快?
【解决方案3】:

我建议使用Tie::File,因为它包含在核心中,并且不要将整个数据结构加载到内存中,而是根据需要从磁盘访问单个记录。

【讨论】:

  • 正确的想法(不将所有内容加载到内存中),但错误的实现(Tie::File)。哈希用于在恒定时间内定位记录,而使用 Tie::File 定位记录需要线性时间。 (+1 取消不应有的 -1)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 2016-03-01
  • 2016-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多