Subject: | Enhancement proposal |
Hi,
I've worked on a reimplementation of this module for use in my company.
Here is the version I ended with, that is backward compatible, contains
the existing features, add 1 new feature, has the same code size, but is
(sometimes really) faster.
Attached are :
- My implementation (file Check.pm)
- The patch agains 0.26 version (file faster_reimpl.patch)
- A benchmark file (file benchmark.pl)
- The benchmark result, that I have commented (file result.txt)
The new feature is that if you ask the template to store the value into
a reference on a hash or array, then it's stored directyl in the array
or hash. Thus you can write :
my @array;
check({ foo => { default => [], store => \@array} }, { foo => [ 42 ] }
# @array is ( 42 );
Enjoy ! :)
dams
Subject: | result.txt |
*** store2
Rate orig new
orig 28169/s -- -18%
new 34483/s 22% --
*** subs1
Rate orig new
orig 25316/s -- -37%
new 40000/s 58% --
# no difference, same code is run
*** no_override
Rate orig new
orig 16129/s -- -8%
new 17544/s 9% --
# first run is valid arg, second is broken arg
# new code is optimized for valid args
*** strict2
Rate orig new
orig 27397/s -- -40%
new 45455/s 66% --
Rate orig new
orig 19231/s -- -13%
new 21978/s 14% --
*** edge_case
Rate orig new
orig 37975/s -- -35%
new 58824/s 55% --
*** preserver_case2
Rate orig new
orig 15385/s -- -11%
new 17241/s 12% --
# first run is valid arg, second is broken arg
# new code is optimized for valid args
*** required
Rate orig new
orig 29851/s -- -40%
new 50000/s 67% --
Rate orig new
orig 21739/s -- -4%
new 22727/s 5% --
# first run is valid arg, second is broken arg
# new code is optimized for valid args
*** strict1
Rate orig new
orig 26316/s -- -38%
new 42553/s 62% --
Rate orig new
orig 19048/s -- -12%
new 21739/s 14% --
*** subs3
Rate orig new
orig 25641/s -- -40%
new 42553/s 66% --
*** unknown1
Rate orig new
orig 18018/s -- -19%
new 22222/s 23% --
*** r1
Rate orig new
orig 6061/s -- -36%
new 9434/s 56% --
*** real_life2
Rate orig new
orig 5848/s -- -37%
new 9259/s 58% --
# first run is valid arg, second is broken arg
# new code is optimized for valid args
*** define1
Rate orig new
orig 27778/s -- -39%
new 45455/s 64% --
Rate orig new
orig 21053/s -- -14%
new 24390/s 16% --
*** real_life
Rate orig new
orig 6993/s -- -14%
new 8130/s 16% --
*** invalid_store
Rate orig new
orig 43103/s -- -34%
new 64935/s 51% --
# they are all failing test, so not that much diff
*** invalid_key_tests
Rate orig new
orig 14493/s -- -16%
new 17241/s 19% --
Rate orig new
orig 14493/s -- -12%
new 16393/s 13% --
Rate orig new
orig 14085/s -- -13%
new 16129/s 15% --
Rate orig new
orig 13699/s -- -12%
new 15625/s 14% --
# last case is broken arg
# new code is optimized for valid args
*** default
Rate orig new
orig 38961/s -- -36%
new 61224/s 57% --
Rate orig new
orig 29412/s -- -40%
new 49180/s 67% --
Rate orig new
orig 28302/s -- -24%
new 37037/s 31% --
Rate orig new
orig 15873/s -- -14%
new 18519/s 17% --
*** big_template
Rate orig new
orig 5348/s -- -22%
new 6897/s 29% --
# Huge gain here, with ALLOW_UNKNOWN = 1
*** unknown2
Rate orig new
orig 35088/s -- -47%
new 66667/s 90% --
*** store1
Rate orig new
orig 28571/s -- -11%
new 32258/s 13% --
*** simple
Rate orig new
orig 30612/s -- -38%
new 49180/s 61% --
*** r2
Rate orig new
orig 5814/s -- -38%
new 9346/s 61% --
*** preserver_case1
Rate orig new
orig 32258/s -- -31%
new 46512/s 44% --
# first run is valid arg, second is broken arg
# new code is optimized for valid args
*** define2
Rate orig new
orig 28571/s -- -39%
new 46512/s 63% --
Rate orig new
orig 21739/s -- -14%
new 25316/s 16% --
*** subs2
Rate orig new
orig 25316/s -- -38%
new 40816/s 61% --
Subject: | benchmark.pl |
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw(cmpthese);
use lib qw(/home/dams/scratchpad/Params-Check-0.26/lib);
use Params::Check;
use Params::Check2;
my %tests =
(
real_life => {
count => 10000,
code_in_func => 'my $foo1; my $foo2; my $foo3',
template => q({
arg1 => { defined => 1, strict_type => 1, default => '' },
arg2 => { default => 'bar', store => \$foo1 },
arg3 => { default => 'mod', store => \$foo2 },
arg4 => { defined => 1, strict_type => 1, default => [], store => \$foo3 },
arg5 => { default => {}, strict_type => 1 },
arg6 => { default => 'a', allow => [ qw(b a c d) ], strict_type => 1},
}),
params => [q({
arg1 => 'foo',
arg2 => 'moz',
arg4 => [ 'answer', 42 ],
arg5 => { answer => 42 },
arg6 => 'c',
})],
},
real_life2 => {
count => 10000,
code => '$Params::Check::PRESERVE_CASE= 0; $Params::Check::ALLOW_UNKNOWN = 1; $Params::Check::SANITY_CHECK_TEMPLATE = 0; ',
template => q({
arg1 => { defined => 1, strict_type => 1, default => ''},
arg2 => { default => 'bar'},
arg3 => { default => 'mod'},
arg4 => { defined => 1, strict_type => 1, default => ''},
arg5 => { default => 'bar'},
arg6 => { default => 'mod'},
arg7 => { defined => 1, strict_type => 1, default => ''},
arg8 => { default => 'bar'},
arg9 => { default => 'mod'},
}),
params => [q({
arg1 => 'foo',
arg2 => 'moz',
arg3 => 'answer',
arg4 => 'foo',
arg5 => 'moz',
arg6 => 'answer',
arg7 => 'foo',
arg8 => 'moz',
arg9 => 'answer',
})],
},
r1 => {
count => 10000,
template => q({
arg1 => { defined => 1, strict_type => 1, default => ''},
arg2 => { default => 'bar'},
arg3 => { default => 'mod'},
arg4 => { defined => 1, strict_type => 1, default => ''},
arg5 => { default => 'bar'},
arg6 => { default => 'mod'},
arg7 => { defined => 1, strict_type => 1, default => ''},
arg8 => { default => 'bar'},
arg9 => { default => 'mod'},
}),
params => [q({
arg1 => 'foo',
arg2 => 'moz',
arg3 => 'answer',
arg4 => 'foo',
arg5 => 'moz',
arg6 => 'answer',
arg7 => 'foo',
arg8 => 'moz',
arg9 => 'answer',
})],
},
r2 => {
count => 10000,
code => '$Params::Check::SANITY_CHECK_TEMPLATE = 0; ',
template => q({
arg1 => { defined => 1, strict_type => 1, default => ''},
arg2 => { default => 'bar'},
arg3 => { default => 'mod'},
arg4 => { defined => 1, strict_type => 1, default => ''},
arg5 => { default => 'bar'},
arg6 => { default => 'mod'},
arg7 => { defined => 1, strict_type => 1, default => ''},
arg8 => { default => 'bar'},
arg9 => { default => 'mod'},
}),
params => [q({
arg1 => 'foo',
arg2 => 'moz',
arg3 => 'answer',
arg4 => 'foo',
arg5 => 'moz',
arg6 => 'answer',
arg7 => 'foo',
arg8 => 'moz',
arg9 => 'answer',
})],
},
simple => {
count => 30000,
template => '{
arg1 => {},
}',
params => ['{
arg1 => 1,
}'],
},
default => {
count => 30000,
template => '{
foo => { default => 1 }
}',
params => [
'{}',
'{ foo => 2}',
'{ FOO => 2}',
'{ -foo => 2}',
],
},
no_override => {
count => 10000,
template => '{
foo => { no_override => 1, default => 42 }
}',
params => [
'{ foo => 13 }',
],
},
required => {
count => 20000,
template => '{
foo => { required => 1 }
}',
params => [
'{ foo => 42 }',
'{ }',
],
},
invalid_key_tests => {
count => 10000,
template => '{
foo => { allow => sub { 0 } }
}',
params => [
'{ foo => 1 }',
'{ foo => "foo" }',
'{ foo => [] }',
'{ foo => bless({},__PACKAGE__) }',
],
},
invalid_store => {
count => 50000,
template => q({
foo => { store => '' }
}),
params => [
'{ }',
],
},
edge_case => {
count => 30000,
template => q({
foo => { default => '' }
}),
params => [
'{ }',
],
},
big_template => {
count => 10000,
template => q({
firstname => { required => 1, defined => 1 },
lastname => { required => 1, store => \$lastname },
gender => { required => 1,
allow => [qr/M/i, qr/F/i],
},
married => { allow => [0,1] },
age => { default => 21,
allow => qr/^\d+$/,
},
id_list => { default => [],
strict_type => 1
},
phone => { allow => sub { 1 if +shift } },
bureau => { default => 'NSA',
no_override => 1
},
}),
params => [
q({
firstname => 'joe',
lastname => 'jackson',
gender => 'M',
married => 1,
age => 21,
id_list => [1..3],
phone => '555-8844',
}),
],
},
preserver_case1 => {
count => 20000,
code => '$Params::Check::PRESERVE_CASE = 1',
template => q({
Foo => { default => 1 }
}),
params => [
'{ Foo => 42 }',
],
},
preserver_case2 => {
count => 20000,
code => '$Params::Check::PRESERVE_CASE = 0',
template => q({
Foo => { default => 1 }
}),
params => [
'{ Foo => 42 }',
],
},
unknown1 => {
count => 20000,
template => q({ }),
params => [
'{ foo => 42 }',
],
},
unknown2 => {
count => 20000,
code => '$Params::Check::ALLOW_UNKNOWN = 1',
template => q({ }),
params => [
'{ foo => 42 }',
],
},
store1 => {
count => 20000,
code => '$Params::Check::NO_DUPLICATES = 1; my $foo;',
template => q({ foo => { store => \$foo } }),
params => [
'{ foo => 42 }',
],
},
store2 => {
count => 20000,
code => '$Params::Check::NO_DUPLICATES = 0; my $foo;',
template => q({ foo => { store => \$foo } }),
params => [
'{ foo => 42 }',
],
},
strict1 => {
count => 20000,
code => '$Params::Check::STRICT_TYPE = 0',
template => q({ foo => { strict_type => 1, default => [] } }),
params => [
'{ foo => [] }',
'{ foo => {} }',
],
},
strict2 => {
count => 20000,
code => '$Params::Check::STRICT_TYPE = 1',
template => q({ foo => { default => [] } }),
params => [
'{ foo => [] }',
'{ foo => {} }',
],
},
define1 => {
count => 20000,
code => '$Params::Check::ONLY_ALLOW_DEFINED = 0',
template => q({ foo => { defined => 1, default => 1 } }),
params => [
'{ foo => 42 }',
'{ foo => undef }',
],
},
define2 => {
count => 20000,
code => '$Params::Check::ONLY_ALLOW_DEFINED = 1',
template => q({ foo => { default => 1 } }),
params => [
'{ foo => 42 }',
'{ foo => undef }',
],
},
subs1 => {
count => 20000,
template => q({ foo => { allow => sub { [] } } }),
params => [
'{ foo => [] }',
],
},
subs2 => {
count => 20000,
template => q({ foo => { allow => sub { {} } } }),
params => [
'{ foo => {} }',
],
},
subs3 => {
count => 20000,
template => q({ foo => { allow => sub { 1 } } }),
params => [
'{ foo => 1 }',
],
},
);
#my @keep = qw(store1 store2);
#my @keep = qw(r1 r2);
#my @keep = qw(real_life real_life2);
#if (@keep) {
# my %f;
# @f{@keep} = @tests{@keep};
# %tests = %f;
#}
while (my ($name, $data) = each(%tests)) {
my ($count, $template, $params, $code, $code_in_func) = @{$data}{qw(count template params code code_in_func)};
local $Params::Check::VERBOSE;
local $Params::Check::NO_DUPLICATES;
local $Params::Check::STRIP_LEADING_DASHES;
local $Params::Check::STRICT_TYPE;
local $Params::Check::ALLOW_UNKNOWN;
local $Params::Check::PRESERVE_CASE;
local $Params::Check::ONLY_ALLOW_DEFINED;
local $Params::Check::SANITY_CHECK_TEMPLATE;
local $Params::Check::WARNINGS_FATAL;
local $Params::Check::CALLER_DEPTH;
local $Params::Check2::VERBOSE;
local $Params::Check2::NO_DUPLICATES;
local $Params::Check2::STRIP_LEADING_DASHES;
local $Params::Check2::STRICT_TYPE;
local $Params::Check2::ALLOW_UNKNOWN;
local $Params::Check2::PRESERVE_CASE;
local $Params::Check2::ONLY_ALLOW_DEFINED;
local $Params::Check2::SANITY_CHECK_TEMPLATE;
local $Params::Check2::WARNINGS_FATAL;
local $Params::Check2::CALLER_DEPTH;
$code_in_func ||= '';
$code ||= '';
my $code_original = $code;
my $code_new = $code;
my $code_new3 = $code;
$code_new =~ s/Check/Check2/g;
$code_new3 =~ s/Check/Check3/g;
print "*** $name\n";
my $i = 0;
foreach (@$params) {
local $SIG{__WARN__} = sub {};
cmpthese($count, {
orig => qq( $code_original; foo($_); sub foo { $code_in_func; Params::Check::check($template, \$_[0]) } ),
new => qq( $code_new; foo($_); sub foo { $code_in_func; Params::Check2::check($template, \$_[0]) } ),
});
}
print "\n";
}
Subject: | faster_reimpl.patch |
Message body is not shown because it is too large.
Subject: | Check.pm |
Message body is not shown because it is too large.