Oneman 0.27 – テンプレートの一括コンパイル機能

Oneman 0.27を公開しました。

今回の変更はPSGIサーバではなくWAFの方がメインで、テンプレートの一括コンパイル機能を追加しています。

use FindBin qw( $Bin );
require MyWebApp;

my $psgi_app = MyWebApp->psgi_app(
    template_dir => "$Bin/templates",
    precompile => qr/ .html z/xms,
);

template_dirで指定したディレクトリ(指定していない場合はカレントディレクトリ)配下をFile::Findを使ってサブディレクトリも含めて辿り、precompileで指定した正規表現にマッチするパスのファイルを事前にコンパイルします。

通常は、ユーザからのリクエストを受け付けてWebアプリが動いてから、必要になった時に初めてコンパイルされます。マルチプロセスのサーバの場合、このコンパイルはforkされてからの処理になるためメモリ効率が良くありません。

precompileオプションを使い、PSGIアプリケーションをコンパイルする時点でテンプレートもあらかじめコンパイルしておく事で、COWの効果を得る事ができます。

また、PSGIではなくmod_perlの環境では、cache()メソッドを使う事でスマートに書く事ができます。

まずstartup.plに以下のように書きます。

use FindBin qw( $Bin );

require MyWebApp;

MyWebApp->build(
    template_dir => "$Bin/templates",
    precompile => qr/ .html z/xms,
)->cache;

cache()メソッドにより、Onemanのメモリ空間にアプリケーションがキャッシュされます。

次にModPerl::Registryで動作するCGIファイルです。

MyWebApp->cache->run;

これでキャッシュしたアプリケーションが動作します。

この機会に、Onemanアプリケーションの様々な使い方をまとめてみます。

# PSGI ファイル内で完結するアプリケーション
Oneman::psgi_app( sub { ... }, ... );

# CGI ファイル内で完結するアプリケーション
Oneman::run( sub { ... }, ... );

# .pl ファイル内で完結するアプリケーションサーバ
Oneman::psgi_app( sub { ... }, ... )->start(...);
or
Oneman::build( sub { ... }, ... )->start(...); # build は psgi_app のエイリアス

# Oneman を継承したアプリケーションモジュールの $psgi_app
MyWebApp->psgi_app(...);

# Oneman を継承したアプリケーションモジュールの CGI 実行
MyWebApp->run(...);

# Oneman を継承したアプリケーションモジュールをサーバとして実行
MyWebApp->psgi_app(...)->start(...);
or
MyWebApp->build(...)->start(...);

# Oneman を継承したアプリケーションモジュールをキャッシュしてから実行
MyWebApp->build(...)->cache; # キャッシュ
MyWebApp->cache->run(...); # 実行

# Oneman の一部機能(テンプレート処理機能等)だけを使いたい場合
my $oneman = Oneman->core(...);
print $oneman->get_content(...);

ソース

http://www.tamashiro.org/src/Oneman-0.27/
http://www.tamashiro.org/src/Oneman-0.27.tar.gz

Oneman 0.26 – 劇的に速度向上、というかこれまでが遅すぎた

Oneman 0.22で触れたとおり、OnemanはKeep-Aliveの実現のためにselectと1バイト取得の連続でリクエストヘッダの読み込みを行っていたため、ヘッダサイズが大きい場合の速度低下が顕著でした。
でもよくよく考えてみると、Oneman 0.21で実装した入力ストリームのバッファリングをリクエスト処理全体で行えば、リクエストラインとヘッダを取得した際にバッファリングしたリクエストボディやパイプラインリクエストを、後の処理に渡せる事に気付きました。気付くのが遅い。。

そこで、Oneman 0.26では入力ストリームを64KiBずつバッファリングするように変更しました。その結果、速度が劇的に向上しています。

