Perl における Prepared Statement のベンチマーク
MySQL と SQLite について測定してみた。「np」がプリペアードステートメントを使わない場合、「p」が使った場合。「;srv」というのはサーバサイドプリペアードステートメントを有効にした場合。
$ ./dbi-prepared-bench.pl Rate sqlite:np mysql:np mysql:p;srv sqlite:p mysql:p sqlite:np 7519/s -- -28% -50% -51% -57% mysql:np 10417/s 39% -- -31% -32% -41% mysql:p;srv 15152/s 102% 45% -- -2% -14% sqlite:p 15385/s 105% 48% 2% -- -12% mysql:p 17544/s 133% 68% 16% 14% --
全般的にプリペアードステートメントを使って DBI::st オブジェクトをキャッシュした方が速い。DBD::mysql で (SQL の構文解析結果全体をキャッシュしうる) サーバサイドプリペアードステートメントを有効にした場合よりも、クライアントサイド版の方が速いというのはおもしろい。サーバと交換するパケットの量が増えるのかな。
以下、ソースコード全掲。
#! /usr/bin/perl use strict; use warnings; use Benchmark qw/:all/; use DBI; my $cl_dbh = DBI->connect('dbi:mysql:test') or die DBI->errstr; my $srv_dbh = DBI->connect('dbi:mysql:test;mysql_server_prepare=1') or die DBI->errstr; my $sqlite_dbh = DBI->connect('dbi:SQLite:dbname=/tmp/foo.db') or die DBI->errstr; $cl_dbh->do('drop table if exists t') or die $cl_dbh->errstr; $cl_dbh->do('create table t (v int not null)') or die $cl_dbh->errstr; $cl_dbh->do('insert into t values (1)') or die $cl_dbh->errstr; { local $sqlite_dbh->{PrintError}; $sqlite_dbh->do('drop table t'); } $sqlite_dbh->do('create table t (v int not null)') or die $sqlite_dbh->errstr; $sqlite_dbh->do('insert into t (v) values (1)') or die $sqlite_dbh->errstr; sub non_prepared { my ($dbh) = @_; $dbh->selectall_arrayref('select * from t where v=1') } sub prepared { my ($dbh, $sthref) = @_; unless ($$sthref) { $$sthref = $dbh->prepare('select * from t where v=?') or die $dbh->errstr; } $$sthref->execute(1) or die $dbh->errstr; $$sthref->fetchall_arrayref; } my ($cl_sth, $srv_sth, $sqlite_sth); cmpthese(10000, { 'mysql:np' => sub { non_prepared($cl_dbh) }, 'mysql:p' => sub { prepared($cl_dbh, \$cl_sth) }, 'mysql:p;srv' => sub { prepared($srv_dbh, \$srv_sth) }, 'sqlite:np' => sub { non_prepared($sqlite_dbh) }, 'sqlite:p' => sub { prepared($sqlite_dbh, \$sqlite_sth) }, });