Modules · Data · 0.09
Frozen
Nested data workers read after a fork without unsharing a page.
Data loaded before a fork is shared with every worker by copy-on-write, until the first read writes to a reference count and the page is copied. Frozen is an immutable, position-independent block of bytes holding nested data, read without touching a reference count.
use Frozen;
# once, when deploying: nested data into a block of bytes
Frozen->freeze_to('catalogue.frz', {
messages => { welcome => 'Hello', bye => 'Goodbye' },
limits => { upload => 1_048_576 },
}, flat => '.');
# before forking: every worker reads it without unsharing a page
my $fz = Frozen->open('catalogue.frz');
my ($greeting) = $fz->get('messages.welcome');
print "$greeting\n"; # Hello
What you get
-
Shared after a fork, really
Reading a Perl hash writes to its reference counts, so the copy-on-write pages a worker reads become its own. A Frozen block is read by offset and never written.
-
Smaller
In its POD's measurement the same data takes 6.4 MB as a block against 13.9 MB as a Perl hash: 2.16 times smaller.
-
Flat as it grows
Lookup stays near flat as a block grows, where a hash slows with size.
get('a.b.c')is one probe into a flat index built withflat => '.'. -
Two doors
The fast door (
get,fetch,child) returns an empty list for an absent key, sinceundefis a value. The tied door is an ordinary-looking hashref, and slower.
In use
use Frozen;
Frozen->freeze_to('settings.frz', { db => { host => 'db1', port => 5432 } });
my $fz = Frozen->open('settings.frz');
# the fast door: a value, or an empty list when the key is absent
my $db = $fz->child($fz->root, 'db');
my ($port) = $fz->fetch($db, 'port');
my @none = $fz->fetch($db, 'user');
print "$port, ", scalar(@none), " values for user\n"; # 5432, 0 values for user
# the tied door: an ordinary-looking hashref, slower
my $h = $fz->tied;
print "$h->{db}{host}\n"; # db1
Measured in its POD
Per hit, best of three runs on one machine, as the POD records it. At 500 keys a Perl hash takes 22.2 ns and Frozen 23.9 ns. At 200,000 keys the hash takes 47.4 ns and Frozen 32.1 ns. For a three-level catalogue, $fz->get('a.b.c') takes 61.4 ns against 88.9 ns for the nested Perl traversal, and the tied door 729.5 ns.
For C
fz_abi.h, resolved at run time through Frozen::_abi_ptr, lets another module's XS read a block with no Perl frame at all, including a block that lives in shared memory and outlives the reader.