Subject: | Moose performance issue in delete method of Native Hash traits |
Date: | Thu, 1 Aug 2019 14:39:58 +0000 |
To: | "bug-Moose [...] rt.cpan.org" <bug-Moose [...] rt.cpan.org> |
From: | "De Carpenter, Niels" <niels.de.carpenter [...] intl.att.com> |
Hi,
We encountered a serious performance degradation when we upgraded from Moose 2.12.13 to 2.2011.
The issue turned out to be cause by very bad performance of the delete method when using Native Hash traits.
The issue is in "lib64/perl5/Moose/Meta/Method/Accessor/Native/Hash/delete.pm", specifically:
# There are no new members so we don't need to coerce new values (none exist)
# and we always want to check the new (empty) hash as a whole.
sub _inline_coerce_new_values { '' }
sub _check_new_members_only { 0 }
This causes all objects in the hash to be checked when deleting a single entry from it.
For very large hashes this has a serious performance impact.
The comment that the new hash will be empty is wrong of course.
Setting _check_new_members_only to 1 fixes the issue.
A small script to reproduce the issue:
#!/usr/bin/perl
package TEST::Entry;
use strict;
use warnings FATAL => 'all';
use Moose;
use namespace::autoclean;
has 'name' => ( # To help identify what the data is while debugging
is => 'ro',
isa => 'Str',
required => '1',
);
has 'value' => (
is => 'ro',
isa => 'Int',
required => '1',
);
__PACKAGE__->meta->make_immutable;
1;
package TEST::HashRef;
use strict;
use warnings FATAL => 'all';
use Moose;
use namespace::autoclean;
has '_entries' => (
traits => ['Hash'],
is => 'ro',
isa => 'HashRef[TEST::Entry]',
default => sub { {} },
handles => {
keys => 'keys',
add => 'set',
get => 'get',
exists => 'exists',
del => 'delete',
count => 'count',
},
);
__PACKAGE__->meta->make_immutable;
package main;
use Time::HiRes qw(gettimeofday tv_interval);
print "OK\n";
my $hash = TEST::HashRef->new();
test(100000);
sub test {
my $count = shift;
my $string = sprintf("test-%s",$count);
print "Running $string\n";
my $name;
for (my $i=1; $i <= $count; $i++) {
$name = sprintf("%s-%s",$string, $i);
$hash->add( $name, TEST::Entry->new( { name => $name, value => '1' } ) );
}
my $t0 = [gettimeofday];
$hash->del($name);
my $e0 = tv_interval( $t0, [gettimeofday] );
my $cnt = $hash->count;
printf( "$string delete of last entry took %s, there are now %d entries in hash\n", $e0,$cnt );
}
1;
Regards,
Niels de Carpentier
DevOps Engineer
Integrator Solutions - Systems and Tools Center of Excellence
AT&T Global Business
Message body is not shown because it is too large.