Oneman 0.25 (Keep-Alive)
    Requests per second:    812.36 [#/sec] (mean)

Oneman 0.26 (Keep-Alive)
    Requests per second:    2402.44 [#/sec] (mean)

Starman 0.3008 (Keep-Alive)
    Requests per second:    2412.10 [#/sec] (mean)

Oneman 0.25 (No Keep-Alive)
    Requests per second:    745.69 [#/sec] (mean)

Oneman 0.26 (No Keep-Alive)
    Requests per second:    1325.19 [#/sec] (mean)

Starman 0.3008 (No Keep-Alive)
    Requests per second:    797.39 [#/sec] (mean)

※いずれも、5回×3セット=15回中の最高値を抜粋しています。

このベンチマークはOneman 0.22の時と同様ですが、Keep-Aliveの扱いにミスがあったので、今回は一部を変更しての実施です。

当時Keep-Aliveを有効にするために、Apache Benchにオプション-kを指定していたのですが、あらためて確認すると、Apache Benchの結果がKeep-Alive requests: 0となっていました。なぜKeep-Aliveが無効になっていたか?
確認したところ、まずStarmanは、出力がストリームになっていたためでした。Apache BenchはHTTP/1.0でリクエストを行うため、サーバはchunkedエンコーディングが使用できません。Hello.psgiはContent-Lengthヘッダを持たないため、chunkedエンコーディングが使用できないStarmanはストリームで出力した上でコネクションを切断していました。
次にOnemanは、当時のバージョン0.22ではConnectionヘッダを出力していなかったためでした。Apache Benchは、レスポンスヘッダにConnectionヘッダによるKeep-Aliveの指定が無い場合、コネクションを切断する仕様のようです。バージョン0.25でConnectionヘッダを付加するよう変更したため、現在はApache BenchのKeep-Aliveに対応できています。

ベンチマークに使用するHello.psgiには、StarmanにKeep-Aliveを行わせるためにContent-Lengthヘッダを追加しました。

my $app = sub {
    my $env = shift;
    return [
        200,
        [ 'Content-Type' => 'text/plain', 'Content-Length' => 11 ],
        [ "Hello World" ],
    ];
};

前回と同様に、OnemanとStarmanをそれぞれ以下のように実行して、Apache Benchにより秒間リクエスト処理数を計測します。

$ perl -MOneman -e 'oneman "Hello.psgi", workers => 10'
$ starman --workers 10 Hello.psgi

OnemanもしくはStarmanと、Apache Benchは、いずれもLet’snote CF-R8 (Windows 7) のVirtualBoxで動作するUbuntu上で実行します。Perlは5.16.2、Apache Benchのオプションは以下のとおり。

$ ab -c 10 -t 1 -k http://127.0.0.1:5000/

OnemanとStarmanを交互に3回実行して、それぞれでabを5回ずつ実行、計30回のベンチマークを取りました。
※抜粋のみで詳細の結果は載せていませんが、Onemanの前バージョンとの比較のためバージョン0.25も同様に計測しました。

Oneman 0.26 (Keep-Alive)
    Requests per second:    1730.11 [#/sec] (mean)
    Requests per second:    2344.59 [#/sec] (mean)
    Requests per second:    2368.74 [#/sec] (mean)
    Requests per second:    2316.48 [#/sec] (mean)
    Requests per second:    2391.14 [#/sec] (mean)

    Requests per second:    1700.56 [#/sec] (mean)
    Requests per second:    2317.97 [#/sec] (mean)
    Requests per second:    2358.85 [#/sec] (mean)
    Requests per second:    2402.44 [#/sec] (mean)
    Requests per second:    2366.34 [#/sec] (mean)

    Requests per second:    1803.13 [#/sec] (mean)
    Requests per second:    2362.14 [#/sec] (mean)
    Requests per second:    2322.59 [#/sec] (mean)
    Requests per second:    2381.22 [#/sec] (mean)
    Requests per second:    2377.59 [#/sec] (mean)

Starman 0.3008 (Keep-Alive)
    Requests per second:    1736.05 [#/sec] (mean)
    Requests per second:    2375.56 [#/sec] (mean)
    Requests per second:    2406.15 [#/sec] (mean)
    Requests per second:    2412.10 [#/sec] (mean)
    Requests per second:    2392.75 [#/sec] (mean)

    Requests per second:    1693.59 [#/sec] (mean)
    Requests per second:    2352.28 [#/sec] (mean)
    Requests per second:    2369.86 [#/sec] (mean)
    Requests per second:    2382.01 [#/sec] (mean)
    Requests per second:    2287.22 [#/sec] (mean)

    Requests per second:    1783.65 [#/sec] (mean)
    Requests per second:    2374.16 [#/sec] (mean)
    Requests per second:    2405.08 [#/sec] (mean)
    Requests per second:    2377.22 [#/sec] (mean)
    Requests per second:    2337.31 [#/sec] (mean)

また、Keep-Aliveを使用しない場合のベンチマークも取りました。結果は以下のとおりです。

$ ab -c 10 -t 1 http://127.0.0.1:5000/
Oneman 0.26 (No Keep-Alive)
    Requests per second:    970.57 [#/sec] (mean)
    Requests per second:    1258.08 [#/sec] (mean)
    Requests per second:    1299.39 [#/sec] (mean)
    Requests per second:    1319.84 [#/sec] (mean)
    Requests per second:    1273.80 [#/sec] (mean)

    Requests per second:    930.12 [#/sec] (mean)
    Requests per second:    1307.79 [#/sec] (mean)
    Requests per second:    1313.25 [#/sec] (mean)
    Requests per second:    1325.19 [#/sec] (mean)
    Requests per second:    1279.76 [#/sec] (mean)

    Requests per second:    977.43 [#/sec] (mean)
    Requests per second:    1289.74 [#/sec] (mean)
    Requests per second:    1297.43 [#/sec] (mean)
    Requests per second:    1308.71 [#/sec] (mean)
    Requests per second:    1276.44 [#/sec] (mean)

Starman 0.3008 (No Keep-Alive)
    Requests per second:    616.22 [#/sec] (mean)
    Requests per second:    769.84 [#/sec] (mean)
    Requests per second:    797.39 [#/sec] (mean)
    Requests per second:    756.97 [#/sec] (mean)
    Requests per second:    759.92 [#/sec] (mean)

    Requests per second:    539.82 [#/sec] (mean)
    Requests per second:    736.61 [#/sec] (mean)
    Requests per second:    707.97 [#/sec] (mean)
    Requests per second:    681.39 [#/sec] (mean)
    Requests per second:    728.07 [#/sec] (mean)

    Requests per second:    491.34 [#/sec] (mean)
    Requests per second:    584.63 [#/sec] (mean)
    Requests per second:    668.69 [#/sec] (mean)
    Requests per second:    668.10 [#/sec] (mean)
    Requests per second:    698.69 [#/sec] (mean)

ソース

http://www.tamashiro.org/src/Oneman-0.26/
http://www.tamashiro.org/src/Oneman-0.26.tar.gz