#!/usr/bin/perl use strict; use warnings; use Time::HiRes qw(time clock); use Try::Tiny; use Test::More; use List::Util qw(shuffle); BEGIN { try { require Cache::Ref::FIFO; require Cache::Ref::LRU; require Cache::Ref::CART; require Cache::Ref::CLOCK; } catch { if ( m{^Can't locate Cache/Ref/(?:FIFO|LRU)\.pm} ) { plan skip_all => "Cache::Ref::FIFO and Cache::Ref::LRU required"; } else { die $_; } }; } use ok 'Cache::Profile'; use ok 'Cache::Profile::CorrelateMissTiming'; use ok 'Cache::Profile::Compare'; # this uses a weighted key set my @keys = shuffle( ( map { 1 .. $_ } 1 .. 25 ), 1 .. 200 ); my %seen; my $sigma = grep { !$seen{$_}++ } @keys; my $size = 20; my $p_fifo = Cache::Profile->new( cache => Cache::Ref::FIFO->new( size => $size ), ); $p_fifo->set( foo => "bar" ); is( $p_fifo->get("foo"), "bar", "simple set/get" ); is( $p_fifo->hit_count, 1, "hit count" ); is( $p_fifo->miss_count, 0, "miss count" ); $p_fifo->reset; is( $p_fifo->get("bar"), undef, "cache miss" ); is( $p_fifo->hit_count, 0, "hit count" ); is( $p_fifo->miss_count, 1, "miss count" ); $p_fifo->reset; my $p_lru = Cache::Profile::CorrelateMissTiming->new( cache => Cache::Ref::LRU->new( size => $size ), ); my $p_cart = Cache::Profile->new( cache => Cache::Ref::CART->new( size => $size ), ); my @more = ( Cache::Ref::CLOCK->new( size => $size ) ); try { require CHI; #push @more, CHI->new( driver => 'Memory', datastore => {}, max_size => $size ); push @more, CHI->new( driver => 'Memory', datastore => {} ); # max size seems broken }; try { require Cache::FastMmap; push @more, Cache::FastMmap->new(cache_size => '1k'); }; try { require Cache::Bounded; push @more, Cache::Bounded->new({ interval => 5, size => $size }); }; try { require Cache::MemoryCache; push @more, Cache::MemoryCache->new(); }; my $cmp = Cache::Profile::Compare->new( caches => \@more ); my ( $get, $set ) = ( 0, 0 ); my $start = clock(); my $end = $start + 0.3 * ( 3 + @more ); sub _waste_time { my @foo; push @foo, [ 1 .. 100 ] for 1 .. 20; } my $i; until ( (clock() > $end) and $i > @keys * 3 ) { pass("making progress") if $get % ( @keys / 3 ) == 0; # looks better when the numbers are moving ;-) my $key = $keys[rand > 0.7 ? int rand @keys : $i++ % @keys]; foreach my $cache ( $p_fifo, $p_lru, $cmp ) { if ( rand > 0.5 ) { unless ( $cache->get($key) ) { _waste_time(); $cache->set( $key => rand > 0.5 ? { foo => "bar", data => [ 1 .. 10 ] } : "blahblah" ); } } else { $cache->compute( $key, sub { _waste_time(); return rand > 0.5 ? { foo => "bar", data => [ 1 .. 10 ] } : "blahblah"; }); } } $get++; $p_cart->compute( $key, sub { $set++; _waste_time(); return rand > 0.5 ? { foo => "bar", data => [ 1 .. 10 ] } : "blahblah"; }); } is( $p_cart->call_count_get, $get, "get count" ); is( $p_cart->call_count_set, $set, "set count" ); is( $p_cart->query_count, $p_fifo->call_count_get, "no multi key queries" ); my $report = $p_cart->report; like( $report, qr/hit rate/i, "report contains 'hit rate'" ); like( $report, qr/${\ $p_cart->hit_count }/, "contains hit count" ); like( $report, qr/${\ $p_cart->query_count }/, "contains query count" ); foreach my $cache ( $p_cart, $p_lru, $cmp->profiles ) { my $hit; foreach my $key ( @keys ) { if ( defined $cache->get($key) ) { $hit++; last; } } ok($hit, "at least one key in cache (" . $cache->moniker . ")"); cmp_ok( $p_cart->hit_rate, '>=', ( ( $size / @keys ) / 2), "hit rate bigger than minimum" ); foreach my $method qw(get set miss) { cmp_ok( $cache->${\"call_count_$method"}, '>=', $sigma, "$method called enough times" ); foreach my $measure qw(real cpu) { cmp_ok( $cache->${\"total_${measure}_time_${method}"}, '>=', 0.001, "some $measure time accrued for $method" ); } } } cmp_ok( $p_cart->hit_rate, '>', $p_lru->hit_rate, "CART beats LRU" ); cmp_ok( $p_lru->hit_rate, '>', $p_fifo->hit_rate, "LRU beats FIFO" ); $p_lru->set(foo => 42); $p_lru->clear; is( $p_lru->get("foo"), undef ); done_testing; # ex: set sw=4 et: