Modules · Data · 0.03
HAMT
Persistent maps, sets and vectors on a hash array mapped trie.
An update returns a new container and leaves the one it was called on exactly as it was. The two share nearly all their memory, so keeping the old one costs a few small nodes, not a copy: a snapshot, an undo step or a value handed to code that must not change it is as cheap as holding a reference.
use HAMT::Map;
my $config = HAMT::Map->new(db => 'prod', timeout => 30);
my $trial = $config->set(timeout => 5); # a new map; $config is unchanged
print $config->get('timeout'), "\n"; # 30
print $trial->get('timeout'), "\n"; # 5
# what changed between two versions, looking only where they differ
$config->diff($trial, sub {
my ($key, $was, $now, $kind) = @_;
print "$kind $key: $was -> $now\n"; # change timeout: 30 -> 5
});
What you get
-
Persistent
An update returns a new container and leaves the old one exactly as it was. Changing a key replaces the few nodes between the root and that key, and every other node is shared between the versions.
-
Map, Set and Vector
HAMT::MapandHAMT::Setare a hash array mapped trie, 32 wide.HAMT::Vectoris the same kind of tree read by position, withpush,insert,sliceandconcat. -
Compare versions by their differences
merge,equalsanddifflook only where two versions differ, so a set compared with its earlier self costs what changed, not its size. -
Transients for building
To build, use
new,from_hashor a transient: an update at a time in a loop is several times slower than any of them. -
Values are read-only
A stored value is copied and the copy is read-only, shared by every version. This is shallow: what a stored reference points at is still the caller's to change.
In use
use HAMT::Set;
use HAMT::Vector;
my $before = HAMT::Set->new(qw(read write));
my $after = $before->add('admin')->remove('write');
print join(',', sort $after->members), "\n"; # admin,read
print $before->has('write') ? "kept\n" : "lost\n"; # kept
my $v = HAMT::Vector->new(1 .. 5);
my $v2 = $v->push(6)->set(0, 'one');
print join(',', $v2->list), "\n"; # one,2,3,4,5,6
Measured in its POD
One run on one machine (Apple M5, perl 5.42.0), as the module's POD records it, with the scripts in its bench/ directory:
One set on 10,000 keys: 3,000 times faster than copying the hash. A union of two versions of a set: 5,000 times faster than (%a, %b). A thousand versions of a 10,000-key map: 1/249 the memory of a thousand copies. A million keys: 0.69 times the memory of a hash.
And the costs: a get of a present key is 2.1 times slower than a hash fetch, and building 100,000 keys 1.4 to 1.9 times slower than a hash.
For C
hamt_abi.h, installed with the module and found through ExtUtils::Depends, gives another extension's C everything the three classes do from Perl: reading, updating, walking, merging, comparing, cutting and joining, with no Perl call in between. The table is versioned and resolved at run time, so the two distributions upgrade independently.