N-QueenをXSを使って実装してみた
とりあえず実装してみた
まずは Makefile.PLの作成
use strict; use ExtUtils::MakeMaker; WriteMakefile ( NAME => 'Solver::NQueen::XS', VERSION_FROM => 'lib/Solver/NQueen/XS.pm', );
次にモジュールファイルの作成.
モダンPerl入門のまま.
package Solver::NQueen::XS; use strict; use warnings; use XSLoader; our $VERSION = '0.00001'; XSLoader::load __PACKAGE__, $VERSION; 1;
次に .xsファイルの作成. SVとかAVとかを積極的に使うことは
ちょっとまだできないですが、今回は int型の解を返したいだけ
なので普通の Cにほぼ等しいですね。
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" void nqueen_sub(int n, int now, int *array, int *answer); int nqueen(int size) { int array[32]; int answer; answer = 0; nqueen_sub(size, 0, array, &answer); return answer; } void nqueen_sub(int n, int now, int *array, int *answer) { int i, j; int flag; for (i = 0; i < n; i++) { flag = 1; for (j = 0; j < now; j++) { if (i == array[j]) { flag = 0; break; } if (i - (now - j) == array[j]) { flag = 0; break; } if (i + (now - j) == array[j]) { flag = 0; break; } } if (flag == 1) { array[now] = i; if (now == n - 1) { (*answer)++; } else { nqueen_sub(n, now + 1, array, answer); } } } } MODULE = Solver::NQueen::XS PACKAGE = Solver::NQueen::XS PROTOTYPES: DISABLED int nqueen(size) int size
構築をする。
% perl Makefile.PL % make
結構エラーが出て意味わからね〜ってなりましたけど、よくよく
見るとパッケージ名が間違っているとかそんなんでした。
で、ベンチマーク。コードは以下のような具合です
#!/usr/bin/perl use strict; use warnings; use lib qw(../lib); use blib qw(../blib); use Benchmark qw(cmpthese); use Solver::NQueen::Pure qw(nqueen); use Solver::NQueen::XS; cmpthese(-10, { Pure => sub { Solver::NQueen::Pure::nqueen(8) }, XS => sub { Solver::NQueen::XS::nqueen(8)}, });
平等にするために, PurePerlの方も関数スタイルにしました。
オブジェクト版で計測したらすごく差が出て、何かの間違いなのかな
って思って関数版にしてみたんだけど、結局ずいぶん差がでました.
以下結果です
Rate Pure XS Pure 21.9/s -- -99% XS 3225/s 14608% --
146倍 XS版の方が早い.
ここまで差が出るものなのかな?どこか間違ってるんじゃないかって
思いたくなるほどですね。素直に受け取るなら, これは書く価値がある
な〜ってなりますね。Cライブラリのバインディングが主なものかと
思っていたんですけど、速度面でも大きなものが得られるんですね.
感想. XSって書いたことなかったけど, ただ Cの関数を呼ぶだけで
あるならば簡単に使えることがわかった. SV, AV, HVなどを積極的に
利用するとなると perlapiとか perlgutsの内容を理解していないと
いけないんでしょうけど, それはこれからというところですね.
その辺を理解して, もうちょっと複雑なものにチャレンジしたいです.
あと既存の Cライブラリのバインディングも作成してみたいですね.
誰かが CPANにあげている可能性が高そうですけど、練習ということで
やってみたいです
なお環境は
Ubuntu 9.04 Linux Kernel 2.6.28.15
Core2duo 2.2GHz Memory 3GHz
修正した Pure Perlの実装
package Solver::NQueen::Pure; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(nqueen); sub nqueen { my $size = shift; my $answer = 0; _nqueen_sub($size, 0, [], \$answer); return $answer; } sub _nqueen_sub { my ($size, $now, $array_ref, $answer_ref) = @_; for ( my $i = 0; $i < $size; $i++ ) { my $flag = 1; for ( my $j = 0; $j < $now; $j++) { my $pos = $array_ref->[$j]; if ($i == $pos) { $flag = 0; last; } if ($i - ($now - $j) == $pos) { $flag = 0; last; } if ($i + ($now - $j) == $pos) { $flag = 0; last; } } if ($flag == 1) { $array_ref->[$now] = $i; if ($now == $size - 1) { ${$answer_ref}++; } else { _nqueen_sub($size, $now + 1, $array_ref, $answer_ref); } } } } 1;