2008年01月25日
PerlのBenchmarkで出るベンチマーク値は、いったい何を表すのだろう?
DoCoMoのiエリアをベースに位置情報でスタンプラリーするようなゲームを作ってるんだけど、DoCoMoのFOMA機で経緯度情報が取れるようになったのを機に、地方でダメダメなiエリアベースのスタンプエリア分けではなく、地方にも公平な独自エリアでのサービスを検討してたりする。
で、そのエリアの再構成方法として、当初iエリアのメッシュ構成を再構成して独自エリアを構成すればいいかなと思ってたのだが、蓋を開けてみると全国のメッシュ数が500万くらいあって、これを直接エリア分けするのはやってられねえ、ということで、現在GISツール等を用いてポリゴンベースでエリア分けしてたりしてる。
でもそうなると今度はポリゴンベースでエリア分けしたものをメッシュデータに変換するロジックを組むのがまた泣きそうな勢いで、それならもうそのままポリゴンデータぶち込んでPostGISなんかでポリゴン衝突判定してやった方が早いんじゃね?と思い、iエリアのデータを用いて、500万件のメッシュレコードをmysqlに叩き込んでやったものと、505件のポリゴンデータをPostGISに叩き込んでやったものの間で、経緯度からエリアを導出するBenchmarkを取ってやりました。
ベンチのコードは概略こんな感じ。
use strict;
use DBI;
use Benchmark qw(:all);
my $pdbh = DBI->connect("dbi:Pg:dbname=iareadb", "xxxxxxxx", "xxxxxxxx");
my $mdbh = DBI->connect("DBI:mysql:database=iareadb", "xxxxxxxx", "xxxxxxxx");
my @latlngs = (
[35.00000,135.0000],
[35.00000,139.0000],
);
my $count = 10000;
my $r = timethese($count,
{'PostGIS-polygon' => '&test1;', 'mysql-mesh' => '&test2;', });
cmpthese $r;
sub test1 {
foreach my $latlng (@latlngs) {
my ($lat,$lng) = @{$latlng};
my $point = "GeomFromText('POINT($lng $lat)',4326)";
my $sql = "SELECT areacode FROM iarea_polygon WHERE polygon " .
"&& $point AND ST_Intersects(polygon,$point)";
my $sth = $pdbh->prepare($sql);
$sth->execute();
my $areacode = $sth->fetchrow_hashref->{areacode};
}
}
sub test2 {
foreach my $latlng (@latlngs) {
my ($lat,$lng) = @{$latlng};
my $mesh7 = mesh7($lat,$lng); <- メッシュIDを導出する関数(省略)
my $sql = "SELECT areacode FROM iarea_mesh WHERE mesh7='$mesh7'";
my $sth = $mdbh->prepare($sql);
$sth->execute();
my $areacode = $sth->fetchrow_hashref->{areacode};
}
}
結果はこんな感じ。
PostGIS-polygon: 225 wallclock secs ( 1.81 usr + 0.24 sys = 2.05 CPU) @ 4878.05/s (n=10000)
mysql-mesh: 15 wallclock secs ( 1.79 usr + 0.22 sys = 2.01 CPU) @ 4975.12/s (n=10000)
Rate PostGIS-polygon mysql-mesh
PostGIS-polygon 4878/s -- -2%
mysql-mesh 4975/s 2% --
秒間実行数どちらも5000件弱と、どちらでもいいよね(ということはデータ管理しやすいポリゴンがいいよね)という話になりそうなんだけど。
でも、ベンチマークを走らせている感にかかっている体感時間的には、明らかに圧倒的にmysqlのメッシュ検索の方が早い。
各結果の前にwallclock secsという項目があり、PostGISでは225、mysqlでは15となってるけど、この比と同じくらいの感覚で明らかにPostGISの方が時間がかかってる。
PerlのBenchmarkや、このwallclock secsという項目についていろいろGoogleで調べてみたけど、どのベンチマーク記事を見ても最後の秒間処理数の結果で比較しており、wallclock secsに注意を払っているものがない。
このwallclock secsというのは何者なのだろう?
もし総試行にかかった時間であるのならば、なぜ試行時間が異なるにも関わらず秒間処理数は同程度というベンチマーク結果が出るのだろう?
1つの「仮説」として、Benchmarkの結果で返って来るのは、全体の処理にかかった時間じゃなく、PerlプログラムがCPUを占有した時間じゃないだろうか?と考えた。
つまり、SQLの返事を待っている間のようなPerlプログラムとしてCPUを占有していない時間についてはBenchmark外になるのかな?飽くまでBenchmarkはPerlプログラムのアルゴリズムとしての効率しか測らず、そのためSQL実行時間以外はほとんど同じDBI処理であるメソッド間のベンチ比較なので、ほとんど同じ結果が返ってくるのかなと。
もしこれが正しければ、インデックスを張っているか張っていないかの差だけで後は全く同じテーブルに、全く同じPerlコードでアクセスした場合、インデックス差があるから実行時間には天地の差が出るはずだけど、Perl部分は全く同じだから秒間処理数は全く同じになるはず、と思って試してみた。
条件は先のベンチプログラムと一緒で、接続先のみmysqlのインデックス付テーブルとインデックスなしテーブルの比較にし、とはいえ500万件のテーブルにインデックスなしでアクセスさせると死ぬので、レコード件数だけ2万件程度に減らして、試行回数も1000ループに減らしてベンチを取ってみた。
結果は以下の通り。
index: 1 wallclock secs ( 0.20 usr + 0.01 sys = 0.21 CPU) @ 4761.90/s (n=1000)
(warning: too few iterations for a reliable count)
nonindex: 109 wallclock secs ( 0.41 usr + 0.08 sys = 0.49 CPU) @ 2040.82/s (n=1000)
Rate nonindex index
nonindex 2041/s -- -57%
index 4762/s 133% --
Perlプログラム部分は全く同じであるにもかかわらず、秒間実行数に倍以上の差が出てしまいました。
かといって、実際のベンチ実行時間は、やはりwallclock secsのところに示されたとおり1:109くらいの体感差があったのだけど、そこまでの差が秒間実行数に出ているわけでもありません。
Perlプログラム部分だけの効率を見ているわけでもない(仮説は崩れた)し、Perl外の処理を含めたコード全体の実行時間を見ているわけでもなさそうなBenchmarkの秒間処理数って、いったい何を測定しているのだろう...。
誰かご存知でしょうか...。
でもまあ一つ判ったのは、2万件のレコードに対しインデックス張った時と張ってない時の差が半分程度で済むはずがないので、こういうケースではベンチマーク結果の秒間処理数よりwallclock secsを見たほうがよさそう、というところだろうか。
とすると、エリア判定に500万件のメッシュレコードを表DBに突っ込むのと、数百件の空間DBに突っ込むのでは、前者の方が10倍くらい高速そう、ということになりそうです。
(ちなみに、EXPLAINで空間インデックス使っているのは確認したので、レコード数が少ないためにインデックスが使われていない、という可能性はなさそうです)
以前、正規表現でのiエリア判定とPostGISでのiエリア判定は、後者が前者の50倍、と書きましたが、これもwallclock secsで比較するなら7倍程度になりそうですね...。
うう...500万件のメッシュ管理嫌だなあ...ポリゴンをメッシュに引き当てるのもめんどいなあ...。
後ついでに、この辺調べてて判ったのは、mysqlは4.1以降MyISAM、5.0以降InnoDBで空間カラムに対応したと言っているけれど、
- 5.0では、InnoDBで空間カラムは作れるけど、空間インデックスは張れない。空間インデックスを張るならMyISAMでないとまだダメみたい。(5.1は未確認)
- MySQLの空間演算子では、まだMBR(最小外接矩形)間の関係しか判定できない。(これは5.1のマニュアルにあったので、5.1でもそうみたい)
今回のようなポリゴンとの衝突判定をするようなケースでは、まだPostGISしか使えなそう。
というようなあたり。
> このwallclock secsというのは何者なのだろう?
CPU を占有しているかどうかに関係なく経過した時間を表す用語ですから、おそらくベンチマーク対象を実行している間にかかった時間の合計でしょうね。
> もし総試行にかかった時間であるのならば、なぜ試行時間が異なるにも関わらず秒間処理数は同程度というベンチマーク結果が出るのだろう?
表示されるメッセージから計算すると
10000 / 2.05(CPU 時間) = 4878.05
10000 / 2.01(CPU 時間) = 4975.12
なので、経過した時間は関係なく CPU を占有した時間と試行回数のみから、秒間試行回数を計算していると言えます。
> 1つの「仮説」として、Benchmarkの結果で返って来るのは、全体の処理にかかった時間じゃなく、PerlプログラムがCPUを占有した時間じゃないだろうか?と考えた。
> つまり、SQLの返事を待っている間のようなPerlプログラムとしてCPUを占有していない時間についてはBenchmark外になるのかな?
表示されるメッセージからすると、秒間試行回数については、CPU を占有していない時間は対象外としか考えられないですね。
> 飽くまでBenchmarkはPerlプログラムのアルゴリズムとしての効率しか測らず、
それはちょっと違うと思います。
Perl プログラムのアルゴリズム的な部分だけでなく、XS の中でなんかやっていても CPU は占有しますし、さらに下の層でソケットレベルでなんかやっていても CPU は占有します。
さらに、クエリの実行を待っている間に別のプロセスが動いたりして、必要なデータがキャッシュから追い出されてしまったら、それを呼び戻す間も CPU を占有することになります。
Perl プログラムのアルゴリズム的な部分が時間のほとんどを占めているのであれば、Perl プログラムのアルゴリズムとしての効率を測っているといると言っても良いと思います。
しかし、それ以外の時間が多いのであれば、そうは言えないでしょう。
> 表示されるメッセージからすると、秒間試行回数については、CPU を占有していない時間は対象外としか考えられないですね。
ソースも見てみましたが、やはり 試行回数 / CPU を占有した時間 でした。
Posted by: あ at 2008年01月31日 21:01なるほど、やっぱりPerlの占有した時間なのですね。
ありがとうございます。
かつ、その占有時間は、キャッシュの利用状況等によっても左右されると。
なので一般的なアルゴリズムベンチマークでは、何十万回とループを回して、その影響を排除しようとしている感じなのでしょうか。
有用な情報をありがとうございました。
Posted by: kokogiko at 2008年02月02日 16:00![[ここギコ!]](http://kokogiko.net/logo.png)



・3Dどきゅめんと…って何?点字文書?(ulikmed)
・3Dどきゅめんと…って何?点字文書?(Appolinariy)
・DoCoMoのGPSでの簡易詐称チェック(けひん)
・3Dどきゅめんと…って何?点字文書?(eurozapc)
・モバイルSuicaへの不満(名無し)
・QWERTYだって単なる慣れの問題、日本でのiPhoneは韓国でのGoogle Mapsの立場(kokogiko)
・iPhoneのGPSはWeb連携できない(kokogiko)
・iPhoneのGPSはWeb連携できない(ハル)
・QWERTYだって単なる慣れの問題、日本でのiPhoneは韓国でのGoogle Mapsの立場(名無し)