川合孝典(2002/3/12)
とはいえ、ちょっとは気になるというわけで、2つのケースについてテストしてみました。
1つは非常に単純なケースで、こちらのページでのベンチマークをベースに、単にページを表示するだけです。
http://java-house.etl.go.jp/ml/archive/j-h-b/027274.html#body
これだけではやる前からPerlが圧倒的に勝つ(というよりTOMCAT対mod_perlなんですけどね)のは当然なので、以下のURLを参考に
http://java-house.etl.go.jp/ml/archive/j-h-b/030140.html
Javaに有利な(^^)計算問題を解かせるようなプログラムの2パターンで試してみました。
あわせて「三大美徳」を支えるPerlの仕組 もご覧いただくと参考になるかもしれません。
反対に計算問題になると当然のごとくJavaの勝ち。この場合にはPerlもコンパイルされた結果ですから、 Perlにおける変数扱いの自由度のせいでしょう。当然こうした計算が多くなるほどPerlには不利。(さらにPHPは...ってことでしょう)
言語としてはPerlのほうが"遅い"といえば、そうなんでしょうねぇ。ただもっと単純な計算や正規表現を使った文字列操作になると大分様子が違ってくるでしょう。さらに、どの時点からの計測かによって感じ方はかなり違いそうです。起動ロスはPHP<Perl<Javaでしょうから、単純なものをJavaで作るのは合わないかもしれません。
もちろんJavaの場合には、実行環境をどうするかによって大きく違ってくるでしょう。そういった意味からもJavaを使うんならウチはでかいからアプリケーション・サーバーを使うんだもんね ぐらいの心構えが必要なのか知らんと思うのです。管理なんかも含めて。
それでもスピードで負けるのは悔しいという方には、エジソンのお言葉を
私は数学が苦手だ。でも数学者を雇うことはできる
確かこうだったと思うんですが間違っているかもしれません。<(__)> 心意気としてはこういうことだということで、 Perlとしては速い言語があれば、それをPerlから利用するのが正しい選択でしょう。今回は、まずはInline CをそれからXSを利用したCのモジュールも作ってみました。そうなれば、そうそう負けることはないようです。
PerlからJavaあるいはその反対も可能なはずですしね。(JPLってその後どうなったんでしょうね?)
今はCORBAで連携させてみようかなぁとJDKの新しいバージョンをダウンロード中。連携させるまでに手間取りましたが(というか今でも無理やり通しているだけ)とりあえず完成でも呼出に時間が掛かる分、数字的にはあまりよくないです。 CORBA連携に関してはちょっとややこしい(Java側がねぇ)ので、別のページでやり方をまとめようと思います。
ご参考:洗練されたPerl: CおよびJavaプログラマーのためのPerl 5.6
Apacheのベンチマーク abを利用し、以下のようなコマンドで
| ./ab -c10 -n100 http://lins/perl/tpg.pl?message=kaba |
それぞれ5回実行、Requests per secondを記録。 (そのようなシェルスクリプトを作成したんですが)
| Java | Perl/CGI.pm | Perl/Apache | PHP | HTML | |
| 1回目 | 52.58 | 70.03 | 164.47 | 201.21 | 361.01 |
| 2回目 | 46.40 | 98.23 | 198.41 | 202.84 | 364.96 |
| 3回目 | 47.57 | 90.42 | 206.61 | 211.42 | 350.88 |
| 4回目 | 49.33 | 100.91 | 205.34 | 203.67 | 363.64 |
| 5回目 | 49.12 | 71.74 | 182.48 | 195.31 | 355.87 |
| 平均回数 | 49.00 | 86.27 | 191.46 | 195.31 | 355.87 |
| 1回の時間(ms) | 20.41 | 11.59 | 5.22 | 4.93 | 2.78 |
ちなみに普通のCGIによるPerlの場合には平均3.30回/303.4msってこれと比較しても、方式が違いすぎるので意味なさ過ぎ。さらにPerl側でCGI.pmの機能をなぜか多用しているスクリプトを他のものにあわせて文字列を編集するように変更するだけで、一回の呼出に2msほどの違い(平均回数でいうと20回ほどの違い)が出ました。比較するときにはなるべく同じ方式にして欲しいものですねぇと思ったりして。
パラメータが100のとき
| Perl | Java | Perl+Inline | PHP | |
| 1回目 | 40.39 | 52.80 | 36.91 | 23.61 |
| 2回目 | 41.53 | 46.84 | 48.12 | 23.78 |
| 3回目 | 42.88 | 43.52 | 91.24 | 24.31 |
| 4回目 | 51.87 | 47.37 | 41.89 | 24.17 |
| 5回目 | 50.56 | 47.44 | 107.99 | 24.34 |
| 平均回数 | 45.45 | 47.59 | 65.23 | 24.04 |
| 1回の時間(ms) | 22.00 | 21.01 | 15.33 | 41.59 |
パラメータが500のとき
| Perl | Java | Perl+Inline | PHP | |
| 1回目 | 7.39 | 49.00 | 26.01 | 2.73 |
| 2回目 | 8.03 | 43.44 | 59.52 | 2.79 |
| 3回目 | 8.05 | 45.68 | 37.73 | 2.71 |
| 4回目 | 7.94 | 44.78 | 76.28 | 2.79 |
| 5回目 | 8.04 | 45.89 | 32.03 | 2.7 |
| 平均回数 | 7.89 | 45.76 | 46.31 | 2.75 |
| 1回の時間(ms) | 126.74 | 21.85 | 21.59 | 363.37 |
ここからはPerl+C(XS)とJavaの直接対決(^^)だけにしました。
パラメータが5000のとき
| Perl+XS | Java | |
| 1回目 | 7.66 | 8.17 |
| 2回目 | 8.51 | 7.85 |
| 3回目 | 8.46 | 7.81 |
| 4回目 | 8.42 | 7.80 |
| 5回目 | 8.41 | 7.80 |
| 平均回数 | 8.29 | 7.89 |
| 1回の時間(ms) | 120.60 | 126.81 |
もしかしたら追いつかれるかなぁ?もっともそんなに計算が得意ならJavaにお任せするんだろうけど。 Inline Javaの場合はVMを起動してたんじゃ重すぎだろうし?別途コマンドラインでも調べてみましたが、そちらでは100,000までやったけど、 Java 9.7秒に対してPerl+XS9.3秒ぐらい。起動部分だけの比較でいうと0.61対0.08だから気持ちつめられたといえるのかどうか。こうなると誤差といってもいいようなレベルかなぁと思ったりするんですが。
| import java.io.PrintWriter; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloWorldExample extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { StringBuffer sb = new StringBuffer("<html><head>"); sb.append("<title>Simple Echo Sample</title>"); sb.append("</head><body bgcolor=\"#ffffff\">"); sb.append("Message...<br>"); sb.append(request.getParameter("message")); sb.append("</body></html>\n"); response.setContentType("text/html; charset=iso-2022-jp"); PrintWriter pw = response.getWriter(); pw.print(sb.toString()); pw.close(); } } |
#!/usr/bin/perl
use CGI qw(:standard :html3);
$cgi = new CGI;
print $cgi->header(
-CONTENT_TYPE => "text/html; charset=iso-2022-jp"
);
print $cgi->start_html(
-TITLE => "Simple Echo Sample",
-BGCOLOR => '#ffffff'
);
print 'Message...<br>';
print $cgi->param ('message');
print $cgi->end_html;
|
#!/usr/bin/perl
use Apache;
my $r= Apache->request();
$r->send_http_header('text/html; charset=iso-2022-jp');
my %hArgs = $r->args();
my $sMsg = "<html><head>" .
"<title>Simple Echo Sample</title>".
"</head><body bgcolor=\"#ffffff\">".
"Message...<br>".
$hArgs{message}.
"</body></html>\n";
print $sMsg;
|
<html><head><title>Simple Echo Sample</title></head> <body bgcolor="#ffffff">Message...<br><?php echo $message ?></body></html> |
<html><head><title>Simple Echo Sample</title></head><body bgcolor="#ffffff">Message...<br>kabadesu</body></html> |
import java.io.PrintWriter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloWorldExample extends HttpServlet {
private boolean isprime( int i ) {
int j;
for( j=2; j*j <= i ; ++j ) if( i%j == 0 ) return false;
return true; /* prime */
}
private int prime(int limit) {
int c = 0;
int p;
for( p=2; c<limit; ++p )
if( isprime( p ) ) ++c;
return p;
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
StringBuffer sb = new StringBuffer("<html><head>");
sb.append("<title>Simple Echo Sample</title>");
sb.append("</head><body bgcolor=\"#ffffff\">");
sb.append("Message...<br>");
sb.append(request.getParameter("message"));
sb.append("CNT:" + prime(
Integer.parseInt(request.getParameter("limit")) ) + "<BR>");
sb.append("</body></html>\n");
response.setContentType("text/html; charset=iso-2022-jp");
PrintWriter pw = response.getWriter();
pw.print(sb.toString());
pw.close();
}
}
|
#!/usr/bin/perl
use strict;
use CGI;
my $cgi = new CGI;
sub isprime($) {
my ($i)=@_;
for(my $j=2; $j*$j <= $i ; ++$j ){
return 0 if( $i%$j == 0 );
}
return 1;
}
sub prime($) {
my ($limit)=@_;
my $c = 0;
my $p;
for($p=2; $c<$limit; ++$p ){
++$c if( isprime( $p ) );
}
return $p;
}
print $cgi->header(
-CONTENT_TYPE => "text/html; charset=iso-2022-jp"
);
my $sMsg = "<html><head>" .
"<title>Simple Echo Sample</title>".
"</head><body bgcolor=\"#ffffff\">".
"Message...<br>".
$cgi->param('message') .
'CNT:' . prime($cgi->param('limit')) . '<BR>'.
"</body></html>\n";
print $sMsg;
|
#!/usr/bin/perl
use strict;
use CGI;
use Inline C => <<CODEEND;
long isprime( long i ) {
long j;
for( j=2; j*j <= i ; ++j ) if( i%j == 0 ) return 0;
/* not prime */
return 1; /* prime */
}
int prime(long lmt) {
long c = 0;
long p;
for( p=2; c<lmt; ++p ) if( isprime( p ) ) ++c;
return p;
}
CODEEND
my $cgi = new CGI;
print $cgi->header(
-CONTENT_TYPE => "text/html; charset=iso-2022-jp",
);
my $sMsg = "<html><head>" .
"<title>Simple Echo Sample</title>".
"</head><body bgcolor=\"#ffffff\">".
"Message...<br>".
$cgi->param('message') .
'CNT:' . prime($cgi->param('limit')) . '<BR>'.
"</body></html>\n";
print $sMsg;
|
#!/usr/bin/perl
use strict;
use CGI;
use Prime_xs;
my $cgi = new CGI;
print $cgi->header(
-CONTENT_TYPE => "text/html; charset=iso-2022-jp",
);
my $sMsg = "<html><head>" .
"<title>Simple Echo Sample</title>".
"</head><body bgcolor=\"#ffffff\">".
"Message...<br>".
$cgi->param('message') .
'CNT:' . Prime_xs::prime($cgi->param('limit')) . '<BR>'.
"</body></html>\n";
print $sMsg;
|
long prime(long lmt); |
Prime_xs.xsの変更
関数の定義を追加するのと、カレントディレクトリにあるprime_xs.hを見つけるように変更するという2点の変更が必要になります。
5c5
< #include <prime_xs.h>
---
> #include "prime_xs.h"
27a28,33
> long isprime(long i) {
> long j;
> for( j=2; j*j <= i ; ++j )
> if( i%j == 0 ) return 0; /* not prime */
> return 1; /* prime */
> }
31d36
<
36a42,52
> long
> prime(lmt)
> long lmt
> CODE:
> {
> long c=0, p;
> for(p=2; c<lmt; ++p) { c+=isprime(p); }
> RETVAL = p;
> }
> OUTPUT:
> RETVAL
|
全体だとこんな感じ
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "prime_xs.h"
static int
not_here(char *s)
{
croak("%s not implemented on this architecture", s);
return -1;
}
static double
constant(char *name, int arg)
{
errno = 0;
switch (*name) {
}
errno = EINVAL;
return 0;
not_there:
errno = ENOENT;
return 0;
}
long isprime(long i) {
long j;
for( j=2; j*j <= i ; ++j )
if( i%j == 0 ) return 0; /* not prime */
return 1; /* prime */
}
MODULE = Prime_xs PACKAGE = Prime_xs double
constant(name,arg)
char * name
int arg
long
prime(lmt)
long lmt
CODE:
{
long c=0, p;
for(p=2; c<lmt; ++p) { c+=isprime(p); }
RETVAL = p;
}
OUTPUT:
RETVAL
|
| <html><head><title>Simple
Echo Sample</title></head> <body bgcolor="#ffffff">Message...<br><?php function isprime($i) { for($j=2; $j*$j <= $i ; ++$j ){ if( $i%$j == 0 ) {return 0; } } return 1; } function prime($limit) { $c = 0; for($p=2; $c<$limit; ++$p ){ $c += isprime($p); } return $p; } echo $message . 'CNT:' . prime($limit) . '<BR>'; ?></body></html> |