package Storage3;
require Tie::Hash;
our @ISA = 'Tie::StdHash';
sub TIEHASH {
my $storage = bless {}, shift;
$storage
}
sub STORE {
return undef if $_[2] eq 'whatever';
$_[0]{$_[1]} = $_[2]
}
1;
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Indent = 1;
use TM;
use Test::More qw(no_plan);
#== TESTS =====================================================================
use constant DONE => 0;
use TM;
use TM::Literal
require_ok( 'TM::ObjectAble' );
if (1||DONE) {
use TM::Materialized::AsTMa;
my $tm = new TM::Materialized::AsTMa (baseuri => 'tm:',
inline => qq{
xxx (yyy)
zzz (yyy)
aaa (bbb)
})->sync_in;
use Class::Trait;
Class::Trait->apply ($tm, "TM::ObjectAble");
ok (scalar @{ $tm->storages } == 0, 'have no store');
use Test::Exception;
throws_ok {
$tm->objectify ('tm:xxx', "whatever");
} qr/no storage/, 'undefined storage';
my %store1; # a simple hash
push @{ $tm->storages }, \%store1;
ok (scalar @{ $tm->storages } == 1, 'have one store');
my %store2; # a simple hash
push @{ $tm->storages }, \%store2;
ok (scalar @{ $tm->storages } == 2, 'have two stores');
$tm->objectify ('tm:xxx', "whatever");
is ($store1{'tm:xxx'}, 'whatever', 'first store hit');
is ($store2{'tm:xxx'}, undef, 'second store nohit');
my %store0;
tie %store0, 'Storage3';
unshift @{ $tm->storages }, \%store0;
$tm->objectify ('tm:yyy', "whatever");
is ($store0{'tm:yyy'}, undef, '0-store nohit');
is ($store1{'tm:yyy'}, 'whatever', 'first store hit');
is ($store2{'tm:yyy'}, undef, 'second store hit');
$tm->objectify ('tm:zzz', "whoever");
is ($store0{'tm:zzz'}, 'whoever', '0-store hit');
is ($store1{'tm:zzz'}, undef, 'first store nohit');
is ($store2{'tm:zzz'}, undef, 'second store nohit');
# warn Dumper \%store0;
my @os = $tm->object ('tm:xxx', 'tm:zzz', 'tm:uuu', 'tm:yyy');
# warn Dumper \@os;
ok (eq_array (\@os,
[
'whatever',
'whoever',
undef,
'whatever',
]), 'rendering objects');
throws_ok {
$tm->deobjectify ('tm:uuu');
} qr/no storage/, 'undefined storage for deleting';
$tm->deobjectify ('tm:zzz');
# warn Dumper \%store0;
is ($store0{'tm:zzz'}, undef, '0-store nohit');
is ($store1{'tm:zzz'}, undef, 'first store nohit');
is ($store2{'tm:zzz'}, undef, 'second store nohit');
$tm->deobjectify ('tm:xxx');
# warn Dumper \%store0;
is ($store0{'tm:xxx'}, undef, '0-store nohit');
is ($store1{'tm:xxx'}, undef, 'first store nohit');
is ($store2{'tm:xxx'}, undef, 'second store nohit');
}
__END__
if (DONE) {
my $tm = new TM;
use Class::Trait;
Class::Trait->apply ($tm, "TM::IndexAble");
ok ($tm->does ('TM::IndexAble'), 'index trait');
can_ok ($tm, 'index', 'deindex');
};
#$debug = 3; # pins down somewhat the tree structure
if (DONE) { # lazy index, built by use, purely functional test
my $taxo = mk_taxo (3, 2, 3);
# my $taxo = mk_taxo (1, 1, 1);
my $tm = new TM;
implant ($tm, $taxo);
verify ($tm, $taxo, 0); # functional test without cache
Class::Trait->apply ($tm, "TM::IndexAble");
my %s = $tm->index ({ axis => 'taxo' });
# warn Dumper \%s;
ok (eq_set ([ qw(superclass.type class.type instance.type subclass.type) ],
[ keys %s ]), 'axes');
map { ok ($_->{hits} == 0, 'stat hits')
&& ok ($_->{requests} == 0, 'stat requests') }
values %s;
verify ($tm, $taxo, 0); # non-silent mode
verify ($tm, $taxo, 0); # non-silent mode, here everything must be already cached
%s = $tm->index;
#warn Dumper \%s; exit;
ok ($s{'instance.type'}->{hits} > 0, 'instance.type hits');
ok ($s{'instance.type'}->{requests} > 0, 'instance.type requests');
ok ($s{'subclass.type'}->{hits} > 0, 'subclass.type hits');
ok ($s{'subclass.type'}->{requests} > 0, 'subclass.type requests');
# warn Dumper \%s;
}
sub _speedo {
my $tm = shift;
my $taxo = shift;
# diag ('speed testing for taxonomy ...');
my $start = Time::HiRes::time;
verify ($tm, $taxo, 1) for 1..5; # speed test
# diag ('... done');
return (Time::HiRes::time - $start);
}
#-- taxo axes
if (DONE) { # lazy index, built by use
my $taxo = mk_taxo (INTENSITY, 2, 2);
my $tm = new TM;
implant ($tm, $taxo);
my $unindexed = _speedo ($tm, $taxo);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'taxo', closed => 0}); # make a cache on that axes
my $cached = _speedo ($tm, $taxo);
ok ($unindexed / $cached > 2, "measurable speedup (taxo) with lazy index ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
# warn Dumper $tm->index;
}
if (DONE) { # eager index
my $taxo = mk_taxo (INTENSITY, 3, 3);
my $tm = new TM;
implant ($tm, $taxo);
my $unindexed = _speedo ($tm, $taxo);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'taxo', closed => 1});
my $indexed = _speedo ($tm, $taxo);
ok ($unindexed / $indexed > 2, "measurable speedup (taxo) with eager index ? ($indexed < $unindexed, ".(sprintf "%.2f", $unindexed/$indexed).")");
}
#-- char axes
sub _speedo_char {
my $tm = shift;
my $taxo = shift;
my $start = Time::HiRes::time;
(_verify_chars ($tm, $taxo, 1) ) for 1..5;
# (_verify_chars ($tm, $taxo, 1) || diag ('... run')) for 1..5;
return (Time::HiRes::time - $start);
}
if (DONE) { # lazy first
my $taxo = mk_taxo (INTENSITY, 3, 3);
my $tm = new TM;
implant ($tm, $taxo);
_verify_chars ($tm, $taxo, 0);
my $unindexed = _speedo_char ($tm, $taxo);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'char', closed => 0}); # make a cache on that axes
my $cached = _speedo_char ($tm, $taxo);
ok ($unindexed / $cached > 2, "measurable speedup (char) with lazy index ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
# warn Dumper $tm->index;
}
if (DONE) { # eager
my $taxo = mk_taxo (INTENSITY, 3, 3);
my $tm = new TM;
implant ($tm, $taxo);
_verify_chars ($tm, $taxo, 0);
my $unindexed = _speedo_char ($tm, $taxo);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'char', closed => 1 }); # make a cache on that axes
my $cached = _speedo_char ($tm, $taxo);
ok ($unindexed / $cached > 2, "measurable speedup (char) with lazy index ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
# warn Dumper $tm->index;
}
#-- reification axes
sub _speedo_reif {
my $tm = shift;
my $taxo = shift;
my $start = Time::HiRes::time;
(_verify_reif ($tm, $taxo, 1)) for 1..5;
# (_verify_reif ($tm, $taxo, 1) || diag ('... run')) for 1..5;
return (Time::HiRes::time - $start);
}
if (DONE) {
my $taxo = mk_taxo (INTENSITY, 3, 3);
my $tm = new TM;
implant ($tm, $taxo);
my @as = $tm->match_forall (char => 1, type => 'occurrence'); # need to have this outside the speed test
_verify_reif ($tm, \@as, 0);
my $unindexed = _speedo_reif ($tm, \@as);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'reify', closed => 0}); # make a cache on that axes
_verify_reif ($tm, \@as, 0);
my $cached = _speedo_reif ($tm, \@as);
ok ($unindexed / $cached > 2, "measurable speedup (reify) with lazy index ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
}
if (DONE) {
my $taxo = mk_taxo (INTENSITY, 3, 3);
my $tm = new TM;
implant ($tm, $taxo);
my @as = $tm->match_forall (char => 1, type => 'occurrence'); # need to have this outside the speed test
my $unindexed = _speedo_reif ($tm, \@as);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'reify', closed => 1});
my $cached = _speedo_reif ($tm, \@as);
ok ($unindexed / $cached > 2, "measurable speedup (reify) with lazy index ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
}
sub _mktmp {
my $tmp;
use IO::File;
use POSIX qw(tmpnam);
do { $tmp = tmpnam() ; } until IO::File->new ($tmp, O_RDWR|O_CREAT|O_EXCL);
return $tmp;
}
if (DONE) { # does it work together with a MLDBM backed map?
my $tmp = _mktmp;
my $taxo = mk_taxo (INTENSITY, 2, 3);
use TM::ResourceAble::MLDBM;
my $tm = new TM::ResourceAble::MLDBM (file => $tmp);
# diag ('populate map...');
implant ($tm, $taxo);
# diag ('... done');
my @as = $tm->match_forall (char => 1, type => 'occurrence'); # need to have this outside the speed test
my $unindexed = _speedo_reif ($tm, \@as);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'reify', closed => 0}); # make a cache on that axes
_verify_reif ($tm, \@as, 0);
# warn Dumper $TM::IndexAble::index;exit;
my %s = $tm->index; # let's see what we have
my $reqs = $s{reify}->{requests};
is ($s{reify}->{hits}, 0, 'open => no hits');
my $cached = _speedo_reif ($tm, \@as);
%s = $tm->index; # let's see what we have
is ($s{reify}->{requests} - $s{reify}->{hits}, $reqs, 'open, but fully cached => hits');
diag ($unindexed / $cached > 2, "no measurable speedup (reify) with lazy index + MLDBM ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
unlink <"$tmp*">;
}
if (DONE) { # MLDBM + detached
my $tmp = _mktmp;
my $taxo = mk_taxo (INTENSITY, 2, 3);
use TM::ResourceAble::MLDBM;
my $tm = new TM::ResourceAble::MLDBM (file => $tmp);
# diag ('populate map...');
implant ($tm, $taxo);
# diag ('... done');
my @as = $tm->match_forall (char => 1, type => 'occurrence'); # need to have this outside the speed test
my $unindexed = _speedo_reif ($tm, \@as);
Class::Trait->apply ($tm, "TM::IndexAble");
$tm->index ({ axis => 'reify', closed => 0, detached => {} });
_verify_reif ($tm, \@as, 0);
my %s = $tm->index; # let's see what we have
#warn " after one run ".Dumper \%s;
my $reqs = $s{reify}->{requests};
is ($s{reify}->{hits}, 0, 'open => no hits');
my $cached = _speedo_reif ($tm, \@as);
%s = $tm->index; # let's see what we have
#warn " after second run ".Dumper \%s;
is ($s{reify}->{requests} - $s{reify}->{hits}, $reqs, 'open, but fully cached => hits');
ok ($unindexed / $cached > 2, "measurable speedup (reify) with lazy,detached index + MLDBM ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
$tm->deindex ('reify');
%s = $tm->index;
ok (! $s{reify}, 'no more reification axis index');
$tm->index ({ axis => 'reify', closed => 1, detached => {} });
# warn Dumper $TM::IndexAble::cachesets{ $tm->{index}->{reify} }; exit;
$cached = _speedo_reif ($tm, \@as);
ok ($unindexed / $cached > 2, "measurable speedup (reify) with eager,detached index + MLDBM ? ($cached < $unindexed, ".(sprintf "%.2f", $unindexed/$cached).")");
%s = $tm->index; # let's see what we have
is ($s{reify}->{requests}, $s{reify}->{hits}, 'closed & each exactly once => reqs == hits');
$tm->deindex ('reify');
# warn Dumper \%TM::IndexAble::cachesets;
ok (! keys %TM::IndexAble::cachesets, 'no more detached' );
%s = $tm->index;
ok (! $s{reify}, 'no more reification axis index');
unlink <"$tmp*">;
}
#sleep 60;
__END__
#-- persistent indices
_mktmps;
#warn Dumper \@tmp;
END { map { unlink <$_*> } @tmp; };
if (DONE) {
use BerkeleyDB ;
use MLDBM qw(BerkeleyDB::Hash) ;
use Fcntl;
my $taxo = mk_taxo (4, 3, 3);
my $unindexed;
{
my %cache;
tie %cache, 'MLDBM', -Filename => $tmp[0], -Flags => DB_CREATE
or die ( "Cannot create DBM file '$tmp[0]: $!");
my $tm = new TM;
implant ($tm, $taxo);
my $idx = new TM::Index::Match ($tm, cache => \%cache);
# warn "\n# verifying first run, should be medium fast";
my $start = Time::HiRes::time;
verify ($tm, $taxo, 1);
$unindexed = (Time::HiRes::time - $start);
# warn "# ====== total time =============== ".(Time::HiRes::time - $start);
# warn "# verifying second run, should be faster";
$start = Time::HiRes::time;
verify ($tm, $taxo, 1);
my $indexed = (Time::HiRes::time - $start);
ok ($indexed < $unindexed, "measurable speedup with persistent index ($indexed < $unindexed)");
# warn "# ====== total time =============== ".(Time::HiRes::time - $start);
untie %cache;
}
{
my %cache;
tie %cache, 'MLDBM', -Filename => $tmp[0], -Flags => DB_CREATE
or die ( "Cannot open DBM file '$tmp[0]: $!");
# warn Dumper \%cache; exit;
my $tm = new TM;
implant ($tm, $taxo);
my $idx = new TM::Index::Match ($tm, cache => \%cache);
# warn "\n# re-verifying second run, should be as fast";
my $start = Time::HiRes::time;
verify ($tm, $taxo, 1);
my $indexed = (Time::HiRes::time - $start);
ok ($indexed < $unindexed, "measurable speedup with persistent index ($indexed < $unindexed)");
# warn "# ====== total time =============== ".(Time::HiRes::time - $start);
untie %cache;
}
}
__END__