I wrote a benchmark to compare 3 caching methods (Cache::FileCache, Cache::MemoryCache, and a simple memory cache from here to cache response from a select using a primary key from a MySQL database.
I was a little surprised to find no speed improvement with Cache::FileCache and Cache::MemoryCache, and was also surprised at how well Randall's hash worked.
Results:
FileCache: 21 wallclock secs (18.24 usr + 1.79 sys = 20.03 CPU) @ 499.25/s (n=10000)
MemoryCache: 9 wallclock secs ( 8.97 usr + 0.02 sys = 8.99 CPU) @ 1112.35/s (n=10000)
SimpleCache: 0 wallclock secs ( 0.41 usr + 0.01 sys = 0.42 CPU) @ 23809.52/s (n=10000)
raw: 11 wallclock secs ( 6.14 usr + 0.64 sys = 6.78 CPU) @ 1474.93/s (n=10000)
Rate FileCache MemoryCache raw SimpleCache
FileCache 499/s -- -55% -66% -98%
MemoryCache 1112/s 123% -- -25% -95%
raw 1475/s 195% 33% -- -94%
SimpleCache 23810/s 4669% 2040% 1514% --
Here is the code:
use strict;
use warnings;
my $DB = "test";
my $USER = "dbusername";
my $PASS = "dbpassword";
my $DEBUG = 0;
my $BENCHMARK_LOOP_LIMIT = 10_000;
my $EXPIRATION_TIME = 60;
use Data::Dumper;
use DBI;
my $dbh = DBI->connect("dbi:mysql:$DB", $USER, $PASS, {RaiseError => 1
+})
|| die "Cannot connect to db";
### Prepare caches
use Cache::FileCache;
my $fc = Cache::FileCache->new({default_expires_in => $EXPIRATION_TIME
+});
$fc->Clear;
use Cache::MemoryCache;
my $mc = Cache::MemoryCache->new({default_expires_in => $EXPIRATION_TI
+ME});
$mc->Clear; # Superfluous
my %cache;
my $flush_interval = 10;
my $flush_time = time + $flush_interval;
use Benchmark qw( timethese cmpthese );
my $results = timethese
($BENCHMARK_LOOP_LIMIT,
{
raw => sub {my $r = raw_load_from_cachetest(5000 + int(ran
+d(30)));},
FileCache => sub {my $r = fc_load_from_cachetest(5000 + int(rand
+(30)));},
MemoryCache => sub {my $r = mc_load_from_cachetest(5000 + int(rand
+(30)));},
SimpleCache => sub {my $r = sc_load_from_cachetest(5000 + int(rand
+(30)));},
},
);
cmpthese($results);
# Load straight from database
sub raw_load_from_cachetest {
my ($id) = @_;
my $sql = qq[select * from cachetest where id=?];
my $sth = $dbh->prepare($sql);
$sth->execute($id);
my $result = $sth->fetchrow_hashref || return;
return $result;
}
# Cached load using Cache::FileCache
sub fc_load_from_cachetest {
my ($id) = @_;
my $result;
my $key = "cachetest$id";
$result = $fc->get($key) || do {
$result = raw_load_from_cachetest($id);
$fc->set($key, $result);
$result
};
return $result;
}
# Cached load using Cache::MemoryCache
sub mc_load_from_cachetest {
my ($id) = @_;
my $result;
my $key = "cachetest$id";
$result = $mc->get($key) || do {
$result = raw_load_from_cachetest($id);
$mc->set($key, $result);
$result
};
return $result;
};
# Cached load using simple cache found at:
sub sc_load_from_cachetest {
my ($id) = @_;
($flush_time, %cache) = time + $flush_interval if $flush_time < time
+;
$cache{$id} ||= raw_load_from_cachetest($id);
}
The database table structure looks like this:
create table cachetest (
id int default '0' not null auto_increment,
a int default '0' not null,
b int default '0' not null,
c int default '0' not null,
d int default '0' not null,
e int default '0' not null,
f int default '0' not null,
g int default '0' not null,
primary key(id)
);
and I pre-filled the table with 20,000 rows of pseudo-random data using
use strict;
use warnings;
### Begin configuration
my $DSN = "dbi:mysql:test";
my $USER = "dbusername";
my $PASS = "dbpassword";
### End configuration
use DBI;
my $dbh = DBI->connect($DSN, $USER, $PASS, {RaiseError => 1})
|| die "Cannot connect to db";
$dbh->do(qq[delete from cachetest]);
for(my $i=1;$i<=20000; $i++) {
$dbh->do(qq[insert into cachetest (a,b,c,d,e,f,g) values (?,?,?,?,?,
+?,?)],
undef,
rand(1_000_000),
rand(1_000_000),
rand(1_000_000),
rand(1_000_000),
rand(1_000_000),
rand(1_000_000),
rand(1_000_000),
);
Edited:
~Tue Jul 23 21:28:45 2002 (GMT),
by footpad: Added <READMORE> tag. Per Consideration.