2009/03/13

Data::ObjectDriverは遅いのか

ネットをうろうろしてて、こんな記事を見つけた
ObjectDriver使えねえ。。

Data::ObjectDriverのあまりの遅さにビックリして自分で同じ機能を書き直したら、速度にして約10倍以上に跳ね上がった。そもそもmemcachedを使わずに裸のDBIでやった方がmemcachedアリのObjectDriverより速いんだから。読み込みループがですよ。すなわち300回実行したら299回はmemcachedを使っている筈のObjectDriverが、300回DBアクセスするDBIより遅いわけ。キャッシュの意味がねぇ(;´Д`)y-~ 言語を絶する。バカどもは最低でもベンチマーク取ってから人に勧めろよ



2008年10月の記事なので大分古いけど、日本ではDODの使用例とか感想がさっぱりないので気になった。
上の記事にベンチマーク結果もコードも貼ってないので、自分でベンチマークを取ってみた。

MySQL, memcached共にlocalにある環境でbenchmarkを実行し、結果は以下の通り。
ついでなので、DBICも一緒に計測した。DBICでcacheする場合のスマートに書く方法を知らべるのが面倒だったので、愚直に書いた。
_poolが付いているものは、MySQLとの接続を1度しか行わないもの。


Rate DBIC DBIC_pool DOD DBI DOD_MemCached DBI_pool DBIC_MemCached DBI_MemCached
DBIC 435/s -- -59% -75% -76% -92% -95% -96% -97%
DBIC_pool 1058/s 143% -- -40% -41% -80% -87% -89% -93%
DOD 1751/s 302% 65% -- -3% -67% -78% -82% -88%
DBI 1805/s 315% 71% 3% -- -66% -77% -82% -88%
DOD_MemCached 5263/s 1109% 397% 201% 192% -- -34% -47% -65%
DBI_pool 8000/s 1738% 656% 357% 343% 52% -- -19% -46%
DBIC_MemCached 9901/s 2174% 836% 465% 449% 88% 24% -- -34%
DBI_MemCached 14925/s 3328% 1310% 752% 727% 184% 87% 51% --


素のDBIよりDOD+memcachedの方が全然早いじゃん。
DBICで透過的にcacheする状態だと、DOD+memcachedのどっちが早いのか気になるところ。


以下にbenchmark用コードを貼っておく

#!/usr/bin/env perl

# 2009/03/13
# MySQL 5.0.27
# memcached 1.2.6
# perl v5.8.8
# Data::ObjectDriver 0.06
# DBIx::Class 0.08012
# Cache::Memcached::Fast 0.14

package DOD;
use strict;
use warnings;
use base qw(Data::ObjectDriver::BaseObject);
use Data::ObjectDriver::Driver::DBI;

__PACKAGE__->install_properties(
{ columns => [ 'id', 'word', 'text' ],
primary_key => ['id'],
datasource => 'test',
driver => Data::ObjectDriver::Driver::DBI->new(
dsn => 'dbi:mysql:dod',
username => 'dod',
password => '',
reuse_dbh => 1,
),
}
);

package DODMemCached;
use strict;
use warnings;
use base qw(Data::ObjectDriver::BaseObject);
use Data::ObjectDriver::Driver::DBI;
use Data::ObjectDriver::Driver::Cache::Memcached;
use Cache::Memcached::Fast;
__PACKAGE__->install_properties(
{ columns => [ 'id', 'word', 'text' ],
primary_key => ['id'],
datasource => 'test',
driver => Data::ObjectDriver::Driver::Cache::Memcached->new(
cache => Cache::Memcached::Fast->new(
{ servers => ['localhost:11211'] }
),
fallback => Data::ObjectDriver::Driver::DBI->new(
dsn => 'dbi:mysql:dod',
username => 'dod',
password => '',
reuse_dbh => 1,
),
),
}
);

package DBIC::Schema::Test;
use strict;
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components("Core");
__PACKAGE__->table("test");
__PACKAGE__->add_columns(
"id",
{ data_type => "INT",
default_value => undef,
is_nullable => 0,
size => 11
},
"word",
{ data_type => "TEXT",
default_value => undef,
is_nullable => 0,
size => 65535,
},
"text",
{ data_type => "TEXT",
default_value => undef,
is_nullable => 1,
size => 65535,
},
);
__PACKAGE__->set_primary_key("id");

package DBIC::Schema;
use strict;
use warnings;
use base qw/DBIx::Class::Schema/;

__PACKAGE__->load_classes(qw/Test/);

package main;
use strict;
use warnings;
use DBI;
use Cache::Memcached::Fast;
use Benchmark qw(cmpthese);
my @connect_info = ( 'dbi:mysql:dod', 'dod', '' );
my $count = shift || 10_000;
my $pool = DBI->connect(@connect_info);
my $schema = DBIC::Schema->connect(@connect_info);
my $cache = Cache::Memcached::Fast->new( { servers => ['localhost:11211'] } );

cmpthese(
$count,
{ 'DBI' => sub {
my $dbh = DBI->connect(@connect_info);
my $sth
= $dbh->prepare(
'SELECT test.id, test.word, test.text FROM test WHERE id = 1'
);
$sth->execute;
my $row = $sth->fetchrow_hashref;
$sth->finish;
$dbh->disconnect;
},
'DBI_pool' => sub {
my $sth
= $pool->prepare(
'SELECT test.id, test.word, test.text FROM test WHERE id = 1'
);
$sth->execute;
my $row = $sth->fetchrow_hashref;
$sth->finish;
},
'DBI_MemCached' => sub {
my $row = $cache->get('DBI_MemCached-Test-1');
unless ($row) {
my $dbh = DBI->connect(@connect_info);
my $sth
= $dbh->prepare(
'SELECT test.id, test.word, test.text FROM test WHERE id = 1'
);
$sth->execute;
my $row = $sth->fetchrow_hashref;
$sth->finish;
$dbh->disconnect;
$cache->set( 'DBI_MemCached-Test-1', $row );
}
},

'DOD' => sub { my $row = DOD->lookup(1); },
'DOD_MemCached' => sub { my $row = DODMemCached->lookup(1); },

'DBIC' => sub {
my $s = DBIC::Schema->connect(@connect_info);
my $row = $s->resultset('Test')->find(1);
},
'DBIC_pool' => sub {
my $row = $schema->resultset('Test')->find(1);
},
'DBIC_MemCached' => sub {
my $row = $cache->get('DBIC_MemCached-Test-1');
unless ($row) {
my $s = DBIC::Schema->connect(@connect_info);
$row = $s->resultset('Test')->find(1);
$cache->set( 'DBIC_MemCached-Test-1', $row );
}
},
}
);
$pool->disconnect;

2008/12/25

Catalystを覚えよう

Catalyst: Accelerating Perl Web Application Development
いい加減、CGI::Application(::Dispatch)だけじゃなくて、Catalystも使えるようになりたいの上記の本を購入した。
お金を払ったことで、勉強しなきゃいけない気がしてくるので、怠惰な自分にはよかったかもしれない。

Chapter 3まで読み終って、Chapter 4の途中。
次の土日で写経してみようと思う。

2008/05/08

[Linux]directoryのtimestampe

Linuxのdirectoryのtimestampの挙動で悩んでしまったので、忘れないようにメモ。
directoryの直下にdirectory/fileが作成された場合、又は直下にあるdirectory/fileが削除された場合に更新される。

2008/04/06

iTunesとMac::iTunes

Mac miniのモニタはゲームと兼用で使用しているため、ゲーム中はiTunesの操作ができない。
WindowsのノートからMacのiTunesを操作できれば万事解決すると考え、検索してみるとVNCでどうにかする人が多いようだ。
iTunesだけを操作したいのにVNCは大げさ。
Windows側がへぼいからかVNCのせいかはわからないが、レスポンス悪いし。

Mac同士だとリモート操作用のアプリケーションが出てたりするが、Windows,Mac間となると急に選択肢がなくなった。
結局CPANで検索するとMac::iTunesなるModuleが見つかる。

Mac::iTunesは内部で動的にAppleScriptを生成して、Mac::AppleScriptで実行する実装。
パフォーマンスは高くないが、そもそもそれほど操作する訳でもないのでOK。
Mac::iTunesの作者がApache::iTunesというmod_perlで動くモジュールも公開してたけど、まだAlpha版、すげー遅いって書いてあるので今回はパス。

Term::ReadLineとMac::iTunesで実装してみた。

ショートカットキーやら、表示周りがださいなー。
そして、使ってるうちに日本語の入力が効かないことが発覚。
Macのterminalでは日本語は使えるが、Term::ReadLineを通すとおかしくなる。
WindowsのTera Term経由だとそもそも日本語の入力がおかしい。
まあ、今度考えよう。

一応コードをさらしておく。


#!/usr/bin/perl

use strict;
use warnings;
use Term::ReadLine;
use Mac::iTunes;

our $VERSION = '0.001';

# setup term
my $prompt = 'iTunes> ';
my $term = Term::ReadLine->new($prompt);
my $OUT = $term->OUT || \*STDOUT;
select($OUT);

# setup itunes controller
my $itunes = Mac::iTunes->controller;

my %return_dispatch = (
state => 1,
current_track_name => 1,
pl => 1,
playlist => 1,
get_playlists => 1,
);

my %alias = (
c => 'current_track_name',
p => 'playpause',
s => 'state',
a => 'activate',
q => 'quit',
h => 'help',
pl => 'get_playlists',
playlsit => 'get_playlists',
);

my %show_status = (
p => 1,
play => 1,
stop => 1,
start => 1,
pause => 1,
playpause => 1,
play_track => 1,
);

while ( defined( $_ = $term->readline($prompt) ) ) {
chomp;
my ( $command, @opts ) = split /\x20/, $_;
my $org_command = $command;
$command = $alias{$command} || $command;
if ( $org_command =~ /^(h|help)$/ ) {
help();
}
else {
my $ret = exec_command( $command, @opts );
if ( $show_status{$org_command} ) {
print $itunes->state;
}
my $show_return = $return_dispatch{$command} ? 1 : 0;
$ret = join "\n", @$ret if ref $ret eq 'ARRAY';
print $ret if $show_return;
}
$term->addhistory($_) if /^\S$/;
}

sub exec_command {
my ( $command, @opts ) = @_;
my $ret;
if ( $command eq 'play' and @opts ) {
$command = 'play_track';
unshift @opts, 1;
}
eval { $ret = @opts ? $itunes->$command(@opts) : $itunes->$command; };
warn $@ if $@;
return $ret;
}

sub help {
print << 'HELP';
command list:
a, activate
start itunes if it stopped.
q, quit
quit itunes if it activated.
play [playlist]
start track.
pl, playlist, get_playlists
show list of iTunes playlist.
stop
stop current track.
pause
pause current track.
next
skip current track.
p, playpause
switch play, pause.
c, current_trach_name
show current track name.
h, help
show this message.
HELP
}

2008/03/23

AppleScriptとMacRuby

Matzにっき
http://www.rubyist.net/~matz/20080317.html#p01

MacRubyがObjective-C(or AppleScript)を乗っ取る

タイトルをつけた人の意図はそこにはないとは思うけど、 これは結構ありそう。

もちろん、Objective-Cのすべてを奪うことはありえないけど、 MacRubyレベルでObjective-Cとの融合が進むと カジュアルな利用はRubyで行うというのは予想以上に広まるかも。

また、AppleScriptの英語モドキよりもいっそRubyのような 「普通のプログラミング言語」を使った方がわかりやすい という人も多いだろう。


AppleScriptよりRubyの方が断然わかりやすい。
AppleScriptのわかりにくさ、書きにくさはなんだろうね。
英語圏の人だと書きやすいのかな。

2008/02/13

bluetoothヘッドセットを購入

今までSkypeで使用していたヘッドセットのマイクが不調で、マイクが音を拾ってくれなかったりしたので購入。

買ったのはPLANTRONICSのVOYAGER 510。
Amazonで割と評判がよかったのでこれにした。
一番小さいのにしても、イヤーピースが若干大きい気がするが、音もしっかり拾ってくれるし、音声もまあまあ。
今まで違ってワイヤレスなので快適だ。


話は変わってSpore。
twitter経由で知ったけど、9月に出るそうで。
動くようなPCもってないし、Mac版も出そうなのでMacBook Proが欲しいなー。

2008/01/20

Mac::Growlを使ってみた

MacにはGrowlという色々なアプリケーションに対応したNotiferがあることを知った。
PerlのMac::Growlモジュールを使えば、自分のスクリプトからGrowl経由で通知を出せるようなので、使ってみた。

まずMac::Growlをインストール。
sudo cpan
install Mac::Growl


使い方
まずは、Growlにアプリケーションを登録する。
use Mac::Growl qw(RegisterNotifications);

RegisterNotifications(
$appname, # 登録するアプリケーション名
['alert', 'note', 'info'], # 登録したアプリケーションで使用する通知の種類。
# 自分が分かりやすい名前にしておくといいと思う
['note'] # デフォルトで使用する通知の種類。なんで複数の値を渡せるのかは知らない
);


んで、実際に使用するスクリプトにGrowl用のコードを埋め込む。
use Mac::Growl qw(PostNotification);

PostNotification(
$appname, # 先ほど登録したアプリケーション名
'alert', # 通知の種類。登録してあるalert, note, infoのうちのどれかを渡す。
$title, # 表示されるタイトル。日本語を渡す場合は、
# Encode等を使用して、UTF-8にしてあげる必要があるようだ。
$description, # 表示される内容。
);


PostNotificationには他にもいくつか引数を渡せるが、とりあえずこれでOK。

時間がかかるスクリプトに仕込んでおけば、結構便利だった。