酒日記

2002 09 09

Mon, 09 Sep 2002

一番絞り 樽生 (5.5% × 1520 ml)

今月の UNIX USER を読んでいたら、「Perl5.8 の新機能」で ithread (インタープリタ・スレッド) が使えるようになったとのこと。 これで「ruby ならスレッドが簡単に使えますよ」とか言われて悔しい思いをすることがなくなるかも! (謎)

...

まずは Perl 5.8.0 のインストール。既存の Perl を壊さないように、/usr/local/perl-5.8 以下にインストールする。

$ tar zxvf perl-5.8.0.tar.gz
$ cd perl-5.8.0
$ ./Configure -de -Duseithreads \
      -Dcc=gcc_2_95_3 \
      -Dprefix=/usr/local/perl-5.8 \
      -Uinstallusrbinperl
$ make
$ make test
$ sudo make install

-Uinstallusrbinperl を指定しておかないと、既存の /usr/bin/perl が上書きされちゃうので注意。

$ /usr/local/perl-5.8/bin/perl -V:useithreads
useithreads='define';

となれば、ithread が有効になってインストールされている。

...

さて、スレッドを使ったサンプルアプリということで、ab (Apache Bench)もどきを作ってみる。 ソースは こちら

#!/usr/local/perl-5.8/bin/perl

use strict;
use threads;
use threads::shared;
use LWP::Simple;
use Getopt::Std;
use Time::HiRes qw(tv_interval gettimeofday);

my %opts;
getopt('cnu', \%opts);

my $total_request : shared = 0;  # $total_request は共有変数
my $success : shared = 0;        # $success は共有変数
my $failed  : shared = 0;        # $failed は共有変数

sub request
{
    my ($url) = @_;
    while($total_request < $opts{n}){
        if(get($url)){           # LWP::Simple:get でドキュメントを取得
            lock($success);      # $success をロック
            $success++;
        }else{
            lock($failed);
            $failed++;
        }
        {
            lock($total_request);
            $total_request++;
        }
        threads->yield;          # CPUを開放、他のスレッドに使わせる
    }
}

my $start_time = [gettimeofday]; # 開始時間設定
my @t;
foreach my $i(1 .. $opts{c}){    # -c で指定した数だけスレッドを起動
    $t[$i] = threads->new(\&request, $opts{u}); 
}

foreach my $i(1 .. $opts{c}){    # すべてのスレッドが終了するまで待つ
    $t[$i]->join;
}

my $end_time = tv_interval($start_time); # 実行時間を求める
my $request_per_second = sprintf("%.2f", $total_request / $end_time);

print <<END;
Concurrency Level:      $opts{c}
Time taken for tests:   $end_time seconds
Complete requests:      $success
Failed requests:        $failed
Requests per second:    $request_per_second [#/sec] (mean)
END

で、これを

$ ./fake_ab.pl -c 10 -n 1000 -u http://aqua/

として実行する。 -c は同時実行スレッド(多重度)、-n は総リクエスト回数。

Concurrency Level:      10
Time taken for tests:   5.65637 seconds
Complete requests:      1009
Failed requests:        0
Requests per second:    178.38 [#/sec] (mean)

このような出力を得る。ab の出力を、部分的にパクってみた。

...

取得する URL をいろいろ変えて、本家 ab との Requests per second を比較してみると、以下のとおり。

/ (ssi な HTML) ... (1)
  fake_ab.pl : 178.38
  ab         : 234.25

/cgi-bin/printenv.cgi (環境変数を表示するだけの mod_perl CGI) ... (2)
  fake_ab.pl : 105.95
  ab         : 121.58

/cgi-bin/index.cgi?rm=view (PostgreSQL に接続して数十レコードを閲覧する mod_perl CGI) ... (3)
  fake_ab.pl : 5.68
  ab         : 5.55

ab は C で書かれてコンパイルされた実行ファイルなので、動作が速い。 それに比べると Perl で実装された fake_ab.pl は遅いので、 単純な HTML を GET するような場合 (1) は、サーバの速度を最大限に引き出せていない。

が、比較的重い CGI (mod_perl) を実行するような場合 (3) なら、クライアントの速度差が無視できるので、 ほぼ同一の結果が得られる。

ふむ。なかなか簡単。


powered by blosxom