【问题标题】:Does merging 2 or more Perl hash references consume more or less twice the memory?合并 2 个或更多 Perl 哈希引用会消耗更多或更少的内存吗?
【发布时间】:2018-09-04 07:52:32
【问题描述】:

鉴于以下代码,$z 引用的哈希是否消耗与 ( %$x, %$y) 使用的内存相同的内存,或多或少?

如果是这样,有没有办法使用单个引用从 $x$y 引用的哈希中调用数据,例如 $z->{$somekeytoXorY},而不影响性能和内存?

use strict;
use warnings;

my $x = {
    1 => 'a',
    2 => 'b',
};

my $y = {
    3 => 'c',
    4 => 'd',
};

my $z = {
    %$x, %$y
};

更新

哈希引用实际上指向使用tieDB_File 创建的大哈希。

我想知道是否有机会只对这些使用单个哈希,这样我就不需要将所有内容转储到内存中。我也可能同时使用两个以上。

【问题讨论】:

  • “不影响性能和内存” 您是否遇到了性能问题或内存问题?如果是这样,那么您应该分析您的代码以发现瓶颈所在,并专注于该代码。如果不是,那么您正在尝试进行不必要的优化。内存容量或 CPU 功率如此昂贵以至于雇用程序员来减少它们具有成本效益的日子已经一去不复返了。如果您可以节省 64GB 的内存,或者将运行时间缩短到几毫秒,那就继续吧。但不同的数据结构选择通常不会带来这样的好处。
  • "哈希引用实际上指向使用 tieDB_File 创建的大哈希" 那么他们根本没有使用 Perl 哈希:你只是在使用与哈希访问非常相似的 API,可以通过虚拟哈希操作有效地进行调用。如果您检查tied 哈希,那么您可能会看到它仍然是空的。内存使用高度依赖于tie 的写入方式,您甚至不应该考虑使用{ %$_ } 之类的结构复制散列。这将创建一个“普通”哈希并破坏tied 接口,因此您的代码将毫无价值。
  • “我想知道 [我真的需要] 是否将所有内容转储到内存中” 将数据从 tied 哈希复制到内存会适得其反。有些事情可能仍然有效,但一旦您这样做,您就失去了tie 功能。你可以总是使用$x->{key} // $y->{key}(或exists $x->{key} ? $x->{key} : $y->{key},正如JGNI所建议的那样),除非你需要考虑出现在两个散列中的元素。你试图变得太聪明,并解决你还没有遇到的问题。
  • 捆绑散列根本不是散列。它们是子程序的接口。由于它们是代码而不是数据,因此通常谈论绑定哈希的内存和性能是没有意义的。

标签: perl


【解决方案1】:

捆绑散列根本不是散列。它们是子程序的接口。由于它们是代码而不是数据,因此通常谈论绑定哈希的内存和性能是没有意义的。

我们先说普通的哈希。

$z = { %$x, %$y }; 会将%$x%$y 的标量复制到%$z,所以是的,它将占用两倍的内存(假设没有重复的键)。

您可以共享标量:

use Data::Alias qw( alias );
my $z = {};
alias $z->{$_} = $x->{$_} for keys(%$x);
alias $z->{$_} = $y->{$_} for keys(%$y);

您仍然会使用与两个哈希中的元素数量成比例的内存,但如果 %$x%$y 实际上是哈希,它会比以前少得多。这可能不会为绑定哈希节省任何内存。

另一种方法是根本不实际合并数据。您可以自己使用绑定哈希...

package Tie::MergedHashes;
use Carp qw( croak );
sub new     { my $pkg = shift; $pkg->TIEHASH(@_); }
sub TIEHASH { bless [ @_ ], $_[0] }
sub STORE   { croak("Not allowed"); }
sub FETCH   { for (@{$_[0]}) { return $_->{$_[1]} if exists($_->{$_[1]}); } return undef; }
...

my $z = {};
tie %$z, MergedHashes => ($y, $x);
$z->{$key}

...但是没有理由让代码看起来像哈希。你可以简单地使用一个对象。

package MergedHashes;
use Carp qw( croak );
sub new   { bless [ @_ ], $_[0] }
sub fetch { for (@{$_[0]}) { return $_->{$_[1]} if exists($_->{$_[1]}); } return undef; }
...

my $z = MergedHashes->new($y, $x);
$z->fetch($key)

【讨论】:

  • 感谢对象建议。我原本想只用一个for循环来检查数据存在的位置,但是这种对象方法更具可读性。
  • 缺点是子/方法调用相当昂贵。
【解决方案2】:

简单的答案是肯定的。

如果您想要两个哈希引用中的所有键而不创建新的哈希引用,并假设您没有重复的键,您可以执行类似的操作。

use List::Util qw( uniq );

my @keys = uniq( keys( %$x ), keys( %$y ) );

然后从任一数组中获取值,例如

my $value = exists $y->{$key} ? $y->{$key} : $x->{$key};

顺便问一下,你为什么使用哈希引用而不是哈希,为什么内存是你需要问这个问题的一个考虑因素?

【讨论】:

    猜你喜欢
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多