川合孝典(2002/3/12)
元々メーリングリストにも流してWikiで公開していたものなんですが、Wikiのサイトが停止したこともあり、こちらに移植して編集しました。バージョンが多少古い目のものが多くなっていますが、このベンチを行ったときには、それほど古くないバージョンだったはずなんですけどねぇ。
結論から先にいましょう。私としてはPHPは立ち上がりは速いけどPerlだって負けてないし、処理そのものはPerlのほうが速いよんということだと思っています。ついでに憎まれ口を叩くと「PHPが比較している相手はPerl4なのでは?」と思っています。「CGIとPerlはきちんと区別してね」とか。「JavaはPerlよりも比較にならないほど速い?」の計算問題のあたりも合わせてご覧ください。あわせてPerl/DBIの次のステップをご覧いただくと参考になるかもしれません。
私としては"○○はCGIよりも速い。だからPerlよりも速い"っていう無茶苦茶なことでもなければあまり気にしません。(今だに結構あるんですよね。Perl=CGIだと書いちゃうような人が。 Perl/CGIって書いて比較していたと思ったら、一番最後にはPerlって書いちゃうようなケースとか。 ex. http://www.atmarkit.co.jp/flinux/php4/php4_1/php1.html )
できればPHPがPerlよりも(もちろんCGIじゃなくてですよ)という根拠になるような資料、PHPが得意な人による比較というものも見たいので、どなたかご存知でしたら教えてください。
通常のCGIとして使用できますが、PHPモジュールをApacheサーバーに組み込むことにより、 Perl/CGIと比較して処理速度の高速化、サーバー負荷の低減が可能です。
とPerlよりも速いというケースが多いようです。CGIってついてるけど、ことさらにPerlって書いてるぐらいだから、mod_perlよりも速いように思っていました。 こんなこと書くくらいだからぶっちぎりで速いんだろうなぁなんて思ってんです。本家dbi-usersでもちょいと 話題になったときでもPHPにはスピードで負けてもみたいな空気があったように感じていたので。
ところが、今回仕事の関係でPHPを調査する機会があってベンチマークをとってみたのですが、私としては手放しで PHPは速いとは思えませんでした。そりゃCGIのPerlと比較したら無条件に速いといえるでしょうけど、 mod_phpの相手は当然mod_perlでしょ?pconnectするんならApache::DBIを使うでしょ?
通常の接続であればPgを利用したプログラムとほぼ同等でしたし、持続的接続を使っている場合であっても レコード数が多ければ、適切にDBIを利用したスクリプトが追い抜いてしまいます。 Perl/DBIのほうが汎用的なインターフェースなのに。 (class.DBIやPEAR.DBはさらに遅かったので対象外ですね)
もちろんこういったベンチマークでは適切な環境で適切なスクリプトを使わなければ正しい値は期待できないと 思います。私の場合、当然(^^)、Perl側に有利な数字がでてしまうのはある程度当たり前だと思っています。 (思っていなかった人は思ってね)
この結果はあくまでも、参考ということで御自分で確認するようにお願いします。 川合がウソ書いているかもしれないと確かめてください。できれば、その結果も教えて欲しいと思っています。 自分のマシンだけじゃ、間違っているのかあっているのかもよくわからないので。<(__)>
特にPHP側については、PHP-usersメーリングリストでは質問させていただき、 指摘された部分は修正しましたが、まだ正しいかどうか不安なのです。
| ./ab -c10 -n100 http://lins/perl/tpg.pl |
それぞれ5回実行、Requests per secondを記録。 (そのようなシェルスクリプトを作成したんですが)
mod_perlの環境としてはApache::Registryを使用し、CGI.pm、DBI、DBD::Pg、Pgをロードしています。
これらのベンチマークについては何回も行ない平均値を取っています。また実行したタイミングによって出てくる数字は多少違ってきますが、傾向としては同じものと考えています。
| bind | arrayref | array | hashref | Pg | PHP | |
| 総平均 | 13.47 | 13.37 | 13.32 | 12.98 | 14.07 | 14.00 |
| 一回の時間(ms) | 74.24 | 74.77 | 75.10 | 77.06 | 71.08 | 71.41 |
| bind | arrayref | array | hashref | Pg | PHP | |
| 総平均 | 11.90 | 11.62 | 11.22 | 10.19 | 11.79 | 11.13 |
| 一回の時間(ms) | 84.00 | 86.05 | 89.12 | 98.14 | 84.82 | 89.82 |
※ちなみにPerlをCGIで利用すると総平均で大体1.77程度。CGIは遅いよね。
Pgには持続的接続がないこともあってDBIのarrayrefを使って計測。
| arrayref | PHP | |
| 総平均 | 38.62 | 45.96 |
| 一回の時間(ms) | 25.90 | 21.76 |
| arrayref | PHP | |
| 総平均 | 26.88 | 24.99 |
| 一回の時間(ms) | 37.21 | 40.01 |
オマケ:この結果を元にPerl側の設定を最速モード(^^)にしたところ
-bind_columns+fetchrow_arrayrefを利用
-CGI.pmの代わりにApacheモジュールを利用
-startup.plで、Apache::DBI->connect_init+RegistryLoaderの利用
通常接続、持続的接続ともに15レコード程度でPHPとほぼ同じ速度になった。
(従来は30〜40レコード)
#!/usr/bin/perl
use strict;
use CGI;
use DBI;
my $oCgi = new CGI;
my $hDb = DBI->connect('dbi:Pg:host=lins dbname=test',
'scott', 'tiger', {AutoCommit=>0, RaiseError=>1}) ||
die "OPEN ERROR:" . $DBI::errstr;
my $hSt = $hDb->prepare(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ? OFFSET 100');
$hSt->execute($oCgi->param('limit')+0);
my $raRow;
my $sRes ='';
my $i=0;
$hSt->bind_columns(\(
my ($no,
$name01, $name02, $name03, $name04, $name05,
$name06, $name07, $name08, $name09, $name10,
$name11, $name12, $name13, $name14, $name15,
$name16, $name17, $name18, $name19,)
));
while($raRow = $hSt->fetchrow_arrayref()) {
for(my $k=0; $k<20;$k++){ $sRes .= $raRow->[$k];}
$sRes.='<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
#!/usr/bin/perl
use strict;
use CGI;
use DBI;
my $oCgi = new CGI;
my $hDb = DBI->connect('dbi:Pg:host=lins dbname=test',
'scott', 'tiger', {AutoCommit=>0, RaiseError=>1}) ||
die "OPEN ERROR:" . $DBI::errstr;
my $hSt = $hDb->prepare(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ? OFFSET 100');
$hSt->execute($oCgi->param('limit')+0);
my $raRow;
my $sRes ='';
my $i=0;
while($raRow = $hSt->fetchrow_arrayref()) {
for(my $k=0; $k<20;$k++){ $sRes .= $raRow->[$k];}
$sRes.='<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
#!/usr/bin/perl
use strict;
use CGI;
use DBI;
my $oCgi = new CGI;
my $hDb = DBI->connect('dbi:Pg:host=lins dbname=test',
'scott', 'tiger', {AutoCommit=>0, RaiseError=>1}) ||
die "OPEN ERROR:" . $DBI::errstr;
my $hSt = $hDb->prepare(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ? OFFSET 100');
$hSt->execute($oCgi->param('limit')+0);
my @raRow;
my $sRes ='';
my $i=0;
while(@raRow = $hSt->fetchrow_array()) {
for(my $k=0; $k<20;$k++){ $sRes .= $raRow[$k];}
$sRes.='<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
カラム名が欲しければ$hSt->{name}などを利用しましょう。 そういえばPHPでは大文字小文字はどうなるんだろう?
#!/usr/bin/perl
use strict;
use CGI;
use DBI;
my $oCgi = new CGI;
my $hDb = DBI->connect('dbi:Pg:host=lins dbname=test',
'scott', 'tiger', {AutoCommit=>0, RaiseError=>1}) ||
die "OPEN ERROR:" . $DBI::errstr;
my $hSt = $hDb->prepare(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ? OFFSET 100');
$hSt->execute($oCgi->param('limit')+0);
my $raRow;
my $sRes ='';
my $i=0;
while($raRow = $hSt->fetchrow_hashref()) {
$sRes .= $raRow->{no};
for(my $k=1; $k<20;$k++){
$sRes .= $raRow->{sprintf("name%02d",$k)};
}
$sRes.='<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
#!/usr/bin/perl
use strict;
use CGI;
use Pg;
my $oCgi = new CGI;
my $oConn = Pg::connectdb(
'host=lins dbname=test user=scott password=tiger');
die $oConn->errorMessage if($oConn->status != PGRES_CONNECTION_OK);
my $result = $oConn->exec(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ' .
$oCgi->param('limit') . ' OFFSET 100');
my @raRow;
my $sRes ='';
my $i=0;
while(@raRow = $result->fetchrow()) {
for(my $k=0; $k<20;$k++){ $sRes .= $raRow[$k];}
$sRes.='<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
#!/usr/bin/perl
use strict;
use Apache;
use DBI;
my $hDb = DBI->connect('dbi:Pg:host=lins dbname=test',
'scott', 'tiger', {AutoCommit=>0, RaiseError=>1}) ||
die "OPEN ERROR:" . $DBI::errstr;
my $hSt = $hDb->prepare(
'SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT ? OFFSET 100');
#my $oCgi = Apache->request();
my %hArgs = Apache->request()->args();
$hSt->execute($hArgs{limit}+0);
my $raRow;
my $sRes ='';
my $i=0;
$hSt->bind_columns(\(
my ($no,
$name01, $name02, $name03, $name04, $name05,
$name06, $name07, $name08, $name09, $name10,
$name11, $name12, $name13, $name14, $name15,
$name16, $name17, $name18, $name19,)
));
while($raRow = $hSt->fetchrow_arrayref()) {
#for(my $k=0; $k<20;$k++){ $sRes .= $aRow[$k];}
$sRes .= $no.
$name01. $name02. $name03. $name04. $name05.
$name06. $name07. $name08. $name09. $name10.
$name11. $name12. $name13. $name14. $name15.
$name16. $name17. $name18. $name19. '<BR>';
$i++;
}
print<<EOH;
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$sRes
</BODY>
</HTML>
EOH
$hSt->finish();
$hDb->disconnect();
|
<HTML>
<HEAD>
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
<?php
$conn=pg_pConnect("host=lins dbname=test user=scott password=tiger");
$result = pg_Exec($conn,
"SELECT * FROM TBL_TEST2 WHERE no >= 5000 ORDER BY no LIMIT $limit OFFSET 100");
$sRes = '';
while(@$row = pg_Fetch_Row ($result, $i++)){
for($k=0;$k<20;++$k) { $sRes .= $row[$k];}
$sRes .= '<BR>';
}
echo $sRes;
?>
</BODY>
</HTML>
<?php
pg_Close($conn);
?>
|