Data-HashMap
view release on metacpan or search on metacpan
or "$map->put_ttl($key, $value, $seconds)", even on maps created without
a default TTL. The expires array is lazily allocated on first use.
Both features use parallel arrays indexed by slot, keeping the core node
struct unchanged. Maps created without LRU/TTL have zero overhead beyond
a never-taken branch.
PERFORMANCE
Benchmarks with 100k entries on Linux x86_64 (higher is better):
INSERT (iterations/sec):
Rate perl_ss perl_ii SA SS IA I32A SI32 SI IS I32S I32 II I16S SI16 I16A I16
perl_ss 17.6/s -- -1% -11% -27% -45% -48% -51% -53% -54% -55% -83% -83% -86% -86% -88% -96%
perl_ii 17.7/s 1% -- -11% -26% -45% -48% -51% -53% -54% -54% -82% -83% -86% -86% -87% -96%
SA 19.8/s 12% 12% -- -18% -38% -42% -45% -47% -48% -49% -80% -81% -84% -84% -86% -95%
SS 24.1/s 37% 36% 22% -- -25% -29% -33% -36% -37% -38% -76% -77% -81% -81% -83% -94%
IA 32.0/s 82% 81% 62% 33% -- -6% -11% -15% -16% -17% -68% -70% -75% -75% -77% -92%
I32A 34.0/s 93% 92% 72% 41% 6% -- -5% -10% -11% -13% -66% -68% -73% -73% -76% -92%
SI32 35.9/s 104% 103% 81% 49% 12% 6% -- -5% -6% -8% -64% -66% -72% -72% -75% -91%
SI 37.6/s 113% 112% 90% 56% 17% 11% 5% -- -2% -3% -63% -65% -70% -70% -73% -91%
IS 38.3/s 118% 116% 93% 59% 20% 13% 7% 2% -- -1% -62% -64% -70% -70% -73% -91%
I32S 38.8/s 121% 119% 96% 61% 21% 14% 8% 3% 1% -- -61% -63% -69% -69% -73% -90%
I32 101/s 472% 468% 408% 318% 214% 196% 180% 168% 163% 159% -- -5% -20% -20% -29% -75%
II 106/s 501% 498% 435% 339% 230% 212% 195% 182% 176% 173% 5% -- -16% -16% -25% -74%
I16S 126/s 618% 614% 538% 425% 295% 272% 252% 236% 230% 226% 26% 19% -- -0% -11% -69%
SI16 126/s 618% 614% 538% 425% 295% 272% 252% 236% 230% 226% 26% 19% 0% -- -11% -69%
I16A 141/s 703% 698% 614% 487% 341% 316% 294% 276% 269% 264% 40% 34% 12% 12% -- -65%
I16 404/s 2196% 2182% 1941% 1577% 1161% 1090% 1027% 976% 955% 941% 302% 282% 220% 220% 186% --
LOOKUP (iterations/sec):
Rate SS_direct perl_ii SS SA perl_ss SI SI32 IS I32S I32A IS_direct IA II I32 SI16 I16A I16S I16
SS_direct 26.0/s -- -15% -17% -18% -21% -30% -31% -43% -49% -54% -54% -60% -68% -76% -84% -86% -90% -93%
perl_ii 30.5/s 17% -- -2% -4% -8% -18% -19% -34% -40% -46% -47% -53% -62% -72% -81% -84% -88% -92%
SS 31.3/s 20% 3% -- -1% -6% -16% -16% -32% -38% -44% -45% -52% -61% -72% -81% -83% -88% -92%
SA 31.7/s 22% 4% 1% -- -4% -15% -15% -31% -38% -44% -45% -51% -61% -71% -80% -83% -88% -92%
perl_ss 33.1/s 27% 9% 6% 5% -- -11% -12% -28% -35% -41% -42% -49% -59% -70% -79% -82% -87% -91%
SI 37.2/s 43% 22% 19% 17% 12% -- -1% -19% -27% -34% -35% -43% -54% -66% -77% -80% -85% -90%
SI32 37.5/s 44% 23% 20% 18% 13% 1% -- -19% -26% -33% -34% -42% -54% -66% -77% -80% -85% -90%
IS 46.0/s 77% 51% 47% 45% 39% 24% 23% -- -9% -18% -20% -29% -43% -58% -71% -75% -82% -88%
I32S 50.7/s 95% 66% 62% 60% 53% 36% 35% 10% -- -10% -11% -22% -37% -54% -69% -73% -80% -86%
I32A 56.3/s 116% 85% 80% 78% 70% 51% 50% 22% 11% -- -2% -13% -30% -49% -65% -70% -78% -85%
IS_direct 57.2/s 120% 87% 83% 80% 73% 54% 53% 24% 13% 2% -- -12% -29% -48% -65% -69% -78% -85%
IA 64.7/s 149% 112% 107% 104% 95% 74% 73% 41% 28% 15% 13% -- -20% -42% -60% -65% -75% -83%
II 80.8/s 210% 165% 158% 155% 144% 117% 116% 76% 59% 43% 41% 25% -- -27% -50% -57% -68% -78%
I32 111/s 325% 263% 254% 249% 234% 198% 196% 141% 118% 97% 94% 71% 37% -- -31% -41% -57% -71%
SI16 161/s 520% 428% 415% 409% 387% 334% 331% 250% 218% 186% 182% 149% 100% 46% -- -13% -37% -57%
I16A 186/s 615% 510% 495% 487% 462% 401% 397% 305% 267% 231% 226% 188% 130% 68% 15% -- -27% -50%
I16S 256/s 885% 740% 719% 709% 674% 589% 584% 457% 405% 355% 348% 296% 217% 132% 59% 38% -- -32%
I16 375/s 1342% 1130% 1100% 1084% 1033% 910% 902% 716% 640% 566% 556% 480% 365% 239% 133% 102% 46% --
INCREMENT (iterations/sec):
Rate perl_ss perl_ii SI32 SI I32 II SI16 I16
perl_ss 26.2/s -- -5% -12% -15% -64% -65% -77% -90%
perl_ii 27.6/s 5% -- -8% -10% -62% -63% -76% -89%
SI32 30.0/s 14% 9% -- -3% -59% -60% -73% -88%
SI 30.7/s 17% 12% 3% -- -58% -59% -73% -88%
I32 73.1/s 179% 165% 144% 138% -- -2% -35% -71%
II 74.4/s 184% 170% 148% 142% 2% -- -34% -71%
SI16 113/s 330% 310% 277% 267% 54% 52% -- -56%
I16 255/s 871% 824% 750% 728% 248% 242% 126% --
DELETE (iterations/sec):
Rate SA SS IS SI32 SI perl_ss I32S IA perl_ii I32A II I32 SI16 I16A I16S I16
SA 10.9/s -- -6% -18% -25% -25% -27% -32% -32% -36% -41% -58% -76% -83% -84% -86% -93%
SS 11.6/s 6% -- -13% -20% -21% -22% -27% -28% -32% -37% -55% -74% -82% -83% -85% -93%
IS 13.3/s 22% 14% -- -9% -9% -11% -17% -17% -23% -28% -48% -70% -80% -81% -83% -92%
SI32 14.6/s 33% 25% 9% -- -0% -2% -9% -10% -15% -21% -44% -68% -78% -79% -81% -91%
SI 14.6/s 34% 26% 10% 0% -- -2% -8% -9% -15% -21% -43% -68% -78% -79% -81% -91%
perl_ss 14.9/s 36% 28% 12% 2% 2% -- -7% -8% -13% -19% -42% -67% -77% -78% -81% -91%
I32S 16.0/s 46% 37% 20% 10% 9% 7% -- -1% -7% -14% -38% -65% -76% -77% -80% -90%
IA 16.1/s 47% 38% 21% 11% 10% 8% 1% -- -6% -13% -38% -64% -76% -76% -79% -90%
perl_ii 17.2/s 57% 48% 29% 18% 18% 16% 8% 7% -- -7% -33% -62% -74% -75% -78% -90%
SI16 65.9/s 503% 467% 395% 353% 351% 343% 313% 310% 283% 257% 155% 46% -- -3% -15% -60%
I16A 68.3/s 525% 487% 413% 369% 367% 359% 328% 324% 297% 270% 164% 51% 4% -- -12% -58%
I16S 78.0/s 614% 571% 486% 436% 433% 424% 388% 384% 353% 322% 202% 73% 18% 14% -- -53%
I16 165/s 1406% 1315% 1136% 1030% 1025% 1005% 930% 922% 856% 791% 537% 265% 150% 141% 111% --
LRU / TTL overhead
LRU and TTL add parallel arrays for linked-list pointers and expiry
timestamps. Maps created without these features have zero overhead
beyond a never-taken branch.
INSERT, II variant (iterations/sec):
Rate II_lru II_lru_ttl II
II_lru 68.8/s -- -2% -16%
II_lru_ttl 70.4/s 2% -- -14%
II 81.9/s 19% 16% --
LOOKUP, II variant (iterations/sec):
Rate II_lru_ttl II_lru II
II_lru_ttl 68.3/s -- -14% -33%
II_lru 79.3/s 16% -- -23%
II 103/s 50% 29% --
LRU EVICTION CHURN: insert 100k into capacity 50k (iterations/sec):
Rate II_lru_ttl II_lru
II_lru_ttl 61.7/s -- -13%
II_lru 71.0/s 15% --
Method vs keyword overhead
Keywords bypass Perl's method dispatch for maximum performance. Method
calls ("$map->get($key)") are convenient but slower:
II variant, 100k operations (iterations/sec):
keyword method overhead
LOOKUP 85.5/s 59.1/s -31%
INSERT 71.1/s 59.0/s -17%
MEMORY
Memory usage with 1M entries (fork-isolated measurements):
Variant Memory Bytes/entry vs Perl hash
------- ------ ----------- ------------
I16* 0.5 MB 16 10x less
bench/each.pl view on Meta::CPAN
hm_i32a_put $m_i32a, $_, "v$_" for 1 .. $N;
my $m_i16a = Data::HashMap::I16A->new();
hm_i16a_put $m_i16a, $_, "v$_" for 1 .. $N16;
# Perl hashes
my %h_ii; $h_ii{$_} = $_ for 1 .. $N;
my %h_ss; $h_ss{"k$_"} = "v$_" for 1 .. $N;
print "-" x 70, "\n";
print "ITERATE all $N entries with each() (iterations/sec)\n";
print "-" x 70, "\n";
cmpthese(-3, {
'perl_ii' => sub {
while (my ($k, $v) = each %h_ii) { }
},
'perl_ss' => sub {
while (my ($k, $v) = each %h_ss) { }
},
'I16' => sub {
while (my ($k, $v) = hm_i16_each $m_i16) { }
bench/each.pl view on Meta::CPAN
'I32A' => sub {
while (my ($k, $v) = hm_i32a_each $m_i32a) { }
},
'I16A' => sub {
while (my ($k, $v) = hm_i16a_each $m_i16a) { }
},
});
print "\n";
print "-" x 70, "\n";
print "ITERATE all $N entries with keys() (iterations/sec)\n";
print "-" x 70, "\n";
cmpthese(-3, {
'perl_ii' => sub {
my @k = keys %h_ii;
},
'perl_ss' => sub {
my @k = keys %h_ss;
},
'II' => sub {
my @k = hm_ii_keys $m_ii;
bench/each.pl view on Meta::CPAN
'I32' => sub {
my @k = hm_i32_keys $m_i32;
},
'SI' => sub {
my @k = hm_si_keys $m_si;
},
});
print "\n";
print "-" x 70, "\n";
print "ITERATE all $N entries with items() vs each-in-loop (iterations/sec)\n";
print "-" x 70, "\n";
cmpthese(-3, {
'perl_each' => sub {
while (my ($k, $v) = each %h_ii) { }
},
'perl_kv' => sub {
my @kv = %h_ii;
},
'II_each' => sub {
while (my ($k, $v) = hm_ii_each $m_ii) { }
lib/Data/HashMap.pm view on Meta::CPAN
without a default TTL. The expires array is lazily allocated on first use.
Both features use parallel arrays indexed by slot, keeping the core
node struct unchanged. Maps created without LRU/TTL have zero overhead
beyond a never-taken branch.
=head1 PERFORMANCE
Benchmarks with 100k entries on Linux x86_64 (higher is better):
INSERT (iterations/sec):
Rate perl_ss perl_ii SA SS IA I32A SI32 SI IS I32S I32 II I16S SI16 I16A I16
perl_ss 17.6/s -- -1% -11% -27% -45% -48% -51% -53% -54% -55% -83% -83% -86% -86% -88% -96%
perl_ii 17.7/s 1% -- -11% -26% -45% -48% -51% -53% -54% -54% -82% -83% -86% -86% -87% -96%
SA 19.8/s 12% 12% -- -18% -38% -42% -45% -47% -48% -49% -80% -81% -84% -84% -86% -95%
SS 24.1/s 37% 36% 22% -- -25% -29% -33% -36% -37% -38% -76% -77% -81% -81% -83% -94%
IA 32.0/s 82% 81% 62% 33% -- -6% -11% -15% -16% -17% -68% -70% -75% -75% -77% -92%
I32A 34.0/s 93% 92% 72% 41% 6% -- -5% -10% -11% -13% -66% -68% -73% -73% -76% -92%
SI32 35.9/s 104% 103% 81% 49% 12% 6% -- -5% -6% -8% -64% -66% -72% -72% -75% -91%
SI 37.6/s 113% 112% 90% 56% 17% 11% 5% -- -2% -3% -63% -65% -70% -70% -73% -91%
IS 38.3/s 118% 116% 93% 59% 20% 13% 7% 2% -- -1% -62% -64% -70% -70% -73% -91%
I32S 38.8/s 121% 119% 96% 61% 21% 14% 8% 3% 1% -- -61% -63% -69% -69% -73% -90%
I32 101/s 472% 468% 408% 318% 214% 196% 180% 168% 163% 159% -- -5% -20% -20% -29% -75%
II 106/s 501% 498% 435% 339% 230% 212% 195% 182% 176% 173% 5% -- -16% -16% -25% -74%
I16S 126/s 618% 614% 538% 425% 295% 272% 252% 236% 230% 226% 26% 19% -- -0% -11% -69%
SI16 126/s 618% 614% 538% 425% 295% 272% 252% 236% 230% 226% 26% 19% 0% -- -11% -69%
I16A 141/s 703% 698% 614% 487% 341% 316% 294% 276% 269% 264% 40% 34% 12% 12% -- -65%
I16 404/s 2196% 2182% 1941% 1577% 1161% 1090% 1027% 976% 955% 941% 302% 282% 220% 220% 186% --
LOOKUP (iterations/sec):
Rate SS_direct perl_ii SS SA perl_ss SI SI32 IS I32S I32A IS_direct IA II I32 SI16 I16A I16S I16
SS_direct 26.0/s -- -15% -17% -18% -21% -30% -31% -43% -49% -54% -54% -60% -68% -76% -84% -86% -90% -93%
perl_ii 30.5/s 17% -- -2% -4% -8% -18% -19% -34% -40% -46% -47% -53% -62% -72% -81% -84% -88% -92%
SS 31.3/s 20% 3% -- -1% -6% -16% -16% -32% -38% -44% -45% -52% -61% -72% -81% -83% -88% -92%
SA 31.7/s 22% 4% 1% -- -4% -15% -15% -31% -38% -44% -45% -51% -61% -71% -80% -83% -88% -92%
perl_ss 33.1/s 27% 9% 6% 5% -- -11% -12% -28% -35% -41% -42% -49% -59% -70% -79% -82% -87% -91%
SI 37.2/s 43% 22% 19% 17% 12% -- -1% -19% -27% -34% -35% -43% -54% -66% -77% -80% -85% -90%
SI32 37.5/s 44% 23% 20% 18% 13% 1% -- -19% -26% -33% -34% -42% -54% -66% -77% -80% -85% -90%
IS 46.0/s 77% 51% 47% 45% 39% 24% 23% -- -9% -18% -20% -29% -43% -58% -71% -75% -82% -88%
I32S 50.7/s 95% 66% 62% 60% 53% 36% 35% 10% -- -10% -11% -22% -37% -54% -69% -73% -80% -86%
I32A 56.3/s 116% 85% 80% 78% 70% 51% 50% 22% 11% -- -2% -13% -30% -49% -65% -70% -78% -85%
IS_direct 57.2/s 120% 87% 83% 80% 73% 54% 53% 24% 13% 2% -- -12% -29% -48% -65% -69% -78% -85%
IA 64.7/s 149% 112% 107% 104% 95% 74% 73% 41% 28% 15% 13% -- -20% -42% -60% -65% -75% -83%
II 80.8/s 210% 165% 158% 155% 144% 117% 116% 76% 59% 43% 41% 25% -- -27% -50% -57% -68% -78%
I32 111/s 325% 263% 254% 249% 234% 198% 196% 141% 118% 97% 94% 71% 37% -- -31% -41% -57% -71%
SI16 161/s 520% 428% 415% 409% 387% 334% 331% 250% 218% 186% 182% 149% 100% 46% -- -13% -37% -57%
I16A 186/s 615% 510% 495% 487% 462% 401% 397% 305% 267% 231% 226% 188% 130% 68% 15% -- -27% -50%
I16S 256/s 885% 740% 719% 709% 674% 589% 584% 457% 405% 355% 348% 296% 217% 132% 59% 38% -- -32%
I16 375/s 1342% 1130% 1100% 1084% 1033% 910% 902% 716% 640% 566% 556% 480% 365% 239% 133% 102% 46% --
INCREMENT (iterations/sec):
Rate perl_ss perl_ii SI32 SI I32 II SI16 I16
perl_ss 26.2/s -- -5% -12% -15% -64% -65% -77% -90%
perl_ii 27.6/s 5% -- -8% -10% -62% -63% -76% -89%
SI32 30.0/s 14% 9% -- -3% -59% -60% -73% -88%
SI 30.7/s 17% 12% 3% -- -58% -59% -73% -88%
I32 73.1/s 179% 165% 144% 138% -- -2% -35% -71%
II 74.4/s 184% 170% 148% 142% 2% -- -34% -71%
SI16 113/s 330% 310% 277% 267% 54% 52% -- -56%
I16 255/s 871% 824% 750% 728% 248% 242% 126% --
DELETE (iterations/sec):
Rate SA SS IS SI32 SI perl_ss I32S IA perl_ii I32A II I32 SI16 I16A I16S I16
SA 10.9/s -- -6% -18% -25% -25% -27% -32% -32% -36% -41% -58% -76% -83% -84% -86% -93%
SS 11.6/s 6% -- -13% -20% -21% -22% -27% -28% -32% -37% -55% -74% -82% -83% -85% -93%
IS 13.3/s 22% 14% -- -9% -9% -11% -17% -17% -23% -28% -48% -70% -80% -81% -83% -92%
SI32 14.6/s 33% 25% 9% -- -0% -2% -9% -10% -15% -21% -44% -68% -78% -79% -81% -91%
SI 14.6/s 34% 26% 10% 0% -- -2% -8% -9% -15% -21% -43% -68% -78% -79% -81% -91%
perl_ss 14.9/s 36% 28% 12% 2% 2% -- -7% -8% -13% -19% -42% -67% -77% -78% -81% -91%
I32S 16.0/s 46% 37% 20% 10% 9% 7% -- -1% -7% -14% -38% -65% -76% -77% -80% -90%
IA 16.1/s 47% 38% 21% 11% 10% 8% 1% -- -6% -13% -38% -64% -76% -76% -79% -90%
perl_ii 17.2/s 57% 48% 29% 18% 18% 16% 8% 7% -- -7% -33% -62% -74% -75% -78% -90%
lib/Data/HashMap.pm view on Meta::CPAN
SI16 65.9/s 503% 467% 395% 353% 351% 343% 313% 310% 283% 257% 155% 46% -- -3% -15% -60%
I16A 68.3/s 525% 487% 413% 369% 367% 359% 328% 324% 297% 270% 164% 51% 4% -- -12% -58%
I16S 78.0/s 614% 571% 486% 436% 433% 424% 388% 384% 353% 322% 202% 73% 18% 14% -- -53%
I16 165/s 1406% 1315% 1136% 1030% 1025% 1005% 930% 922% 856% 791% 537% 265% 150% 141% 111% --
=head2 LRU / TTL overhead
LRU and TTL add parallel arrays for linked-list pointers and expiry timestamps.
Maps created without these features have zero overhead beyond a never-taken branch.
INSERT, II variant (iterations/sec):
Rate II_lru II_lru_ttl II
II_lru 68.8/s -- -2% -16%
II_lru_ttl 70.4/s 2% -- -14%
II 81.9/s 19% 16% --
LOOKUP, II variant (iterations/sec):
Rate II_lru_ttl II_lru II
II_lru_ttl 68.3/s -- -14% -33%
II_lru 79.3/s 16% -- -23%
II 103/s 50% 29% --
LRU EVICTION CHURN: insert 100k into capacity 50k (iterations/sec):
Rate II_lru_ttl II_lru
II_lru_ttl 61.7/s -- -13%
II_lru 71.0/s 15% --
=head2 Method vs keyword overhead
Keywords bypass Perl's method dispatch for maximum performance. Method calls
(C<< $map->get($key) >>) are convenient but slower:
II variant, 100k operations (iterations/sec):
keyword method overhead
LOOKUP 85.5/s 59.1/s -31%
INSERT 71.1/s 59.0/s -17%
=head1 MEMORY
Memory usage with 1M entries (fork-isolated measurements):
Variant Memory Bytes/entry vs Perl hash
------- ------ ----------- ------------
( run in 1.632 second using v1.01-cache-2.11-cpan-71847e10f99 )