IPアドレスから携帯キャリア判別のベンチマーク

Net::CIDR::MobileJPと、PostgreSQL の inet で速度を比べてみる。

PostgreSQLのinet型を使って、IPアドレスから携帯のキャリアを判定してみます。

http://www.mono-space.net/blog/pgsql/e070511_ipaddress.htm

元ネタは net-cidr-mobilejp-scraper.pl で抽出した YAML (117アドレス) で、以下の手法を比較。

結果。

               Rate   memcached          pg        perl pg_prepared
memcached    5435/s          --        -16%        -73%        -80%
pg           6494/s         19%          --        -68%        -76%
perl        20202/s        272%        211%          --        -25%
pg_prepared 27027/s        397%        316%         34%          --

memcached が一番遅いという、意外な結果に。
PostgreSQL では prepare するかしないかで 4倍差。

[追記]
上記結果は嘘でした(なんかおかしいな、とは思ったのだけど)。というのは、Benchmark モジュールの Rate 計算に用いる時間は Perl が使用した CPU 時間だから。Perl 内で完結しない memcachedPostgreSQL を扱う場合は、CPU時間ではなく経過時間を使わないと意味がない。
ということで、cmpthese ではなく timethese を使った結果は以下。

Benchmark: timing 100000 iterations of memcached, perl, pg, pg_prepared... 
 memcached: 21 wallclock secs (17.42 usr +  1.37 sys = 18.79 CPU) @ 5321.98/s (n=100000)
      perl:  5 wallclock secs ( 4.98 usr +  0.00 sys =  4.98 CPU) @ 20080.32/s (n=100000)
        pg: 37 wallclock secs (12.08 usr +  2.28 sys = 14.36 CPU) @ 6963.79/s (n=100000)
pg_prepared: 16 wallclock secs ( 2.45 usr +  0.81 sys =  3.26 CPU) @ 30674.85/s (n=100000)

"@ xxxx/s" の部分は Perl の CPU 時間ベースの計算なので、見るべきは "X wallclock secs" の部分。これを見ると、perl > pg_prepared > memcached > pg です。

#!/usr/bin/perl

use strict;
use Benchmark qw/ :all /;
use Net::CIDR::MobileJP;
use Cache::Memcached;
use DBI;

my $yaml = shift;
my $addr = shift;

my $cidr  = Net::CIDR::MobileJP->new( $yaml );
my $dbh   = DBI->connect( 'dbi:Pg:dbname=fujiwara', 'fujiwara', '',
                          { RaiseError => 1, AutoCommit => 0 } );
my $sql   = 'SELECT carrier FROM mobile_addr WHERE ? << addr LIMIT 1';
my $sth_p = $dbh->prepare($sql);
my $cache = Cache::Memcached->new({ servers => [ '127.0.0.1:11211' ] });
$cache->set( $addr => $cidr->get_carrier($addr) );

cmpthese( 100000, {
    perl => sub {
        $cidr->get_carrier($addr);
    },
    memcached => sub {
        $cache->get($addr);
    },
    pg_prepared => sub {
        $sth_p->execute($addr);
        my $r = $sth_p->fetchrow_arrayref;
        $r ? $r->[0] : 'N';
    },
    pg => sub {
        my $sth = $dbh->prepare($sql);
        $sth->execute($addr);
        my $r = $sth->fetchrow_arrayref;
        $r ? $r->[0] : 'N';
    }
});