blog20100901

2013/08/20 - プログラミング言語 Perl にまつわる etc. - Perl monger
参考 : perldoc, perldoc.jp, search.cpan.org, perldoc.perl.org ...
「 初めての Perl 第 6 版 」(オライリー・ジャパン発行 ISBN978-4-87311-567-2) 」
「 続・初めての Perl 改訂版 」(オライリー・ジャパン発行 ISBN4-87311-305-9) 」
「 Effective Perl 第 2 版 」(翔泳社発行 ISBN978-4-7981-3981-4) 」 ... etc,.

Perl Perl_7 mod_perl 翻訳 Web Server

Perl mp1 翻訳 パフォーマンスチューニング (d222)

目次 - Perl Index


Theme



Perl について、復習を兼ねて断片的な情報を掲載して行く連載その d222 回。

今回は、mod_perl「 Documentation / General Documentation / Part V: mod_perl Advocacy / Popular Perl Complaints and Myths 」で言及のあった mod_perl 1.0 向けのドキュメント「 Stas Bekman's Performance Guide (Performance Tuning) 」を翻訳して確認します。(!) 後継の mod_perl 2 は多くの変更点があります。詳細は d193 等を参照してください。

正確な内容は 原文 を確認してください。誤解や誤訳がある場合はご指摘ください。




説明 : Description



An exhaustive list of various techniques you might want to use to get the most performance possible out of your mod_perl server: configuration, coding, memory use, and more.

あなたの mod_perl サーバから可能な限り最大限のパフォーマンスをゲットするためにあなたが使いたいと思う様々なテクニックの徹底的なリストです: 構成, コーディング, メモリ利用, その他.


全体像 : The Big Picture



To make the user's Web browsing experience as painless as possible, every effort must be made to wring the last drop of performance from the server. There are many factors which affect Web site usability, but speed is one of the most important. This applies to any webserver, not just Apache, so it is very important that you understand it.

ユーザの Web ブラウジング体験を可能な限り苦痛のないようにするためには, サーバからのパフォーマンスを最後の一滴まで絞りとらなければなりません. Web サイトのユーザビリティに影響する多くのファクタがありますが, スピードはもっとも重要なもののひとつです. これは Apache だけでなく, あらゆる web サーバに当てはまりますので, それをあなたが理解しておくことはとても重要です.
How do we measure the speed of a server? Since the user (and not the computer) is the one that interacts with the Web site, one good speed measurement is the time elapsed between the moment when she clicks on a link or presses a Submit button to the moment when the resulting page is fully rendered.

私たちはどのようにしてサーバのスピードを測るのでしょうか ? ユーザ (コンピュータではない) は Web サイトと相互作用するもので, グッドなスピード計測のひとつは彼女がリンクをクリックするかサブミットボタンを押した瞬間とその結果のページが完全にレンダリングされる瞬間までの間に経過する時間です.
The requests and replies are broken into packets. A request may be made up of several packets, a reply may be many thousands. Each packet has to make its own way from one machine to another, perhaps passing through many interconnection nodes. We must measure the time starting from when the first packet of the request leaves our user's machine to when the last packet of the reply arrives back there.

リクエストやリプライはパケットに散り散りになります. リクエストはいくつかのパケットに, リプライは数千にされるかもしれません. 各パケットはあるマシンから他へそれぞれの道を, おそらく多くの相互接続ノードを通過しなければなりません. 私たちはリクエストの最初のパケットが私たちのユーザのマシンを離れたときからリプライの最後のパケットがそこに戻るまでの時間を計測しなければなりません.
A webserver is only one of the entities the packets see along their way. If we follow them from browser to server and back again, they may travel by different routes through many different entities. Before they are processed by your server the packets might have to go through proxy (accelerator) servers and if the request contains more than one packet, packets might arrive to the server by different routes with different arrival times, therefore it's possible that some packets that arrive earlier will have to wait for other packets before they could be reassembled into a chunk of the request message that will be then read by the server. Then the whole process is repeated in reverse.

web サーバはパケットがその経路の途中でみかけるエンティティのひとつでしかありません. もし私たちがブラウザからサーバへそして再び戻ってゆくそれらをフォローするなら, 多くの異なるエンティティを通り異なるルートを移動することでしょう. それらがあなたのサーバで処理される前にそのパケットはプロキシ (アクセラレータ) サーバを通過しなければならないかもしれませんしリクエストが複数のパケットを含んでいるなら, パケットは異なる到着時間で異なるルートによってサーバに到着するかもしれず, したがって早く到着したあるパケットはそれらがそのサーバによって読まれるリクエストメッセージのチャンク (# 塊り) に再構築される前に他のパケットを待たなければならない可能性があります. そしてこのプロセス全体は逆に繰り返されます.
You could work hard to fine tune your webserver's performance, but a slow Network Interface Card (NIC) or a slow network connection from your server might defeat it all. That's why it's important to think about the Big Picture and to be aware of possible bottlenecks between the server and the Web.

あなたはあなたのウェブサーバのパフォーマンスを微調整するためにハードワークできますが, 遅いネットワークカード (NIC) やあなたのサーバからの遅いネットワーク接続はすべてを打ち負かすかもしれません. だからこそ全体像について考えてサーバと Web との間のボトルネックを認識することが重要なのです.
Of course there is little that you can do if the user has a slow connection. You might tune your scripts and webserver to process incoming requests ultra quickly, so you will need only a small number of working servers, but you might find that the server processes are all busy waiting for slow clients to accept their responses.

もちろんユーザが遅い接続をもっている場合にあなたが行えることは少しだけです. あなたは着信のリクエストを超速で処理するためにあなたのスクリプトと web サーバを調整できるでしょうから, あなたが必要とするワーキングサーバの数は少しだけにできますが, あなたは遅いクライアントのレスポンスの受け入れを待つためにサーバプロセスがすべてビジーであることを見つけるかもしれません.
But there are techniques to cope with this. For example you can deliver the respond after it was compressed. If you are delivering a pure text respond--gzip compression will sometimes reduce the size of the respond by 10 times.

しかしこれに対処するためのテクニックがあります. 例えばあなたはレスポンスが圧縮された後にそれを配信できます. もしあなたがピュアなテキストのレスポンスを配信しているなら -- gzip 圧縮はそのレスポンスのサイズを 10 倍削減する場合があります.
You should analyze all the involved components when you try to create the best service for your users, and not the web server or the code that the web server executes. A Web service is like a car, if one of the parts or mechanisms is broken the car may not go smoothly and it can even stop dead if pushed too far without first fixing it.

あなたはあなたがあなたのユーザのためのベストなサービスを作成するときにすべての関連するコンポーネントを分析しなければなりません, そしてそれは web サーバや web サーバが実行するコードではありません. Web サービスは自動車のようなもので, あるパーツやメカニズムが壊れているとその自動車はスムーズにはいかなくなるかもしれませんし最初にそれをフィックスせずに無理をするとその自動車はストップして死んでしまうことさえあります.
And let me stress it again--if you want to have a success in the web service business you should start worrying about the client's browsing experience and not only how good your code benchmarks are.

そしてそれは再び私に強調させます -- もしあなたが web サービスビジネスでの成功を掴みたいならあなたはクライアントのブラウジング体験の心配からスタートすべきでありあなたのコードベンチマークがどのようにグッドかだけではありません.


システム分析 : System Analysis



Before we try to solve a problem we need to identify it. In our case we want to get the best performance we can with as little monetary and time investment as possible.

私たちが問題の解決をトライする前に私たちはそれを特定する必要があります. 私たちのケースで私たちはできるだけ少ない資金と時間投資でベストなパフォーマンスをゲットしたいと思っています.


ソフトウェア要件 : Software Requirements



Covered in the section "Choosing an Operating System".

セクション "Choosing an Operating System" でカバーされています.


ハードウェア要件 : Hardware Requirements



(META: Only partial analysis. Please submit more points. Many points are scattered around the document and should be gathered here, to represent the whole picture. It also should be merged with the above item!)

(META: 部分的な分析のみ. より多くのポイントを投稿してください. 多くのポイントがドキュメント周辺に散らばっているのでここに集められるべきです, その全体像を表すために. またそれは上記アイテムと統合されるべきです !)
You need to analyze all of the problem's dimensions. There are several things that need to be considered:

あなたはすべての問題のディメンション (# 寸法) を分析する必要があります. 考慮する必要があるものがいくつかあります:

  • How long does it take to process each request?

    各リクエストの処理にどのくらい時間がかかりますか ?

  • How many requests can you process simultaneously?

    あなたが同時に処理できるリクエストはいくつありますか ?

  • How many simultaneous requests are you planning to get?

    あなたは同時にいくつののリクエストをゲットする計画ですか ?

  • At what rate are you expecting to receive requests?

    あなたはどれくらいの割合でリクエストを受け取ると予想しますか ?


The first one is probably the easiest to optimize. Following the performance optimization tips in this and other documents allows a perl (mod_perl) programmer to exercise their code and improve it.

最初のものはおそらく最適化がもっとも簡単です. これと他のドキュメントのパフォーマンス最適化チップスに従うことで perl (mod_perl) プログラマはそのコードを演習しそれを改善することができます.
The second one is a function of RAM. How much RAM is in each box, how many boxes do you have, and how much RAM does each mod_perl process use? Multiply the first two and divide by the third. Ask yourself whether it is better to switch to another, possibly just as inefficient language or whether that will actually cost more than throwing another powerful machine into the rack.

2 つ目のものは RAM の機能です. 各ボックスにどのくらいの RAM があり, あなたがいくつのボックスをもっていて, どのくらいの RAM を各 mod_perl プロセスが使うでしょうか. 最初の 2 つを乗算して 3 つ目を除算します. 非効率な言語と同様に, 他にスイッチすることがベターなのかあるいは別のパワフルなマシンをラックに投入することなのかどちらが実際は高コストなのかをあなた自身に尋ねてください.
Also ask yourself whether switching to another language will even help. In some applications, for example to link Oracle runtime libraries, a huge chunk of memory is needed so you would save nothing even if you switched from Perl to C.

他の言語への切り替えが助けになるかさえもあなた自身に尋ねてください. 一部のアプリケーションでは, 例えば Oracle ランタイムライブラリにリンクするために, メモリの巨大なチャンク (# 塊り) が必要とされるのであなたが Perl から C にスイッチしたとしてもあなた何も節約できないでしょう.
The last two are important. You need a realistic estimate. Are you really expecting 8 million hits per day? What is the expected peak load, and what kind of response time do you need to guarantee? Remember that these numbers might change drastically when you apply code changes and your site becomes popular. Remember that when you get a very high hit rate, the resource requirements don't grow linearly but exponentially!

最後の 2 つは重要です. あなたは現実的な見積もりを必要とします. あなたは本当に 800 万ヒット/day を期待しますか ? 予想されるピーク負荷はどのくらいで, あなた保証しなければならないレスポンスタイムはどのようなものですか ? あなたがとても高いヒット率をゲットすると, リソース要件は直線的でなく指数関数的に増えることを覚えておいてください !
More coverage is provided in the section "Choosing Hardware".

より広い範囲はセクション "Choosing Hardware" で提供されています.


不可欠なツール : Essential Tools



In order to improve performance we need measurement tools. The main tool categories are benchmarking and code profiling.

パフォーマンスを向上させるために私たちにはツールの測定が必要です. メインのツールカテゴリはベンチマークとコードプロファイリングです.
It's important to understand that in a major number of the benchmarking tests that we will execute we will not look at the absolute result numbers but the relation between the two and more result sets, since in most cases we would try to show which coding approach is preferable and the you shouldn't try to compare the absolute results collected while running the same benchmarks on your machine, since you won't have the exact hardware and software setup anyway. So this kind of comparison would be misleading. Compare the relative results from the tests running on your machine, don't compare your absolute results with those in this Guide.

私たちが実行するベンチマークの大部分で私たちは絶対的な結果の数値ではなく 2 つ以上の結果のセットの関係性を確認するのだと理解することが重要です, ほとんどのケースで私たちはどのコーディングアプローチが好ましいかを示そうとするからであってあなたはあなたのマシンで同じベンチマークを実行して集められた絶対的なリザルトの比較を試すべきではありません, あなたは全く同じハードウェアとソフトウェアのセットアップを何らもっていないだろうからです. ですからこの種の比較はミスリーディングするかもしれません. あなたのマシンで実行しているテストからの相対的な結果を比較してください, このガイドでの絶対的な結果とあなたのそれを比較してはいけません.


ベンチマークアプリケーション : Benchmarking Applications



How much faster is mod_perl than mod_cgi (aka plain perl/CGI)? There are many ways to benchmark the two. I'll present a few examples and numbers below. Check out the benchmark directory of the mod_perl distribution for more examples.

mod_perl は mod_cgi (aka プレーンな perl/CGI) よりもどのくらい速いでしょうか ? この 2 つをベンチマークするための方法はたくさんあります. 私はいくつかの例と数値を以下に示します. より多くの例は mod_perl ディストリビューションの benchmark ディレクトリをチェックしてください.
If you are going to write your own benchmarking utility, use the Benchmark module for heavy scripts and the Time::HiRes module for very fast scripts (faster than 1 sec) where you will need better time precision.

もしあなたがあなた独自のベンチマークユーティリティを書くのであれば, ヘビーなスクリプトでは Benchmark モジュールをとても高速なスクリプト (1 sec より高速) であなたがベターな時間精度を必要とするところには Time::HiRes モジュールを使います.
There is no need to write a special benchmark though. If you want to impress your boss or colleagues, just take some heavy CGI script you have (e.g. a script that crunches some data and prints the results to STDOUT), open 2 xterms and call the same script in mod_perl mode in one xterm and in mod_cgi mode in the other. You can use lwp-get from the LWP package to emulate the browser. The benchmark directory of the mod_perl distribution includes such an example.

特別なベンチマークを書く必要はありません. もしあなたがあなたのボスや同僚を感心させたいなら, あなたが持っている何かヘビーなスクリプト (e.g 何かのデータをクランチして結果を STDOUT に出力するスクリプト) をとりあげて, 2 つの xterms をオープンし 1 つの xterm では mod_perl モードでもう一方では mod_cgi モードで同じスクリプトをコールするだけです. あなたはブラウザをエミュレートするために LWP パッケージからの lwp-get を使えます. mod_perl ディストリビューションの benchmark ディレクトリはそのような例を含んでいます.
See also two tools for benchmarking: ApacheBench and crashme test

ベンチマークのための 2 つのツールも参照してください: ApacheBenchcrashme test です


Perl コードのベンチマーク : Benchmarking Perl Code



If you are going to write your own benchmarking utility, use the Benchmark module and the Time::HiRes module where you need better time precision (less than 10msec).

もしあなたがあなた独自のベンチマークユーティリティを書くなら, Benchmark モジュールとあなたがベターな時間精度 (10msec 未満) を必要とするところでは Time::HiRes モジュールを使います.
An example of the Benchmark.pm module usage:

Benchmark.pm モジュールの利用法の例です:

benchmark.pl
------------
use Benchmark;

timethis (1_000,
sub {
my $x = 100;
my $y = log ($x ** 100) for for (0..10000);
});


% perl benchmark.pl
timethis 1000: 25 wallclock secs (24.93 usr + 0.00 sys = 24.93 CPU)

If you want to get the benchmark results in micro-seconds you will have to use the Time::HiRes module, its usage is similar to Benchmark's.

あなたがマイクロ秒でのベンチマーク結果をゲットしたいならあなたは Time::HiRes モジュールを使う必要があります, その使用方法は Benchmark のものと似ています.

use Time::HiRes qw(gettimeofday tv_interval);
my $start_time = [ gettimeofday ];
sub_that_takes_a_teeny_bit_of_time();
my $end_time = [ gettimeofday ];
my $elapsed = tv_interval($start_time,$end_time);
print "The sub took $elapsed seconds."

See also the crashme test.

crachme test も参照してください.


永続的な DB 接続でのグラフィックヒットカウンタのベンチマーク : Benchmarking a Graphic Hits Counter with Persistent DB Connections



Here are the numbers from Michael Parker's mod_perl presentation at the Perl Conference (Aug, 98). (Sorry, there used to be links here to the source, but they went dead one day, so I removed them). The script is a standard hits counter, but it logs the counts into a mysql relational DataBase:

こちらは Perl カンファレンス (Aug, 98) での Michael Parker の mod_perl プレゼンテーションからの数値です. (すみません, ここにはソースへのリンクがありましたが, それはある日なくなっていましたので, 私はそれを削除しました). このスクリプトはスタンダードなヒットカウンタですが, カウントを mysql リレーショナルデータベースに記録します.

Benchmark: timing 100 iterations of cgi, perl... [rate 1:28]

cgi: 56 secs ( 0.33 user 0.28 sys = 0.61 cpu)
perl: 2 secs ( 0.31 user 0.27 sys = 0.58 cpu)

Benchmark: timing 1000 iterations of cgi, perl... [rate 1:21]

cgi: 567 secs ( 3.27 user 2.83 sys = 6.10 cpu)
perl: 26 secs ( 3.11 user 2.53 sys = 5.64 cpu)

Benchmark: timing 10000 iterations of cgi, perl... [rate 1:21]

cgi: 6494 secs ( 34.87 user 26.68 sys = 61.55 cpu)
perl: 299 secs ( 32.51 user 23.98 sys = 56.49 cpu)

We don't know what server configurations were used for these tests, but I guess the numbers speak for themselves.

私たちはこのテストのためにどのようなサーバ構成が使われているかを知りませんが, 私は数値がそれを物語っているとおもいます.
The source code of the script was available at http://www.realtime.net/~parkerm/perl/conf98/sld006.htm. It's now a dead link. If you know its new location, please let me know.

このスクリプトのソースコードは http://www.realtime.net/~parkerm/perl/conf98/sld006.htm で利用可能でした. 今はリンク切れしています. もしあなたが新しいロケーションを知っているなら, お知らせください。


ベンチマークのレスポンスタイム : Benchmarking Response Times



In the next sections we will talk about tools that allow us to benchmark response times.

次のセクションでは私たちはレスポンスタイムのベンチマークをできるようにするツールについて話します.


ApacheBench



ApacheBench (ab) is a tool for benchmarking your Apache HTTP server. It is designed to give you an idea of the performance that your current Apache installation can give. In particular, it shows you how many requests per second your Apache server is capable of serving. The ab tool comes bundled with the Apache source distribution.

ApacheBench (ab) はあなたの Apache HTTP サーバをベンチマークするためのツールです. それはあなたの現在の Apache インストールが与えることができるパフォーマンスのアイデアをあなたに与えるように設計されています. 特に, それはあなたの Apache サーバが秒あたりどのくらいのリクエストをサーブできるかをあなたに示します. ab ツールは Apache ソースディストリビューションにバンドルされています.
Let's try it. We will simulate 10 users concurrently requesting a very light script at www.example.com/perl/test.pl. Each simulated user makes 10 requests.

それを試してみましょう. 私たちは www.example.com/perl/test.pl でとても軽いスクリプトを同時にリクエストする 10 ユーザをシミュレーションします. シミュレートされたユーザは 10 のリクエストをします.

% ./ab -n 100 -c 10 www.example.com/perl/test.pl

The results are:

この結果は:

Document Path: /perl/test.pl
Document Length: 319 bytes

Concurrency Level: 10
Time taken for tests: 0.715 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 60700 bytes
HTML transferred: 31900 bytes
Requests per second: 139.86
Transfer rate: 84.90 kb/s received

Connection Times (ms)
min avg max
Connect: 0 0 3
Processing: 13 67 71
Total: 13 67 74

We can see that under load of ten concurrent users our server is capable of processing 140 requests per second. Of course this benchmark is correct only when the script under test is used. We can also learn about the average processing time, which in this case was 67 milli-seconds. Other numbers reported by ab may or may not be of interest to you.

私たちは 10 の同時ユーザの負荷のもとで私たちのサーバは秒あたり 140 リクエストを処理できることを見れます. もちろんこのベンチマークはテストでこのスクリプトが使われたときにのみ正しいものです. 私たちはアベレージ処理時間についても学ぶことができます, それはこのケースでは 67 ミリ秒でした. ab によってレポートされた他の数値はあなたにとって興味深いかもしれませんしそうではないかもしれません.
For example if we believe that the script perl/test.pl is not efficient we will try to improve it and run the benchmark again, to see whether we have any improve in performance.

例えば私たちがこのスクリプト perl/test.pl が効果的ではないと確信しているなら私たちが何らか改善されたパフォーマンスをもてたのかどうかを確認するために, 私たちはそれを改善して再びベンチマークにトライできます.
HTTPD::Bench::ApacheBench, available from CPAN, provides a Perl interface for ab.

HTTPD::Bench::ApacheBench は, CPAN から利用可能で, ab 用の Perl インターフェイスを提供します.


httperf



httperf is a utility written by David Mosberger. Just like ApacheBench, it measures the performance of the webserver.

httperf は David Mosberger によって書かれたユーティリティです. ちょうど ApacheBench のように, これは web サーバのパフォーマンスを測定します.
A sample command line is shown below:

サンプルのコマンドラインは以下で示します:

httperf --server hostname --port 80 --uri /test.html \
--rate 150 --num-conn 27000 --num-call 1 --timeout 5

This command causes httperf to use the web server on the host with IP name hostname, running at port 80. The web page being retrieved is /test.html and, in this simple test, the same page is retrieved repeatedly. The rate at which requests are issued is 150 per second. The test involves initiating a total of 27,000 TCP connections and on each connection one HTTP call is performed. A call consists of sending a request and receiving a reply.

このコマンドは httperf を IP 名 hostname, port 80 で走っている web サーバを使うようにします. 取得される web ページは /test.html で, このシンプルなテストでは, 同じページが繰り返し取得されます. リクエストが発行されるレートは秒あたり 150 です. このテストは合計 27,000 TCP コネクションを伴い各コネクションではひとつの HTTP コールが実行されます. コールは送信するリクエストと受信するリプライで構成されます.
The timeout option defines the number of seconds that the client is willing to wait to hear back from the server. If this timeout expires, the tool considers the corresponding call to have failed. Note that with a total of 27,000 connections and a rate of 150 per second, the total test duration will be approximately 180 seconds (27,000/150), independently of what load the server can actually sustain. Here is a result that one might get:

タイムアウトオプションはクライアントがサーバからの応答を待つのをいとわない秒数を定義します. このタイムアウトが期限切れした場合, このツールは対応するコールが失敗したと見なします. 合計 27,000 コネクションと秒あたり 150 のレートでは, トータルのテスト期間がおよそ 180 秒 (27,000/150) になることに注目してください, そのサーバがどのくらいの負荷に実際耐えることができるかとは無関係です.こちらはそれがゲットするであろう結果です:

Total: connections 27000 requests 26701 replies 26701 test-duration 179.996 s

Connection rate: 150.0 conn/s (6.7 ms/conn, <=47 concurrent connections)
Connection time [ms]: min 1.1 avg 5.0 max 315.0 median 2.5 stddev 13.0
Connection time [ms]: connect 0.3

Request rate: 148.3 req/s (6.7 ms/req)
Request size [B]: 72.0

Reply rate [replies/s]: min 139.8 avg 148.3 max 150.3 stddev 2.7 (36 samples)
Reply time [ms]: response 4.6 transfer 0.0
Reply size [B]: header 222.0 content 1024.0 footer 0.0 (total 1246.0)
Reply status: 1xx=0 2xx=26701 3xx=0 4xx=0 5xx=0

CPU time [s]: user 55.31 system 124.41 (user 30.7% system 69.1% total 99.8%)
Net I/O: 190.9 KB/s (1.6*10^6 bps)

Errors: total 299 client-timo 299 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0

httperf download (# リンク切れ, 2021 現在は GitHub: https://github.com/httperf/httperf で maybe available)


http_load



http_load is yet another utility that does webserver load testing. It can simulate 33.6kbps modem connection (-throttle) and allows you to provide a file with a list of URLs, which we be fetched randomly. You can specify how many parallel connections to run using the -parallel N option, or you can specify the number of requests to generate per second with -rate N option. Finally you can tell the utility when to stop by specifying either the test time length (-seconds N) or the total number of fetches (-fetches N).

http_load は web サーバ負荷テストを行うまた別のユーティリティです. それは 33.6kbps のモデムコネクションをシミュレート (-throttle) できてあなたが URL のリストによるファイル提供をできるようにします, そしてそれは私たちにランダムにフェッチ (# 取得) されます. あなたは -parallel N オプションを使ってどのくらい並列コネクションを実行するかを指定できますし, あるいはあなたは -rate N オプションで秒あたりに生成するリクエストの数を指定できます. 最後にあなたはテスト時間の長さ (-second N) またはフェッチの合計数 (-fetches N) を指定することでいつストップするかをユーティリティに伝えることができます.
A sample run with the file urls including:

このファイルの urls を含んだサンプルの実行です:

http://www.example.com/foo/
http://www.example.com/bar/

We ask to generate three requests per second and run for only two seconds. Here is the generated output:

私たちは秒あたり 3 つのリクエストを生成して 2 秒だけ走るように依頼します. こちらが生成された出力です:

% ./http_load -rate 3 -seconds 2 urls
http://www.example.com/foo/: check-connect SUCCEEDED, ignoring
http://www.example.com/bar/: check-connect SUCCEEDED, ignoring
http://www.example.com/bar/: check-connect SUCCEEDED, ignoring
http://www.example.com/bar/: check-connect SUCCEEDED, ignoring
http://www.example.com/foo/: check-connect SUCCEEDED, ignoring
5 fetches, 3 max parallel, 96870 bytes, in 2.00258 seconds
19374 mean bytes/connection
2.49678 fetches/sec, 48372.7 bytes/sec
msecs/connect: 1.805 mean, 5.24 max, 0.79 min
msecs/first-response: 291.289 mean, 560.338 max, 34.349 min

So you can see that it has reported 2.5 requests per second. Of course for the real test you will want to load the server heavily and run the test for a longer time to get more reliable results.

ですから私たちはそれが秒あたり 2.5 リクエストのレポートをもっていることを見ることができます. もちろんリアルのテストではあなたはより頼りになる結果をゲットするために重い負荷をサーバにかけて長時間テストを実行したほうがよいでしょう。
Note that when you provide a file with a list of URLs make sure that you don't have empty lines in it. If you do -- the utility won't work complaining:

あなたが URL のリストでファイルを提供する時はあなたがそれに空行をもっていないかを確認するように注意してください. もしあなたがそれをすると -- そのユーティリティは文句をいって機能しません:

./http_load: unknown protocol -

http_load download


crashme スクリプト : the crashme Script



This is another crashme suite originally written by Michael Schilli (and was located at http://www.linux-magazin.de site, but now the link has gone). I made a few modifications, mostly adding my () operators. I also allowed it to accept more than one url to test, since sometimes you want to test more than one script.

これはもともと Michael Schilli によって書かれた別の crashme スイートです (そしてそれは http://www.linux-magazin.de サイトにおかれていましたが, 今はリンクがなくなっています). 私はいくつか修正をしました, 主に my () オペレーターを追加しました. 私はそれがテストのために 1 つ以上の url を受けとれるようにもしました, あなたが 1 つ以上のスクリプトをテストしたほうがよい場合があるからです.
The tool provides the same results as ab above but it also allows you to set the timeout value, so requests will fail if not served within the time out period. You also get values for Latency (seconds per request) and Throughput (requests per second). It can do a complete simulation of your favorite Netscape browser :) and give you a better picture.

このツールは上記 ab と同様の結果を提供しますがこれはあなたにタイムアウト値をセットできるようにするので, そのタイムアウト期間でサーブされない場合リクエストは失敗します. またあなたは Latency (リクエストあたりの秒) と Throughput (秒あたりのリクエスト) の値もゲットできます. これはあなたのお気に入りの Netscape ブラウザの完全なシミュレーションを行うことができて :) あなたにベターな描画を与えます.
I have noticed while running these two benchmarking suites, that ab gave me results from two and a half to three times better. Both suites were run on the same machine, with the same load and the same parameters, but the implementations were different.

私はこれら 2 つのベンチマークスイートを実行している間に気づきました, ab は 2.5 から 3 倍ベターな結果を私に与えたのです. 両方のスイートは同じマシン上で, 同じ負荷と同じパラメータで実行しましたが, その実施結果は違いました.
Sample output:

サンプルの出力:

URL(s): http://www.example.com/perl/access/access.cgi
Total Requests: 100
Parallel Agents: 10
Succeeded: 100 (100.00%)
Errors: NONE
Total Time: 9.39 secs
Throughput: 10.65 Requests/sec
Latency: 0.85 secs/Request

And the code:

そしてそのコード:
The LWP::Parallel::UserAgent benchmark: code/lwp-bench.pl

LWP::Parallel::UserAgent ベンチマーク: code/lwp-bench.pl (# ベンチマーク用 Perl コード (lwp-bench.pl) のダウンロードリンク)


PerlHandler のベンチマーク : Benchmarking PerlHandlers



The Apache::Timeit module does PerlHandler Benchmarking. With the help of this module you can log the time taken to process the request, just like you'd use the Benchmark module to benchmark a regular Perl script. Of course you can extend this module to perform more advanced processing like putting the results into a database for a later processing. But all it takes is adding this configuration directive inside httpd.conf:

Apache::Timeit モジュールは PerlHandler のベンチマークを行います. このモジュールの助けによってあなたはリクエストの処理にかかった時間を記録できます, ちょうどあなたが Benchmark モジュールを使って通常の Perl スクリプトのベンチマークを行うのと同じようにです. もちろんあなたは後で処理するために結果をデータベースに置くなどより高度な処理を実行するためにこのモジュールを拡張できます. しかし必要なことは httpd.conf 内に構成ディレクティブを追加するだけです:

PerlFixupHandler Apache::Timeit

Since scripts running under Apache::Registry are running inside the PerlHandler these are benchmarked as well.

Apache::Registry のもとで走るスクリプトは PerlHandler の内側で走るのでそれらもベンチマークされます.
An example of the lines which show up in the error_log file:

error_log ファイル内で表示される行の例です:

timing request for /perl/setupenvoff.pl:
0 wallclock secs ( 0.04 usr + 0.01 sys = 0.05 CPU)
timing request for /perl/setupenvoff.pl:
0 wallclock secs ( 0.03 usr + 0.00 sys = 0.03 CPU)

The Apache::Timeit package is a part of the Apache-Perl-contrib files collection available from CPAN.

Apache::Timeit パッケージは CPAN から利用可能な Apache-Perl-contrib ファイルコレクションの一部です.


その他のベンチマークツール : Other Benchmarking Tools



Other tools you may want to take a look at:

あなたがちょっと見てみたいかもしれない他のツール:

  • HTTP::WebTest
    HTTP::WebTest module runs tests on remote URLs or local web files containing Perl/JSP/HTML/JavaScript/etc. and generates a detailed test report.

    HTTP::WebTest モジュールはリモート URL や Perl/JSP/HTML/JavaScript/etc を含むローカルの web ファイルでテストを実行します. そして詳細なテストレポートを生成します.
    It's available from CPAN.

    これは CPAN から利用可能です.

  • HTTP::Monkeywrench
    HTTP::Monkeywrench is a test-harness application to test the integrity of a user's path through a web site.

    HTTP::Monkeywrench は web サイトを通るユーザの経路の整合性をテストするためのテストハーネスアプリケーションです.
    It's available from CPAN.

    これは CPAN から利用可能です.

  • Apache::Recorder and HTTP::RecordedSession
    Apache::Recorder is a mod_perl handler that records an HTTP session and stores it on the web server's file system. HTTP::RecordedSession reads the recorded session from the file system, and formats it for playback using HTTP::WebTest or HTTP::Monkeywrench. This is useful when writing acceptance and regression tests.

    Apache::Recorder は HTTP セッションを記録してそれを web サーバのファイルシステム上に格納する mod_perl ハンドラです. HTTP::RecordedSession はファイルシステムからその記録されたセッションを読みこんで, それを HTTP::WebTest や HTTP:Monkeywrench を使ってプレイバック (# 再生) するためにフォーマットします. これは受けいれや回帰のテストを書くときに便利です.
    It's available from CPAN.

    これは CPAN から利用可能です.

  • Webstone
    This tool is somewhat complex to set up, but once you get it running it gives you stats that you could only duplicate with ab or http_load if you did quite a bit of extra scripting around them. It also allows multiple client machines to be used for providing heavy loads. This tool is useful if you need to know things like at what point people start finding your sight slow, as opposed to at what point the server becomes unresponsive.

    このツールはセットアップがやや複雑ですが, あなたがこれを一度実行できればそれはあなたにあなたが ab や http_load の周辺でかなり追加のスクリプティングをしなければならないものと同じようなスタッツを与えます. またこれはヘビーな負荷を提供するために使われる複数のクライアントマシンも使えるようにします. このツールはサーバがレスポンスしなくなった時点ではなく, どの時点で人々があなたのサイトが遅いと感じ始めたかをあなたが知る必要がある場合に便利です.
    Webstone download

  • Flood
    Flood is a load-tester being developed through the Apache Software Foundation. From the Flood FAQ:

    Flood は Apache Software Foundation を介して開発されている負荷テスタです. Flood FAQ より:
    "Flood is a profile-driven HTTP load tester. In layman's terms, it means that flood is capable of generating large amounts of web traffic. Flood's flexibility and power arises in its configuration syntax. It is able to work well with dynamic content."

    "Flood はプロファイル駆動の HTTP 負荷テスタです. 素人の言葉では, flood が大量の web トラフィックを生成できることを意味します. Flood のフレキシビリティとパワーはその構成シンタックスに現れます. それは動的コンテンツでよく機能します."
    Flood download




コードのプロファイリングテクニック : Code Profiling Technigues



The profiling process helps you to determine which subroutines or just snippets of code take the longest time to execute and which subroutines are called most often. Probably you will want to optimize those.

プロファイリングのプロセスはどのサブルーチンやどこのコードスニペットが実行にもっとも長い時間を取るのかであったりどのサブルーチンがもっとも頻繁にコールされているのかを断定するためにあなたを助けます. おそらくあなたはそれらを最適化したいと思うはずです.
When do you need to profile your code? You do that when you suspect that some part of your code is called very often and may be there is a need to optimize it to significantly improve the overall performance.

いつあなたはあなたのコードのプロファイルを必要としますか ? あなたのコードの一部がとても頻繁にコールされていて全体的なパフォーマンス全体が著しく向上されるためには最適化の必要があるのではないかとあなたが疑うときにあなたはそれを行います.
For example if you have ever used the diagnostics pragma, which extends the terse diagnostics normally emitted by both the Perl compiler and the Perl interpreter, augmenting them with the more verbose and endearing descriptions found in the perldiag manpage. You know that it might tremendously slow you code down, so let's first prove that it is correct.

例えばもしあなたが diagnostics プラグマを使ったことがあるなら, それは Perl コンパイラと Perl インタプリタの両方によって普通は発行される簡潔な診断 (# diagnostics) を, perldiag man ページ内で見つかるより詳細で親しみやすい説明で増加します. それがあなたのコードをとてつもなく遅くすることをあなたは知っていますので, それが正しいことを最初に証明しましょう.
We will run a benchmark, once with diagnostics enabled and once disabled, on a subroutine called test_code.

私たちは test_code と呼ばれるサブルーチンで, 1 度は診断を有効化して 1 度は無効化して, ベンチマークを実行します.
The code inside the subroutine does an arithmetic and a numeric comparison of two strings. It assigns one string to another if the condition tests true but the condition always tests false. To demonstrate the diagnostics overhead the comparison operator is intentionally wrong. It should be a string comparison, not a numeric one.

このサブルーチン内のコードは 2 つの文字列の算術と数値比較を行います. これはもし条件テストが 真 ならある文字列を他に割当てますが条件テストは常に 偽 です. 診断のオーバーヘッドをデモンストレーションするためにこの比較演算子は意図的に間違えています. それは文字列比較であるべきで, 数値のそれではありません.

use Benchmark;
use diagnostics;
use strict;

my $count = 50000;

disable diagnostics;
my $t1 = timeit($count, \&test_code);

enable diagnostics;
my $t2 = timeit($count, \&test_code);

print "Off: ",timestr($t1),"\n";
print "On : ",timestr($t2),"\n";

sub test_code {
my ($a,$b) = qw(foo bar);
my $c;
if ($a == $b) {
$c = $a;
}
}

For only a few lines of code we get:

わずか数行のコードだけで私たちはこれをゲットします:

Off: 1 wallclock secs ( 0.81 usr + 0.00 sys = 0.81 CPU)
On : 13 wallclock secs (12.54 usr + 0.01 sys = 12.55 CPU)

With diagnostics enabled, the subroutine test_code() is 16 times slower, than with diagnostics disabled!

diagnostics が有効化されると, diagnostics を無効化したよりも, サブルーチン test_code() が 16 倍遅くなります!
Now let's fix the comparison the way it should be, by replacing == with eq, so we get:

では == を eq でリプレイスして, この比較をあるべき方法に修正しましょう, すると私たちはこれをゲットします:

my ($a,$b) = qw(foo bar);
my $c;
if ($a eq $b) {
$c = $a;
}

and run the same benchmark again:

そして同じベンチマークを再び実行します:

Off: 1 wallclock secs ( 0.57 usr + 0.00 sys = 0.57 CPU)
On : 1 wallclock secs ( 0.56 usr + 0.00 sys = 0.56 CPU)

Now there is no overhead at all. The diagnostics pragma slows things down only when warnings are generated.

いまオーバヘッドはまったくありません. diagnostics プラグマは警告が生成された場合にのみものごとを遅くします.
After we have verified that using the diagnostics pragma might adds a big overhead to execution runtime, let's use the code profiling to understand why this happens. We are going to use Devel::DProf to profile the code. Let's use this code:

diagnostics プラグマの使用は実行のランタイムに大きなオーバヘッドを追加しうることを私たちが確認した後で, なぜこれが起こるのかを理解するためにコードプロファイリングを使ってみましょう. 私たちはコードのプロファイルに Devel::Dprof を使います. このコードを使いましょう:

dignostics.pl
-------------
use diagnostics;
print "Content-type:text/html\n\n";
test_code();
sub test_code {
my ($a,$b) = qw(foo bar);
my $c;
if ($a == $b) {
$c = $a;
}
}

Run it with the profiler enabled, and then create the profiling stastics with the help of dprofpp:

これをプロファイラを有効化して実行します, それから dprofpp のヘルプでプロファイリング統計を作成します:

% perl -d:DProf diagnostics.pl
% dprofpp

Total Elapsed Time = 0.342236 Seconds
User+System Time = 0.335420 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
92.1 0.309 0.358 1 0.3089 0.3578 main::BEGIN
14.9 0.050 0.039 3161 0.0000 0.0000 diagnostics::unescape
2.98 0.010 0.010 2 0.0050 0.0050 diagnostics::BEGIN
0.00 0.000 -0.000 2 0.0000 - Exporter::import
0.00 0.000 -0.000 2 0.0000 - Exporter::export
0.00 0.000 -0.000 1 0.0000 - Config::BEGIN
0.00 0.000 -0.000 1 0.0000 - Config::TIEHASH
0.00 0.000 -0.000 2 0.0000 - Config::FETCH
0.00 0.000 -0.000 1 0.0000 - diagnostics::import
0.00 0.000 -0.000 1 0.0000 - main::test_code
0.00 0.000 -0.000 2 0.0000 - diagnostics::warn_trap
0.00 0.000 -0.000 2 0.0000 - diagnostics::splainthis
0.00 0.000 -0.000 2 0.0000 - diagnostics::transmo
0.00 0.000 -0.000 2 0.0000 - diagnostics::shorten
0.00 0.000 -0.000 2 0.0000 - diagnostics::autodescribe

It's not easy to see what is responsible for this enormous overhead, even if main::BEGIN seems to be running most of the time. To get the full picture we must see the OPs tree, which shows us who calls whom, so we run:

main::BEGIN がほとんどの時間実行されているように見えたところで, この膨大なオーバーヘッドの原因が何かを確認するのは簡単ではありません. 全体像をゲットするために私たちは OP (# OPeration) ツリーを見なければなりません, それは誰が誰をコールしているのかを私たちに示します, ですから私たちはこれを実行します:

% dprofpp -T

and the output is:

そしてその出力がこれです:

main::BEGIN
diagnostics::BEGIN
Exporter::import
Exporter::export
diagnostics::BEGIN
Config::BEGIN
Config::TIEHASH
Exporter::import
Exporter::export
Config::FETCH
Config::FETCH
diagnostics::unescape
.....................
3159 times [diagnostics::unescape] snipped
.....................
diagnostics::unescape
diagnostics::import
diagnostics::warn_trap
diagnostics::splainthis
diagnostics::transmo
diagnostics::shorten
diagnostics::autodescribe
main::test_code
diagnostics::warn_trap
diagnostics::splainthis
diagnostics::transmo
diagnostics::shorten
diagnostics::autodescribe
diagnostics::warn_trap
diagnostics::splainthis
diagnostics::transmo
diagnostics::shorten
diagnostics::autodescribe

So we see that two executions of diagnostics::BEGIN and 3161 of diagnostics::unescape are responsible for most of the running overhead.

そして私たちは diagnostics::BEGIN の 2 つの実行と diagnostics::unescape の 3161 (# 3159?) が実行中のオーバヘッドの大部分の原因であることを見ます.
If we comment out the diagnostics module, we get:

もし私たちが diagnostics モジュールをコメントアウトすると, 私たちはこれをゲットします:

Total Elapsed Time = 0.079974 Seconds
User+System Time = 0.059974 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
0.00 0.000 -0.000 1 0.0000 - main::test_code

It is possible to profile code running under mod_perl with the Devel::DProf module, available on CPAN. However, you must have apache version 1.3b3 or higher and the PerlChildExitHandler enabled during the httpd build process. When the server is started, Devel::DProf installs an END block to write the tmon.out file. This block will be called at server shutdown. Here is how to start and stop a server with the profiler enabled:

CPAN で利用可能な, Devel::Dprof では mod_perl のもとでコードを実行してプロファイルができるようになります. しかしながら, あなたは apache バージョン 1.3b3 以上をもち httpd のビルドプロセスの間で PerlChildExitHandler が有効化されていなければなりません. サーバがスタートされたとき, Devel::Dprof は tmon.out ファイルを書くために END ブロックをインストールします. このブロックはサーバのシャットダウン時にコールされます. こちらがプロファイラを有効化してサーバをスタートとストップする方法です (# csh 系):

% setenv PERL5OPT -d:Dprof
% httpd -X -d `pwd` &
... make some requests to the server here ...
(... ここでサーバにいくつかのリクエストをします ...)
% kill `cat logs/httpd.pid`
% unsetenv PERL5OPT
% dprofpp

The Devel::DProf package is a Perl code profiler. It will collect information on the execution time of a Perl script and of the subs in that script (remember that print() and map() are just like any other subroutines you write, but they come bundled with Perl!)

Devel::Dprof パッケージは Perl コードプロファイラです. それは Perl スクリプトとそのスクリプトのサブルーチンの実行時間を収集します (print() や map() はあなたが書いた他のサブルーチンとまるで同じですが, それらは Perl にバンドルされてやってくることを覚えておいてください !)
Another approach is to use Apache::DProf, which hooks Devel::DProf into mod_perl. The Apache::DProf module will run a Devel::DProf profiler inside each child server and write the tmon.out file in the directory $ServerRoot/logs/dprof/$$ when the child is shutdown (where $$ is the number of the child process). All it takes is to add to httpd.conf:

別のアプローチは Apache::DProf を使うことで, それは Devel::Dprof を mod_perl にフックします. Apache::DProf モジュールは Apache::DProf モジュールは各 child サーバの内側で Devel::DProf プロファイラを実行しその child がシャットダウンするときにディレクトリ $ServerRoot/logs/dprof/$$ に tmon.out ファイルを書きます ($$ は child プロセスの番号です). 必要なことは httpd.conf にこれを追加するだけです:

PerlModule Apache::DProf

Remember that any PerlHandler that was pulled in before Apache::DProf in the httpd.conf or startup.pl, will not have its code debugging information inserted. To run dprofpp, chdir to $ServerRoot/logs/dprof/$$ and run:

httpd.conf や startup.pl で Apache::DProf の前においた PerlHandler はいずれも, インサートされたそのコードデバッギング情報をもたないことを覚えておいてください. dprofpp を実行するには, $ServerRoot/logs/dprof/$$ に chdir してこれを実行します:

% dprofpp

(Lookup the ServerRoot directive's value in httpd.conf to figure out what's your $ServerRoot.)

(何があなたの $ServerRoot なのかを見つけだすために httpd.conf で ServerRoot ディレクティブの値を探してください.)


プロセスのメモリ測定 : Measuring the Memory of the Process



Very important aspect of performance tuning is to make sure that your applications don't use much memory, since if they do you cannot run many servers and therefore in most cases under a heavy load the overall performance degrades.

パフォーマンスチューニングのとても重要な側面はあなたのアプリケーションが多量のメモリを使わないように確認することです, もしそうなるとあなたは多くのサーバを実行できなくなりほとんどのケースで重い負荷のもとでのパフォーマンス全体が低下するからです.
In addition the code may not be clean and leak memory, which is even worse, since if the same process serves many requests and after each request more memory is used, after awhile all RAM will be used and machine will start swapping (use the swap partition) which is a very undesirable event, since it may lead to a machine crash.

加えてそのコードはクリーンではなくメモリリークしているかもしれません, さらに悪いことに, 同じプロセスが多くのリクエストをサーブして各リクエストの後でより多くのメモリが使われたとすると, しばらくして全ての RAM が使われてマシンがスワッピングし始めます (スワップパーテーションを使う) これはとても望ましくないイベントです, それがマシンクラッシュを導くかもしれないからです.
The simplest way to figure out how big the processes are and see whether they grow is to watch the output of top(1) or ps(1) utilities.

プロセスがどのくらいビッグかを把握してそれらが成長しているかどうかを確認するもっともシンプルな方法は top(1) や ps(1) ユーティリティの出力をウォッチすることです.
For example the output of top(1):


8:51am up 66 days, 1:44, 1 user, load average: 1.09, 2.27, 2.61
95 processes: 92 sleeping, 3 running, 0 zombie, 0 stopped
CPU states: 54.0% user, 9.4% system, 1.7% nice, 34.7% idle
Mem: 387664K av, 309692K used, 77972K free, 111092K shrd, 70944K buff
Swap: 128484K av, 11176K used, 117308K free 170824K cached

PID USER PRI NI SIZE RSS SHARE STAT LIB %CPU %MEM TIME COMMAND
29225 nobody 0 0 9760 9760 7132 S 0 12.5 2.5 0:00 httpd_perl
29220 nobody 0 0 9540 9540 7136 S 0 9.0 2.4 0:00 httpd_perl
29215 nobody 1 0 9672 9672 6884 S 0 4.6 2.4 0:01 httpd_perl
29255 root 7 0 1036 1036 824 R 0 3.2 0.2 0:01 top
376 squid 0 0 15920 14M 556 S 0 1.1 3.8 209:12 squid
29227 mysql 5 5 1892 1892 956 S N 0 1.1 0.4 0:00 mysqld
29223 mysql 5 5 1892 1892 956 S N 0 0.9 0.4 0:00 mysqld
29234 mysql 5 5 1892 1892 956 S N 0 0.9 0.4 0:00 mysqld

Which starts with overall information of the system and then displays the most active processes at the given moment. So for example if we look at the httpd_perl processes we can see the size of the resident (RSS) and shared (SHARE) memory segments. This sample was taken on the production server running linux.

これはシステムの全体情報から始まりその時点でもっともアクティブなプロセスを表示します. ですから例えば私たちが httpd_perl プロセスを見ると私たちは常駐 (RSS) と共有 (SHARE) メモリセグメントのサイズを見ることができます. このサンプルは linux が走るプロダクションサーバで取られました.
But of course we want to see all the apache/mod_perl processes, and that's where ps(1) comes to help. The options of this utility vary from one Unix flavor to another, and some flavors provide their own tools. Let's check the information about mod_perl processes:

しかしもちろん私たちは全ての apache/mod_perl プロセスをみたいと思います, それには ps(1) が役に立ちます. このユーティリティのオプションは Unix フレーバー毎に異なり, 一部のフレーバはそれ独自のツールを提供します. では mod_perl プロセスについてのインフォメーションをチェックしましょう:

% ps -o pid,user,rss,vsize,%cpu,%mem,ucomm -C httpd_perl
PID USER RSS VSZ %CPU %MEM COMMAND
29213 root 8584 10264 0.0 2.2 httpd_perl
29215 nobody 9740 11316 1.0 2.5 httpd_perl
29216 nobody 9668 11252 0.7 2.4 httpd_perl
29217 nobody 9824 11408 0.6 2.5 httpd_perl
29218 nobody 9712 11292 0.6 2.5 httpd_perl
29219 nobody 8860 10528 0.0 2.2 httpd_perl
29220 nobody 9616 11200 0.5 2.4 httpd_perl
29221 nobody 8860 10528 0.0 2.2 httpd_perl
29222 nobody 8860 10528 0.0 2.2 httpd_perl
29224 nobody 8860 10528 0.0 2.2 httpd_perl
29225 nobody 9760 11340 0.7 2.5 httpd_perl
29235 nobody 9524 11104 0.4 2.4 httpd_perl

Now you can see the resident (RSS) and virtual (VSZ) memory segments (and shared memory segment if you ask for it) of all mod_perl processes. Please refer to the top(1) and ps(1) man pages for more information.

これであなたは常駐 (RSS) とバーチャル (VSZ) メモリセグメント (それからもしあなたが望むなら共有メモリセグメント) をみることができます. 詳細は top(1) と ps(1) の man ページを参照してください.
You probably agree that using top(1) and ps(1) is cumbersome if we want to use memory size sampling during the benchmark test. We want to have a way to print memory sizes during the program execution at desired places. If you have GTop modules installed, which is a perl glue to the libgtop library, it's exactly what we need.

おそらくあなたは私たちがベンチマークテスト中にメモリサンプリングを使いたいと思った場合に top(1) や ps(1) を使うことが煩わしいことに同意するでしょう. 私たちは希望の場所でプログラムの実行中にメモリサイズを出力する方法をもったほうがよいでしょう. もしあなたがインストールされた GTop モジュールをもっているなら, それは libgtop ライブラリのための perl 接着剤で, 私たちがまさに必要としているものです.
Note: GTop requires the libgtop library but is not available for all platforms. See the docs in the source at ftp://ftp.gnome.org/pub/GNOME/stable/sources/gtop/ to check whether your platform/flavor is supported.

注: GTop は libgtop を要求しますがすべてのプラットフォームで利用可能ではありません. あなたのプラットフォーム/フレーバがサポートされているかどうかをチェックするために ftp://ftp.gnome.org/pub/GNOME/stable/sources/gtop/ のソースで docs を参照してください.
GTop provides an API for retrieval of information about processes and the whole system. We are interested only in memory sampling API methods. To print all the process related memory information we can execute the following code:

GTop はプロセスとシステム全体についてのインフォメーションを取得するための API を提供します. 私たちはメモリサンプリングの API メソッドにのみ興味があります. プロセスに関連したすべてのメモリインフォメーションを出力するために私たちは次のコードを実行します:

use GTop;
my $gtop = GTop->new;
my $proc_mem = $gtop->proc_mem($$);
for (qw(size vsize share rss)) {
printf " %s => %d\n", $_, $proc_mem->$_();
}

When executed we see the following output (in bytes):

これが実行されると私たちは次の出力を見ます (バイトで):

size => 1900544
vsize => 3108864
share => 1392640
rss => 1900544

So if we are interested in to print the process resident memory segment before and after some event we just do it: For example if we want to see how much extra memory was allocated after a variable creation we can write the following code:

ですから私たちが何らかのイベントの前後でプロセス常駐メモリセグメントの出力に関心がある場合に私たちはこれを行うのです: 例えば私たちが変数を作成した後でどのくらいの追加メモリが割当てられているのかを見たいなら私たちはこのコードを書くことができます:

use GTop;
my $gtop = GTop->new;
my $before = $gtop->proc_mem($$)->rss;
my $x = 'a' x 10000;
my $after = $gtop->proc_mem($$)->rss;
print "diff: ",$after-$before, " bytes\n";

and the output

そしてその出力がこれです

diff: 20480 bytes

So we can see that Perl has allocated extra 20480 bytes to create $x (of course the creation of after needed a few bytes as well, but it's insignificant compared to a size of $x)

したがって私たちは Perl が $x 作成のために追加で 20480 バイト割り当てたことを見ることができます (もちろん after の作成にも数バイト必要とされますが, $x のサイズと比較すると些細なものです)
The Apache::VMonitor module with help of the GTop module allows you to watch all your system information using your favorite browser from anywhere in the world without a need to telnet to your machine. If you are looking at what information you can retrieve with GTop, you should look at Apache::VMonitor as it deploys a big part of the API GTop provides.

GTop のヘルプで Apache::VMonitor モジュールはあなたがあなたのマシンに telnet することなくあなた好みのブラウザを使って世界のどこからでもあなたのシステムのすべてのインフォメーションをウォッチできるようにします. もしあなたが GTop で取得できる情報がどのようなものかを調べているなら, あなたは GTop が提供する API の大部分を配置している Apache::VMonitor を見てみるとよいでしょう.
If you are running a true BSD system, you may use BSD::Resource::getrusage instead of GTop. For example:

もしあなたが真の BSD システムを実行しているなら, あなたは GTop の代わりに BSD::Resource::getrusage を使ったほうがよいでしょう. 例えば:

print "used memory = ".(BSD::Resouce::getrusage)[2]."\n"

For more information refer to the BSD::Resource manpage.

詳細は BSD::Resource man ページを参照してください.


サブルーチンのメモリ使用量の測定 : Measuring the Memory Usage of Subroutines



With help of Apache::Status you can find out the size of each and every subroutine.

Apache::Status のヘルプによってあなたはあらゆるサブルーチンのサイズを見つけだすことができます.

  1. Build and install mod_perl as you always do, make sure it's version 1.22 or higher.

    あなたがいつもするように mod_perl をビルドしてインストールし, そのバージョンが 1.22 以上であることを確認する.

  2. Configure /perl-status if you haven't already:

    まだあなたがこれをもっていないなら /perl-status を構成する:

    <Location /perl-status>
    SetHandler perl-script
    PerlHandler Apache::Status
    order from all
    #deny from all
    #allow from ...
    </Location>

  3. Add to httpd.conf

    httpd.conf にこれを追加する

    PerlSetVar StatusOptionsAll On
    PerlSetVar StatusTerse On
    PerlSetVar StatusTerseSize On
    PerlSetVar StatusTerseSizeMainSummary On

    PerlModule B::TerseSize

  4. Start the server (best in httpd -X mode)

    そのサーバをスタートする (httpd -X モードがベスト)

  5. From your favorite browser fetch http://localhost/perl-status

    あなたのお気に入りのブラウザから http://localhost/perl-status をフェッチする

  6. Click on 'Loaded Modules' or 'Compiled Registry Scripts'

    'Loaded Modules' または 'Compiled Registry Scripts' をクリックする

  7. Click on the module or script of your choice (you might need to run some script/handler before you will see it here unless it was preloaded)

    あなたが選択したモジュールかスクリプトをクリックする (それがプレロードされていない場合はあなたがそれを見る前にあなたは何らかの script/handler を実行する必要があるかもしれない)

  8. Click on 'Memory Usage' at the bottom

    下部の 'Memory Usage' をクリックする

  9. You should see all the subroutines and their respective sizes.

    あなたはすべてのサブルーチンとそれぞれのサイズを見るはず.


Now you can start to optimize your code. Or test which of the several implementations is of the least size.

これであなたはあなたのコードの最適化をスタートできます. あるいはいくつかの実装でどれが最小サイズなのかをテストします.
For example let's compare CGI.pm's OO vs. procedural interfaces:

例えば CGI.pm の OO (# オブジェクト指向) vs. 手続き型インターフェイスの比較をしてみましょう:
As you will see below the first OO script uses about 2k bytes while the second script (procedural interface) uses about 5k.

あなたが以下で見るように最初の OO スクリプトが約 2k バイトを使う一方で 2 番目のスクリプト (手続き型インターフェイス) は約 5k を使います.
Here are the code examples and the numbers:

こちらがコード例と番号です:


  1. cgi_oo.pl
    ---------
    use CGI ();
    my $q = CGI->new;
    print $q->header;
    print $q->b("Hello");


  2. cgi_mtd.pl
    ---------
    use CGI qw(header b);
    print header();
    print b("Hello");


After executing each script in single server mode (-X) the results are:

各スクリプトをシングルサーバモード (-X) で実行した後の結果はこれです:


  1. Totals: 1966 bytes | 27 OPs

    handler 1514 bytes | 27 OPs
    exit 116 bytes | 0 OPs


  2. Totals: 4710 bytes | 19 OPs

    handler 1117 bytes | 19 OPs
    basefont 120 bytes | 0 OPs
    frameset 120 bytes | 0 OPs
    caption 119 bytes | 0 OPs
    applet 118 bytes | 0 OPs
    script 118 bytes | 0 OPs
    ilayer 118 bytes | 0 OPs
    header 118 bytes | 0 OPs
    strike 118 bytes | 0 OPs
    layer 117 bytes | 0 OPs
    table 117 bytes | 0 OPs
    frame 117 bytes | 0 OPs
    style 117 bytes | 0 OPs
    Param 117 bytes | 0 OPs
    small 117 bytes | 0 OPs
    embed 117 bytes | 0 OPs
    font 116 bytes | 0 OPs
    span 116 bytes | 0 OPs
    exit 116 bytes | 0 OPs
    big 115 bytes | 0 OPs
    div 115 bytes | 0 OPs
    sup 115 bytes | 0 OPs
    Sub 115 bytes | 0 OPs
    TR 114 bytes | 0 OPs
    td 114 bytes | 0 OPs
    Tr 114 bytes | 0 OPs
    th 114 bytes | 0 OPs
    b 113 bytes | 0 OPs


Note, that the above is correct if you didn't precompile all CGI.pm's methods at server startup. Since if you did, the procedural interface in the second test will take up to 18k and not 5k as we saw. That's because the whole of CGI.pm's namespace is inherited and it already has all its methods compiled, so it doesn't really matter whether you attempt to import only the symbols that you need. So if you have:

注意してください, 上記はあなたがすべての CGI.pm のメソッドをサーバのスタートアップでプレコンパイルしなかった場合で正しいです. もしあなたがそうしたなら, 2 番目のテストの手続き型インターフェイスは 18k までかかり私たちが見た 5k ではありません. それは CGI.pm の名前空間全体が継承されてすでにすべてのメソッドがコンパイルされているからで, あなたがあなたが必要とするシンボルだけのインポートを試みても実際は関係ありません. ですからもしあなたがサーバスタートアップスクリプトで:

use CGI qw(-compile :all);

in the server startup script. Having:

これをもっている場合. これや:

use CGI qw(header);

or

これを持つことは

use CGI qw(:all);

is essentially the same. You will have all the symbols precompiled at startup imported even if you ask for only one symbol. It seems to me like a bug, but probably that's how CGI.pm works.

本質的に同じです. あなたがたった 1 つのシンボルを依頼したとしてもあなたはスタートアップでコンパイルされインポートされたすべてのシンボルをもつのです. 私にはそればバグのように見えますが, おそらくそれが CGI.pm の仕組なのです.
BTW, you can check the number of opcodes in the code by a simple command line run. For example comparing 'my %hash' vs. 'my %hash = ()'.

ちなみに, シンプルなコマンドラインを実行することによってあなたはコード内のオペコードの数をチェックできます. 例えば 'my %hash' vs. 'my %hash = ()' を比較します.

% perl -MO=Terse -e 'my %hash' | wc -l
-e syntax OK
4

% perl -MO=Terse -e 'my %hash = ()' | wc -l
-e syntax OK
10

最初のものが少ないオペコードです.
Note that you shouldn't use Apache::Status module on production server as it adds quite a bit of overhead for each request.

リクエスト毎にかなりのオーバヘッドが追加されるのであなたはプロダクションサーバで Apache::Status モジュールを使うべきではないことに注意してください.


あなたのオペレーティングシステムを知る : Know Your Operating System



In order to get the best performance it helps to get intimately familiar with the Operating System (OS) the web server is running on. There are many OS specific things that you may be able to optimize which will improve your web server's speed, reliability and security.

ベストなパフォーマンスを得るためには web サーバが走るオペレーティングシステム (OS) を熟知することが助けになります. あなたの web サーバの速度, 信頼性やセキュリティを向上させるあなたが最適化できるであろう多くの OS 固有のものがあります.
The following sections will reveal some of the most important details you should know about your OS.

次のセクションはあなたがあなたの OS について知っておくべき最も重要な詳細のいくつかを明らかにします.


メモリ共有 : Sharing Memory



The sharing of memory is one very important factor. If your OS supports it (and most sane systems do), you might save memory by sharing it between child processes. This is only possible when you preload code at server startup. However, during a child process' life its memory pages tend to become unshared.

メモリの共有はとても重要なファクタです. もしあなたの OS がそれをサポートするなら (そしてほとんどのまともなシステムはそれをします), あなたはメモリを child プロセス間で共有することによりそれを節約できるかもしれません. これはあなたがサーバのスタートアップでコードをプレロードしたときにのみ可能です. しかしながら, child プロセスの生存中はそのメモリページは共有されないようになる傾向があります.
There is no way we can make Perl allocate memory so that (dynamic) variables land on different memory pages from constants, so the copy-on-write effect (we will explain this in a moment) will hit you almost at random.

私たちが Perl にメモリを割当てさせて (動的) 変数が定数とは異なるメモリページに到達するようにする方法はありませんので, copy-on-write 効果 (私たちはこれをすぐに説明します) はほとんどランダムにあなたをヒットします.
If you are pre-loading many modules you might be able to trade off the memory that stays shared against the time for an occasional fork by tuning MaxRequestsPerChild. Each time a child reaches this upper limit and dies it should release its unshared pages. The new child which replaces it will share its fresh pages until it scribbles on them.

もしあなたが多くのモジュールをプレロードしている場合あなたは MaxRequestsPerlChild を調整することで共有されたままのメモリを時折発生するフォークの時間とトレードオフできるかもしれません. child がこの上限に達して die するたびにそれはその非共有ページを解放しなければなりません. それをリプレイスする新しい child はそれにらくがきをするまでそのフレッシュなページを共有します.
The ideal is a point where your processes usually restart before too much memory becomes unshared. You should take some measurements to see if it makes a real difference, and to find the range of reasonable values. If you have success with this tuning the value of MaxRequestsPerChild will probably be peculiar to your situation and may change with changing circumstances.

理想はあなたのプロセスが通常するリスタートする多くのメモリが非共有になる前の段階です. あなたはそれが実際の違いになるか確認するため, 妥当な値の範囲を見つけるためにいくつかの測定をとらなければなりません. もしあなたがこのチューニングで成功したなら MaxRequestsPerChild の値はおそらくあなたのシチュエーションに特有で環境が変われば (# その値も) 変わるはずです.
It is very important to understand that your goal is not to have MaxRequestsPerChild to be 10000. Having a child serving 300 requests on precompiled code is already a huge overall speedup, so if it is 100 or 10000 it probably does not really matter if you can save RAM by using a lower value.

あなたのゴールが 10000 になる MaxRequestsPerChild をもつことではないということを理解しておくことはとても重要です. プレコンパイルされたコードで 300 リクエストをサーブする child をもつことはすでに全体的なスピードアップなので, もしあなたが低い値を使うことで RAM を節約できるならそれが 100 なのか 10000 なのかはおそらく重要ではありません.
Do not forget that if you preload most of your code at server startup, the newly forked child gets ready very fast, because it inherits most of the preloaded code and the perl interpreter from the parent process.

もしサーバのスタートアップであなたのコードのほとんどをあなたがプレロードしている場合, 新しくフォークされた child は非常に高速に準備されることを忘れないでください, なぜならそれはプレロードされたコードのほとんどと parent プロセスからの perl インタプリタを継承するからです.
During the life of the child its memory pages (which aren't really its own to start with, it uses the parent's pages) gradually get `dirty' - variables which were originally inherited and shared are updated or modified -- and the copy-on-write happens. This reduces the number of shared memory pages, thus increasing the memory requirement. Killing the child and spawning a new one allows the new child to get back to the pristine shared memory of the parent process.

child の存続中にそのメモリページ (スタート時にはそれ自体ではなく, parent のページを使います) は徐々に `ダーティ` になります - もともと継承され共有されている変数はアップデートまたは変更されます - そして copy-on-write が発生します. これは共有されたメモリページの数を削減し, メモリ要求を増加します. child をキルして新しいものを生み出すとその新しい child はその parent プロセスの汚れていない共有メモリに戻ることができます.
The recommendation is that MaxRequestsPerChild should not be too large, otherwise you lose some of the benefit of sharing memory.

お勧めは MaxRequestPerChild が大きくなり過ぎないようにすることです, そうでなければあなたは共有メモリの利点の一部を失います.
See Choosing MaxRequestsPerChild for more about tuning the MaxRequestsPerChild parameter.

MaxRequestsPerChild パラメータのチューニングについて詳細は Choosing MaxRequestsPerChild を参照してください.


私のメモリはどのように共有されるのか ? : How Shared Is My Memory?



You've probably noticed that the word shared is repeated many times in relation to mod_perl. Indeed, shared memory might save you a lot of money, since with sharing in place you can run many more servers than without it. See the Formula and the numbers.

あなたは mod_perl に関してワード共有が何度も繰り返されたことにおそらく気づいたでしょう. 実際に, 共有メモリであなたは多くのお金を節約できるかもしれません, 共有する場所ではそれがない場所よりもずっと多くのサーバをあなたは実行できるからです. Formula and the numbers を参照してください.
How much shared memory do you have? You can see it by either using the memory utility that comes with your system or you can deploy the GTop module:

あなたはどのくらいの共有メモリをもつでしょうか ? あなたはあなたのシステムに付属するメモリユーティリティを使うかあなたが展開できる GTop モジュールのいずれかによってそれを見ることができます:

use GTop ();
print "Shared memory of the current process: ",
GTop->new->proc_mem($$)->share,"\n";

print "Total Shared memory: ",
GTop->new->mem->share,"\n";

When you watch the output of the top utility, don't confuse the RES (or RSS) columns with the SHARE column. RES is RESident memory, which is the size of pages currently swapped in.

あなたが top ユーティリティの出力をウォッチするときには, SHARE カラムと RES (or RSS) カラムを混同しないでください. RES は RESident (# 常駐) メモリで, それは現在スワップインされているページのサイズです.


リアルなメモリ使用量の計算 : Calculating Real Memory Usage



I have shown how to measure the size of the process' shared memory, but we still want to know what the real memory usage is. Obviously this cannot be calculated simply by adding up the memory size of each process because that wouldn't account for the shared memory.

私はプロセスの共有メモリのサイズをどのように測定するかを示しましたが, 私たちはまだ実際のメモリ使用量を知りたいと思っています. 当然ですがシンプルに各プロセスのメモリサイズを加算することでは共有メモリを考慮しないためこれを計算できません.
On the other hand we cannot just subtract the shared memory size from the total size to get the real memory usage numbers, because in reality each process has a different history of processed requests, therefore the shared memory is not the same for all processes.

一方で私たちは実際のメモリ使用量の値をゲットするために合計サイズから共有メモリサイズを減算することはできません, なぜなら現実で各プロセスは処理されたリクエストの異なるヒストリをもっており, したがってその共有メモリはすべてのプロセスで同じではないからです.
So how do we measure the real memory size used by the server we run? It's probably too difficult to give the exact number, but I've found a way to get a fair approximation which was verified in the following way. I have calculated the real memory used, by the technique you will see in the moment, and then have stopped the Apache server and saw that the memory usage report indicated that the total used memory went down by almost the same number I've calculated. Note that some OSs do smart memory pages caching so you may not see the memory usage decrease as soon as it actually happens when you quit the application.

では私たちが実行しているサーバによって使われている実際のメモリサイズを私たちはどのようにして計測するのでしょうか ? 正確な値を与えることはおそらくとても難しいですが, 私は次の方法で検証されたフェアな近似値をゲットする方法を見つけました. 私はあなたがすぐに目にするテクニックによって, 実際のメモリ使用量を計算しました, それからサーバをストップしてそのメモリ使用量のレポートをみると使用されたメモリの合計が私が計算したのとほとんど同じ値だけ減少していることが示されていました. なお一部の OS はスマートメモリページキャッシングを行うのであなたがアプリケーションを終了したときにメモリ使用量の減少が実際に発生していてもあなたはそれをすぐには見れないかもしれません.
This is a technique I've used:

これは私が使ったテクニックです:

  1. For each process sum up the difference between shared and system memory. To calculate a difference for a single process use:

    プロセス毎に共有とシステムメモリの違いを合計する. シングルプロセスの違いを計算するためにはこれを使う:

    use GTop;
    my $proc_mem = GTop->new->proc_mem($$);
    my $diff = $proc_mem->size - $proc_mem->share;
    print "Difference is $diss bytes\n";

  2. Now if we add the shared memory size of the process with maximum shared memory, we will get all the memory that actually is being used by all httpd processes, except for the parent process.

    これで私たちが最大共有メモリでプロセスの共有メモリサイズを追加すると, 私たちはすべての httpd プロセスによって使われている実際のすべてのメモリをゲットする.

  3. Finally, add the size of the parent process.

    最後に, parent プロセスのサイズを加える.


Please note that this might be incorrect for your system, so you use this number on your own risk.

これはあなたのシステムでは正しくないかもしれませんので, あなたはあなたの責任でこの値を使うように注意してください.
I've used this technique to display real memory usage in the module Apache::VMonitor, so instead of trying to manually calculate this number you can use this module to do it automatically. In fact in the calculations used in this module there is no separation between the parent and child processes, they are all counted indifferently using the following code:

私はモジュール Apache::VMonitor でリアルなメモリ使用量を表示するためにこのテクニックを使いましたので, この数値を手動で計算することにトライする代わりにあなたはこのモジュールを使ってそれを自動的に行えます. 実際のこのモジュールで使われている計算では parent と child プロセスの間での分割はなく, それらは次のコードを使って区別なくカウントされます:

use GTop ();
my $gtop = GTop->new;
my $total_real = 0;
my $max_shared = 0;
# @mod_perl_pids is initialized by Apache::Scoreboard, irrelevant here
# @mod_perl_pids は Apache::Scoreboard によって初期化されるが, ここでは関係ない
my @mod_perl_pids = some_code();
for my $pid (@mod_perl_pids)
my $proc_mem = $gtop->proc_mem($pid);
my $size = $proc_mem->size($pid);
my $share = $proc_mem->share($pid);
$total_real += $size - $share;
$max_shared = $share if $max_shared < $share;
}
my $total_real += $max_shared;

So as you see we that we accumulate the difference between the shared and reported memory:

ですからあなた見てのとおり私たちは共有とレポートされたメモリの違いを蓄積して:

$total_real += $size - $share;

and at the end add the biggest shared process size:

最後に最も大きな共有プロセスサイズを追加しますので:

my $total_real += $max_shared;

So now $total_real contains approximately the really used memory.

$total_real はおよそのリアルな使用メモリを含むことになります.


私の変数は共有されている ? : Are My Variables Shared?



How do you find out if the code you write is shared between the processes or not? The code should be shared, except where it is on a memory page with variables that change. Some variables are read-only in usage and never change. For example, if you have some variables that use a lot of memory and you want them to be read-only. As you know the variable becomes unshared when the process modifies its value.

あなたが書いたコードがプロセス間で共有されているのかされていないのかを確認するにはどうしたらよいでしょうか ? そのコードは共有されているはずです, それが変数が変化するメモリページ上にある場合以外は. 一部の変数は使用方法がリードオンリーで変化することはありません. 例えば, あなたが多くのメモリを使ういくつかの変数をもっていてそれらをリードオンリーにしたい場合です. あなたが知っているようにプロセスがその値を変更するとその変数は非共有になります.
So imagine that you have this 10Mb in-memory database that resides in a single variable, you perform various operations on it and want to make sure that the variable is still shared. For example if you do some matching regular expression (regex) processing on this variable and want to use the pos() function, will it make the variable unshared or not?

ではあなたがこの単一の変数にある 10Mb のインメモリのデータベースをもっていると想像してください, あなたはその上でさまざまな操作を実行してその変数がまだ共有されていることを確認したいとをおもいます. 例えばあなたがこの変数上で何らかのマッチングの正規表現 (regex) 処理を行って pos() ファンクションを使いたい場合に, その変数は非共有になるのでしょうかならないのでしょうか ?
The Apache::Peek module comes to rescue. Let's write a module called MyShared.pm which we preload at server startup, so all the variables of this module are initially shared by all children.

Apache::Peek モジュールが助けにきます. 私たちがサーバのスタートアップでプレロードする MyShared.pm と呼ばれるモジュールを書きましょう, そしてこのモジュールのすべての変数はすべての child たちによって最初に共有されます.

MyShared.pm
----------
package MyShared;
use Apache::Peek;

my $readonly = "Chris";

sub match { $readonly =~ /\w/g; }
sub print_pos{ print "pos: ",pos($readonly),"\n";}
sub dump { Dump($readonly); }
1;

This module declares the package MyShared, loads the Apache::Peek module and defines the lexically scoped $readonly variable which is supposed to be a variable of large size (think about a huge hash data structure), but we will use a small one to simplify this example.

このモジュールはパッケージ MyShared を宣言して, Apache::Peek モジュールをロードし大きなサイズの変数になると想定されるレキシカルスコープの $readonly 変数 (巨大なハッシュデータ構造を考えてください) を宣言しますが, 私たちはこの例をシンプルにするために小さなものを使います.
Now we write the script that prints the process ID (PID) and calls all three functions. The goal is to check whether pos() makes the variable dirty and therefore unshared.

そして私たちはプロセス ID (PID) を出力して 3 つのファンクション全てをコールするスクリプトを書きます. そのゴールは pos() が変数をダーティにすることにより非共有にされるかどうかをチェックするすることです.

share_test.pl
-------------
use MyShared;
print "Content-type: text/plain\r\n\r\n";
print "PID: $$\n";
MyShared::match();
MyShared::print_pos();
MyShared::dump();

Before you restart the server, in httpd.conf set:

あなたがサーバをリースタートするまえに, httpd.conf でこれをセットして:

MaxClients 2

for easier tracking. You need at least two servers to compare the print outs of the test program. Having more than two can make the comparison process harder.

トラッキングを簡単にします. あなたはテストプログラムの出力の比較のために少なくとも 2 つのサーバが必要です. 2 つより多くもつとその比較プロセスを難しくします.
Now open two browser windows and issue the request for this script several times in both windows, so you get different processes PIDs reported in the two windows and each process has processed a different number of requests to the share_test.pl script.

それから 2 つのブラウザウィンドウをオープンして両方のウィンドウのこのスクリプトに何度かリクエストを発行すると, あなたはレポートされた異なるプロセスの PID をゲットし各プロセスは share_test.pl スクリプトへの違う数のリクエストを処理します.
In the first window you will see something like that:

最初のウィンドウで私たちはこのようなものを:

PID: 27040
pos: 1
SV = PVMG(0x853db20) at 0x8250e8c
REFCNT = 3
FLAGS = (PADBUSY,PADMY,SMG,POK,pPOK)
IV = 0
NV = 0
PV = 0x8271af0 "Chris"\0
CUR = 5
LEN = 6
MAGIC = 0x853dd80
MG_VIRTUAL = &vtbl_mglob
MG_TYPE = 'g'
MG_LEN = 1

And in the second window:

そして 2 番目のウィンドウでこのようなものをみるはずです:

PID: 27041
pos: 2
SV = PVMG(0x853db20) at 0x8250e8c
REFCNT = 3
FLAGS = (PADBUSY,PADMY,SMG,POK,pPOK)
IV = 0
NV = 0
PV = 0x8271af0 "Chris"\0
CUR = 5
LEN = 6
MAGIC = 0x853dd80
MG_VIRTUAL = &vtbl_mglob
MG_TYPE = 'g'
MG_LEN = 2

We see that all the addresses of the supposedly big structures are the same, 0x8250e8c for SV, and 0x8271af0 for PV, therefore the variable data structure is almost completely shared. The only difference is in SV.MAGIC.MG_LEN record, which is not shared.

私たちは想定するすべての大きな構造のアドレスが同じであることを見ます, SV で 0x8250e8c, そして PV で 0x8271af0, したがってその変数のデータ構造はほとんど完全に共有されます. 唯一の違いは SV.MAGIC.MG_LEN レコードにあり, それは共有されていません.
So given that the $readonly variable is a big one, its value is still shared between the processes, while part of the variable data structure is non-shared. But it's almost insignificant because it takes a very little memory space.

ですから $readonly 変数が大きいもので与えられると, その値はプロセス間でまだ共有されます, 一方で変数データ構造の一部は非共有とされます. しかしそれはほんの少しのメモリスペースしか取らないのでほとんどささいなことです.
Now if you need to compare more than variable, doing it by hand can be quite time consuming and error prune. Therefore it's better to correct the testing script to dump the Perl data-types into files (e.g /tmp/dump.$$, where $$ is the PID of the process) and then using diff(1) utility to see whether there is some difference.

今もしあなたがより多くの変数の比較が必要な場合, それを手動で行うとかなりの時間を消費して間違いがち (# s/prune/prone/ ?) です. したがってそのテストスクリプトを正しくするために Perl データタイプをファイルにダンプして (e.g /tmp/dump.$$, $$ はプロセスの PID) 何か違いがあるかを確認するために diff(1) ユーティリティを使うことがベターです.
So correcting the dump() function to write the info to the file will do the job. Notice that we use Devel::Peek and not Apache::Peek. The both are almost the same, but Apache::Peek prints it output directly to the opened socket so we cannot intercept and redirect the result to the file. Since Devel::Peek dumps results to the STDERR stream we can use the old trick of saving away the default STDERR handler, and open a new filehandler using the STDERR. In our example when Devel::Peek now prints to STDERR it actually prints to our file. When we are done, we make sure to restore the original STDERR filehandler.

ですからファイルにその情報を書くように dump() ファンクションを修正してその仕事を行います. 私たちは Apache::Peek ではなく Devel::Peek を使うことに注意してください. 両方ともほぼ同じですが, Apache::Peek はそれをオープンされたソケットに直接出力するので私たちはその結果をインターセプトしてファイルにリダイレクトすることができません. Devel::Peek は結果を STDERR ストリームにダンプするので私たちはデフォルトの STDERR ハンドラを保存する古いトリックを使ったり, STDERR を使って新しいファイルハンドルのオープンができます. 私たちの例では Devel::Peek が STDERR に出力するとそれは実際に私たちのファイルに出力します. 私たちがそれを完了すると, 私たちはオリジナルの STDERR ファイルハンドラを確実にレストアします.
So this is the resulting code:

そしてこれがその結果のコードです:

MyShared2.pm
---------
package MyShared2;
use Devel::Peek;

my $readonly = "Chris";

sub match { $readonly =~ /\w/g; }
sub print_pos { print "pos: ",pos($readonly),"\n";}
sub dump{
my $dump_file = "/tmp/dump.$$";
print "Dumping the data into $dump_file\n";
open OLDERR, ">&STDERR";
open STDERR, ">",.$dump_file or die "Can't open $dump_file: $!";
Dump($readonly);
close STDERR;
open STDERR, ">&OLDERR";
}
1;

When if we modify the code to use the modified module:

私たちが修正したモジュールを使うためにコードを修正するときは:

share_test2.pl
-------------
use MyShared2;
print "Content-type: text/plain\r\n\r\n";
print "PID: $$\n";
MyShared2::match();
MyShared2::print_pos();
MyShared2::dump();

And run it as before (with MaxClients 2), two dump files will be created in the directory /tmp. In our test these were created as /tmp/dump.1224 and /tmp/dump.1225. When we run diff(1):

そして前のようにそれを実行します (MaxClients 2), 2 つのダンプファイルがディレクトリ /tmp に作成されます. 私たちのテストでそれらは /tmp/dump.1224 と tmp/dump.1225 として作成されます. 私たちが diff(1) を実行すると:

% diff /tmp/dump.1224 /tmp/dump.1225
12c12
< MG_LEN = 1
---
> MG_LEN = 2

We see that the two padlists (of the variable readonly) are different, as we have observed before when we did a manual comparison.

私たちは 2 つの (変数 readonly) の padlist (# perl 内部の構造体) が違うことを見ます, 私たちが以前に行った手動での比較で観察したのと同様です.
In fact we if we think about these results again, we get to a conclusion that there is no need for two processes to find out whether the variable gets modified (and therefore unshared). It's enough to check the datastructure before the script was executed and after that. You can modify the MyShared2 module to dump the padlists into a different file after each invocation and than to run the diff(1) on the two files.

実際に私たちがこれらの結果について再び考えると, 私たちは変数が変更されている (したがって非共有) かどうかを見つけだすために 2 つのプロセスは必要ないとの結論にいたりました. それはスクリプトが実行される前とその後でデータ構造をチェックすれば十分です. あなたは呼びだしごとに padlist を異なるファイルにダンプしてその 2 つのファイルで diff(1) を実行するために MySahred2 モジュールを変更できます.
If you want to watch whether some lexically scoped (with my ()) variables in your Apache::Registry script inside the same process get changed between invocations you can use the Apache::RegistryLexInfo module instead. Since it does exactly this: it makes a snapshot of the padlist before and after the code execution and shows the difference between the two. This specific module was written to work with Apache::Registry scripts so it won't work for loaded modules. Use the technique we have described above for any type of variables in modules and scripts.

もしあなたが同じプロセスの内側のあなたの Apache::Registry スクリプト内にある (my () で) レキシカルスコープ化された変数が呼びだしの間で変更されたかどうかを監視したい場合あなたは代わりに Apache::RegistryLexInfo モジュールを使えます. まさにこれを行うために: コードの実行前後の padlist のスナップショットを作成してその 2 つの間の違いを示す. この特定のモジュールは Apache::Registry スクリプトで動作するように書かれているのでロードされたモジュールでは動作しません. モジュールやスクリプトの中の任意の変数タイプには私たちが上で説明したテクニックを使ってください.
Surely another way of ensuring that a scalar is readonly and therefore sharable is to either use the constant pragma or readonly pragma. But then you won't be able to make calls that alter the variable even a little, like in the example that we just showed, because it will be a true constant variable and you will get compile time error if you try this:

スカラがリードオンリーでありしたがって共有できるかどうかを確実に保証する他の方法は constant プラグマまたは readonly プラグマのどちらかを使うことです. ただしあなたは私たちがさきほど示した例でのように, その変数を少しでも変更するようにコールすることはできません, なぜならそれは真の定数変数になるからでもしあなたがこれをトライするとあなたはコンパイルタイムでエラーをゲットするでしょう:

MyConstant.pm
-------------
package MyConstant;
use constant readonly => "Chris";

sub match { readonly =~ /\w/g; }
sub print_pos { print "pos: ",pos(readonly),"\n";}
1;


% perl -c MyConstant.pm

Can't modify constant item in match position at MyConstant.pm line
5, near "readonly)"
MyConstant.pm had compilation errors.

However this code is just right:

しかしこのコードであれば適切です:

MyConstant1.pm
-------------
package MyConstant1;
use constant readonly => "Chris";

sub match { readonly =~ /\w/g; }
1;



サーバスタートアップでの Perl モジュールのプレロード : Preloading Perl Modules at Server Startup



You can use the PerlRequire and PerlModule directives to load commonly used modules such as CGI.pm, DBI and etc., when the server is started. On most systems, server children will be able to share the code space used by these modules. Just add the following directives into httpd.conf:

あなたはサーバがスタートされるときに, CGI.pm, DBI その他のような一般的に使われるモジュールをロードするために PerlRequire や PerlModule ディレクティブを使うことができます. ほとんどのシステムでは, サーバ child 達はそれらのモジュールに使われるコードスペースを共有できるようになります. httpd.conf に次のディレクティブを追加するだけです:

PerlModule CGI
PerlModule DBI

But an even better approach is to create a separate startup file (where you code in plain perl) and put there things like:

しかしよりベターなアプローチは別のスタートアップファイル (プレーン perl でコーディングする場所) を作成しそこにこのようなものを置くことです:

use DBI ();
use Carp ();

Don't forget to prevent importing of the symbols exported by default by the module you are going to preload, by placing empty parentheses () after a module's name. Unless you need some of these in the startup file, which is unlikely. This will save you a few more memory bits.

モジュール名の後に空の丸カッコ () を置くことで, あなたがプレロードするモジュールによってデフォルトでエクスポートされるシンボルのインポートを防ぐことを忘れないでください. その可能性は低いですが, スタートアップファイルでそれらの一部をあなたが必要とする場合でない限りはです. これはさらに幾ばくかのメモリビットを節約します.
Then you require() this startup file in httpd.conf with the PerlRequire directive, placing it before the rest of the mod_perl configuration directives:

つぎにあなたは httpd.conf 内で PerlRequire ディレクティブによりこのスタートアップファイルを require() して, 残りの mod_perl 構成ディレクティブの前にそれをおきます:

PerlRequire /path/to/start-up.pl

CGI.pm is a special case. Ordinarily CGI.pm autoloads most of its functions on an as-needed basis. This speeds up the loading time by deferring the compilation phase. When you use mod_perl, FastCGI or another system that uses a persistent Perl interpreter, you will want to precompile the functions at initialization time. To accomplish this, call the package function compile() like this:

CGI.pm は特別なケースです. 普通 CGI.pm はそのファンクションのほとんど必要に応じてをオートロードします. これはコンパイルフェーズを延期することでローディングタイムをスピードアップします. あなたが mod_perl, FastCGI あるいは永続的に Perl インタプリタを使う他のシステムを使うとき, あなたは初期化時にファンクションをプレコンパイルしたいと思うでしょう. これを成すためには, パッケージファンクション compile() をこのようにコールします:

use CGI ();
CGI->compile(':all');

The arguments to compile() are a list of method names or sets, and are identical to those accepted by the use() and import() operators. Note that in most cases you will want to replace ':all' with the tag names that you actually use in your code, since generally you only use a subset of them.

compile() への引数はメソッド名のリストかセットで, use() や import() オペレータが受けいれるものたちと同じです. なおほとんどのケースであなたはあなたのコード内では実際に使うタグ名で ':all' をリプレイスしたいと思うはずです, 通常あなたはそれらのサブセットのみを使うからです.
Let's conduct a memory usage test to prove that preloading, reduces memory requirements.

プレロードすることが, メモリ要求を削減することを証明するためにメモリ使用量のテストを実施してみましょう.
In order to have an easy measurement we will use only one child process, therefore we will use this setting:

簡単な測定をするために私たちは 1 つの child プロセスのみを使います, したがって私たちはこのセッティングを使います:

MinSpareServers 1
MaxSpareServers 1
StartServers 1
MaxClients 1
MaxRequestsPerChild 100

We are going to use the Apache::Registry script memuse.pl which consists of two parts: the first one preloads a bunch of modules (that most of them aren't going to be used), the second part reports the memory size and the shared memory size used by the single child process that we start. and of course it prints the difference between the two sizes.

私たちは 2 つのパートで構成される Apache::Registry スクリプトの memuse.pl を使います: 最初のものはモジュールの束をプレロード (そのほとんどは使われません) します, 次のパートは私たちがスタートするシングルの child プロセスを使うことでそのメモリサイズと共有メモリサイズをレポートします. そしてもちろんそれは 2 つのサイズ間の違いを出力します.

memuse.pl
---------
use strict;
use CGI ();
use DB_File ();
use LWP::UserAgent ();
use Strable ();
use DBI ();
use GTop ();

my $r = shift;
$r->send_http_header('text/plain');
my $proc_mem = GTop->new->proc_mem($$);
my $size = $proc_mem->size;
my $share = $proc_mem->share;
my $diff = $size - $share;
printf "%10s %10s %10s\n", qw(Size Shared Difference);
printf "%10d %10d %10d (beytes)\n", $size,$share,$diff;

First we restart the server and execute this CGI script when none of the above modules preloaded. Here is the result:

最初に私たちはサーバをリスタートして上記モジュールのプレロードがされていないなときにこの CGI スクリプトを実行します. こちらがそのリザルトです:

Size Shared Diff
4706304 2134016 2572288 (bytes)

Now we take all the modules:

そして私たちはこのモジュールすべてをとって:

use strict;
use CGI ();
use DB_File ();
use LWP::UserAgent ();
use Storable ();
use DBI ();
use GTop ();

and copy them into the startup script, so they will get preloaded. The script remains unchanged. We restart the server and execute it again. We get the following.

それらをスタートアップスクリプトにコピーしますので, それらはプレロードされます. そのスクリプトは変更されずにあります. 私たちはサーバをリスタートしてそれを再び実行します. 私たちは次のものをゲットします:

Size Shared Diff
4710400 3997696 712704 (bytes)

Let's put the two results into one table:

2 つの結果をひとつのテーブルに置いてみましょう:

Preloading Size Shared Diff
Yes 4710400 3997696 712704 (bytes)
No 4706304 2134016 2572288 (bytes)
--------------------------------------------
Difference 4096 1863680 -1859584

You can clearly see that when the modules weren't preloaded the shared memory pages size, were about 1864Kb smaller relative to the case where the modules were preloaded.

モジュールがプレロードされないときの共有メモリのページサイズが, モジュールがプレロードされてたケースに比べて約 1864kb 小さいことをあなたははっきり見ることができます.
Assuming that you have had 256M dedicated to the web server, if you didn't preload the modules, you could have:

あなたが web サーバのために 256M をもっていると仮定すると, あなたがモジュールをプレロードしない場合, あなたは:

268435456 = X * 2572288 + 2134016

X = (268435456 - 2134016) / 2572288 = 103

103 servers.

103 のサーバをもつことができます.
Now let's calculate the same thing with modules preloaded:

それではモジュールをプレロードして同じものを計算してみましょう:

268435456 = X * 712704 + 3997696

X = (268435456 - 3997696) / 712704 = 371

You can have almost 4 times more servers!!!

あなたはほぼ 4 倍も多くサーバをもてます !!!
Remember that we have mentioned before that memory pages gets dirty and the size of the shared memory gets smaller with time? So we have presented the ideal case where the shared memory stays intact. Therefore the real numbers will be a little bit different, but not far from the numbers in our example.

メモリページはダーティになり共有メモリのサイズは時間と共に小さくなることを私たちが前に言及したことを覚えていますか ? そこで私たちは共有メモリがそのままであり続けるという理想的なケースを紹介しました. したがって実際の数値は少々違うはずです, しかし私たちの例の数値から遠くはないでしょう.
Also it's obvious that in your case it's possible that the process size will be bigger and the shared memory will be smaller, since you will use different modules and a different code, so you won't get this fantastic ratio, but this example is certainly helps to feel the difference.

またあなたのケースでプロセスサイズがより大きくなり共有メモリがより小さくなる可能性があることは明らかです, あなたは異なるモジュールと異なるコードを使うはずだからであり, あなたはこのファンタスティックな比率をゲットすることはできませんが, この例は間違いなくその違いを感じることに役立ちます.


サーバスタートアップでのレジストリスクリプトのプレロード : Preloading Registry Scripts at Server Startup



What happens if you find yourself stuck with Perl CGI scripts and you cannot or don't want to move most of the stuff into modules to benefit from modules preloading, so the code will be shared by the children. Luckily you can preload scripts as well. This time the Apache::RegistryLoader modules comes to aid. Apache::RegistryLoader compiles Apache::Registry scripts at server startup.

もしあなたが Perl CGI で行き詰ったことにあなた自身で気づいてモジュールのプレロードからのベネフィットのためにあなたが多くのものをモジュールに移動できないまたはしたくない場合は何が起こるでしょうか, そのコードは child 達によって共有されることになります. 幸いあなたはスクリプトもプレロードできます. 今回は Apache::RegistryLoader モジュールが助けになります. Apache::RegistryLoader はサーバのスタートアップで Apache::Registry スクリプトをコンパイルします.
For example to preload the script /perl/test.pl which is in fact the file /home/httpd/perl/test.pl you would do the following:

例えば実際には /home/httpd/perl/test.pl にあるスクリプト /perl/test.pl をプレロードするためにあなたは次を行えます:

use Apache::RegistryLoader ();
Apache::RegistryLoader->new->handler("/perl/test.pl",
"/home/httpd/perl/test.pl");

You should put this code either into <Perl> sections or into a startup script.

あなたは <Perl> セクションかスタートアップスクリプトの中のどちらかにこのコードを置く必要があります.
But what if you have a bunch of scripts located under the same directory and you don't want to list them one by one. Take the benefit of Perl modules and put them to a good use. The File::Find module will do most of the work for you.

しかしあなたが同じディレクトリに置かれたスクリプトの束をもっていてそれらをひとつひとつリストしたくない場合はどうでしょう. Perl モジュールのベネフィットをとり良い感じに使ってそれらを置いてください. File::Find モジュールはあなたのためにほとんどの作業を行います.
The following code walks the directory tree under which all Apache::Registry scripts are located. For each encountered file with extension .pl, it calls the Apache::RegistryLoader::handler() method to preload the script in the parent server, before pre-forking the child processes:

次のコードは Apache::Registry スクリプトが置かれたディレクトリツリーのもとを歩きます. 各拡張子 .pl のファイルに遭遇するたびに, それは child プロセスがプレフォークする前に, Apache::RegistryLoader::Handler() メソッドをコールして parent サーバにそのスクリプトをプレロードします.

use File::Find qw(finddepth);
use Apache::RegistryLoader ();
{
my $scripts_root_dir = "/home/httpd/perl/";
my $rl = Apache::RegistryLoader->new;
finddepth
(
sub {
return unless /\.pl$/;
my $url = "$File::Find::dir/$_";
$url =~ s|$scripts_root_dir/?|/|;
warn "pre-loding $url\n";
# preload $url
my $status = $rl->handler($url);
unless($status == 200) {
warn "pre-load of `$url' failed, status=$status\n";
}
},
$scripts_root_dir);
}

Note that we didn't use the second argument to handler() here, as in the first example. To make the loader smarter about the URI to filename translation, you might need to provide a trans() function to translate the URI to filename. URI to filename translation normally doesn't happen until HTTP request time, so the module is forced to roll its own translation. If filename is omitted and a trans() function was not defined, the loader will try using the URI relative to ServerRoot.

なお私たちはここで最初の例のようには, 2 番目の引数に handler() を使いませんでした. URI のファイル名へのトランスレーションについてローダーをよりスマートにするために, あなたは URI からファイル名へのトランスレーションのために trans() ファンクションを提供する必要があるかもしれません. URI からファイル名へのトランスレーションは普通 HTTP リクエスト時にまでは行われませんので, このモジュールは自らそのトランスレーションに取り掛からざるをえません. ファイル名が省略されて trans() ファンクションが定義されていない場合, このローダーは SeverRoot に相対的な URI の利用をトライします.
A simple trans() function can be something like that:

シンプルな trans() ファンクションはこのような感じになります:

sub mytrans {
my $uri = shift;
$uri =~ s|^/perl/|/home/httpd/perl/|;
return $uri;
}

You can easily derive the right translation by looking at the Alias directive. The above mytrans() function is matching our Alias:

あなたは Alias ディレクティブを見ることで正しいトランスレーションを簡単に導き出すことができます. 上記の mytrans() ファンクションはこの私たちの Alias にマッチングします:

Alias /perl/ /home/httpd/perl/

After defining the URI to filename translation function you should pass it during the creation of the Apache::RegistryLoader object:

URI からファイル名のトランスレーションファンクションを定義したらあなたは Apache::RegistryLoader オブジェクトの作成中にそれを渡す必要があります.

my $rl = Apache::RegistryLoader->new(trans => \&mytrans);

I won't show any benchmarks here, since the effect is absolutely the same as with preloading modules.

私はここでベンチマークは何も示しません, その効果がモジュールのプレロードとまったく同じだからです.
See also BEGIN blocks

BEGIN blocks も参照してください (# For mod_perl2 and for mod_perl1 (Apache::RegistryLoader))


サーバスタートアップでのモジュール初期化 : Modules Initializing at Server Startup



We have just learned that it's important to preload the modules and scripts at the server startup. It turns out that it's not enough for some modules and you have to prerun their initialization code to get more memory pages shared. Basically you will find an information about specific modules in their respective manpages. We will present a few examples of widely used modules where the code can be initialized.

私たちはサーバのスタートアップでのモジュールとスクリプトのプレロードが重要であることを学びました. それが一部のモジュールでは十分でないことがわかりあなたがより多くのメモリページを共有するためにはそれらの初期化コードをプレラン (# 事前実行) しなければなりません. 基本的にあなたは特定のモジュールに関する情報をそれぞれの man ページで見つけることができます. 私たちはそのコードを初期化できる広く使われているモジュールのいくつかの例を紹介します.


DBI.pm の初期化 : Initializing DBI.pm



The first example is the DBI module. As you know DBI works with many database drivers falling into the DBD:: category, e.g. DBD::mysql. It's not enough to preload DBI, you should initialize DBI with driver(s) that you are going to use (usually a single driver is used), if you want to minimize memory use after forking the child processes. Note that you want to do this under mod_perl and other environments where the shared memory is very important. Otherwise you shouldn't initialize drivers.

最初の例は DBI モジュールです. あなたが知っている通り DBI は DBI:: カテゴリ e.g. DBD::mysql に分類される多くのデータベースドライバで動作します. もしあなたが child プロセスがフォークしたあとのメモリ使用を最小にしたい場合は, DBI のプレロードだけでは不十分で, あなたはあなたが使うドライバ (通常はひとつのドライバが使われる) で DBI を初期化しなければなりません. なお共有メモリがとても重要な mod_perl やその他の環境のもとであなたはこれをするとよいです. そうでないならあなたはドライバの初期化をするべきではありません.
You probably know already that under mod_perl you should use the Apache::DBI module to get the connection persistence, unless you open a separate connection for each user--in this case you should not use this module. Apache::DBI automatically loads DBI and overrides some of its methods, so you should continue coding like there is only a DBI module.

あなたはすでに知っているかもしれませんが mod_perl のもとで永続的なコネクションをゲットするためにあなたは Apache::DBI モジュールを使わなければなりません, あなたが各ユーザのために個別のコネクションをオープンするのでなければ -- そのケースであなたはこのモジュールを使わないでしょう. Apache::DBI は自動的に DBI をロードしてそのメソッドの一部をオーバライドしますので, あなたは DBI モジュールしかないかのようにコーディングを続けられるはずです.
Just as with modules preloading our goal is to find the startup environment that will lead to the smallest "difference" between the shared and normal memory reported, therefore a smaller total memory usage.

モジュールのプレロードと同じく私たちのゴールはレポートされる共有と通常のメモリ間で最小の "違い" をもたらすスタートアップ環境を見つけることです, したがってトータルのメモリ使用量はより少なくなります.
And again in order to have an easy measurement we will use only one child process, therefore we will use this setting in httpd.conf:

そして再び簡単な測定にするために私たちはひとつだけの child プロセスを使います, したがって私たちは httpd.conf でこの設定を使います:

MinSpareServers 1
MaxSpareServers 1
StartServers 1
MaxClients 1
MaxRequestsPerChild 100

We always preload these modules:

私たちはこれらのモジュールを常にプレロードします:

use Gtop();
use Apache::DBI(); # preloads DBI as well (DBI もプレロード)

We are going to run memory benchmarks on five different versions of the startup.pl file.

私たちは 5 つの異なる startup.pl ファイルのバージョンでメモリベンチマークを実行します.

  • option 1
    Leave the file unmodified.

    ファイルは変更せずそのまま.

  • option 2
    Install MySQL driver (we will use MySQL RDBMS for our test):

    MySQL ドライバをインストール (私たちは私たちのテストに MySQL RDBMS を使う):

    DBI->install_driver("mysql");

    It's safe to use this method, since just like with use(), if it can't be installed it'll die().

    このメソッドの利用は安全, use() と同じく, もしそれがインストールできなければ die() するため.

  • option 3
    Preload MySQL driver module:

    MySQL ドライバモジュールをプレロード:

    use DBD::mysql;

  • option 4
    Tell Apache::DBI to connect to the database when the child process starts (ChildInitHandler), no driver is preload before the child gets spawned!

    child プロセスがスタートするとき (ChildInitHandler) にデータベースに接続するように Apache::DBI に伝える, child が生まれる前にドライバはプレロードしない !

    Apache::DBI->connect_on_init('DBI:mysql:test::localhost',
    "",
    "",
    {
    PrintError => 1, # warn() on errors (エラーで warn())
    RaiseError => 0, # don't die on error (エラーで die しない)
    AutoCommit => 1, # commit executes (コミットを実行)
    # immediately (即時)
    }
    )
    or die "Cannot connect to databese: $DBI::errstr";

  • option 5
    Options 2 and 4: using connect_on_init() and install_driver().

    Options 2 と 4: connect_on_init() と install_driver() を使う.


Here is the Apache::Registry test script that we have used:

こちらが私たちが使った Apache::Registry テストスクリプトです:

preload_dbi.pl
--------------
use strict;
use GTop ();
use DIB ();

my $dbh = DBI->connect("DBI:mysql:test::localhost",
"",
"",
{
PrintError => 1, # warn() on errors
RaiseError => 0, # don't die on error
AutoCommit => 1, # commit executes
# immediately
}
)
or die "Cannot connect to database: $DBI::errstr";

my $r = shift;
$r->send_http_header('text/plain');

my $do_sql = "show tables";
my $sth = $dbh->prepare($do_sql);
$sth->execute();
my @date = ();
while (my @row = $sth->fetchrow_array) {
push @data, @row;
}
print "Data: @data\n";
$dbh->disconnect(); # NOP under Apache::DBI (Apache::DBI のもとでは何もしない)

my $proc_mem = GTop->new->proc_mem($$);
my $size = $proc_mem->size;
my $share = $proc_mem->share;
my $diff = $size - $share;
printf "%8s %8s %8s\n", qw(Size Shared Diff);
printf "%8d %8d %8d (bytes)\n",$size,$share,$diff;

The script opens a opens a connection to the database 'test' and issues a query to learn what tables the databases has. When the data is collected and printed the connection would be closed in the regular case, but Apache::DBI overrides it with empty method. When the data is processed a familiar to you already code to print the memory usage follows.

このスクリプトはデータベース 'test' へのコネクションをオープンしてデータベースがもつテーブルを学習するためのクエリを発行します. そのデータが集められて出力されるとそのコネクションは通常のケースではクローズされますが, Apache::DBI はそれを空のメソッドでオーバライドします (# $dbh->disconnect(); の行). データが処理されるとメモリ使用量を出力するためのあなたがすでに慣れ親しんだコードが続きます.
The server was restarted before each new test.

これらの新しいテストの前にサーバはリスタートされました.
So here are the results of the five tests that were conducted, sorted by the Diff column:

そしてこちらが実施された 5 つのテストの結果で, Diff カラムでソートされています:

  1. After the first request:

    最初のリクエストの後:

    Test type Size Shared Diff
    --------------------------------------------------------------
    install_driver (2) 3465216 2621440 843776
    install_driver & connect_on_init (5) 3461120 2609152 851968
    preload driver (3) 3465216 2605056 860160
    nothing added (1) 3461120 2494464 966656
    connect_on_init (4) 3461120 2482176 978944


  2. After the second request (all the subsequent request showed the same results):

    2 番目のリクエストの後 (後に続くすべてのリクエストは同じ結果を示しました):

    Test type Size Shared Diff
    --------------------------------------------------------------
    install_driver (2) 3469312 2609152 860160
    install_driver & connect_on_init (5) 3481600 2605056 876544
    preload driver (3) 3469312 2588672 880640
    nothing added (1) 3477504 2482176 995328
    connect_on_init (4) 3481600 2469888 1011712


Now what do we conclude from looking at these numbers. First we see that only after a second reload we get the final memory footprint for a specific request in question (if you pass different arguments the memory usage might and will be different).

ではこれらの数値を見て私たちはどんな結論をだすのでしょうか. 最初に私たちは 2 番目のリロードでのみ問題の特定のリクエストのための最終的なメモリのフットプリントを私たちがゲットすることを見ます (あなたが異なる引数を渡すならメモリ使用量は異なるでしょう).
But both tables show the same pattern of memory usage. We can clearly see that the real winner is the startup.pl file's version where the MySQL driver was installed (2). Since we want to have a connection ready for the first request made to the freshly spawned child process, we generally use the version (5) which uses somewhat more memory, but has almost the same number of shared memory pages. The version (3) only preloads the driver which results in smaller shared memory. The last two versions having nothing initialized (1) and having only the connect_on_init() method used (4). The former is a little bit better than the latter, but both significantly worse than the first two versions.

しかしどちらのテーブルも同じメモリ使用量のパターンを示します. 私たちはリアルな勝者が startup.pl ファイルの MySQL ドライバがインストールされたバージョン (2) であることを明確に見ることができます. 私たちは生まれたばかりの child プロセスへの最初の要求のために準備しておく接続をもちたいので, 私たちは通常幾分多めにメモリを使うバージョン (5) を使いますが, ほとんどの同じ共有メモリページの数値です. バージョン (3) はドライバのみをプレロードするのでより少ない共有メモリの結果になります. 最後の 2 つのバージョンは初期化をしない (1) と connect_on_init() メソッドのみが使われる (4) です. 前者は後者よりほんの少しベターですが, 最初の 2 つのバージョンよりはどちらも著しく劣っています.
To remind you why do we look for the smallest value in the column diff, recall the real memory usage formula:

なぜ私たちがカラム diff で最小値を探すのかをあなたが気づくために, 実際のメモリ使用量の式を思い出してください.

RAM_dedicated_to_mod_perl = diff * number_of_processes
+ the_processes_with_largest_shared_memory

Notice that the smaller the diff is, the bigger the number of processes you can have using the same amount of RAM. Therefore every 100K difference counts, when you multiply it by the number of processes. If we take the number from the version (2) vs. (4) and assume that we have 256M of memory dedicated to mod_perl processes we will get the following numbers using the formula derived from the above formula:

なお diff がより小さいことは, あなたが使える同じ RAM の量でプロセスの数がより大きくなります. したがってあなたがプロセスの数を乗算するとき, 100k ごとに差をカウントします. バージョン (2) vs. (4) から数値をとって私たちが mod_perl のための 256M メモリをもっていると仮定すると私たちは上の式から派生したこの式を使って次の数値をゲットします:

RAM - largest_shared_size
N_of Procs = -------------------------
Diff

268435456 - 2609152
(ver 2) N = ------------------- = 309
860160

268435456 - 2469888
(ver 4) N = ------------------- = 262
1011712

So you can tell the difference (17% more child processes in the first version).

ですから私たちは違いが分かります (最初のバージョンでは child プロセスが 17% 増しです).


CGI.pm の初期化 : Initializing CGI.pm



CGI.pm is a big module that by default postpones the compilation of its methods until they are actually needed, thus making it possible to use it under a slow mod_cgi handler without adding a big overhead. That's not what we want under mod_perl and if you use CGI.pm you should precompile the methods that you are going to use at the server startup in addition to preloading the module. Use the compile method for that:

CGI.pm はデフォルトでそのメソッドのコンパイルを実際に必要とされるまで後回しにする大きなモジュールですから, 大きなオーバヘッドを追加することなく遅い mod_cgi ハンドラのもとでも使うことができます. それは私たちが mod_perl のもとで望むものではなくもしあなたが CGI.pm を使うならあなたはそのモジュールのプレローディングに加えてサーバスタートアップで使うそのメソッドをプレコンパイルすべきです. そのために compile メソッドを使い:

use CGI;
CGI->compile(':all');

where you should replace the tag group :all with the real tags and group tags that you are going to use if you want to optimize the memory usage.

ここでもしあなたがメモリ使用量の最適化をしたいのであればあなたはタググループ :all をあなたが使うことになる実際のタグやグループタグでリプレイスしなければなりません.
We are going to compare the shared memory foot print by using the script which is back compatible with mod_cgi. You will see that you can improve performance of this kind of scripts as well, but if you really want a fast code think about porting it to use Apache::Request for CGI interface and some other module for HTML generation.

私たちは mod_cgi との後方互換のスクリプトを使うことで共有メモリのフットプリントを比較します. あなたはあなたがこの種のスクリプトのパフォーマンスも改善できることを見ることになるでしょうが, あなたが本当に高速なコードを望むなら CGI インターフェイスのための Apache::Request や HTML 生成のための他のモジュールを使うようにそれを移植することについて考えてください.
So here is the Apache::Registry script that we are going to use to make the comparison:

そしてこちらが私たちが比較するために使う Apache::Registry スクリプトです:

preload_cgi_pm.pl
-----------------
use strict;
use CGI ();
use GTop ();

my $q = new CGI;
print $q->header('text/plain');
print join "\n", map {"$_ => ".$q->param($_) } $q->param;
print "\n";

my $proc_mem = GTop->new->proc_mem($$);
my $size = $proc_mem->size;
my $share = $proc_mem->share;
my $diff = $size - $share;
printf "%8s %8s %8s\n", qw(Size Shared Diff);
printf "%8d %8d %8d (bytes)\n",$size,$share,$diff;

The script initializes the CGI object, sends HTTP header and then print all the arguments and values that were passed to the script if at all. At the end as usual we print the memory usage.

このスクリプトは CGI オブジェクトを初期化して, HTTP ヘッダを送信してからもしスクリプトに渡された引数と値があればそのすべてを出力します. 最後に私たちはいつもどおりメモリ使用量を出力します.
As usual we are going to use a single child process, therefore we will use this setting in httpd.conf:

いつものように私たちは単一の child プロセスを使います, 従って私たちは httpd.conf でこのセッティングを使います:

MinSpareServers 1
MaxSpareServers 1
StartServers 1
MaxClient 1
MaxRequestsPerChild 100

We are going to run memory benchmarks on three different versions of the startup.pl file. We always preload this module:

私たちは 3 つの異なるバージョンの startup.pl ファイルでメモリベンチマークを実行します. 私たちはこのモジュールを常にプレロードします:

use Gtop ();


  • option 1
    Leave the fiel unmodified.

    ファイルは変更せずそのまま.

  • option 2
    Preload CGI.pm

    CGI.pm をプレロード:

    use CGI ();

  • option 3
    Preload CGI.pm and pre-compile the method that we are going to use in the script:

    CGI.pm をプレロードして私たちがスクリプト内で使うメソッドをプレコンパイル:

    use CGI ();
    CGI->compile(qw(header param));


The server was restarted before each new test.

それぞれの新しいテストの前にサーバはリスタートしました.
So here are the results of the five tests that were conducted, sorted by the Diff column:

そしてこちらが実施した 5 つ (# s/5/3/) のテストの結果で, Diff カラムでソートされています:

  1. After the first reqest:

    最初のリクエストの後:

    Version Size Shared Diff Test type
    --------------------------------------------------------------------
    1 3321856 2146304 1175552 not preloaded
    2 3321856 2326528 995328 preloaded
    3 3244032 2465792 778240 preloaded & methods+compiled

  2. After the second request (all the subsequent request showed the same results):

    2 番目のリクエストの後 (後続のすべてのリクエストは同じ結果を示しました):

    Version Size Shared Diff Test type
    --------------------------------------------------------------------
    1 3325952 2134016 1191936 not preloaded
    2 3325952 2314240 1011712 preloaded
    3 3248128 2445312 802816 preloaded & methods+compiled


The first version shows the results of the script execution when CGI.pm wasn't preloaded. The second version with module preloaded. The third when it's both preloaded and the methods that are going to be used are precompiled at the server startup.

最初のバージョンは CGI.pm がプレロードされていないときのスクリプト実行の結果を示します. 2 番目のバージョンはモジュールがプレロードされています. 3 つ目はプレロードとサーバスタートアップで使われるそのメソッドがプレコンパイルされた両方のときです.
By looking at the version one of the second table we can conclude that, preloading adds about 20K of shared size. As we have mention at the beginning of this section that's how CGI.pm was implemented--to reduce the load overhead. Which means that preloading CGI is almost hardly change a thing. But if we compare the second and the third versions we will see a very significant difference of 207K (1011712-802816), and we have used only a few methods (the header method loads a few more method transparently for a user). Imagine how much memory we are going to save if we are going to precompile all the methods that we are using in other scripts that use CGI.pm and do a little bit more than the script that we have used in the test.

2 番目のテーブルのバージョン 1 を見ると私たちは結論づけることができます, プレロードが共有サイズを約 20K 追加することを. 私たちがこのセクションの冒頭で述べたようにこのように CGI.pm は実装されています - ロードのオーバヘッドを削減するために. それは CGI をプレロードしてもほとんどなにも変わらないことを意味します. しかし私たちが 2 番目と 3 番目のバージョンを比較した場合は 207K (1011712-802816) のかなり有意な違いを私たちは目にします, そして私たちはわずかなメソッドしか使っていません (header メソッドはユーザに透過的にさらにいくつかのメソッドをロードします). 私たちが CGI.pm を使う他のスクリプト内で使うすべてのメソッドを私たちがプレコンパイルして私たちがテストで使ったスクリプトよりも少し多くのことを行う場合にどのくらいのメモリを私たちは節約できるかを想像してください.
But even in our very simple case using the same formula, what do we see? (assuming that we have 256MB dedicated for mod_perl)

しかし同じ式を使う私たちのとてもシンプルなケースであっても, 私たちは何を見るでしょうか ? (私たちは mod_perl のための 256MB をもっていると想定しています)

RAM - largest_shared_size
N_of Procs = -------------------------
Diff

268435456 - 2134016
(ver 1) N = ------------------- = 223
1191936

268435456 - 2445312
(ver 3) N = ------------------- = 331
802816

If we preload CGI.pm and precompile a few methods that we use in the test script, we can have 50% more child processes than when we don't preload and precompile the methods that we are going to use.

私たちが CGI.pm をプレロードして私たちがこのテストスクリプトで使ういくつかのメソッドをプレコンパイルした場合, 私たちがプレロードも私たちが使うメソッドのプレコンパイルも私たちがしなかった場合より 50% 増しで child プロセスをもつことができます.
META: I've heard that the 3.x generation will be less bloated, so probably I'll have to rerun this using the new version.

META: 私は 3.x 世代は膨張が少ないと聞いています, ですからおそらく私たちはこの新しいバージョンを使ってこれを再実行しなければなりません.


mergemem による共有メモリの増加 : Increasing Shared Memory With mergemem



mergemem is an experimental utility for linux, which looks very interesting for us mod_perl users: http://www.complang.tuwien.ac.at/ulrich/mergemem/

mergemem は linux 用の実験的なユーティリティで, それは私たち mod_perl ユーザにとってとても興味深く見えます: http://www.complang.tuwien.ac.at/ulrich/mergemem/
It looks like it could be run periodically on your server to find and merge duplicate pages. It won't halt your httpds during the merge, this aspect has been taken into consideration already during the design of mergemem: Merging is not performed with one big systemcall. Instead most operation is in userspace, making a lot of small systemcalls.

それはあなたのサーバ上で重複ページを見つけてマージするために定期的に実行できるように見えます. それはマージ中にあなたの httpd を停止することがなく, この側面は mergemem の設計中ですでに考慮されています: マージはひとつの大きなシステムコールでは実行されません. 代わりにほとんどのオペレーションはユーザスペースで, 多くの小さなシステムコールで行われます.
Therefore blocking of the system should not happen. And, if it really should turn out to take too much time you can reduce the priority of the process.

したがってシステムのブロックは発生しないはずです. また, もしそれが本当に多くの時間を取る場合あなたはプロセスのプライオリティを下げることができます.
This software comes with a utility called memcmp to tell you how much you might save.

このソフトウェアはあなたにあなたがどのくらい節約できるかを伝える memcmp と呼ばれるユーティリティがついています.


mod_perl からのサブプロセスのフォークと実行 : Forking and Executing Subprocesses from mod_perl



It's desirable to avoid forking under mod_perl. Since when you do, you are forking the entire Apache server, lock, stock and barrel. Not only is your Perl code and Perl interpreter being duplicated, but so is mod_ssl, mod_rewrite, mod_log, mod_proxy, mod_speling (it's not a typo!) or whatever modules you have used in your server, all the core routines, etc.

mod_perl のもとでフォークすることは回避することが望ましいです. あなたがそれをすると, あなたは Apache サーバの, どれもこれも全体をフォークするからです. あなたの Perl コードと Perl インタプリタだけではありません, mod_ssl, mod_rewrite, mod_log, mod_proxy, mod_speling (これはタイプミスではありません !) やあなたがあなたのサーバで使用するモジュール, すべてのコアルーチン, etc. など何もかもが複製されるのです.
Modern Operating Systems come with a very light version of fork which adds a little overhead when called, since it was optimized to do the absolute minimum of memory pages duplications. The copy-on-write technique is the one that allows to do so. The gist of this technique is as follows: the parent process memory pages aren't immediately copied to the child's space on fork(), but this is done only when the child or the parent modifies the data in some memory pages. Before the pages get modified they get marked as dirty and the child has no choice but to copy the pages that are to be modified since they cannot be shared any more.

モダンなオペレーティングシステムはメモリページの複製を最小限にするための最適化が行われているため, コールされたときに少しのオーバヘッドを追加するとても軽量なフォークのバージョンがついてきます. このテクニックの要点は次のものです: parent プロセスのメモリページは fork() 上の child のスペースにすぐにはコピーされず, これはその child かその parent がメモリページの一部でデータを変更したときにのみ行われます. そのページが変更される前にそれはダーティとしてマークされ child はそれをもう共有できなくなるので修正されたそのページをコピーせざるをえません.
If you need to call a Perl program from your mod_perl code, it's better to try to covert the program into a module and call it a function without spawning a special process to do that. Of course if you cannot do that or the program is not written in Perl, you have to call via system() or is equivalent, which spawn a new process. If the program written in C, you may try to write a Perl glue code with help of XS or SWIG architectures, and then the program will be executed as a perl subroutine.

もしあなたがあなたの mod_perl コードから Perl プログラムをコールする必要があるなら, それをするために特別なプロセスを生み出すことなくそのプログラムをモジュールの中に隠してそのファンクションをコールするようにトライすることがベターです. もちろんあなたがそれを行えない場合やそのプログラムが Perl で書かれていない場合は, 新しいプロセスを生む, system() や同等のものを介してあなたはコールしなければなりません. もしそのプログラムが C で書かれているなら, あなたは XS や SWIG アーキテクチャの助けで Perl グルー (# 接着剤) コードを書いて, それからそのプログラムを perl サブルーチンとして実行することにトライできるかもしれません.
Also by trying to spawn a sub-process, you might be trying to do the "wrong thing". If what you really want is to send information to the browser and then do some post-processing, look into the PerlCleanupHandler directive. The latter allows you to tell the child process after request has been processed and user has received the response. This doesn't release the mod_perl process to serve other requests, but it allows to send the response to the client faster. If this is the situation and you need to run some cleanup code, you may want to register this code during the request processing via:

またサブプロセスを生み出すことをトライすることは, あなたが "間違ったこと" のトライしていることになるかもしれません. もしあなたが本当にやりたいことがブラウザに情報を送信して何らかの後処理をしたいのであれば, PerlCleanupHandler ディレクティブを調べてください. 後者はリクエストが処理されてユーザがそのレスポンスを受信した後にあなたが child プロセスと話せるようにします. これは他のリクエストをサーブするために mod_perl プロセスをリリースしませんが, クライアントへのレスポンスをより速く送信できるようにします. もしこの状況であなたが何らかのクリーンアップコードを実行する必要があるなら, あなたはリクエストの処理中にこのコードを登録すると良いかもしれません:

my $r = shift;
$r->register_cleanup(\&do_cleanup);
sub do_cleanup{ # some clean-up code here }

But when a long term process needs to be spawned, there is not much choice, but to use fork(). We cannot just run this long term process within Apache process, since it'll first keep the Apache process busy, instead of letting it do the job it was designed for. And second, if Apache will be stopped the long term process might be terminated as well, unless coded properly to detach from Apache processes group.

しかし長期的なプロセスを生み出す必要があるときは, 選択肢はあまりなく, fork() を使います. 私たちはこの長期的なプロセスを Apache プロセス内で実行できません, それが設計された仕事をする代わりに, それはまず Apache プロセスをビジーにし続けるからです. そして次に, もし Apache が停止されるとその長期的なプロセスも終了されます, Apache プロセスグループから適切にデタッチするためにコーディングされていない限り.
In the following sections we are going to discuss how to properly spawn new processes under mod_perl.

続くセクションで私たちは mod_perl のもとでどのようにして適切に新しいプロセスを生み出すかを論じます.


新しいプロセスをフォークする : Forking a New Process



This is a typical way to call fork() under mod_perl:

これは mod_perl のもとで fork() をコールする典型的な方法です:

defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
# Parent runs this block
# このブロックで Parent が走る
} else {
# Child runs this block
# some code comes here
# このブロックで Child が走る
# 何らかのコードがここに来る
CORE::exit(0);
}
# possibly more code here usually run by the parent
# 通常はおそらくより多くのコードがここで parent によって実行される

When using fork(), you should check its return value, since if it returns undef it means that the call was unsuccessful and no process was spawned. Something that can happen when the system is running too many processes and cannot spawn new ones.

fork() を使うとき, あなたはリターン値をチェックするべきです, もしリターン値が undef の場合はそのコールが失敗してプロセスが生成されなかったことを意味するからです. それはシステムがとても多くのプロセスを実行していて新しいものを生成できないときに発生することです.
When the process is successfully forked--the parent receives the PID of the newly spawned child as a returned value of the fork() call and the child receives 0. Now the program splits into two. In the above example the code inside the first block after if will be executed by the parent and the code inside the first block after else will be executed by the child process.

プロセスがフォークに成功すると -- その parent は新しく生成された child の PID をその fork() コールのリターンされる値として受けとりその child は 0 を受けとります. これでそのプログラムは 2 つに分けられます. 上記の例では if の後の最初のブロック内のコードがその parent によって実行され else の後の最初のブロック内のコードがその child プロセスによって実行されます.
It's important not to forget to explicitly call exit() at the end of the child code when forking. Since if you don't and there is some code outside the if/else block, the child process will execute it as well. But under mod_perl there is another nuance--you must use CORE::exit() and not exit(), which would be automatically overridden by Apache::exit() if used in conjunction with Apache::Registry and similar modules. And we want the spawned process to quit when its work is done, otherwise it'll just stay alive use resources and do nothing.

フォークした時にはその child コードの最後で明示的に exit() をコールすることを忘れないことは重要です. もしあなたがそれをせずに何らかのコードが if/else ブロックの外側にある場合, その child プロセスがそれも実行するからです. しかし mod_perl のもとでは他のニュアンスがあります -- あなたは exit() ではなく CORE::exit() を使わなければなりません, それは Apache::Registry や同様のモジュールと組み合わせて使われると Apache::exit() によって自動的にオーバライドされます. そして私たちは生成したプロセスの作業が完了したときにそれを終了させます, そうでなければそれはリソースを使い何もすることなくただ存在し続けます.
The parent process usually completes its execution path and enters the pool of free servers to wait for a new assignment. If the execution path is to be aborted earlier for some reason one should use Apache::exit() or die(), in the case of Apache::Registry or Apache::PerlRun handlers a simple exit() will do the right thing.

parent プロセスは通常その実行パスを完備していて新しい割り当てを待つためにフリーなサーバのプールに入ります. もしその実行パスが何らかの理由で早期に中止される (# abort される) 場合は Apache::exit() か die() を使うべきで, Apache::Registry や ApachePerlRun ハンドラのケースではシンプルな exit() が適切なことを行います.
The child shares with parent its memory pages until it has to modify some of them, which triggers a copy-on-write process which copies these pages to the child's domain before the child is allowed to modify them. But this all happens afterwards. At the moment the fork() call executed, the only work to be done before the child process goes on its separate way is setting up the page tables for the virtual memory, which imposes almost no delay at all.

child はメモリページのいくつかが変更されるまで parent とそれを共有します, それ (# 変更) はその child がそれらを変更できるようになる前にそれらのページを child の領域にコピーする copy-on-write プロセスをトリガーします. しかしこれはすべて後になって起こることです. fork() コールが実行された時点で, child プロセスが別の道に行く前に行われる唯一の作業は仮想メモリのためのページテーブルをセッティングすることです, それが遅延をしいることはほぼありません.


Parent プロセスを開放する : Freeing the Parent Process



In the child code you must also close all the pipes to the connection socket that were opened by the parent process (i.e. STDIN and STDOUT) and inherited by the child, so the parent will be able to complete the request and free itself for serving other requests. If you need the STDIN and/or STDOUT streams you should re-open them. You may need to close or re-open the STDERR filehandle. It's opened to append to the error_log file as inherited from its parent, so chances are that you will want to leave it untouched.

child のコードであなたは parent プロセスによってオープンされて child によって継承されたコネクションソケット (i.e. STDIN と STDOUT) へのすべてのパイプもクローズしなければなりません, それにより parent はそのリクエストを完了して他のリクエストのために自身を解放できます. もしあなたが STDIN and/or STDOUT ストリームが必要ならあなたはそれらを再オープンするべきです. あなたは STDERR ファイルハンドルのクローズまたは再オープンを必要かもしれません. それはその parent からの継承として error_log ファイルに追加するためにオープンされているので, おそらくあなたはそれをさわることなくそのままにしておいたほうがよいでしょう.
Under mod_perl, the spawned process also inherits the file descriptor that's tied to the socket through which all the communications between the server and the client happen. Therefore we need to free this stream in the forked process. If we don't do that, the server cannot be restarted while the spawned process is still running. If an attempt is made to restart the server you will get the following error:

mod_perl のもとでは, 生成されたプロセスはサーバとクライアントの間で発生したすべてのコミュニケーションを介するソケットに結びつけられたファイルディスクリプタも継承します. したがって私たちはフォークされたプロセス内でこのストリームを開放する必要があります. 私たちがそれを行わないと, 生成されたプロセスがまだ走っている間にそのサーバはリスタートされることができません. もしそのサーバのリスタートを試みた場合あなたは次のエラーをゲットするでしょう:

[Mon Dec 11 19:04:13 2000] [crit]
(98)Address already in use: make_sock:
could not bind to address 127.0.0.1 port 8000

Apache::SubProcess comes to help and provides a method cleanup_for_exec() which takes care of closing this file descriptor.

このファイルディスクリプタをクローズすることを引き受けるメソッド cleanup_for_exec() を提供する Apache::SubProcess は助けになります.
So the simplest way is to freeing the parent process is to close all three STD* streams if we don't need them and untie the Apache socket. In addition you may want to change process' current directory to / so the forked process won't keep the mounted partition busy, if this is to be unmounted at a later time. To summarize all this issues, here is an example of the fork that takes care of freeing the parent process.

ですから parent プロセスを開放するためのもっともシンプルな方法は 3 つの STD* ストリームを私たちが必要としない場合はそのすべてをクローズして Apache ソケットを解くことです. さらにプロセスの現在のディレクトリを変更して フォークされたプロセスがマウントされたパーティションをビジーにし続けないようにすることができます, もしこれが後でアンマウントされるなら. この課題のすべてを要約するために, こちらが parent プロセスの解放を引き受けるフォークの例です.

use Apache::SubProcess;
defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
# Parent runs this block
} else {
# Child runs this block
$r->cleanup_for_exec(); # untie the socket
chdir '/' or die "Can't chdir to /: $!";
close STDIN;
close STDOUT;
close STDERR;

# some code comes here

CORE::exit(0);
}
# possibly more code here usually run by the parent

Of course between the freeing the parent code and child process termination the real code is to be placed.

もちろん parent コードの解放と child プロセスの終了の間には実際のコードが配置されます.


フォークされたプロセスのデタッチ : Detaching the Forked Process



Now what happens if the forked process is running and we decided that we need to restart the web-server? This forked process will be aborted, since when parent process will die during the restart it'll kill its child processes as well. In order to avoid this we need to detach the process from its parent session, by opening a new session with help of setsid() system call, provided by the POSIX module:

さてフォークされたプロセスが走っていて私たちがその web サーバのリスタートが必要だと判断した場合には何が起こるでしょうか ? このフォークされたプロセスは中断されます, そのリスタート中にparent プロセスが die しそれがその child プロセスたちも kill するからです. これを避けるためには私たちは POSIX モジュールによって提供される, setsid() (# Set Session ID, セッションを作成してプロセスグループ ID を設定する) システムコールのヘルプにより新しいセッションをオープンすることで, その parent セッションからそのプロセスをデタッチする必要があります:

use POSIX 'setsid';

defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
# Parent runs this block
} else {
# Child runs this block
setsid or die "Can't start a new session: $!";
...
}

Now the spawned child process has a life of its own, and it doesn't depend on the parent anymore.

これで生成された child プロセスは独自のライフをもち, もはや parent 依存することはなくなります.


ゾンビプロセスの回避 : Avoiding Zombie Processes



Now let's talk about zombie processes.

ではゾンビプロセスについてお話ししましょう.
Normally, every process has its parent. Many processes are children of the init process, whose PID is 1. When you fork a process you must wait() or waitpid() for it to finish. If you don't wait() for it, it becomes a zombie.

普通, すべてのプロセスは parent をもっています. 多くのプロセスは init プロセスの child たちで, その (# init プロセス) の PID は 1 です. あなたがプロセスをフォークするときあなたはそれが終わるまで wait() か waitpid() をしなければなりません. もしあなたがそのために wait() しないと, それはゾンビになります.
A zombie is a process that doesn't have a parent. When the child quits, it reports the termination to its parent. If no parent wait()s to collect the exit status of the child, it gets "confused" and becomes a ghost process, that can be seen as a process, but not killed. It will be killed only when you stop the parent process that spawned it!

ゾンビは parent をもたないプロセスです. child が終了すると, その終了をその parent にレポートします. もし parent その child の終了ステータスを集めるために wait() していない場合, それ (# child) は "混乱" してゴーストプロセスになり, それをプロセスと見ることはできますが, kill されません. それはあなたがそれを生成した parent をストップしたときにのみ kill されます !
Generally the ps(1) utility displays these processes with the <defunc> tag, and you will see the zombies counter increment when doing top(). These zombie processes can take up system resources and are generally undesirable.

通常 ps(1) ユーティリティは <defunc> タグでそれらのプロセスを表示しますし, top() を行うとあなたは zombies カウンタが増加するのをみることができるでしょう. これらのゾンビプロセスはシステムリソースを取りますし通常は望ましくありません.
So the proper way to do a fork is:

ですからフォークを行う適切な方法はこれです:

my $r = shift;
$r->send_http_header('text/plain');

defined (my $kid = fork) or die "Cannot fork:$!";
if ($kid) {
waitpid($kid,0);
print "Parent has finished\n";
} else {
# do something
CORE::exit(0);
}

In most cases the only reason you would want to fork is when you need to spawn a process that will take a long time to complete. So if the Apache process that spawns this new child process has to wait for it to finish, you have gained nothing. You can neither wait for its completion (because you don't have the time to), nor continue because you will get yet another zombie process. This is called a blocking call, since the process is blocked to do anything else before this call gets completed.

ほとんどのケースであなたがフォークしたい唯一の理由はあなたが完了までに長い時間がかかるプロセスの生成を必要とするときです. ですからもしこの新しい child プロセスを生成した Apache プロセスがそれが終わるまで待つとすると, あなたは何も得られません. あなたはそれを待つことも (あなたには時間がないので), 続行することもできません (あなたがまた別のゾンビプロセスをゲットしてしまうので ). これはブロッキングコールと呼ばれます, このコールが完了する前にそのプロセスが他のことを行うことがブロックされるからです.
The simplest solution is to ignore your dead children. Just add this line before the fork() call:

最もシンプルなソリューションはあなたの dead した (# 亡くなった) child たちを無視することです. fork() コールの前にこの行を追加します:

$SIG{CHLD} = 'IGNORE';

When you set the CHLD (SIGCHLD in C) signal handler to 'IGNORE', all the processes will be collected by the init process and are therefore prevented from becoming zombies. This doesn't work everywhere, however. It proved to work at least on Linux OS.

あなたが CHLD (C では SIGCHILD) ハンドラを 'IGNORE' にセットすると, すべてのプロセスは init プロセスによって収集されるのでゾンビになることが防止されます. しかしながら, これはすべての場所では動作しません. これは少なくとも Linux OS 上で動作することが証明されています.
Note that you cannot localize this setting with local(). If you do, it won't have the desired effect.

なおあなたはこのセッティングを local() でローカライズすることはできません. もしあなたがそれをすると, これは期待された効果をもたないでしょう.
[META: Can anyone explain why localization doesn't work?]

[META: なぜローカライゼーションが機能しないかを誰か説明できませんか ?]
So now the code would look like this:

ですからそのコードはこのようになります:

my $r = shift;
$r->send_httpd_header('text/plain');

$SIG{CHLD} = 'IGNORE';

defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
print "Parent has finished\n";
} else {
# do something time-consuming
CORE::exti(0);
}

Note that waitpid() call has gone. The $SIG{CHLD} = 'IGNORE'; statement protects us from zombies, as explained above.

waitpid() コールがいなくなったことに注目してください. 上で説明したように, $SIG{CHLD} = 'IGNORE'; ステートメントがゾンビから私たちを守ります.
Another, more portable, but slightly more expensive solution is to use a double fork approach.

また別の, よりポータブル (# 移植性がある) だが, すこし高価なソリューションはダブルフォークアプローチを使うことです.

my $r = shift;
$r->send_httpd_header('text/plain');

defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
waitpid($kid,0);
} else {
defined (my $grandkid = fork) or die "Kid cannot fork:$!\n";
if ($grandkid) {
CORE::exit(0);
} else {
# code here
# do something long lasting
CORE::exit(0);
}
}

Grandkid becomes a "child of init", i.e. the child of the process whose PID is 1.

Grandkid は "init の child" になります, i.e. PID が 1 のプロセスの child です.
Note that the previous two solutions do allow you to know the exit status of the process, but in our example we didn't care about it.

前の 2 つのソリューションはプロセスの終了ステータスをあなたが分かるようにしていましたが, 私たちの例で私たちはそれについてケアしていないことに注目してください.
Another solution is to use a different SIGCHLD handler:

別のソリューションは異なる SIGCHLD ハンドラを使うことです:

use POSIX 'WNOHANG';
$SIG{CHLD} = sub { while( waitpid(-1,WNOHANG)>0 ) {} };

Which is useful when you fork() more than one process. The handler could call wait() as well, but for a variety of reasons involving the handling of stopped processes and the rare event in which two children exit at nearly the same moment, the best technique is to call waitpid() in a tight loop with a first argument of -1 and a second argument of WNOHANG. Together these arguments tell waitpid() to reap the next child that's available, and prevent the call from blocking if there happens to be no child ready for reaping. The handler will loop until waitpid() returns a negative number or zero, indicating that no more reapable children remain.

これはあなたが 1 つ以上のプロセスを fork() するときに便利です. このハンドラは wait() もコールできますが, ストップしたプロセスのハンドリングや 2 つの child たちがほぼ同時に終了するレアなイベントを含むさまざまな理由で, そのベストテクニックは最初のタイトなループの中で引数 -1 と次の引数 WNOHANG とともに waitpid() をコールすることです. これらの引数と一緒に利用可能な次の child を収穫し, もし収穫する準備ができている child がいない場合にコールがブロックされることを防止するように waitpid() に伝えます. そのハンドラは waitpid() が負の数かゼロをリターンするまでループします, それはもう収穫できる child が残っていないことを示します.
While you test and debug your code that uses one of the above examples, You might want to write some debug information to the error_log file so you know what happens.

あなたが上の例のひとつを使ってあなたのコードをテストしたりデバッグしている間に, あなたはデバッグ情報のいくつかを error_log ファイルに書くことができるのであなたは何が起こったかを知ることができます.
Read perlipc manpage for more information about signal handlers.

シグナルハンドラについての詳細は perlipc man ページを読んでください.


完全なフォークの例 : A Complete Fork Example



Now let's put all the bits of code together and show a well written fork code that solves all the problems discussed so far. We will use an Apache::Registry script for this purpose:

ではすべてのコードの破片をまとめてこれまでに論じたすべての問題を解決するよく書かれたフォークコードを示します. 私たちはこの目的のために Apache::Registry スクリプトを使います:

proper_fork1.pl
---------------
use strict;
use POSIX 'setsid';
use Apache::SubPorcess;

my $r = shift;
$r->send_http_header("text/plain");

$SIG{CHLD} = 'IGNORE';
defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
print "Parent $$ has finished, kid's PID: $kid\n";
} else {
$r->cleanup_for_exec(); # untie the socket
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null'
or die "Can't write to /dev/null: $!";
open STDERR, '>/tmp/log' or die "Can't write to /tmp/log: $!";
setsid or die "Can't start a new session: $!";

my $oldfh = select STDERR;
local $| = 1;
select $oldfh;
warn "started\n";
# do something time-consuming
sleep 1, warn "$_\n" for 1..20;
warn "completed\n";

CORE::exit(0); # terminate the process
}

The script starts with the usual declaration of the strict mode, loading the POSIX and Apache::SubProcess modules and importing of the setsid() symbol from the POSIX package.

このスクリプトは通常の strict モードの宣言でスタートし, POSIX と Apache::SubProcess モジュールをロードして POSIX パッケージからシンボル setsid() のインポートをします.
The HTTP header is sent next, with the Content-type of text/plain. The parent process gets ready to ignore the child, to avoid zombies and the fork is called.

次に text/plain の Content-type で, この HTTP ヘッダが送信されます. その parent プロセスはゾンビを回避するために, child を無視する準備をして fork がコールされます.
The program gets its personality split after fork and the if conditional evaluates to a true value for the parent process, and to a false value for the child process, therefore the first block is executed by the parent and the second by the child.

このプログラムはフォークしたあとにそのパーソナリティを分割して if 条件は parent プロセスで真値, child プロセスで偽値に評価されます, したがって最初のブロックは parent によって 2 番目は child によって実行されます.
The parent process announces his PID and the PID of the spawned process and finishes its block. If there will be any code outside it will be executed by the parent as well.

その parent プロセスは彼の PID と生成されたプロセスの PID をアナウンスしてそのブロックを終了します. もし外側に何らかのコードがある場合はそれも parent によって実行されます.
The child process starts its code by disconnecting from the socket, changing its current directory to /, opening the STDIN and STDOUT streams to /dev/null, which in effect closes them both before opening. In fact in this example we don't need neither of these, so we could just close() both. The child process completes its disengagement from the parent process by opening the STDERR stream to /tmp/log, so it could write there, and creating a new session with help of setsid(). Now the child process has nothing to do with the parent process and can do the actual processing that it has to do. In our example it performs a simple series of warnings, which are logged into /tmp/log:

その child はソケットから切断し, その現在のディレクトリを / に変更し, STDIN と STDOUT ストリームを /dev/null にオープンする, 事実上はオープンの前にそれら両方をクローズすることで, そのコードをスタートします. 実際にこの例で私たちはそれらのどちらも不要ですから, 私たちは両方とも close() するだけ (# 何も出力はしない) です. その child プロセスは /tmp/log への STDERR ストリームをオープンして, そこに書込めるようにして, setsid() のヘルプで新しいセッションを作成することによってその parent からの離脱を完了します. これでその child プロセスはその parent プロセスとの関係をもたなくなりそれが行うべき実際の処理を行えます. 私たちの例では /tmp/log に記録される, 一連の警告を実行します:

my $oldfh = select STDERR;
local $| = 1;
select $oldfh;
warn "started\n";
# do something time-consuming
sleep 1, warn "$_\n" for 1..20;
warn "completed\n";

The localized setting of $|=1 unbuffers the STDERR stream, so we can immediately see the debug output generated by the program. In fact this setting is not required when the output is generated by warn().

$|=1 (perldoc.jp for $|) のローカライズ設定は STDERR ストリームを非バッファにしますので, 私たちはこのプログラムによるそのデバッグの出力をすぐに見ます. 実際は warn() によって出力が生成されるときにこのセッティングは不要です.
Finally the child process terminates by calling:

最後にこれをコールしてその child プロセスを終了します:

CORE::exit(0);

which make sure that it won't get out of the block and run some code that it's not supposed to run.

これはそれがブロックの外に出て実行することが想定されていない何かのコードを実行しないようにします.
This code example will allow you to verify that indeed the spawned child process has its own life, and its parent is free as well. Simply issue a request that will run this script, watch that the warnings are started to be written into the /tmp/log file and issue a complete server stop and start. If everything is correct, the server will successfully restart and the long term process will still be running. You will know that it's still running, if the warnings will still be printed into the /tmp/log file. You may need to raise the number of warnings to do above 20, to make sure that you don't miss the end of the run.

このコード例は実際に生成された child プロセスが独自のライフをもち, その parent もまたフリーであることをあなたが確認できるようにします. このスクリプトを実行するリクエストをシンプルに発行して, その警告が /tmp/log ファイルに書込まれ始めることをウォッチして完全なサーバのストップとスタートを発行します. もしすべてが正しいなら, そのサーバはリスタートを成功して長期プロセスはまだ実行されています. もしその警告がまだ /tmp/log へ出力されていれば, あなたはそれがまだ実行されていることを知るでしょう. あなたは警告の数値を 20 以上に引き上げる必要があるかもしれません, あなたがその実行の終了を見逃さないようにするために.

started
1
2
3
4
5
completed



長時間走る外部プログラムをスタートする : Starting a Long Running External Program



But what happens if we cannot just run a Perl code from the spawned process and we have a compiled utility, i.e. a program written in C. Or we have a Perl program which cannot be easily converted into a module, and thus called as a function. Of course in this case we have to use system(), exec(), qx() or `` (back ticks) to start it.

けれども生成されたプロセスから Perl コードを私たちが実行できなかったり私たちがコンパイルされたユーティリティ, i.e. C で書かれたプログラムをもっている場合はどうなるのでしょうか. あるいは私たちは簡単にはモジュールにコンバートできず, それによりファンクションとしてコールされることができない Perl プログラムをもっている場合です. もちろんこのケースで私たちはそれを system(), exec(), qx() あるいは `` (バックチック) を使ってスタートしなければなりません.
When using any of these methods and when the Taint mode is enabled, we must at least add the following code to untaint the PATH environment variable and delete a few other insecure environment variables. This information can be found in the perlsec manpage.

これらのいずれかの方法を使うときに Taint (# 汚染) モードが有効化されている場合, 私たちは PATH 環境変数を非汚染にするために少なくとも次のコードを追加していくつか他の非セキュアな環境変数を削除しなければなりません. この情報は perlsec man ページで見つけることができます.

$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

Now all we have to do is to reuse the code from the previous section.

これで私たちがしなければならないことは前のセクションのコードを再利用することだけとなります.
First we move the core program into the external.pl file, add the shebang first line so the program will be executed by Perl, tell the program to run under Taint mode (-T) and possibly enable the warnings mode (-w) and make it executable:

最初に私たちはコアプログラムを external.pl ファイルに移動して, 最初の行にシェバンを追加することで Perl によってそのプログラムが実行されるようにし, そのプログラムに Taint モード (-T) のもとで走るように伝えて可能なら warnings モード (-w) を有効しつつそれを実行可能にします:

external.pl
-----------
#!/usr/bin/perl -Tw

open STDIN, '/dev/null/' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null'
or die 'Can't write to /dev/null: $!";
open STDERR, '>/tmp/log' or die "Can't write to /tmp/log: $!";

my $oldfh = select STDERR;
local $| = 1;
select $oldfh;
warn "started\n";
# do something time-consuming
sleep 1, warn "$_\n" for 1..20;
warn "completed\n";

Now we replace the code that moved into the external program with exec() to call it:

そして私たちはそれをコールするために外部プログラムに移動したコードを exec() でリプレイスします:

proper_fork_exec.pl
-------------------
use strict;
use POSIX 'setsid';
use Apache::SubProcess;

$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

my $r = shift;
$r->send_httpd_header("text/html");

$SIG{CHLD} = 'IGNORE';

defined (my $kid = fork) or die "Cannot fork: $!\n";
if ($kid) {
print "Parent has finished, kid's PID: $kid\n";
} else {
$r->cleanup_for_exec(); # untie the socket
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null'
or die "Can't write /dev/null: $!";
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
setsid or die "Can't start a new session: $!";

exec "/home/httpd/perl/external.pl" or die "Cannot execute exec: $!";
}

Notice that exec() never returns unless it fails to start the process. Therefore you shouldn't put any code after exec()--it will be not executed in the case of success. Use system() or back-ticks instead if you want to continue doing other things in the process. But then you probably will want to terminate the process after the program has finished. So you will have to write:

なお exec() はそのプロセスのスタートに失敗しない限りは決してリターンしません. したがってあなたは exec() の後にどのようなコードも置くべきではありません -- それは成功したケースでは実行されることがないはずです. もしあなたがそのプロセスで他の何かを続けて行いたい場合は代わりに system() やバックチック (# ``) を使います. しかしあなたはおそらくそのプログラムが終了した後にはそのプロセスを終了した方がよいでしょう. ですからあなたこのように書くべきです:

system "/home/httpd/perl/external.pl" or die "Cannot execute system: $!";
CORE:exit(0);

Another important nuance is that we have to close all STD* stream in the forked process, even if the called program does that.

その他の重要なニュアンスは私たちはそのフォークされたプロセスですべての STD* ストリームをクローズしなければならないということです, たとえそれがコールされたプログラムで行われたとしてもです.
If the external program is written in Perl you may pass complicated data structures to it using one of the methods to serialize Perl data and then to restore it. The Storable and FreezeThaw modules come handy. Let's say that we have program master.pl calling program slave.pl:

もし外部プログラムが Perl で書かれているならあなたは Perl データをシリアライズしてリストアするメソッドのひとつを使ってそれに複雑なデータ構造を渡すこともできます. Storable と FreezeThaw モジュールは便利です. では私たちが slave.pl をコールするプログラム master.pl をもっているとしましょう:

master.pl
---------
# we are within the mod_perl code
use Storable ();
my @params = (foo => 1, bar => 2);
my $params = Storable::freeze(\@params);
exec "./slave.pl", $params or die "Cannot execute exec: $!";

slace.pl
--------
#!/usr/bin/perl -w
use Storable ();
my @params = @ARGV ? @{ Storable::thaw(shift)||[] } : ();
# do something

As you can see, master.pl serializes the @params data structure with Storable::freeze and passes it to slave.pl as a single argument. slave.pl restores the it with Storable::thaw, by shifting the first value of the ARGV array if available. The FreezeThaw module does a very similar thing.

あなたが見るように, master.pl は Storable::freeze で @params データ構造をシリアライズしてそれを単一の引数として slave.pl に渡します. slave.pl はもし利用可能なら ARGV の最初の値を shift して, Storable::thaw によってそれをリストアします. FreezeThaw モジュールはとても似たことを行います.


短時間走る外部プログラムをスタートする : Starting a Short Running External Program



Sometimes you need to call an external program and you cannot continue before this program completes its run and optionally returns some result. In this case the fork solution doesn't help. But we have a few ways to execute this program. First using system():

ときにあなたは外部プログラムをコールする必要があるがあなたはこのプログラムがその実行を完了してオプションで何らかの結果をリターンするまでは続きができない場合があります. このケースではフォークソリューションはヘルプになりません. しかし私たちはこのプログラムを実行するいくつかの方法をもっています. 最初は system() を使うものです:

system "perl -e 'print 5+5'"

We believe that you will never call the perl interperter for doing this simple calculation, but for the sake of a simple example it's good enough.

私たちはあなたがこのシンプルな計算のために perl インタプリタをコールすることは決してないと信じていますが, シンプルな例のためにはそれで十分です.
The problem with this approach is that we cannot get the results printed to STDOUT, and that's where back-ticks or qx() come to help. If you use either:

このアプローチの問題は私たちが STDOUT に出力された結果をゲットできないことであり, その場合は バックチックや qx() がヘルプになります. あなたがいずれかを使う場合:

my $result = `perl -e 'print 5+5'`;

or

my $result = qx{perl -e 'print 5+5'};

the whole output of the external program will be stored in the $result variable.

外部プログラムの出力全体が $result 変数に格納されるはずです.
Of course you can use other solutions, like opening a pipe (| to the program) if you need to submit many arguments and more evolved solutions provided by other Perl modules like IPC::Open2 which allows to open a process for both reading and writing.

もちろんあなたは他のソリューションを使えます, もしあなたがたくさんの引数を送る必要があるならパイプをオープン (プログラムへの |) したり読込みと書込みの両方のためのプロセスをオープンできるようにする IPC::Open2 のような他のモジュールによって提供されるより進化したソリューションです.


正しい方法で system() や exec() を実行する : Executing system() or exec() in the Right Way



The exec() and system() system calls behave identically in the way they spawn a program. For example let's use system() as an example. Consider the following code:

システムコールの exec() や system() はプログラムを生成する方法と同じように振る舞います. 例えば例として system() を使って見ましょう. 次のコードを考えてみてください:

system("echo","Hi");

Perl will use the first argument as a program to execute, find /bin/echo along the search path, invoke it directly and pass the Hi string as an argument.

Perl は実行するプログラムとして最初の引数を使い, 検索パスに沿って /bin/echo を見つけ, それを直接呼びだして引数として Hi 文字列を渡します.
Perl's system() is not the system(3) call [C-library]. This is how the arguments to system() get interpreted. When there is a single argument to system(), it'll be checked for having shell metacharacters first (like *,?), and if there are any--Perl interpreter invokes a real shell program (/bin/sh -c on Unix platforms). If you pass a list of arguments to system(), they will be not checked for metacharacters, but split into words if required and passed directly to the C-level execvp() system call, which is more efficient. That's a very nice optimization. In other words, only if you do:

Perl の system() は system(3) コール [C-ライブラリ] ではありません. system() への引数はこのようにして解釈されます. system() にひとつの引数があるとき, それは最初にシェルのメタキャラクタ (*, ? のような) をもっているかがチェックされ, そのどれかがある場合 -- Perl インタプリタはリアルのシェルプログラム (Unix プラットフォームでは /bin/sh -c) を呼びだします. もしあなたが system() に引数のリストを渡すと, それらはメタキャラクタのチェックをされることはありませんが, 必要に応じてワードに分割し C-レベルの execvp() システムコールに直接渡されます, それはより効率的です. これはとてもナイスな最適化です. 言い換えれば, あなたがこれをする場合にのみ:

system "sh -c 'echo *'"

will the operating system actually exec() a copy of /bin/sh to parse your command. But even then since sh is almost certainly already running somewhere, the system will notice that (via the disk inode reference) and replace your virtual memory page table with one pointing to the existing program code plus your data space, thus will not create this overhead.

オペレーティングシステムはあなたのコマンドを解析するために実際に /bin/sh のコピーを exec() します. しかしそれでも sh はほぼ確実にどこかですでに実行されているので, そのシステムはそれに気づき (ディスク inode リファレンスを介して) あなたの仮想メモリページテーブルをその既存のプログラムコードとあなたのデータスペースを指すものにリプレイスします, ゆえにこのオーバヘッドは作成されません.


プロキシ用の OS 固有のパラメータ : OS Specific Parameters for Proxying



Most of the mod_perl enabled servers use a proxy front-end server. This is done in order to avoid serving static objects, and also so that generated output which might be received by slow clients does not cause the heavy but very fast mod_perl servers from idly waiting.

mod_perl が有効化されたサーバのほとんどはプロキシのフロントエンドサーバを使います. これは静的オブジェクトをサーブすることを回避するために行われます, またそれは遅いクライアントによって受信されるかもしれない生成された出力が重いがとても高速な mod_perl サーバを無駄に待機することをさせないようにします.
There are very important OS parameters that you might want to change in order to improve the server performance. This topic is discussed in the section: Setting the Buffering Limits on Various OSes

サーバのパフォーマンスを向上されるためにあなたが変更したほうが良いかもしれないとても重要な OS パラメータがあります. このトピックはこのセクションで議論されています: Setting the Buffering Limits on Various OSes


Apache 構成を微調整することによるパフォーマンスチューニング : Performance Tuning by Tweaking Apache Configuration



Correct configuration of the MinSpareServers, MaxSpareServers, StartServers, MaxClients, and MaxRequestsPerChild parameters is very important. There are no defaults. If they are too low, you will under-use the system's capabilities. If they are too high, the chances are that the server will bring the machine to its knees.

MinSpareServers, MaxSpareServers, StartServers, MaxClient, それから MaxRequestsPerChild パラメータの正しい構成はとても重要です. これらはデフォルトではありません. もしこれらが低すぎると, あなたはそのシステムの能力を活用できません. もしこれらが高すぎると, おそらくそのサーバはそのマシンを屈服させるでしょう.
All the above parameters should be specified on the basis of the resources you have. With a plain apache server, it's no big deal if you run many servers since the processes are about 1Mb and don't eat a lot of your RAM. Generally the numbers are even smaller with memory sharing. The situation is different with mod_perl. I have seen mod_perl processes of 20Mb and more. Now if you have MaxClients set to 50: 50x20Mb = 1Gb. Do you have 1Gb of RAM? Maybe not. So how do you tune the parameters? Generally by trying different combinations and benchmarking the server. Again mod_perl processes can be of much smaller size with memory sharing.

すべての上記パラメータはあなたがもつリソースに基づいて指定されるべきです. プレーンの apache サーバは, もしあなたがたくさんのサーバを実行してもそのプロセスは約 1 Mb であなたの RAM をほとんど食べないのでたいしたことはありません. 一般的にメモリ共有でその数値はさらに小さくなります. このシチュエーションは mod_perl では異なります. 私は 20Mb 以上の mod_perl プロセスを見たことがあります. これでもしあなたが MaxClients を 50 にセットすると: 50x20Mb = 1Gb. あなたは 1Gb の RAM をもっていますか ? おそらくそれはないでしょう. ではあなたはどのようにこのパラメータを調整すればよいのでしょうか ? 一般的には異なるコンビーネーションをトライしてそのサーバのベンチマーキングをします. ここでもメモリ共有で mod_perl プロセスのサイズをかなり小さいサイズにできます.
Before you start this task you should be armed with the proper weapon. You need the crashme utility, which will load your server with the mod_perl scripts you possess. You need it to have the ability to emulate a multiuser environment and to emulate the behavior of multiple clients calling the mod_perl scripts on your server simultaneously. While there are commercial solutions, you can get away with free ones which do the same job. You can use the ApacheBench ab utility which comes with the Apache distribution, the crashme script which uses LWP::Parallel::UserAgent, httperf or http_load.

あなたがこのタスクをスタートする前にあなたは適切な武器を装備するべきです. あなたは crashme ユーティリティが必要です, それはあなたが所有する mod_perl スクリプトをあなたのサーバにロードします. あなたはマルチユーザ環境をエミュレートしてあなたのサーバ上で同時に mod_perl スクリプトをコールするマルチクライアントの振る舞いをエミュレートする機能をもつものが必要です. 商用のソリューションがある一方で, あなたは同じ仕事をするフリーのものに逃げることができます. あなたは Apache ディストリビューションについてくる ApacheBench ab ユーティリティ, LWP::Parallel::UserAgent, httperfhttp_load を使用する crashme スクリプトを使えます.
It is important to make sure that you run the load generator (the client which generates the test requests) on a system that is more powerful than the system being tested. After all we are trying to simulate Internet users, where many users are trying to reach your service at once. Since the number of concurrent users can be quite large, your testing machine must be very powerful and capable of generating a heavy load. Of course you should not run the clients and the server on the same machine. If you do, your test results would be invalid. Clients will eat CPU and memory that should be dedicated to the server, and vice versa.

あなたがテストされるシステムよりもよりパワフルなマシンでロードジェネレータ (テストリクエストを生成するクライアント) を実行するようにすることが重要です. 結局私たちはインターネットユーザをシミュレートすることにトライするのです, それは一度に多くのユーザがあなたのサービスに到達しようとしています. 同時ユーザの数はかなり大きくなるかもしれないので, あなたのテストマシンはとてもパワフルでヘビーな負荷を生成できる能力でなければいけません. もちろんあなたは同じマシンでクライアントとサーバを実行するべきではありません. もしあなたがそうすると, あなたのテスト結果は無効になるでしょう. クライアントはサーバのためであるはずの CPU とメモリを食べてしまいますしその逆も然りです.


ApacheBench での構成のチューニング : Configuration Tuning with ApacheBench



We are going to use ApacheBench (ab) utility to tune our server's configuration. We will simulate 10 users concurrently requesting a very light script at http://www.example.com/perl/access/access.cgi. Each simulated user makes 10 requests.

私たちは私たちのサーバ構成を調整するために ApacheBench (ab) ユーティリティを使います. 私たちは http://www.example.com/perl/access/access.cgi でとても軽量なスクリプトを同時にリクエストする 10 ユーザをシミュレートします. シミュレートされたユーザはそれぞれ 10 のリクエストをします.

% ./ab -n 100 -c 10 http://www.example.com/perl/access/access.cgi

The results are:

この結果は:

Document Path: /perl/access/access.cgi
Document Length: 16 bytes

Concurrency Level: 10
Time taken for tests: 1.683 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 16100 bytes
HTML transferred: 1600 bytes
Requests per second: 59.42
Transfer rate: 9.57 kb/s received

Connnection Times (ms)
min avg max
Connect: 0 29 101
Processing: 77 124 1259
Total: 77 153 1360

The only numbers we really care about are:

私たちが本当にケアするのはこの数値のみです:

Complete requests: 100
Failed requests: 0
Requests per second: 59.42

Let's raise the request load to 100 x 10 (10 users, each makes 100 requests):

リクエストの負荷を 100 x 10 (10 ユーザ, 各 100 リクエストを作成) に上げてみましょう:

% ./ab -n 1000 -c 10 http://www.example.com/perl/access/access.cgi
Concurrency Level: 10
Complete requests: 1000
Failed requests: 0
Requests per second: 139.76

As expected, nothing changes -- we have the same 10 concurrent users. Now let's raise the number of concurrent users to 50:

予想通り, 何も変わりません -- 私たちは同じ 10 の同時ユーザをもちます. では同時ユーザの数を 50 に上げてみましょう:

% ./ab -n 1000 -c 50 http://www.example.com/perl/access/access.cgi
Complete requests: 1000
Failed requests: 0
Requests per second: 133.01

We see that the server is capable of serving 50 concurrent users at 133 requests per second! Let's find the upper limit. Using -n 10000 -c 1000 failed to get results (Broken Pipe?). Using -n 10000 -c 500 resulted in 94.82 requests per second. The server's performance went down with the high load.

私たちはこのサーバが 133 リクエスト/秒 で 50 の同時ユーザをサーブする能力であることを見ます ! 上限リミットを見つけましょう. -n 10000 -c 1000 を使うと結果のゲットに失敗しました (パイプが壊れた ?). -n 10000 -c 500 を使うと 94.82 リクエスト/秒 の結果になりました. この高い負荷でこのサーバのパフォーマンスが下がりました.
The above tests were performed with the following configuration:

上記テストは次の構成で実行されました:

MinSpareServers 6
MaxSpareServers 8
StartServers 10
MaxClients 50
MaxRequestsPerChild 1500

Now let's kill each child after it serves a single request. We will use the following configuration:

では各 child がひとつのリクエストをサーブしたあとにそれを kill してみましょう. 私たちは次の構成を使います:

MinSpareServers 6
MaxSpareServers 8
StartServers 10
MaxClients 100
MaxRequestsPerChild 1

Simulate 50 users each generating a total of 20 requests:

それぞれ合計 20 のリクエストを生成する 50 のユーザをシミュレートします:

% ./ab -n 1000 -c 50 http://www.example.com/perl/access/access.cgi

The benchmark timed out with the above configuration.... I watched the output of ps as I ran it, the parent process just wasn't capable of respawning the killed children at that rate. When I raised the MaxRequestsPerChild to 10, I got 8.34 requests per second. Very bad - 18 times slower! You can't benchmark the importance of the MinSpareServers, MaxSpareServers and StartServers with this kind of test.

上記構成でこのベンチマークはタイムアウトしました.... 私は私がそれを実行しているときに ps の出力をウォッチしました, parent プロセスは kill された child たちを再生することができないようでした. 私が MaxRequestsPerChild を 10 に上げると, 私は 8.34 リクエスト/秒 をゲットしました. とても悪い - 18 倍も遅いです ! あなたはこの種のテストでは MinSpareServers, MaxSpareServers それから StartServers の重要性をテストできません.
Now let's reset MaxRequestsPerChild to 1500, but reduce MaxClients to 10 and run the same test:

ここで MaxRequestsPerChild を 1500 にリセットしてみましょう, しかし MaxClients は 10 に減らして同じテストを実行します:

MinSpareServers 6
MaxSpareServers 8
StartServers 10
MaxClients 10
MaxRequestsPerChild 1500

I got 27.12 requests per second, which is better but still 4-5 times slower. (I got 133 with MaxClients set to 50.)

私は 27.12 リクエスト/秒をゲットしました, これはベターですがまだ 4-5 倍遅いです. (私は MaxClients を 50 にセットして 133 をゲットしました.)
Summary: I have tested a few combinations of the server configuration variables (MinSpareServers, MaxSpareServers, StartServers, MaxClients and MaxRequestsPerChild). The results I got are as follows:

サマリ: 私はいくつかこのサーバ構成変数の組み合わせをテストしました (MinSpareServers, MaxSpareServers, StartServers, MaxClients それから MaxRequestsPerChild). 私がえた結果は次のとおりです:
MinSpareServers, MaxSpareServers and StartServers are only important for user response times. Sometimes users will have to wait a bit.

MinSpareServers, MaxSpareServers それと StartServers はユーザのレスポンスタイムにのみ重要です. ときどきユーザはすこし待つでしょう.
The important parameters are MaxClients and MaxRequestsPerChild. MaxClients should be not too big, so it will not abuse your machine's memory resources, and not too small, for if it is your users will be forced to wait for the children to become free to serve them. MaxRequestsPerChild should be as large as possible, to get the full benefit of mod_perl, but watch your server at the beginning to make sure your scripts are not leaking memory, thereby causing your server (and your service) to die very fast.

重要なパラメータは MaxClient と MaxRequestsPerChild です. MaxClients は大きすぎてはいけません, そうすればあなたのマシンのメモリリソースを濫用することはありません, そして小さすぎないでください, もしそうならあなたのユーザは child たちが彼らをサーブするためにフリーになることを待たざるをえなくなります. MaxRequestsPerChild はできるだけ大きくしなければなりません, mod_perl のフルベネフィットをゲットするためです, しかしあなたのスクリプトがメモリをリークしていないことをあなたのサーバの開始時にウォッチしてください, それによりあなたのサーバ (とあなたのサービス) はあっという間に die するはめになります.
Also it is important to understand that we didn't test the response times in the tests above, but the ability of the server to respond under a heavy load of requests. If the test script was heavier, the numbers would be different but the conclusions very similar.

また私たちは上でレスポンスタイムをテストしたのではなく, 私たちはリクエストの重い負荷のもとで対応するサーバの能力をテストしたことを理解しておくことは重要です. もしテストスクリプトがより重い場合, この数値は異なりますがとても似た結果になるでしょう.
The benchmarks were run with:

このベンチマークはこれで実行しました:

HW: RS6000, 1Gb RAM
SW: AIX 4.1.5 . mod_perl 1.16, apache 1.3.3
Machine running only mysql, httpd docs and mod_perl servers.
Machine was _completely_ unloaded during the benchmarking.

After each server restart when I changed the server's configuration, I made sure that the scripts were preloaded by fetching a script at least once for every child.

私はサーバの構成を変更し各サーバを再起動したあとで, 私はすべての child で少なくとも 1 度はスクリプトをフェッチすることでそのスクリプトがプレロードされているかを確認しました.
It is important to notice that none of the requests timed out, even if it was kept in the server's queue for more than a minute! That is the way ab works, which is OK for testing purposes but will be unacceptable in the real world - users will not wait for more than five to ten seconds for a request to complete, and the client (i.e. the browser) will time out in a few minutes.

リクエストが 1 分以上サーバのキューに保持されていたとしても, それがタイムアウトしなかったことに気づくことは重要です ! それが ab の動作方法であり, それはテストの目的には OK ですが, リアルワールドでは受けいれられることではありません - ユーザはリクエストが完了するまで 5 から 10 秒以上は待ちませんし, そのクライアント (i.e. そのブラウザ) は数分でタイムアウトします.
Now let's take a look at some real code whose execution time is more than a few milliseconds. We will do some real testing and collect the data into tables for easier viewing.

では実行時間が数ミリ秒ちょっとのリアルなコードの一部を見てみましょう. 私たちは一部リアルなテストをして見やすくするためにデータをテーブルに集めました.
I will use the following abbreviations:

私は次の略語をつかいます:

NR = Total Number of Request
NC = Concurrency
MC = MaxClients
MRPC = MaxRequestsPerChild
RPS = Requests per second

Running a mod_perl script with lots of mysql queries (the script under test is mysqld limited) (http://www.example.com/perl/access/access.cgi?do_sub=query_form), with the configuration:

たくさんの mysql クエリで mod_perl を実行しています (このテストのスクリプトは msqld に制限されています) (http://www.example.com/perl/access/access.cgi?do_sub=query_from), この構成では:

MinSpareServers 8
MaxSpareServers 16
StartServers 10
MaxClients 50
MaxRequestsPerChild 5000

gives us:

これが与えられます:

NR NC RPS comment
------------------------------------------------
10 10 3.33 # not a reliable figure (信頼できない数字)
100 10 3.94
1000 10 4.62
1000 50 4.09

Conclusions: Here I wanted to show that when the application is slow (not due to perl loading, code compilation and execution, but limited by some external operation) it almost does not matter what load we place on the server. The RPS (Requests per second) is almost the same. Given that all the requests have been served, you have the ability to queue the clients, but be aware that anything that goes into the queue means a waiting client and a client (browser) that might time out!

結論: ここで私はアプリケーションが遅いとき (perl のロード, コードのコンパイルと実行によるものではなく, 何らかの外部オペレーションによって制限されているる) サーバで私たちがどのような負荷をかけるかはほとんど関係しないことを示したいと思いました. RPS (リクエスト/秒) はほとんど同じです. すべてのリクエストがサーブされると, あなたはクライアントをキューすることができますが, キューに入るものは待機するクライアントとタイムアウトする可能性があるクライアント (ブラウザ) であることを認識してください !
Now we will benchmark the same script without using the mysql (code limited by perl only): (http://www.example.com/perl/access/access.cgi), it's the same script but it just returns the HTML form, without making SQL queries.

では mysql の利用を除外して私たちは同じスクリプトをベンチマークします (コードは perl にのみ制限されます): (http://www.example.com/perl/access/access.cgi), それは同じスクリプトですが HTML をリターンするだけです. SQL クエリの作成はしません.

MinSpareServers 8
MaxSpareServers 16
StartServers 10
MaxClients 50
MaxRequestsPerChild 5000

NR NC RPS comment
------------------------------------------------
10 10 26.95 # not a reliable figure
100 10 30.88
1000 10 29.31
1000 50 28.01
1000 100 29.74
10000 200 24.92
100000 400 24.95

Conclusions: This time the script we executed was pure perl (not limited by I/O or mysql), so we see that the server serves the requests much faster. You can see the number of requests per second is almost the same for any load, but goes lower when the number of concurrent clients goes beyond MaxClients. With 25 RPS, the machine simulating a load of 400 concurrent clients will be served in 16 seconds. To be more realistic, assuming a maximum of 100 concurrent clients and 30 requests per second, the client will be served in 3.5 seconds. Pretty good for a highly loaded server.

結論: 今回私たちが実行したスクリプトはピュア perl なので (I/O や mysql で制限されない), 私たちはサーバがとても高速にリクエストをサーブすることを見ます. あなたはリクエスト/秒の数値がどの負荷でもほとんど同じだが, 同時クライアントの数が MaxClients の数を超えると低くなっていくことを見ることがます. 25 RPS で, このマシンがシミュレートする 400 の同時クライアントは 16 秒でサーブされます. より現実的になると, 最大 100 の同時クライアントと 30 リクエスト/秒を想定すると, クライアントは 3.5 秒でサーブされます. 高負荷のサーバとしてはかなりグッドです.
Now we will use the server to its full capacity, by keeping all MaxClients clients alive all the time and having a big MaxRequestsPerChild, so that no child will be killed during the benchmarking.

次にすべての MaxClients クライアントを常にキープして大きな MaxRequestPerChild をもつことによって, 私たちはこのサーバのフルの能力を使います, ですからベンチマークの間 child は kill されません.

MinSpareServers 50
MaxSpareServers 50
StartServers 50
MaxClients 50
MaxRequestsPerChild 5000

NR NC RPS comment
------------------------------------------------
100 10 32.05
1000 10 33.14
1000 50 33.17
1000 100 31.72
10000 200 31.60

Conclusion: In this scenario there is no overhead involving the parent server loading new children, all the servers are available, and the only bottleneck is contention for the CPU.

結論: このシナリオでは parent サーバが新しい child をロードすることに関するオーバヘッドがなく, すべてのサーバが利用可能です, そして唯一のボトルネックは CPU の競合だけです.
Now we will change MaxClients and watch the results: Let's reduce MaxClients to 10.

次に私たちは MaxClients を変更してその結果をウォッチします: MaxClients を 10 に減らしてみましょう.

MinSpareServers 8
MaxSpareServers 10
StartServers 10
MaxClients 10
MaxRequestsPerChild 5000

NR NC RPS comment
------------------------------------------------
10 10 23.87 # not a reliable figure
100 10 32.64
1000 10 32.82
1000 50 30.43
1000 100 25.68
1000 500 26.95
2000 500 32.53

Conclusions: Very little difference! Ten servers were able to serve almost with the same throughput as 50 servers. Why? My guess is because of CPU throttling. It seems that 10 servers were serving requests 5 times faster than when we worked with 50 servers. In that case, each child received its CPU time slice five times less frequently. So having a big value for MaxClients, doesn't mean that the performance will be better. You have just seen the numbers!

結論: 違いはほんの少しです ! 10 のサーバは 50 のサーバとほとんど同じスループットでサーブできました. なぜでしょう ? 私の推測では CPU スロットリングが理由です. 10 のサーバは私たちが 50 のサーバで作業したときよりも 5 倍速くリクエストをサーブしているように見えます. そのケースでは, 各 child は 1/5 倍の頻度で CPU タイムスライスを受けとりました. ですから MaxClients で大きな値を持つことは, パフォーマンスがベターになることを意味しません. あなたはその数値を見たばかりです !
Now we will start drastically to reduce MaxRequestsPerChild:

次に私たちは MaxRequestsPerChild を極端に減らしてみます:

MinSpareServers 8
MaxSpareServers 16
StartServers 10
MaxClients 50

NR NC MRPC RPS comment
------------------------------------------------
100 10 10 5.77
100 10 5 3.32
1000 50 20 8.92
1000 50 10 5.47
1000 50 5 2.83
1000 100 10 6.51

Conclusions: When we drastically reduce MaxRequestsPerChild, the performance starts to become closer to plain mod_cgi.

結論: 私たちが MaxRequestsPerChild を極端に減らすと, そのパフォーマンスはプレーンの mod_cgi に近づき始めました.
Here are the numbers of this run with mod_cgi, for comparison:

こちらは比較のために, これを mod_cgi で実行した数値です:

MinSpareServers 8
MaxSpareServers 16
StartServers 10
MaxClients 50

NR NC RPS comment
------------------------------------------------
100 10 1.12
1000 50 1.14
1000 100 1.13

Conclusion: mod_cgi is much slower. :) In the first test, when NR/NC was 100/10, mod_cgi was capable of 1.12 requests per second. In the same circumstances, mod_perl was capable of 32 requests per second, nearly 30 times faster! In the first test each client waited about 100 seconds to be served. In the second and third tests they waited 1000 seconds!

結論: mod_cgi はかなり遅いですね. :) 最初のテストでは, NR/NC は 100/10 で, mod_cgi は 1.12 リクエスト/秒の能力でした. 同じ状況で, mod_perl は 32 リクエスト/秒の能力で, 30 倍近く高速でした ! 最初のテストで各クライアントはサーブされるために約 100 秒待ちました. 2 番目と 3 番目のテストでそれらは 1000 秒待ちました !


MaxClients の選択 : Choosing MaxClients



The MaxClients directive sets the limit on the number of simultaneous requests that can be supported. No more than this number of child server processes will be created. To configure more than 256 clients, you must edit the HARD_SERVER_LIMIT entry in httpd.h and recompile. In our case we want this variable to be as small as possible, because in this way we can limit the resources used by the server children. Since we can restrict each child's process size (see Preventing Your Processes from Growing), the calculation of MaxClients is pretty straightforward:

MaxClients ディレクティブはサポートされる同時リクエストの制限をセットします. この数値を超える数の child サーバプロセスは作成されません. 256 クライアント以上に構成するには, あなたは http.h の HARD_SERVER_LIMIT エントリを編集して再コンパイルしなければなりません. 私たちのケースで私たちはこの変数をできるだけ小さくするようにしました, このやり方で私たちはそのサーバの child たちによって使われるリソースを制限できるからです. 私たちは各 child のプロセスサイズを制限できるので (Preventing Your Processes form Growing を参照), MaxClients の計算はとても簡単です.

Total RAM Dedicated to the Webserver
MaxClients = ------------------------------------
MAX child's process size

So if I have 400Mb left for the webserver to run with, I can set MaxClients to be of 40 if I know that each child is limited to 10Mb of memory (e.g. with Apache::SizeLimit).

ですからもし私が web サーバの実行で 400Mb の残りをもっている場合に, 私が各 child は 10Mb のメモリに制限されている (e.g. Apache::SizeLimit で) と知っていれば私は MaxClients を 40 にセットできます.
You will be wondering what will happen to your server if there are more concurrent users than MaxClients at any time. This situation is signified by the following warning message in the error_log:

常に MaxClients より多くの同時ユーザがいるとあなたのサーバがどうなるのかあなたは疑問に思うでしょう. このシチュエーションは error_log の中で次の警告メッセージで知らされます:

[Sun Jan 24 12:05:32 1999] [error] server reached MaxClients setting,
consider raising the MaxClients setting

There is no problem -- any connection attempts over the MaxClients limit will normally be queued, up to a number based on the ListenBacklog directive. When a child process is freed at the end of a different request, the connection will be served.

問題はありません -- MaxClients を超える接続の試みは ListenBacklog ディレクティブに基づく数値に達するまで, いずれも普通はキューされます. 異なるリクエストの終わりで child プロセスが解放されたときに, そのコネクションはサーブされます.
It is an error because clients are being put in the queue rather than getting served immediately, despite the fact that they do not get an error response. The error can be allowed to persist to balance available system resources and response time, but sooner or later you will need to get more RAM so you can start more child processes. The best approach is to try not to have this condition reached at all, and if you reach it often you should start to worry about it.

クライアントがすぐにサーブされるのではなくキューに置かれるのでこれはエラーです, それにも関わらずそれらはエラーレスポンスをゲットしないということがこの事実です. このエラーは利用可能なシステムリソースとレスポンスタイムのバランスをとるためにそのままにしておくことができますが, 遅かれ早かれあなたはより多くの RAM をゲットしてあなたがより多くの child プロセスをスタートできるようすることが必要になるでしょう. ベストのアプローチはこの条件にまったくリーチしないように試みることで, もしあなたがこれに頻繁にリーチするならあなたはそれについて心配し始めたほうがよいです.
It's important to understand how much real memory a child occupies. Your children can share memory between them when the OS supports that. You must take action to allow the sharing to happen - See Preload Perl modules at server startup. If you do this, the chances are that your MaxClients can be even higher. But it seems that it's not so simple to calculate the absolute number. If you come up with a solution please let us know! If the shared memory was of the same size throughout the child's life, we could derive a much better formula:

child が実際のメモリをどれくらい占有するかを理解することは重要です. OS がメモリのシェアをサポートしていればあなたの child たちはその間でそれができます. 共有が発生できるようにするためにあなたはアクションしなければなりません - Preload Perl modules at server startup を参照してください. あなたがこれを行うと, あなたの MaxClients をより高くできる可能性があります. しかしその絶対的な数値を計算するのはシンプルではないように見えます. もしあなたがソリューションを思いついたらぜ是非私たちに教えてください ! もし child のライフを通じて共有メモリが同じサイズなら, 私たちははるかにベターな式を導くことができたでしょう:

Total_RAM + Shared_RAM_per_Child * (MaxClients - 1)
MaxClients = ---------------------------------------------------
Max_Process_Size

which is:

これ (# 上の式) がこれ (# 下の式) です:

Total_RAM - Shared_RAM_per_Child
MaxClients = ---------------------------------------
Max_Process_Size - Shared_RAM_per_Child

Let's roll some calculations:

いくつかの計算を回してみましょう:

Total_RAM = 500Mb
Max_Process_Size = 10Mb
Shared_RAM_per_Child = 4Mb

500 - 4
MaxClients = --------- = 82
10 - 4

With no sharing in place

共有しない場合では

500
MaxClients = --------- = 50
10

With sharing in place you can have 64% more servers without buying more RAM.

共有する場合であなたは追加で RAM を購入することなく 64% 多くのサーバをもつことができます.
If you improve sharing and keep the sharing level, let's say:

もしあなたが共有を改善しつつその共有レベルを維持する場合, 仮にこうしてみましょう:

Total_RAM = 500Mb
Max_Process_Size = 10Mb
Shared_RAM_per_Child = 8Mb

500 - 8
MaxClients = --------- = 246
10 - 8

392% more servers! Now you can feel the importance of having as much shared memory as possible.

392% も多いサーバです ! これであなたはできるだけ多くの共有メモリを持つことの重要性を感じることができます.


MaxRequestsPerChild の選択 : Choosing MaxRequestsPerChild



The MaxRequestsPerChild directive sets the limit on the number of requests that an individual child server process will handle. After MaxRequestsPerChild requests, the child process will die. If MaxRequestsPerChild is 0, then the process will live forever.

MaxRequestsPerChild ディレクティブは個別の child サーバプロセスが処理するリクエストの数の制限をセットします. MaxRequestsPerChild リクエストのあとで, その child プロセスは die します. もし MaxRequestsPerChild が 0 なら, そのプロセスは永遠に生存します.
Setting MaxRequestsPerChild to a non-zero limit solves some memory leakage problems caused by sloppy programming practices, whereas a child process consumes more memory after each request.

MaxRequestsPerChild を非ゼロ制限に設定して杜撰なプログラミング行為によるメモリリーク問題が発生することを解決しますが, 一方で child プロセスはリクエストごとにより多くのメモリを消費します.
If left unbounded, then after a certain number of requests the children will use up all the available memory and leave the server to die from memory starvation. Note that sometimes standard system libraries leak memory too, especially on OSes with bad memory management (e.g. Solaris 2.5 on x86 arch).

もし際限のないままにしておくと, 特定の数のリクエストのあとで child たちは利用可能なすべてのメモリを使い切ってサーバをメモリ飢餓からの die にしてしまいます. なお標準のシステムライブライもときどきメモリを漏らします, とくにメモリ管理がよろしくない OS で (e.g. X86 アーキテクチャ上の Solaris 2.5).
If this is your case you can set MaxRequestsPerChild to a small number. This will allow the system to reclaim the memory that a greedy child process consumed, when it exits after MaxRequestsPerChild requests.

もしこれがあなたのケースならあなたは MaxRequestsPerChild を小さい数値にセットできます. これはそのシステムが貪欲な child プロセスが MaxRequestsPerChild リクエストのあとで終了するときに, それが消費したメモリを取り戻せるようにします.
But beware -- if you set this number too low, you will lose some of the speed bonus you get from mod_perl. Consider using Apache::PerlRun if this is the case.

しかし注意してください -- もしあなたがこの数値をとても低くセットすると, あなたは mod_perl からゲットするスピードボーナスの一部を失うでしょう. そのケースでは Apache::PerlRun の使用を検討してください.
Another approach is to use the Apache::SizeLimit or Apache::GTopLimit modules. By using either of these modules you should be able to discontinue using the MaxRequestPerChild, although for some developers, using both in combination does the job. In addition these modules allow you to kill httpd processes whose shared memory size drops below a specified limit or unshared memory size crosses a specified threshold.

他のアプローチは Apache::SizeLimit や Apache::GTopLimit モジュールを使うことです. これらのモジュールのいずれかを使うことであなたは MaxRequestsPerChild の使用をやめることができるはずです, けれども一部の開発者たちは, 両方を組み合わせて使って仕事をします. 加えてこれらのモジュールは共有メモリサイズが指定された制限を下回ったり非共有メモリサイズが指定されたスレッショルドを越える httpd プロセスの kill をあなたにできるようにします.
See also Preload Perl modules at server startup and Sharing Memory.

Preload Perl modules at server startupSharing Memory も参照してください.


MinSpareServers, MaxSpareServers と StartServers の選択 : Choosing MinSpareServers, MaxSpareServers and StartServers



With mod_perl enabled, it might take as much as 20 seconds from the time you start the server until it is ready to serve incoming requests. This delay depends on the OS, the number of preloaded modules and the process load of the machine. It's best to set StartServers and MinSpareServers to high numbers, so that if you get a high load just after the server has been restarted the fresh servers will be ready to serve requests immediately. With mod_perl, it's usually a good idea to raise all 3 variables higher than normal.

mod_perl を有効化すると, あなたがサーバをスタートしそれがインカミングリクエストをサーブする準備ができるまでに 20 秒ほど時間がかかるはずです. この遅れは OS, プレロードモジュールの数やそのマシンのプロセスの負荷によります. StartServers と MinSpareServers は高い数値にセットするのがベストです, そうすればもしリスタートした直後にあなたが高い負荷をえたとしてもフレッシュなサーバはすぐにリクエストをサーブする準備ができるようになります. mod_perl では, 3 つの変数をノーマルよりも高くするのが通常はグッドなアイデアです.
In order to maximize the benefits of mod_perl, you don't want to kill servers when they are idle, rather you want them to stay up and available to handle new requests immediately. I think an ideal configuration is to set MinSpareServers and MaxSpareServers to similar values, maybe even the same. Having the MaxSpareServers close to MaxClients will completely use all of your resources (if MaxClients has been chosen to take the full advantage of the resources), but it'll make sure that at any given moment your system will be capable of responding to requests with the maximum speed (assuming that number of concurrent requests is not higher than MaxClients).

mod_perl のベネフィットを最大にするためには, あなたはサーバのアイドルのときにそれらを kill するのではなく, むしろあなたはそれらを起きたままにしておいて新しいリクエストをすぐに処理できるようにしたほうがよいです. 理想的な構成は MinSpareServers と MaxSpareServers を似た値, おそらく同じにセットすることだと私は考えます. MaxSpareServers を MaxClients に近づけるとあなたのリソースのすべてを完全に使います (MaxClients がリソースのすべてのアドバンテージをとるように選択されている場合) が, それはいつでもあなたのシステムが最大限のスピードでリクエストにレスポンスできるようにします (同時リクエストの数が MaxClients よりは高くないと想定).
Let's try some numbers. For a heavily loaded web site and a dedicated machine I would think of (note 400Mb is just for example):

いくつかの数値をトライしてみましょう. 重い負荷の web サイトと専用のマシンだと私はこのように考えるかもしれません (400Mb は単なる例です):

Available to webserver RAM: 400Mb
Child's memory size bounded: 10Mb
MaxClients: 400/10 = 40 (larger with mem sharing)
StartServers: 20
MinSpareServers: 20
MaxSpareServers: 35

However if I want to use the server for many other tasks, but make it capable of handling a high load, I'd think of:

しかしながらもし私がそのサーバを多くの他のタスクに使いたいのだが, 高い負荷を処理できるようにしたい場合, 私はこのように考えるでしょう:

Available to webserver RAM: 400Mb
Child's memory size bounded: 10Mb
MaxClients: 400/10 = 40
StartServers: 5
MinSpareServers: 5
MaxSpareServers: 10

These numbers are taken off the top of my head, and shouldn't be used as a rule, but rather as examples to show you some possible scenarios. Use this information with caution!

これらの数値はぱっと思いついたもので, ルールとしてではなく, あなたに可能なシナリオを示す例として使われるべきです. この情報は注意して使ってください!


5 つのパラメータすべてを調整するためのベンチマークのサマリ : Summary of Benchmarking to tune all 5 parameters



OK, we've run various benchmarks -- let's summarize the conclusions:

OK, 私たちは様々なベンチマークを実行しました -- 結論を要約しましょう:

  • MaxRequestsPerChild
    If your scripts are clean and don't leak memory, set this variable to a number as large as possible (10000?). If you use Apache::SizeLimit or Apache::GTopLimit, you can set this parameter to 0 (treated as infinity).

    あなたのスクリプトがクリーンでメモリリークがないなら, 可能な限り大きな数値にこの変数をセットします (10000?). あなたが Apache::SizeLimit や Apache::GTopLimit を使うなら, あなたはこのパラメータを 0 (無限として扱われる) にセットできます.

  • StartServers
    If you keep a small number of servers active most of the time, keep this number low. Keep it low especially if MaxSpareServers is also low, as if there is no load Apache will kill its children before they have been utilized at all. If your service is heavily loaded, make this number close to MaxClients, and keep MaxSpareServers equal to MaxClients.

    あなたがほとんどの時間でアクティブなサーバを少数にキープする場合は, この数値を低くキープします. MaxSpareServers も低い場合は, 特にこれを低くキープしてください, Apache はまるで負荷がないかのようにその child たちを利用する前にそれらを kill してしまいます. もしあなたのサービスが重い負荷であるなら, この数値を MaxClient に近づけて, MaxSpareServers を MaxClients と同じにキープします.

  • MinSpareServers
    If your server performs other work besides web serving, make this low so the memory of unused children will be freed when the load is light. If your server's load varies (you get loads in bursts) and you want fast response for all clients at any time, you will want to make it high, so that new children will be respawned in advance and are waiting to handle bursts of requests.

    あなたのサーバが web サービスの他にも別の作業を実行する場合は, これを低くすることで未使用の child たちのメモリが負荷が軽い時に解放されるようにします. もしあなたのサーバの負荷が変動 (あなたはバーストで負荷をえる) してあなたがいつでもすべてのクライアントに高速にレスポンスしたいなら, あなたはこれを高くして, その新しい child たちが予め再生成されリクエストのバーストを処理するために待機するようにするとよいでしょう.

  • MaxSpareServers
    The logic is the same as for MinSpareServers - low if you need the machine for other tasks, high if it's a dedicated web host and you want a minimal delay between the request and the response.

    このロジックは MinSpareServers と同じです - あなたが別のタスクのためにマシンを要するなら低く, それが web ホスト専用でありあなたがリクエストとレスポンスの間を最小の遅延にしたいなら高くします.

  • MaxClients
    Not too low, so you don't get into a situation where clients are waiting for the server to start serving them (they might wait, but not for very long). However, do not set it too high. With a high MaxClients, if you get a high load the server will try to serve all requests immediately. Your CPU will have a hard time keeping up, and if the child size * number of running children is larger than the total available RAM your server will start swapping. This will slow down everything, which in turn will make things even slower, until eventually your machine will die. It's important that you take pains to ensure that swapping does not normally happen. Swap space is an emergency pool, not a resource to be used routinely. If you are low on memory and you badly need it, buy it. Memory is cheap.

    低すぎなければ, そのサーバがクライアントにサービス開始をすることをそれらが待機するようなシチュエーションにあなたがおちいることはありません (それらは待つかもしれないが, とても長いわけではありません). しかしながら, 高くしすぎないでください. 高い MaxClients で, もしあなたが高い負荷をゲットするとそのサーバはすべてのリクエストに即時サーブすることにトライます. あなたの CPU はついていくことに苦労し, もしその child のサイズ * 実行中の child たちの数が利用可能な RAM の合計よりも大きくなるとあなたのサーバはスワッピングを始めます. これは何かもを遅くします, それは最後にあなたのマシンが die するまで, よりいっそう遅いものにしつづけます. あなたは普通はスワッピングが発生しないように苦心することが重要です. スワップスペースは緊急のプールであり, 日常的に使われるリソースではありません. もしあなたのメモリが不足していてそれをひどく必要とするなら, 購入してください. メモリは安価です.
    But based on the test I conducted above, even if you have plenty of memory like I have (1Gb), increasing MaxClients sometimes will give you no improvement in performance. The more clients are running, the more CPU time will be required, the less CPU time slices each process will receive. The response latency (the time to respond to a request) will grow, so you won't see the expected improvement. The best approach is to find the minimum requirement for your kind of service and the maximum capability of your machine. Then start at the minimum and test like I did, successively raising this parameter until you find the region on the curve of the graph of latency and/or throughput against MaxClients where the improvement starts to diminish. Stop there and use it. When you make the measurements on a production server you will have the ability to tune them more precisely, since you will see the real numbers.

    しかし私が上で実施したテストに基づくと, 私がもつように十分なメモリ (1Gb) をあなたがもったとしても, 増やした MaxClients はパフォーマンスの改善をあなたに与えない場合があります. より多くのクライアントが走っていれば, より多くの CPU タイムが必要とされ, 各プロセスはより少ない CPU タイムスライスを受けとるでしょう. そのレスポンスレイテンシ (リクエストにレスポンスされる時間) は増加するので, 期待された改善をあなたは見ることができません. ベストなアプローチはあなたのサービスの種類の最小要件とあなたのマシンの最大能力をみつけることです. 最小値からスタートして私がやったようにテストして, レイテンシ and/or スループットのグラフのカーブ上で MaxClients に対する改善が減少し始める領域をみつけるまでこのパラメータを上げ続けます. そこでストップしてそれを使います. あなたがプロダクションサーバ上で測定すればあなたはより的確に調整する能力をもつことができます, あなたはリアルな数値を見ることができるからです.
    Don't forget that if you add more scripts, or even just modify the existing ones, the processes will grow in size as you compile in more code. Probably the parameters will need to be recalculated.

    あなたがスクリプトを追加する, あるいは既存のそれを修正した場合, そのプロセスは増えたコードをあなたがコンパイルするほどに増加することを忘れないでください. おそらくそのパラメータは再計算を必要とするでしょう.




KeepAlive



If your mod_perl server's httpd.conf includes the following directives:

もしあなたの mod_perl サーバの httpd.conf が次のディレクティブを含んでいる場合:

KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15

you have a real performance penalty, since after completing the processing for each request, the process will wait for KeepAliveTimeout seconds before closing the connection and will therefore not be serving other requests during this time. With this configuration you will need many more concurrent processes on a server with high traffic.

あなたはリアルなパフォーマンスのペナルティをもちます, 各リクエストの処理が完了した後で, そのプロセスはそのコネクションをクローズする前に KeepAliveTimeout 秒待機することになりしたがってこの時間の間は別のリクエストをサーブできないからです. この構成では高いトラフィックでのサーバ上であなたはより多くの同時接続が必要になります.
If you use some server status reporting tools, you will see the process in K status when it's in KeepAlive status.

もしあなたが一部のサーバステータスレポートツールを使っている場合, あなたはプロセスが KeepAlive ステータスにあるときにそれを K ステータスでみるでしょう.
The chances are that you don't want this feature enabled. Set it Off with:

おそらくあなたはこの機能を有効にしたくないでしょう. これでそれを Off にセットします:

KeepAlive Off

the other two directives don't matter if KeepAlive is Off.

KeepAlive を Off にした場合は他の 2 つのディレクティブは重要ではありません.
You might want to consider enabling this option if the client's browser needs to request more than one object from your server for a single HTML page. If this is the situation the by setting KeepAlive On then for each page you save the HTTP connection overhead for all requests but the first one.

クライアントのブラウザがあなたのサーバのシングル HTML ページから 1 つ以上のオブジェクトをリクエストする必要がある場合あなたはこのオプションを有効にすることを検討したほうがよいかもしれません. もしこれが KeepAlive On によるシチュエーションであるなら各ページであなたは最初のもの以外のすべてのリクエストのための HTTP 接続のオーバヘッドを節約できます.
For example if you have a page with 10 ad banners, which is not uncommon today, you server will work more effectively if a single process serves them all during a single connection. However, your client will see a slightly slower response, since banners will be brought one at a time and not concurrently as is the case if each IMG tag opens a separate connection.

例えばもしあなたが 10 の広告バナーのページをもっている場合, 今日ではそれは珍しいことではない, シングルのコネクションの間でシングルのプロセスがそれらすべてをサーブすればあなたのサーバはより効果的に機能します. しかしながら, あなたのクライアントは少し遅いレスポンスを見ることになります, バナーはひとつずつもってこられて各 IMG タグが別々のコネクションをオープンするケースのように同時ではないからです.
Since keepalive connections will not incur the additional three-way TCP handshake they are kinder to the network.

keepalive コネクションは追加の 3 ウェイ TCP ハンドシェイクを招くことはないのでネットワークに優しいです.
SSL connections benefit the most from KeepAlive in case you didn't configure the server to cache session ids.

SSL コネクションはあなたがサーバがセッション id をキャッシュするように構成しなかった場合に KeepAlive から最も恩恵をうけます.
You have probably followed the advice to send all the requests for static objects to a plain Apache server. Since most pages include more than one unique static image, you should keep the default KeepAlive setting of the non-mod_perl server, i.e. keep it On. It will probably be a good idea also to reduce the timeout a little.

あなたはプレーンな Apache server に静的なオブジェクトへのすべてのリクエストを送信するためにおそらくこのアドバイスに従うはずです. ほとんどのページは複数のユニークな静的イメージを含んでいるため, あなたは非 mod_perl サーバのデフォルトの KeepAlive セッティングを維持したほうがよいです, i.e. On をキープ. タイムアウトをやや少なくすることもおそらくグッドなアイデアです.
One option would be for the proxy/accelerator to keep the connection open to the client but make individual connections to the server, read the response, buffer it for sending to the client and close the server connection. Obviously you would make new connections to the server as required by the client's requests.

プロキシ/アクセラレータのためのひとつのオプションはクライアントへのコネクションをオープンでキープするがサーバへは個別にコネクションにするようにして, レスポンスを読み込み, クライアントに送信するためにそれをバッファしてサーバコネクションをクローズすることです. あなたは明確にクライアントのリクエストに応じてサーバへの新しいコネクションを作成するのです.


PerlSetupEnv Off



PerlSetupEnv Off is another optimization you might consider. This directive requires mod_perl 1.25 or later.

PerlSetupEnv Off はあなたが考慮するかもしれない別の最適化です. このディレクティブは mod_perl 1.25 以上を必要とします.
When this option is enabled, mod_perl fiddles with the environment to make it appear as if the code is called under the mod_cgi handler. For example, the $ENV{QUERY_STRING} environment variable is initialized with the contents of Apache::args(), and the value returned by Apache::server_hostname() is put into $ENV{SERVER_NAME}.

このオプションが有効化されると, mod_perl はコードが mod_cgi ハンドラのもとで呼ばれたように見せかけるために環境をいじります. 例えば, $ENV{QUERY_STRING} 環境変数が Apache::args() のコンテンツで初期化され, Apache::server_hostname() によってリターンされる値は $ENV{SERVER_NAME} におかれます.
But %ENV population is expensive. Those who have moved to the Perl Apache API no longer need this extra %ENV population, and can gain by turning it Off. Scripts using the CGI.pm module require PerlSetupEnv On because that module relies on a properly populated CGI environment table.

しかし %ENV 実装は効果です. Perl Apache API に移行した人たちはもうこの余分な %ENV 実装を必要とせず, それを Off にすることでゲインできます. CGI.pm モジュールを使うスクリプトはモジュールが適切に実装された CGI 環境テーブルを頼りにしているため PerlSetupEnv On を必要とします.
By default it is turned On.

デフォルトでは On にされています.
Note that you can still set enviroment variables when PerlSetupEnv is turned Off. For example when you use the following configuration:

なおあなたは PerlSetupEnv が Off であっても環境変数をセットできます. 例えばあなたが次の構成を使う場合で:

PerlSetupEnv Off
PerlModule Apache::RegistryNG
<Location /perl>
PerlSetEnv TEST hi
SetHandler perl-script
PerlHandler Apache::RegistryNG
Option +ExeCGI
</Location>

and you issue a request for this script:

それからあなたはこのスクリプトのためにリクエストを発行し:

setupenvoff.pl
--------------
use Data::Dumper;
my $r = Apache->require();
$r->send_http_header('text/plain');
print Dumper(\%ENV);

you should see something like this:

あなたはこのようなものを見るはずです:

$VAR1 = {
'GATEWAY_INTERFACE' => 'CGI-Perl/1.1',
'MOD_PERL' => 'mod_perl/1.25',
'PATH' => '/usr/lib/perl5/5.00503:... snipped ...',
'TEST' => 'hi'
};

Note that we got the value of the TEST environment variable we set in httpd.conf.

私たちは私たちが httpd.conf でセットした TEST 環境変数の値をゲットしたことに注目してください.


Apache による stat() コール数を削減する : Reducing the Number of stat() Calls Made by Apache



If you watch the system calls that your server makes (using truss or strace while processing a request, you will notice that a few stat() calls are made. For example when I fetch http://localhost/perl-status and I have my DocRoot set to /home/httpd/docs I see:

もしあなたがあなたのサーバが行うシステムコールを見ると (リクエストの処理中に truss や strace を使って), あなたはいくつかの stat() コールが行われていることに気づくでしょう. 例えば私が http://localhost/perl-status をフェッチして私が私のドキュメントルートを /home/httpd/docs/ にセットしていたら私はこれを見ます:

[snip]
stat("/home/httpd/docs/perl-status", 0fbffff8cc) = -1
ENOENT (No such file or directory)
stat("/home/htpd/docs", {st_mode=S_IFDIR|0755,
st_size=1024, ...}) = 0
[snip]

If you have some dynamic content and your virtual relative URI is something like /news/perl/mod_perl/summary (i.e., there is no such directory on the web server, the path components are only used for requesting a specific report), this will generate five(!) stat() calls, before the DocumentRoot is found. You will see something like this:

もしあなたがいくつかの動的コンテンツをもっていてあなたの仮想相対 URI が /news/perl/mod_perl/summary のようなものである場合 (i.e., web サーバ上にそのようなディレクトリはなくて, そのパスコンポーネントは特定のレポートをリクエストするためにのみ使われる), これはその DocumentRoot が見つかる前に, 5 つ (!) の stat() コールを生成します. あなたはこのようなものを見るでしょう:

stat("/home/httpd/docs/news/perl/mod_perl/summary", 0xbffff744) = -1
ENOENT (No such file or directory)
stat("/home/httpd/docs/news/perl/mod_perl", 0xbffff744) = -1
ENOENT (No such file or directory)
stat("/home/httpd/docs/news/perl", 0xbffff744) = -1
ENOENT (No such file or directory)
stat("/home/httpd/docs/news", 0xbffff744) = -1
ENOENT (No such file or directory)
stat("/home/httpd/docs",
{st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0

How expensive those calls are? Let's use the Time::HiRes module to find out.

これらのコールはどれくらい高価でしょうか ? 確認するために Time::HiRes モジュールを使いましょう.

stat_call_sample.pl
-------------------
use Time::HiRes qw(gettimeofday tv_interval);
my $calls = 1_000_000;

my $start_time = [ gettimeofday ];

stat "/foo" for 1..$calls;

my $end_time = [gettimeofday ];

my $elapsed = tv_interval($start_time,$end_time) / $calls;

print "The average execution time: $elapsed seconds\n";

This script takes a time sample at the beginnig, then does 1_000_000 stat() calls to a non-existing file, samples the time at the end and prints the average time it took to make a single stat() call. I'm sampling a 1M stats, so I'd get a correct average result.

このスクリプトはその最初でタイムサンプルを取り, 非存在ファイルに 1_000_000 stat() コールを行って, その最後で時間のサンプルしてひとつの stat() コールをするのにかかった平均タイムを出力します.
Before we actually run the script one should distinguish between two different situation. When the server is idle the time between the first and the last system call will be much shorter than the same time measured on the loaded system. That is because on the idle system, a process can use CPU very often, and on the loaded system lots of processes compete over it and each process has to wait for a longer time to get the same amount of CPU time.

私たちが実際にこのスクリプトを実行する前に 2 つの異なるシチュエーションを区別しなければなりません. サーバがアイドルのとき最初と最後のシステムコールの間の時間は負荷があるシステムで測定した同じ時間よりもはるかに短くなります. なぜならアイドルのシステム上では, プロセスはとても頻繁 CPU を使うことができ, 負荷があるシステム上では多くのプロセスがそこで競合するからであり各プロセスが同じ量の CPU 時間をゲットするために長い時間待たねばならないからです.
So first we run the above code on the unloaded system:

ですから最初に私たちは負荷のないシステムで上のコードを実行します:

% perl stat_call_sample.pl
The average execution time: 4.209645e-06 seconds

So it takes about 4 microseconds to execute a stat() call. Now let start a CPU intensive process in one console. The following code keeps CPU busy all the time.

するとそれは stat() コールの実行に約 4 マイクロ秒とります. では 1 つのコンソールで CPU に集中するプロセスをスタートしてみましょう.

% perl -e '1**1 while 1'

And now run the stat_call_sample.pl script in the other console.

次に他のコンソールで stat_call_sample.pl スクリプトを実行します.

% perl stat_call_sample.pl
The average execution time: 8.777301e-06 seconds

You can see that the average time has doubled (about 8 microseconds). And this is obvious, since there were two processes competing over CPU. Now if run 4 occurrences of the above code:

あなたは平均タイムが 2 倍になっていることを見ます (約 8 マイクロ秒). そしてこれは明らかに, CPU 上で競合する 2 つのプロセスがあるからです. ここで上記コードの出現を 4 つにして実行すると:

% perl -e '1**1 while 1' &
% perl -e '1**1 while 1' &
% perl -e '1**1 while 1' &
% perl -e '1**1 while 1' &

And when running our script in parallel with these processes, we get:

そしてこれらのプロセスと並行に私たちを実行すると, 私たちは:

% perl stat_call_sample.pl
2.0853558e-05 seconds

about 20 microseconds. So the average stat() system call is 5 times longer now. Now if you have 50 mod_perl processes that keep the CPU busy all the time, the stat() call will be 50 times slower and it'll take 0.2 milliseconds to complete a series of call. If you have five redundant calls as in the strace example above, they adds up to one millisecond. If you have more processes constantly consuming CPU, this time adds up. Now multiply this time by the number of processes that you have and you get a few seconds lost. As usual, for some services this loss is insignificant, while for others a very significant one.

約 20 マイクロ秒をゲットします. ですから stat() コールのアベレージは現在 5 倍長くなります. これでもしあなたが常に CPU をビジーにし続ける 50 の mod_perl プロセスをもっている場合, その stat() コールは 50 倍遅くなり一連のコールを完了するために 0.2 マイクロ秒かかるでしょう. もしあなたが上記 strace の例のように余分に 5 つのコールをもっていたら, それらは 1 秒にのぼります. もしあなたが CPU をコンスタントに消費するより多くのプロセスをもつ場合, この時間はより増えます. ここでこの時間をあなたがもつプロセスの数で乗算するとあなたは数秒失います. いつものように, 一部のサービスでこのロスは重要ではありません, 一方他ではとても重要なものです.
So why Apache does all these redundant stat() calls? You can blame the default installed TransHandler for this inefficiency. Of course you could supply your own, which will be smart enough not to look for this virtual path and immediately return OK. But in cases where you have a virtual host that serves only dynamically generated documents, you can override the default PerlTransHandler with this one:

ではなぜ Apache はこれらすべての余分な stat() コールをするのでしょうか ? あなたはこの非効率性についてデフォルトでインストールされる TransHandler を咎めることができます. もちろんあなたはこの仮想パスを探さずにすぐに OK をリターンする十分にスマートなものを, あなた独自に提供することもできます. しかしあなたが動的に生成されたドキュメントのみをサーブするバーチャルホストをもっているケースでは, あなたはデフォルトの PerlTransHandler をこれでオーバライドできます.

PerlModule Apache::Constants
<VirtualHost 10.10.10.10:80>
...
PerlTransHandler Apache::Constants::OK
...
</VirtualHost>

As you see it affects only this specific virtual host.

あなたが見てのとおりこれはこの特定のバーチャルホストにのみ影響します.
This has the effect of short circuiting the normal TransHandler processing of trying to find a filesystem component that matches the given URI -- no more 'stat's!

これは与えられた URI とマッチするファイルシステムコンポーネントを見つようとする普通の TransHandler 処理を短絡するの効果をもちます -- もう 'stat' はいらない !
Watching your server under strace/truss can often reveal more performance hits than trying to optimize the code itself!

strace/truss のもとであなたのサーバをウォッチするとそのコード自体の最適化を試みるよりも多くのパフォーマンスヒット (# 余分な負荷) を明らかにできることがよくあります.
For example unless configured correctly, Apache might look for the .htaccess file in many places, if you don't have one and add many open() calls.

例えば正しく構成されていない場合, Apache は多くの場所で .htaccess ファイルを探し, もしあなたがそれをもっていないなら多くの open() コールを追加するかもしれません.
Let's start with this simple configuration, and will try to reduce the number of irrelevant system calls.

このシンプルな構成でスタートして, 不適切なシステムコールの数の削減にトライしてみましょう.

DocumentRoot "/home/httpd/docs"
<Location /foo/test>
SetHandler perl-script
PerlHandler Apache::Foo
</Location>

The above configuration allows us to make a request to /foo/test and the Perl handler() defined in Apache::Foo will be executed. Notice that in the test setup there is no file to be executed (like in Apache::Registry). There is no .htaccess file as well.

上記構成は私たちに /foo/test へのリクエストの作成と Apache::Foo で定義された Perl handler() の実行をできるようにします. このテストのセットアップでは実行されるファイルがないことに注意してください (Apache::Registry のように). .htaccess ファイルもありません.
This is a typical generated trace.

これは典型的な生成されたトレースです.

stat("/home/httpd/docs/foo/test", 0xbffff8fc) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs/foo", 0xbffff8fc) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs",
{st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0
open("/.htaccess", O_RDONLY) = -1 ENOENT
(No such file or directory)
open("/home/.htaccess", O_RDONLY) = -1 ENOENT
(No such file or directory)
open("/home/httpd/.htaccess", O_RDONLY) = -1 ENOENT
(No such file or directory)
open("/home/httpd/docs/.htaccess", O_RDONLY) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs/test", 0xbffff774) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs",
{st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0

Now we modify the <Directory> entry and add AllowOverride None, which among other things disables .htaccess files and will not try to open them.

ここで私たちは <Directory> エントリを修正し AllowOverride None を追加します, それはとりわけ .htaccess ファイルを無効にしてそれらのオープンをトライしません.

<Directory />
AllowOverride None
</Directory>

We see that the four open() calls for .htaccess have gone.

私たちは .htaccess のための 4 つの open() コールがいなくなったことを見ます.

stat("/home/httpd/docs/foo/test", 0xbffff8fc) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs/foo", 0xbffff8fc) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs",
{st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0
stat("/home/httpd/docs/test", 0xbffff774) = -1 ENOENT
(No such file or directory)
stat("/home/httpd/docs",
{st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0

Let's try to shortcut the foo location with:

では foo のロケーションをこれでショートカットしてみましょう:

Alias /foo /

Which makes Apache to look for the file in the / directory and not under /home/httpd/docs/foo. Let's run it:

これは Apache が / ディレクトリでファイルを探すようにして /home/httpd/docs/foo 配下ではなくします. これを実行してみましょう:

stat("//test", 0xbffff8fc) = -1 ENOENT (No such file or directory)

Wow, we've got only one stat call left!

ワォ, 私たちは残りの stat コールひとつだけをゲットしました!
Let's remove the last Alias setting and use:

最後の Alias 設定を削除して上で説明したこれ:

PerlModule Apahce::Constants
PerlTransHandler Apache::Constants::OK

as explained above. When we issue the request, we see no stat() calls. But this is possible only if you serve only dynamically generated documents, i.e. no CGI scripts. Otherwise you will have to write your own PerlTransHandler to handle requests as desired.

を使ってみましょう. 私たちがリクエストを発行しても, 私たちは stat() コールを見ることはありません. しかしこれはあなたが動的に生成されたドキュメントのみをサーブする場合にのみに可能です, i.e. CGI スクリプトではない場合です. そうでなければあなたは要望通りにリクエストを処理するあなた独自の PerlTransHandler を書かねばなりません.
For example this PerlTransHandler will not lookup the file on the filesystem if the URI starts with /foo, but will use the default PerlTransHandler otherwise:

例えばこの PerlTransHandler はも URI が /foo でスタートする場合にファイルシステム上でファイルを探しませんが, そうでなければデフォルトの PerlTransHandler を使います:

PerlTransHandler 'sub { return shift->uir() =~ m|^/foo| \
? Apache::Constants::OK \
: Apache::Constants::DECLINED; }'

Let's see the same configuration using the <Perl> section and a dedicated package:

<Perl> セクションと専用のパッケージを使って同じ構成を見てみましょう:

<Perl>
package My::Trans;
use Apahce::Constants qw(:common);
sub handler {
my $r = shift;
return OK if $r->uri() =~ m|^/foo|;
return DECLINED;
}

package Apache::ReadConfig;
$PerlTransHandler = "My::Trans";
</Perl>

As you see we have defined the My::Trans package and implemented the handler() function. Then we have assigned this handler to the PerlTransHandler.

あなたが見てのとおり私たちは定義された My::Trans パッケージと handler() ファンクションの実装をもちました. そして私たちはこのハンドラを PerlTransHandler にアサインしました.
Of course you can move the code in the module into an external file, (e.g. My/Trans.pm) and configure the PerlTransHandler with

もちろんあなたはこのモジュールのコードを外部ファイルに移動して, (e.g. My/Trans.pm) この通常の方法で

PerlTransHandler My::Trans

in the normal way (no <Perl>; section required).

PerlTransHandler を構成できます (<Perl> セクションを必要としません).
There is an even simpler way to save that last stat() call. Instead of using PerlTransHandler combined with:

最後の stat コールを節約するさらにシンプルな方法があります. PerlTransHandler をこれと組み合わせて使う代わりに:

Alias /foo /

私たちはこれを使えます:

AliasMatch ^/foo /

which in the current implementation (at least in apache-1.3.28) doesn't incur the stat() call. Using the regex instead of prefix matching might slow things a bit, but is probably still faster than the stat() call.

これは現在の実装では (少なくとも apache-1.3.28 では) stat() コールを招くことはしません. プレフィックスマッチングの代わりに正規表現を使うと遅くなるかもしれませんが, おそらく stat() コールよりは速いです.


TMTOWTDI: 利便性と習慣 vs. パフォーマンス : TMTOWTDI: Convenience and Habit vs. Performance



TMTOWTDI (sometimes pronounced "tim toady"), or "There's More Than One Way To Do It" is the main motto of Perl. In other words, you can gain the same goal by coding in many different styles, using different modules and deploying the same modules in different ways.

TMTOWTDI (# There's More Than One Way To Do It) ("ティム トディ" と発音されることがある), あるいは "それを行う方法はひとつではない" は Perl の主なモットーです. 言い換えれば, あなたは多くの異なるスタイルでのコーディング, 異なるモジュールの利用や同じモジュールの異なるやりかたでの展開で同じゴールを得ることができます.
Unfortunately when you come to the point where performance is the goal, you might have to learn what's more efficient and what's not. Of course it might mean that you will have to use something that you don't really like, it might be less convenient or it might be just a matter of habit that one should change.

残念ながらあなたがパフォーマンスがゴールである場所にくると, あなたは何がより効果的で何がそうでないかを学ばなければならないでしょう. もちろんそれはあなたが本当には好きではないものをあなたが何かを使わなければならないことを意味し, それはそれほど便利ではなくなったり習慣を変更しなければならないかもしれません.
So this section is about performance trade-offs. For almost each comparison we will provide the theoretical difference and then run benchmarks to support the theory, since however good the theory its the numbers we get in practice that matter.

ですからこのセクションはパフォーマンスのトレードオフについてです. ほとんどすべての比較で私たちは理論的な違いを提供しそのセオリをサポートするためにベンチマークを実行します, どれほどグッドなセオリであっても実践で私たちがゲットする数値が重要だからです.
"Premature optimizations are evil", the saying goes. I believe that knowing how to write an efficient code in first place, where it doesn't make the quality and clarity suffer saves time in the long run. That's what this section is mostly about.

"時期尚早の最適化は悪", といわれます. クオリティと明快さを損なうことのない効率的なコードの書き方を最初の時点で知っていれば, 長期的には時間を節約できると私は信じています. それがこのセクションの目的のほとんどです.
In the following benchmarks, unless told different the following Apache configuration has been used:

次のベンチマークでは, 違うと言われない限りは次の Apache 構成が使われます:

MinSpareServers 10
MaxSpareServers 20
StartServers 10
MaxClients 20
MaxRequestsPerChild 10000



Apache::Registry PerlHandler vs. Custom PerlHandler



At some point you have to decide whether to use Apache::Registry and similar handlers and stick to writing scripts for the content generation or to write pure Perl handlers.

ある時点であなたは Apache::Registry や同様のハンドラを使ってコンテンツ生成のためのスクリプトを書くことにするのかピュアな Perl ハンドラを書くことにするのかを決めなければなりません.
Apache::Registry maps a request to a file and generates a subroutine to run the code contained in that file. If you use a PerlHandler My::Handler instead of Apache::Registry, you have a direct mapping from request to subroutine, without the steps in between. These steps include:

Apache::Registry はファイルへのリクエストをマップしてそのファイルに含まれるコードを実行するためのサブルーチンを生成します. もしあなたが PerlHandler My::Handler を Apache::Registry の代わりに使う場合, あなたは間のステップを除いた, リクエストからサブルーチンへの直接のマッピングをもちます. そのステップはこれを含みます:

  1. run the stat() on the script's filename ($r->filename)

    スクリプトのファイル名 ($r->filename) 上で stat() を実行する

  2. check that the file exists and is executable

    そのファイルが存在するかチェックして実行する

  3. generate a Perl package name based on the request's URI ($r->uri)

    リクエストの URI ($r->uri) に基づいた Perl パッケージ名を生成する

  4. go to the directory the script resides in (chdir basename $r->filename)

    そのスクリプトが存在するディレクトリに行く (chdir basename $r->filename)

  5. compare the file's and stored in memory compiled subroutine's last modified time (if it was compiled already)

    ファイルのものとメモリに格納されたコンパイル済みのサブルーチンの最終変更時間を比較する (もしそれがすでにコンパイル済みなら)

  6. if modified or not compiled, compile the subroutine

    もし変更されていたりコンパイルされていなければ, そのサブルーチンをコンパイルする

  7. go back to the previous directory (chdir $old_cwd)

    以前のディレクトリに戻る (chdir $old_cwd)


If you cut out those steps, you cut out some overhead, plain and simple. Do you need to cut out that overhead? May be yes, may be not. Your requirements determine that.

もしあなたがこれらのステップをカットするなら, あなたは一部のオーバーヘッドをカットします, プレーンでシンプルです. あなたはそのオーバーヘッドのカットが必要ですか ? イエスかもしれないし, ノーかもしれません. あなたの要求がそれを決めます.
You should take a look at the sister Apache::Registry modules (e.g. Apache::RegistryNG and Apache::RegistryBB) that don't perform all these steps, so you can still choose to stick to using scripts to generate the content. The greatest added value of scripts is that you don't have to modify the configuration file to add the handler configuration and restarting the server for each newly written content handler.

あなたが Apache::Registry モジュールの姉妹 (e.g. Apache::RegistryNG や Apache::RegistryBB) を見ればそれらはこれらのステップを実行しないので, あなたはコンテンツ生成のためにスクリプトを使うことにこだわることを選択できます. スクリプトの最もグレートな付加価値はハンドラ構成を追加するために構成ファイルを変更することや新しく書かれた各コンテンツハンドラのためにサーバをリスタートする必要がないことです.
Now let's run benchmarks and compare.

ではベンチマークを実行して比較してみましょう.
We want to see the overhead that Apache::Registry adds compared to the custom handler and whether it becomes insignificant when used for the heavy and time consuming code. In order to do that we will run two benchmarks sets: the first so called a light set will use an almost empty script, that only sends a basic header and one word as content; the second will be a heavy set which will add some time consuming operation to the script's and the handler's code.

私たちはカスタムハンドラと比較された Apache::Registry が追加するオーバーヘッドとそれがヘビーで時間を消費するコードを使った時に些細なのかどうかを確認したいのです. それを行うために私たちは 2 つのベンチマークセットを実行します: 最初のいわゆる軽量セットはほとんどの空のスクリプトを使います, それは基本的なヘッダとコンテンツとしてひとつのワードのみ送信します; 次のものは重量セットでこれはそのスクリプトとハンドラのコードにいくつか時間を消費する操作を追加します.
For the light set we are going to use the registry.pl script running under Apache::Registry:

軽量セットで私たちは Apache::Registry のもとで走る registry.pl スクリプトを使います:

benchmarks/registry.pl
----------------------
use strict;
print "Content-type: text/plain\r\n\r\n";
print "Hello";

And the following content generation handler:

そして次のコンテンツ生成ハンドラ:

Benchmark/Handler.pm
--------------------
package Benchmark::Handler;
use Apache::Constant qw(:common);

sub handler{
$r = shift;
$r->send_http_header('text/html');
$r->print("Hello");
return OK;
}
1;

We will add this settings to httpd.conf.

私たちはこのセッティングを httpd.conf に追加します.

PerlModule Benchmark::Handler
<Location /benchmark_handler>
SetHandler perl-script
PerlHandler Benchmark::Handler
</Location>

The first directive worries to preload and compile the Benchmark::Handler module. The rest of the lines tell Apache to execute the subroutine Benchmark::Handler::handler when a request with relative URI /benchmark_handler is made.

最初のディレクティブは Benchmark::Handler モジュールのプレロードとコンパイルを懸念するものです. 残りの行は相対 URI /benchmark_handler でのリクエストが作成されたときにサブルーチン Benchmark::Handler::handler を実行することを Apache に伝えます.
We will use the usual configuration for Apache::Registry scripts, where all the URIs starting with /perl are remapped to the files residing under /home/httpd/perl/ directory.

私たちは Apache::Registry スクリプトの通常構成を使います, /perl で始まるすべての URI は /home/httpd/perl/ 配下に存在するファイルに再マップされます.

Alias /perl/ /home/httpd/perl/
<Location /perl>
SetHandler perl-script
PerlHandler +Apache::Registry
Options ExecCGI
PerlSendHeader On
</Location>

We will use the Apache::RegistryLoader to preload and compile the script at the server startup as well, so the benchmark will be fair through the benchmark and only the processing time will be measured. To accomplish the preloading we add the following code to the startup.pl file:

私たちはサーバのスターアップでも Apache::RegistryLoader を使います, ですからこのベンチマークはベンチマークを通じてフェアになりその処理時間のみが測定されます. このプレロードを成すために私たちは次のコードを startup.pl ファイルに追加します:

use Apache::RegistryLoader ();
Apache::RegistryLoader->new->handler(
"/perl/benchmarks/registry.pl",
"/home/httpd/perl/benchmarks/registry.pl"):

To create the heavy benchmark set let's leave the above code examples unmodified but add some CPU intensive processing operation (it can be also an IO operation or a database query.)

重いベンチマークセットを作成するために上記コード例を変更せずに CPU インテンシブ (# CPU に集中的) な処理操作を追加してみましょう (IO オペレーションやデータベースクエリでもよし).

my $x = 100;
my $y = log ($x ** 100) for (0..10000);

This code does lots of mathematical processing and therefore very CPU intensive.

このコードはたくさんの数学的処理を行うのでとても CPU インテンシブです.
Now we are ready to proceed with the benchmark. We will generate 5000 requests with 15 as a concurrency level using the Apache::Benchmark module.

これで私たちはベンチマークを始める準備ができました. 私たちは Apache::Benchmark モジュールを使って同時レベル 15 の 5000 リクエストを生成します.
Here are the reported results:

こちらがレポートされた結果です:

------------------------------
name | avtime rps
------------------------------
light handler | 15 911
light registry | 21 680
------------------------------
heavy handler | 183 81
heavy registry | 191 77
------------------------------

Let's look at the results and answer the previously asked questions.

結果を確認して前に尋ねられたクエスチョンに答えてみましょう.
First let's compare the results from the light set. We can see that the average overhead added by Apache::Registry (compared to the custom handler) is about:

最初に軽量セットからの結果を比較してみましょう. 私たちが確認できる Apache::Registry によって追加された平均のオーバヘッド (カスタムハンドラと比較して) はおよそ:

21 - 15 = 6 milliseconds

per request.

1 リクエストあたりです.
Thus the difference in speed is about 40% (15 vs. 21). Note that this doesn't mean that the difference in the real world applications is such big. And the results of the heavy set confirm that.

従って速度の差は約 40% です (15 vs. 21). なおこれはリアルワールドでの差がこのように大きいことは意味しません. そして重量セットの結果はそうであることを確認します.
In the heavy set the average processing time is almost the same for the Apache::Registry and the custom handler. You can clearly see that the difference between the two is almost the same one that we have seen in the light set's results. It has grown from 6 milliseconds to 8 milliseconds (191-183). Which means that the identical heavy code that has been added was running for about 168 milliseconds (183-15). It doesn't mean that the added code itself has been running for 168 milliseconds. It means that it took 168 milliseconds for this code to be completed in a multi-process environment where each process gets a time slice to use the CPU. The more processes are running the more time the process will have to wait to get the next time slice when it can use the CPU.

重量セットで平均処理タイムは Apache::Registry とカスタムハンドラでほとんど同じです. あなたはその 2 つの間の違いが私たちが軽量セットの結果で見たものとほとんどの同じものであることをはっきりと確認できます. それは 6 ミリ秒から 8 ミリ秒に増加しました (191-183). これは追加された同一の重いコードが約 168 ミリ秒 (183-15) 走ったことを意味します. これはその追加されたコード自身が 168 ミリ秒走っていることを意味しません. これはこのコードが各プロセスが CPU を使うためにタイムスライスをゲットするマルチプロセス環境でコンパイルされるために 168 ミリ秒とったことを意味します. より多くのプロセスが走っているほどそのプロセスはそれが CPU を使用できる次のタイムスライスを得るためにより多くの時間待たねばなりません.
We have the second question answered as well. You can see that when the code is not just the hello script, the overhead of the extra operations done but the Apache::Registry module, is almost insignificant. It's a non zero though, so it depends on your requirements, and if another 5-10 millisecons overhead are quite tolerable, you may choose to use Apache::Registry.

私たちは 2 番目のクエスチョンの答えももっています. あなたはそのコードが単なる hello スクリプトではない場合に, 実行される追加操作のオーバーヘッドだけでなく Apache::Registry モジュールも, ほとんど重要ではないことを確認できます. けれどもこれは非ゼロなので, あなたの要件に依存しますし, 別の 5-10 ミリ秒のオーバーヘッドが許容できるなら, あなたは Apache::Registry の利用を選択することもできるでしょう.
The interesting thing is that when the server under test runs on a very slow machine the results are completely different. I'll present them here for comparison:

興味深いのはテスト下のサーバをとても遅いマシンで実行したときにその結果が完全に異なることです. 私は比較のためにそれらをここに提示します:

------------------------------
name | avtime rps
------------------------------
light handler | 50 196
light registry | 160 61
------------------------------
heavy handler | 149 67
heavy registry | 822 12
------------------------------

First of all the difference of 6 milliseconds in the average processing time we have seen on the fast machine when running the light set, now has grown to 110 milliseconds. Which means that a few extra operations, that Apache::Registry does, turn to be very expensive on the slow machine.

まず最初に軽量セットを実行しているときに高速なマシンで私たちが見た平均処理時間の 6 ミリ秒の差は, 今では 110 ミリ秒に増えました. ということは Apache::Registry が行う, いくつかの追加のオペレーションが, 遅いマシン上でとても高価になるということです.
Second, you can see that when the heavy set is used, there is no preservation of the 110 milliseconds as we have seen on the fast machine, which we obviously would expect to see, since the code that was added should take the same time to execute in the handler and the script. But instead we see a difference of 673 milliseconds (822-149).

次に, あなたは重量セットが使われたとき, 私たちが最初のマシンで見た 110 ミリ秒が維持されていないことがあなたは見ることができます, それはこの追加されたコードがハンドラとスクリプトで実行するために同じ時間がかかるはずであるため, 私たちが明らかに見ることを予期していたことです. しかし代わりに私たちは 673 ミリ秒 (822-149) の差を見ます.
The explanation lies in fact that the difference between the machines isn't merely in the CPU speed. It's possible that there are many other things that are different. For example the size of the processor cache. If one machine has a processor cache large enough to hold the whole handler and the other doesn't this can be very significant, given that in our heavy benchmark set, 99.9% of the CPU activity was dedicated to running the calculation code.

このマシン間の差は CPU スピードだけではないという事実の説明があります. 他にもたくさんのことがが違う可能性があります. 例えばプロセッサキャッシュのサイズです. もしひとつのマシンがハンドラ全体を保持するのに十分に大きなプロセッサキャッシュを持っていて他方がそうでない場合に, 私たちの重いベンチマークセットで, CPU アクティビティの 99.9% がその計算コードに注がれたとすれば, これはとても重要になります.
But this also shows you again, that none of the results and conclusion made here should be taken for granted. Certainly, most chances are that you will see a similar behavior on your machine, but only after you have run the benchmarks and analyzed the received results, you can be sure what is the best for you using the setup under test. If you later you happen to use a different machine, make sure to run the tests again, as they can lead to complete different decision as we have just seen when we have tried the same benchmark on a different machine.

しかしこれはまたここで作られた結果や結論が当然のことではないということを, あなたに再び示します. 確かに, あなたはあなたのマシン上で同様の動作を見ることが多いでしょうが, あなたがベンチマークを実行して受けとった結果を分析した後でのみ, あなたはテスト下のセットアップを使ってあなたのために何がベストであるかを確認できます. もしあなたが後で違うマシンを使うことになる場合は, 確実にそのテストを再度実行してください, それは私たちが異なるマシン上で同じベンチマークを試したときに私たちが見たように完全に異なる決定を導くかもしれません.


"Bloatware" modules



Perl modules like IO:: are very convenient, but let's see what it costs us to use them. (perl5.6.0 over OpenBSD)

IO:: のような Perl モジュールはとても便利です, しかしそれらを使うための私たちのコストを見てみましょう (OpenBSD 上の perl5.6.0).

% wc `perl -MIO -e 'print join("\n", sort values %INC, "")'`
124 696 4166 /usr/local/lib/perl5/5.6.0/Carp.pm
580 2465 17661 /usr/local/lib/perl5/5.6.0/Class/Struct.pm
400 1495 10455 /usr/local/lib/perl5/5.6.0/Cwd.pm
313 1589 10377 /usr/local/lib/perl5/5.6.0/Exporter.pm
225 784 5651 /usr/local/lib/perl5/5.6.0/Exporter/Heavy.pm
92 339 2813 /usr/local/lib/perl5/5.6.0/File/Spec.pm
442 1574 10276 /usr/local/lib/perl5/5.6.0/File/Spec/Unix.pm
115 398 2806 /usr/local/lib/perl5/5.6.0/File/stat.pm
406 1350 10265 /usr/local/lib/perl5/5.6.0/IO/Socket/INET.pm
143 429 3075 /usr/local/lib/perl5/5.6.0/IO/Socket/UNIX.pm
7168 24137 178650 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/Config.pm
230 1052 5995 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/Errno.pm
222 725 5216 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/Fcntl.pm
47 101 669 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO.pm
239 769 5005 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Dir.pm
169 549 3956 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/File.pm
594 2180 14772 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Handle.pm
252 755 5375 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Pipe.pm
77 235 1709 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Seekable.pm
428 1419 10219 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Socket.pm
452 1401 10554 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/Socket.pm
127 473 3554 /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/XSLoader.pm
52 161 1050 /usr/local/lib/perl5/5.6.0/SelectSaver.pm
139 541 3754 /usr/local/lib/perl5/5.6.0/Symbol.pm
161 609 4081 /usr/local/lib/perl5/5.6.0/Tie/Hash.pm
109 390 2479 /usr/local/lib/perl5/5.6.0/strict.pm
79 370 2589 /usr/local/lib/perl5/5.6.0/vars.pm
318 1124 11975 /usr/local/lib/perl5/5.6.0/warnings.pm
30 85 722 /usr/local/lib/perl5/5.6.0/warnings/register.pm
13733 48195 349869 total

Moreover, that requires 116 happy trips through the kernel's namei(). It syscalls open() a remarkable 57 times, 17 of which failed but leaving 38 that were successful. It also syscalled read() a curiously identical 57 times, ingesting a total of 180,265 plump bytes. To top it off, this increases your resident set size by two megabytes!

さらに, それはカーネルの namei() を通して 116 のハッピートリップを必要とします. それは 57 回も open() をシステムコールし, その内 17 は失敗しましたが 38 は成功しました. read() もまた奇妙にも同じ 57 回システムコールされ, ふくよかな合計 180,265 バイトを取り込みます. 挙句に, これはあなたの常駐セットサイズ (# Resident Set Size: RSS, メインメモリ内の占有部分) を 2 メガバイト増やします!
Happy mallocking...

ハッピー mallocking...
It seems that CGI.pm suffers from the same disease:

CGI.pm も同じ病を患っているようにみえます:

% wc `perl -MCGI -le 'print for values %INC'`
1368 6920 43710 /usr/local/lib/perl5/5.6.0/overload.pm
6481 26122 200840 /usr/local/lib/perl5/5.6.0/CGI.pm
7849 33042 244550 total

You have 16 trips through namei, 7 successful opens, 2 unsuccessful ones, and 213k of data read in.

あなたは namei を通じて 16 トリップ, 7 の成功した open, 2 の失敗, それから 213k のデータ読み込みをもちます.
This is a perlbloat.pl that shows how much memory is acquired by Perl when you run some. So we can easily test the overhead of loading some modules.

これは perlbloat.pl であなたがいくつかを実行したときに Perl によってどのくらいのメモリが確保されたかを示します. ですから私たちはいくつかのモジュールのローディングのオーバーヘッドを簡単にテスト出来ます.

#!/usr/bin/perl -w

use GTop ();

my $gtop = GTop->new;
my $beforte = $gtop->proc_mem($$)->size;

for (@ARGV) {
if (eval "require $_") {
eval {
$_->import;
};
}
else {
eval $_;
die $@ if $@;
}
}

my $after = $gtop->proc_mem($$)->size;
printf "@ARGV added %s\n", GTop::size_string($after - $before);

Now let's try to load IO, which loads IO::Handle, IO::Seekable, IO::File, IO::Pipe, IO::Socket and IO::Dir:

では IO のロードにトライしてみましょう, これは IO::Handle, IO::Seekable, IO::File, IO::Pipe, IO::Socket および IO::Dir をロードします:

$ ./perlbloat.pl 'use IO;'
use IO; added 1.5M

"Only" 1.5 MB overhead. Now let's load CGI (v2.74) and compile all its methods:

1.5 MB オーバーヘッド "だけ" です. では次に CGI (v2.74) をロードしてそのメソッドをすべてコンパイルしてみましょう:

% ./perlbloat.pl 'use CGI: CGI->compile(":all")'
use CGI; CGI->compile(":all") added 1.8M

Almost 2MB extra memory. Let's compare CGI.pm with its younger sister, whose internals are implemented in C.

ほぼ 2MB の追加メモリです. では CGI.pm をその妹と比較してみましょう, その内部は C で実装されています.

% ./perlbloat.pl 'use Apache::Request'
use Apache::Request added 48k

48KB. A significant difference isn't it?

48KB. 大幅な違いですね ?
The following numbers show memory sizes in KB (virtual and resident) for v5.6.0 of Perl on four different operating systems, The three calls each are without any modules, with just -MCGI, and with -MIO (never with both):

次の数値は 4 つの異なるオペレーションシステム上での Perl の v5.6.0 の (バーチャル (# vsz) および常駐 (# rss) メモリサイズを KB で示し, それぞれの 3 つのコールはモジュール無し, -MCGI のみ, それから -MIO 付き (両方ではない) です:

OpenBSD FreeBSD Redhat Linux Solaris
vsz rss vsz rss vsz rss vsz rss
Raw Perl 736 772 832 1208 2412 980 2928 2272
w/ CGI 1220 1464 1308 1828 2972 1768 3616 3232
w/ IO 2292 2580 2456 3016 4080 2868 5384 4976

Anybody who's thinking of choosing one of these might do well to digest these numbers first.

これらのいずれかをチョイスすることを考えている人は最初にこれらの数値を消化しておくと良いかもしれません.


Apache::args vs. Apache::Request::param vs. CGI::param



Apache::args, Apache::Request::param and CGI::param are the three most common ways to process input arguments in mod_perl handlers and scripts. Let's write three Apache::Registry scripts that use Apache::args, Apache::Request::param and CGI::param to process a form's input and print it out. Notice that Apache::args is considered identical to Apache::Request::param only when you have single valued keys. In the case of multi-valued keys (e.g. when using check-box groups) you will have to write some extra code: If you do a simple:

Apache::args, Apache::Request::param と CGI::param は mod_perl ハンドラとスクリプトでインプット引数を処理するためのもっとも一般的な 3 つの方法です. Apache::args, Apache::Request::param と CGI::param を使ってフォームのインプットを処理してそれを出力する 3 つの Apache::Registry スクリプトを書いてみましょう. なお Apache::args はあなたが単一と評価された key を持っているときにのみ Apache::Request::param と同一だとみなされます. 複数と評価された key のケースで (e.g. チェックボックスグループを使うとき) あなたはいくつか追加のコードを書かなければならないでしょう: もしあなたがシンプルにこれを行った場合:

my %params = $r->args;

only the last value will be stored and the rest will collapse, because that's what happens when you turn a list into a hash. Assuming that you have the following list:

最後の値のみが格納され残りはつぶれます, これは (# key の名前が同じ場合で) リストをハッシュに入れる時に発生することだからです. あなたが次のリストをもっていると仮定します:

(rules => 'Apache', rules => 'Perl', rules => 'mod_perl')

and assign it to a hash, the following happens:

そしてそれをハッシュに割当てると, 次 (# の処理) が発生します:

$hash{rules} = 'Apache';
$hash{rules} = 'Perl';
$hash{rules} = 'mod_perl';

So at the end only the:

ですから最後のこのペアだけが:

rules => 'mod_perl'

pair will get stored. With CGI.pm or Apache::Request you can solve this by extracting the whole list by its key:

格納されます. CGI.pm や Apache::Request ではあなたはリスト全体をその key で抽出することでこれを解決できます:

my @values = $q->params('rules');

In addition Apache::Request and CGI.pm have many more functions that ease input processing, like handling file uploads. However Apache::Request is much faster since its guts are implemented in C, glued to Perl using XS code.

加えて Apache::Request と CGI.pm はファイルアップロードの処理のような, インプット処理を楽にするより多くのファンクションを持っています. しかしながら Apache::Request はその内臓が C で実装され, XS コードを使って Perl に接着されているのでより高速です.
Assuming that the only functionality you need is the parsing of key-value pairs, and assuming that every key has a single value, we will compare the following almost identical scripts, by trying to pass various query strings.

あなたが必要な機能が key-value ペアの解析と仮定し, 各 key がひとつの value を持っていると仮定して, 私たちは様々なクエリ文字列を渡すことによって, 次のほとんど同一のスクリプトを比較します.
Here's the code:

こちらがそのコードです:

file:processing_with_apache_args.pl
-----------------------------------
use strict;
my $r = shift;
$r->send_http_header('text/plain');
my %args = $r->args;
print join "\n", map {$_ => ".$args{$_} } keys %args;

file:processing_with_apache_request.pl
--------------------------------------
use strict;
use Apache::Request ();
my $r = shift;
my $q = Apache::Request->new($r);
$r->send_http_header('text/plain');
my %args = map {$_ => $q->param($_) } $q->param;
print join "\n", map {"$_ => ".$args{$_} } keys %args;

file:processing_with_cgi_pm.pl
------------------------------
use strict;
use CGI;
my $r = shift;
$r->send_http_header('text/plain');
my $q = new CGI;
my %args = map {$_ => $q->param($_) } $q->param;
print join "\n", map {"$_ => ".$args{$_} } keys %args;

All three scripts are preloaded at server startup:

すべての 3 つのスクリプトはサーバのスタートアップでプレロードされます:

<Perl>
use Apache::RegistryLoader ();
Apache::RegistryLoader->new->handler(
"/perl/processing_with_cgi_pm.pl",
"/home/httpd/perl/processing_with_cgi_pm.pl"
);
Apache::RegistryLoader->new->handler(
"/perl/processing_with_apache_request.pl",
"/home/httpd/perl/processing_with_apache_request.pl"
);
Apahce::RegistryLoader->new->handler(
"/perl/processing_with_apache_args.pl",
"/hoem/httpd/perl/processing_with_apache_args.pl"
);
</Perl>

We use four different query strings, generated by:

私たちは 4 つの異なるクエリ文字列を使います, これによって生成された:

my @queries = (
join("&", map { $_=" . 'e' x 10} ('a'..'b')),
join("&", map { $_=" . 'e' x 50} ('a'..'b')),
join("&", map { $_=" . 'e' x 5 } ('a'..'z')),
join("&", map { $_=" . 'e' x 10} ('a'..'z')),
);

The first string is:

最初の文字列はこれです:

a=eeeeeeeeee&b=eeeeeeeeee

which is 25 characters in length and consists of two key/value pairs. The second string is also made of two key/value pairs, but the value is 50 characters long (total 105 characters). The third and the forth strings are made from 26 key/value pairs, with the value lengths of 5 and 10 characters respectively, with total lengths of 207 and 337 characters respectively. The query_len column in the report table is one of these four total lengths.

これは 25 文字の長さで 2 つの key/value ペアで構成されています. 2 番目の文字列もまた 2 つの key/value ペアになりますが, value は 50 文字の長さ (合計 105 文字) です. 3 番目と 4 番目の文字列は 26 key/value からなり, value の長さはそれぞれ 5 と 10 文字で, 合計の長さはそれぞれ 207 と 337 文字です. レポートテーブルの query_len カラムはこれら 4 つの合計の長さです.

---------------------------------------------
name val_len pairs query_len | avtime rps
---------------------------------------------
apreq 10 2 25 | 51 945
apreq 50 2 105 | 53 907
r_args 50 2 105 | 53 906
r_args 10 2 25 | 53 899
apreq 5 26 207 | 64 754
apreq 10 26 337 | 65 742
r_args 5 26 207 | 73 665
r_args 10 26 337 | 74 657
cgi_pm 50 2 105 | 85 573
cgi_pm 10 2 25 | 87 559
cgi_pm 5 26 207 | 188 263
cgi_pm 10 26 337 | 188 262
---------------------------------------------

Where apreq stands for Apache::Request::param(), r_args stands for Apache::args() or $r->args() and cgi_pm stands for CGI::param().

apreq は Apache::Request::param() を表します, r_args は Apache::args() または $r->args() を表し cgi_pm は CGI::param() を表します.
You can see that Apache::Request::param and Apache::args have similar performance with a few key/value pairs, but the former is faster with many key/value pairs. CGI::param is significantly slower than the other two methods.

Apache::Request::param と Apache::args はいくつかの key/value ペアで似たパフォーマンスを持ちますが, 多量の key/value ペアでは前者が高速であることをあなたは見ることができます. CGI::param は他 2 つのメソッドよりも著しく低速です.


mod_perl のもとでの $| の使用とベターな print() テクニック : Using $|=1 Under mod_perl and Better print() Techniques.



As you know, local $|=1; disables the buffering of the currently selected file handle (default is STDOUT). If you enable it, ap_rflush() is called after each print(), unbuffering Apache's IO.

あなたが知っているとおり, local $|=1; は現在選択されているファイルハンドル (デフォルトは STDOUT) のバッファリングを無効にします. もしあなたがそれを有効にすると, 各 print() の後で ap_rflush() がコールされ, Apache の IO をアンバッファリングします.
If you are using multiple print() calls (_bad_ style in generating output) or if you just have too many of them, then you will experience a degradation in performance. The severity depends on the number of print() calls that you make.

あなたが複数の print() コールを使っている (出力の生成では _bad_ なスタイル) あるいはとても多くのそれをもっている場合, あなたは低下したパフォーマンスを体験するでしょう. その酷さはあなたがコールする print() の数に依存します.
Many old CGI scripts were written like this:

多くの古い CGI スクリプトはこのように書かれていました:

print "<BODY BGCOLOR=\"black\" TEXT=\"white\">";
print "<H1>";
print "Hello";
print "</H1>;
print "<A HREF=\"foo.html\"> foo </A>";
print "</BODY>";

This example has multiple print() calls, which will cause performance degradation with $|=1. It also uses too many backslashes. This makes the code less readable, and it is also more difficult to format the HTML so that it is easily readable as the script's output. The code below solves the problems:

この例は複数の print() コールをもっていて, $|=1 によるパフォーマンスの低下が発生します. またとても多くのバックスラッシュも使っています. これはコードの可読性を低くしますし, スクリプトの出力を読みやすくするために HTML をフォーマットすることもより難しくします. 以下のコードはこの問題を解決します:

print qq{
<BODY BGCOLOR="back" TEXT="white">
<H1>
Hello
</H1>
<A HREF="foo.html"> foo </A>
</BODY>
};

I guess you see the difference. Be careful though, when printing a <HTML> tag. The correct way is:

あなたがこの違いをわかると私は思います. しかし <HTML> タグを出力するときは, 気をつけてください. 正しいやりかたはこれです:

print qq{<HTML>
<HEAD></HEAD>
<BODY>
}

If you try the following:

もしあなたがこれをトライすると:

print qq{
<HTML>
<HEAD></HEAD>
<BODY>
}

Some older browsers expect the first characters after the headers and empty line to be <HTML> with no spaces before the opening left angle-bracket. If there are any other characters, they might not accept the output as HTML and print it as a plain text. Even if it works with your browser, it might not work for others.

古いブラウザのいくつかはヘッダと空行の後の最初の文字列が開きの左アングルブラケットの前にスペースがない <HTML> になることを期待します. もし何か他の文字があると, HTML としての出力と受けとらずプレーンテキストとしてそれを出力するかもしれません. それがあなたのブラウザで機能したとしても, 他では機能しないかもしれないのです.
One other approach is to use `here' documents, e.g.:

また別のアプローチは `ヒア' ドキュメントの利用です, e.g.:

print <<EOT;
<HTML>
<HEAD></HEAD>
<BODY>
EOT

Now let's go back to the $|=1 topic. I still disable buffering, for two reasons:

では $|=1 のトピックに戻りましょう. 2 つの理由で, 私はまだバッファリングを無効にしています:

  • I use relatively few print() calls. I achieve this by arranging for my print() statements to print multiline HTML, and not one line per print() statement.

    私は比較的少ない print() コールを使います. 私は print() ステートメントごとに 1 行ではなく, 複数行の HTML を出力するように私の print() ステートメントをアレンジすることでこれを成します.

  • I want my users to see the output immediately. So if I am about to produce the results of a DB query which might take some time to complete, I want users to get some text while they are waiting. This improves the usability of my site. Ask yourself which you like better: getting the output a bit slower, but steadily from the moment you've pressed the Submit button, or having to watch the "falling stars" for a while and then get the whole output at once, even if it's a few milliseconds faster - assuming the browser didn't time out during the wait.

    私は私のユーザにそのアウトプットをすぐに見てもらいと思っています. ですからもし私が完了するためにやや時間をとる DB クエリの結果を生成する場合に, 私はそれらを待っている間でユーザにいくつかのテキストをゲットさせたいのです. これは私のサイトのユーザビリティを改善します. あなたがよりどちらを好むのか自問してください: アプトプットは少し遅いが, あなたがサブミットボタンを押したその時から途切れないでいる, あるいはしばらく "流れ星" を観察してから全体を 1 度にゲットする, それが数ミリ高速だとして - ブラウザが待ちの間にタイムアウトしないと仮定する.


An even better solution is to keep buffering enabled, and use a Perl API rflush() call to flush the buffers when needed. This way you can place the first part of the page that you are going to send to the user in the buffer, and flush it a moment before you are going to do some lengthy operation, like a DB query. So you kill two birds with one stone: you show some of the data to the user immediately, so she will feel that something is actually happening, and you have no performance hit from disabled buffering.

さらにベターな解決策はバッファリングを有効で維持して, 必要な時にバッファをフラッシュする Perl API の rflush() コールを使うことです. この方法はあなたがユーザに送信するページの最初の部分をバッファに配置して, DB クエリのように, やや長いオペレーションを行う少し前にそれをフラッシュできます. ですからあなたは 1 つの石で 2 羽の鳥をキルできます: あなたはユーザにデータのいくつかをすぐに示しますので, 彼女は何かが実際に起こっていると感じて, 無効化されたバッファリングからのパフォーマンスヒット (# 影響) をもたなくてすみます.

use CGI ();
my $r = shift;
my $q = new CGI;
print $q->header('text/html');
print $q->start_html;
print $q->p("Searching...Please wait");
$r->rflush;
# imitate a lengthy operation
# 長いオペレーションの模倣
for (1..5) {
sleep 1;
}
print $q->p("Done!");

Conclusion: Do not blindly follow suggestions, but think what is best for you in each case.

結論: 提案に闇雲に従わないようして, あなたのそれぞれのケースでベストが何かを考えてください.
Note: It might happen that some browsers do not render the page before they have received a significant amount. This is especially true if you insert <link< or <script> tags in your HTML header that require the browser to load a separate file. In that case, the user won't be able to see the content at once, no matter if you flush the buffers or not.

ノート: 一部のブラウザが相当量を受信するまでページをレンダしないことが起こるかもしれません. これはブラウザが別のファイルをロードする必要があるためにあなたの HTML ヘッダにあなたが <link< (# <link> のタイポ?) や <script> タグをインサートした場合は特に真実です. このケースで, ユーザは 1 度でコンテンツを見ることはできません, あなたがバッファをフラッシュするかしないかに関わらずです.
A workaround for this might be to use an output filter that replaces these tags with the files they refer to.

これの回避策はこれらのタグをそれらが参照するファイルでリプレイスするアウトプットフィルタを使うことです.


グローバル vs. 完全修飾変数 : Global vs. Fully Qualified Variables



It's always a good idea to avoid using global variables where it's possible. Some variables must be either global, such as @ISA or else fully qualified such as @MyModule::ISA, so that Perl can see them from different packages.

できるだけグローバル変数の使用を回避することは常にグッドアイデアです. いくつかの変数は @ISA のようにグローバルであるか, @MyModule::ISA のように完全修飾されるかのいずれかでなければなりません, それにより Perl は異なるパッケージからそれらを見ることができます.
A combination of strict and vars pragmas keeps modules clean and reduces a bit of noise. However, the vars pragma also creates aliases, as does Exporter, which eat up more memory. When possible, try to use fully qualified names instead of use vars.

strict と vars プラグマの組み合わせはモジュールをクリーンに保って少しだけノイズを減らします. しかし, vars プラグマは Exporter がするように, エイリアスも作成し, それはより多くのメモリを消費します. 可能な場合は, use vars の代わりに完全修飾名の利用にトライしてください.

For example write:

例えばこう書きます:

package MyPackage1;
use strict;
use vars; # 公正な比較のためにのみ追加 (added only for fair comparison)
@MyPackage1::ISA = qw(CGI);
$MyPackage1::VERSION = "1.00";
1;

instead of:

これの代わりに:

package MyPackage2;
use strict;
use vars qw(@ISA $VERSION);
@ISA = qw(CGI);
$VERSION = "1.00";
1;

Note that we have added the vars pragma in the package that doesn't use it so the memory comparison will be fair.

私たちはメモリ比較がフェアになるようにパッケージに使用しない vars プラグマを追加したことに注目してください.
Here are the numbers under Perl version 5.6.0

こちらが Perl バージョン 5.6.0 のもとでの数値です

% perl -MGTop -MMyPackage1 -le 'print GTop->new->proc_mem($$)->size'
2023424
% perl -MGTop -MMyPackage2 -le 'print GTop->new->proc_mem($$)->size'
2031616

We have a difference of 8192 bytes. So every few global variables declared with vars pragma add about 8KB overhead.

私たちは 8192 バイトの違いをもちます. したがって vars プラグマで宣言されたそれぞれのグローバル変数はおよそ 8KB のオーバーヘッドを追加します.
Note that Perl 5.6.0 introduced a new our() pragma which works like my () scope-wise, but declares global variables.

なお Perl 5.6.0 は my () スコープのようでありつつ, グローバル変数を宣言する新しい our プラグマを導入しました.

package MyPackage3;
use strict;
use vars; # not needed, added only for fair comparison
our @ISA = qw(CGI);
our $VERSION = "1.00";
1;

which uses the same amount of memory as a fully qualified global variable:

これは完全修飾名のグローバル変数と同じ量のメモリを使います:

% perl -MGTop -MMyPackage3 -le 'print GTop->new->proc_mem($$)->size'
2023424

Imported symbols act just like global variables, they can add up quick:

インポートされたシンボルはまるでグローバル変数のように機能し, それらはすぐに増加します:

% perlbloat.pl 'use POSIX ()'
use POSIX () added 316k

% perlbloat.pl 'use POSIX'
use POSIX added 696k

That's 380k worth of aliases. Now let's say 6 different Apache::Registry scripts 'use POSIX;' for strftime() or some other function: 6 * 380k = 2.3Mb

これは 380k のエイリアスに相当します. ここで 6 の異なる Apache::Registry スクリプトが strftime() やその他ファンクションのために 'use POSIX' するとしましょう: 6 * 380k = 2.3Mb
One could save 2.3Mb per single process with 'use POSIX ();' and using fully qualifying POSIX:: function calls.

'use POSIX ();' で完全修飾された POSIX:: ファンクションコールを使うとシングルプロセスあたり 2.3Mb を節約できることになります.


オブジェクトメソッドコール vs. ファンクションコール : Object Methods Calls vs. Function Calls



Which subroutine calling form is more efficient: Object methods or functions?

サブルーチンをコールする形式はどちらがより効果的でしょうか: オブジェクトメソッド or ファンクション ?


軽いサブルーチンでのオーバヘッド : The Overhead with Light Subroutines



Let's do some benchmarking. We will start doing it using empty methods, which will allow us to measure the real difference in the overhead each kind of call introduces. We will use this code:

いくつかベンチマークをしてみましょう. 私たちはそれを空のメソッドを使って始めます, それは私たちに各種コールが導入するオーバーヘッドでのリアルな違いを計測できるようにします. 私たちはこのコードを使います:

bench_call1.pl
--------------
package Foo;

use strict;
use Benchmark;

sub bar { };

timethese(50_000, {
method => sub { Foo->bar() },
function => sub { Foo::bar('Foo');},
});

The two calls are equivalent, since both pass the class name as their first parameter; function does this explicitly, while method does this transparently.

この 2 つのコールは等価です, 両方ともその最初の引数としてクラス名を渡しているからです; function はこれを明示的に行います, 一方で method はこれを透過的に行います.
The benchmarking result:

ベンチマーキングの結果です:

Benchmark: timing 50000 iterations of function, method...
function: 0 wallclock secs ( 0.80 usr + 0.05 sys = 0.85 CPU)
method: 1 wallclock secs ( 1.51 usr + 0.08 sys = 1.59 CPU)

We are interested in the 'total CPU times' and not the 'wallclock seconds'. It's possible that the load on the system was different for the two tests while benchmarking, so the wallclock times give us no useful information.

私たちは 'total CPU times' に関心があります 'wallclock seconds' ではありません. システム上の負荷が 2 つのベンチマーキングの間で異なるかもしれないので, wallclock times (# 実時間) は有益な情報を私たちに与えません.
We see that the method calling type is almost twice as slow as the function call, 0.85 CPU compared to 1.59 CPU real execution time. Why does this happen? Because the difference between functions and methods is the time taken to resolve the pointer from the object, to find the module it belongs to and then the actual method. The function form has one parameter less to pass, less stack operations, less time to get to the guts of the subroutine.

私たちは method をコールするタイプが function コールよりもほぼ 2 倍遅いことを見ます, 実実行時間は 1.59 CPU と比較して 0.85 CPU です. なぜこうなるのでしょうか ? なぜなら function と method の間の違いはそのオブジェクトからのポインタを解決し, それが属するモジュールそして実際の method を見つけるためにとる時間だからです. function 形式は渡すパラメータが 1 つ減り, スタックオペレーションが減り, サブルーチンの中身をえるための時間が減ります.
perl5.6+ does better method caching, Foo->method() is a little bit faster (some constant folding magic), but not Foo->$method(). And the improvement does not address the @ISA lookup that still happens in either case.

perl5.6+ はベターな method コールをして, Foo->method() が少し高速です (いくつかの定数畳み込みマジック) が, Foo->$method() はそうなりません. またこの改善はどちらのケースでも発生する @ISA ルックアップは対処していません.


重いサブルーチンでのオーバヘッド : The Overhead with Heavy Subroutines



But that doesn't mean that you shouldn't use methods. Generally your functions do something, and the more they do the less significant is the time to perform the call, because the calling time is effectively fixed and is probably a very small overhead in comparison to the execution time of the method or function itself. Therefore the longer execution time of the function the smaller the relative overhead of the method call. The next benchmark proves this point:

しかしそれはあなたが method を使うべきではないということを意味するものではありません. 一般的にあなたの function は何かを行い, それらがより多くを行うとコールを実行するための時間はそれほど重要ではなくなります, なぜならそのコールの時間は効果的に固定されていて method や function 自体の実行時間と比較しておそらくとても小さなオーバヘッドだからです. したがって, function の実行時間が長いほど method コールの相対的なオーバヘッドが小さくなります. 次のベンチマークはこのポイントを証明します:

bench_call2.pl
--------------
package Foo;

use strict;
use Benchmark;

sub bar {
my $class = shift;

my ($x,$y) = (100,100);
$y = log ($x ** 10) for (0..20);
};

timethese(50_000, {
method => sub { Foo->bar() },
function => sub { Foo::bar('Foo');},
});

We get a very close benchmarks!

私たちはとても近いベンチマークをゲットします !

function: 33 wallclock secs (15.81 usr + 1.12 sys = 16.93 CPU)
method: 32 wallclock secs (18.02 usr + 1.34 sys = 19.36 CPU)

Let's make the subroutine bar even slower:

サブルーチン bar をもっと遅くしてみましょう:

sub bar {
my $class = shift;

my ($x,$y) = (100,100);
$y = log ($x ** 10) for (0..40);
};

And the result is amazing, the method call convention was faster than function:

するとその結果はアメージングで, method コールの記法が function よりも高速でした:

function: 81 wallclock secs (25.63 usr + 1.84 sys = 27.47 CPU)
method: 61 wallclock secs (19.69 usr + 1.49 sys = 21.18 CPU)

In case your functions do very little, like the functions that generate HTML tags in CGI.pm, the overhead might become a significant one. If your goal is speed you might consider using the function form, but if you write a big and complicated application, it's much better to use the method form, as it will make your code easier to develop, maintain and debug, saving programmer time which, over the life of a project may turn out to be the most significant cost factor.

あなたの function がとても小さなことを行うケースでは, CGI.pm で HTML タグを生成する function のように, そのオーバヘッドは重要なものになるかもしれません. もしあなたのゴールがスピードならあなたは function 形式の利用を考慮したほうがよいでしょう, しかしもしあなたが大きくて複雑なアプリケーションを書く場合は, method 形式を使うのがずっとベターです, それはあなたのコードの開発, 保守およびデバッグをより簡単にして, プログラマの時間を節約できるからで, それはプロジェクトのライフ上で最も重要なコストファクタになるかもしれません.


すべてのメソッドはファンクションよりも遅い ? : Are All Methods Slower than Funtions?



Some modules' API is misleading, for example CGI.pm allows you to execute its subroutines as functions or as methods. As you will see in a moment its function form of the calls is slower than the method form because it does some voodoo work when the function form call is used.

いくつかのモジュールの API はミスリーディングです, 例えば CGI.pm はあなたに function または method としてサブルーチンを実行できるようにします. あなたがすぐに見るようにそのコールの function 形式はその function コールが使われたときににいくつかの ブードゥーワーク (# まじないのような作業) を行うので method 形式よりも遅いです.

use CGI;
my $q = new CGI;
$q->param('x',5);
my $x = $q->param('x');

vs

use CGI qw(:standard);
param('x',5);
my $x = param('x');

As usual, let's benchmark some very light calls and compare. Ideally we would expect the methods to be slower than functions based on the previous benchmarks:

いつもどおり, いくつかとても軽量なコールをベンチマークして比較してみましょう. 理想的には前のベンチマークに基づいて私たちは method が function よりも遅いことを期待します:

bench_call3.pl
--------------
use Benchmark;

use CGI qw(:standard);
$CGI::NO_DEBUG = 1;
my $q = new CGI;
my $x;
timethese
(20000, {
method => sub {$q->param('x',5); $x = $q->param('x'); },
function => sub { param('x',5); $x = param('x'); },
});

The benchmark is written is such a way that all the initializations are done at the beginning, so that we get as accurate performance figures as possible. Let's do it:

このベンチマークはすべての初期化が最初に行われるような用法で書かれているので, 私たちは可能な限り正確なパフォーマンス値をゲットします. ではこれをやってみましょう:

% ./bench_call3.pl

function: 51 wallclock secs (28.16 usr + 2.58 sys = 30.74 CPU)
method: 39 wallclock secs (21.88 usr + 1.74 sys = 23.62 CPU)

As we can see methods are faster than functions, which seems to be wrong. The explanation lays in the way CGI.pm is implemented. CGI.pm uses some fancy tricks to make the same routine act both as a method and a plain function. The overhead of checking whether the arguments list looks like a method invocation or not, will mask the slight difference in time for the way the function was called.

私たちが見るとおり method が function よりも高速ですが, これは間違っているように見えます. その真相は CGI.pm が実装されている方法にあります. CGI.pm は同じルーチンを method とプレーンな function として機能するようにするためにいくつかファンシーなトリックを使っています. その引数リストが method 呼び出しのように見えるかどうかをチェックするオーバヘッドは, function がコールされるやり方の時間の些細な差をマスクする (# 覆い隠す) のです.
If you are intrigued and want to investigate further by yourself the subroutine you want to explore is called self_or_default. The first line of this function short-circuits if you are using the object methods, but the whole function is called if you are using the functional forms. Therefore, the functional form should be slightly slower than the object form.

もしあなたが興味をそそられてあなた自身でさらなる調査をしたいならあなたが探検するサブルーチンは self_of_default と呼ばれるものです. この function の最初の行はあなたがオブジェクト method を使うと短絡しますが, あなたが function 形式を使うと function 全体がコールされます. したがって, function 形式はオブジェクト形式よりもわずかに遅くなるのです.


インポートされたシンボルとメモリ使用量 : Imported Symbols and Memory Usage



There is a real memory hit when you import all of the functions into your process' memory. This can significantly enlarge memory requirements, particularly when there are many child processes.

あなたがあなたのプロセスのメモリにすべての function をインポートしたときに実際のメモリヒットがあります. これは著しくメモリ要求を大きくするかもしれません, 特に多くの child プロセスがあるときには.
In addition to polluting the namespace, when a process imports symbols from any module or any script it grows by the size of the space allocated for those symbols. The more you import (e.g. qw(:standard) vs qw(:all)) the more memory will be used. Let's say the overhead is of size X. Now take the number of scripts in which you deploy the function method interface, let's call that Y. Finally let's say that you have a number of processes equal to Z.

名前空間を汚染することに加えて, プロセスが任意のモジュールや任意のスクリプトからシンボルをインポートするときにそれらのシンボルのために割当てられるスペースのサイズの分だけ大きくなります. あなたがより多くインポートすれば (e.g. qw(:standard) vs qw(:all)) より多くのメモリが使われます. サイズ X のオーバーヘッドがあるとしましょう. そしてあなたがデプロイする function メソッドインターフェイスのスクリプトの数をとり, それを Y と呼ぶことにします. 最後に Z に等しいプロセスの数をあなたが持っているとします.
You will need X*Y*Z size of additional memory, taking X=10k, Y=10, Z=30, we get 10k*10*30 = 3Mb!!! Now you understand the difference.

あなたは追加メモリのサイズ X*Y*Z が必要で, X=10k, Y=10, Z=30 でとると, 私たちは 10k*10*30 = 3Mb をゲットします !!! これであなたはその違いを理解します.
Let's benchmark CGI.pm using GTop.pm. First we will try it with no exporting at all.

GTop.pm を使って CGI.pm をベンチマークしましょう. 最初に私たちはまったく何もエクスポートしないことにトライします.

use GTop ();
use CGI ();
print GTop->new->proc_mem($$)->size;

1,949,696

Now exporting a fer dozens symbols:

ここで数十個のシンボルをエクスポートします:

use GTop ();
use CGI qw(:standard);
print GTop->new->proc_mem($$)->size;

1,966,080

And finally exporting all the symbols (about 130)

そして最後にすべてのシンボルをインポートします (約 130)

use GTop ();
use CGI qw(:all);
print GTop->new->proc_mem($$)->size;

1,970,176

Results:

結果です:

import symbols size(bytes) delta(bytes) relative to ()
--------------------------------------
() 1949696 0
qw(:standard) 1966080 16384
qw(:all) 1970176 20480

(# "delta(bytes) relative to ()" は () に対する差分)
So in my example above X=20k => 20K*10*30 = 6Mb. You will need 6Mb more when importing all the CGI.pm's symbols than when you import none at all.

ですから上記の私の例では X=20k => 20K*10*30 = 6Mb です. あなたは CGI.pm のシンボルをすべてインポートする場合にあなたがまったく何もインポートしない場合よりも 6Mb より多く必要になるでしょう.
Generally you use more than one script, run more than one process and probably import more symbols from the additional modules that you deploy. So the real numbers are much bigger.

通常あなたは 1 つ以上のスクリプトを使い, 1 つ以上のプロセスを実行してあなたがデプロイする追加のモジュールからより多くのシンボルをインポートするはずです. ですから実際の数値ははるかに大きくなります.
The function method is faster in the general case, because of the time overhead to resolve the pointer from the object.

通常のケースで function メソッドはより高速です, object からのポインタを解決するためのオーバーヘッドの時間が理由です (# ここでは関数指向 (function method) とオブジェクト指向 (object method) を比較している).
If you are looking for performance improvements, you will have to face the fact that having to type My::Module::my_method might save you a good chunk of memory if the above call must not be called with a reference to an object, but even then it can be passed by value.

もしあなたがパフォーマンスの改善を求めているなら, 上記のコールが object へのリファレンスでコールできない場合にあなたは My::Module::my_method とタイプすることであなたがかなりの量のメモリを節約できるだろうという事実と向き合わなければなりません, しかしそれでも値で渡すことはできます.
I strongly endorse Apache::Request (libapreq) - Generic Apache Request Library. Its core is written in C, giving it a significant memory and performance benefit. It has all the functionality of CGI.pm except the HTML generation functions.

私は Apache::Request (libapreq) - Generic Apache Request Library (汎用 Apache リクエストライブラリ) を強く支持します. そのコアは C で書かれていて著しいメモリとパフォーマンスの恩恵を与えます. それは HTML 生成 function を除く CGI.pm の全ての機能を持っています.


内挿, 連結あるいはリスト : Interpolation, Concatenation or List



Somewhat overlapping with the previous section we want to revisit the various approaches of mungling with strings, and compare the speed of using lists of strings compared to interpolation. We will add a string concatenation angle as well.

前のセクションとやや重複して私たちは文字列をいじくる様々なアプローチを再考して内挿 (# interpolation, 補間, 展開という場合もある) と文字列リスト (# list) を使う速度を比較したいと思います. 私たちは文字列接続 (# concatenation) の視点も追加します.
When the strings are small, it almost doesn't matter whether interpolation or a list is used. Here is a benchmark:

文字列が小さいときは, interpolation と list のどちらが使われるかは重要ではありません. こちらがベンチマークです:

use Benchmark;
use Symbol;
my $fh = gensysm;
open $fh, ">/dev/null" or die;

my ($one, $two, $three, $four) = ('a'..'d');

timethese(1_000_000,
{
interp => sub {
print $fh "$one$two$three$four";
},
list => sub {
print $fh $one, $two, $three, $four;
},
conc => sub {
print $fh $one.$two.$three.$four;
},
});

Benchmark: timing 1000000 iterations of conc, interp, list...
conc: 3 wallclock secs ( 3.38 usr + 0.00 sys = 3.38 CPU)
interp: 3 wallclock secs ( 3.45 usr + -0.01 sys = 3.44 CPU)
list: 2 wallclock secs ( 2.58 usr + 0.00 sys = 2.58 CPU)

The concatenation technique is very similar to interpolation. The list technique is a little bit faster than interpolation. But when the strings are large, lists are significantly faster. We have seen this in the previous section and here is another benchmark to increase our confidence in our conclusion. This time we use 1000 character long strings:

concatenation テクニックは interpolation ととても似ています. list テクニックは intaerpolation よりも少し高速です. しかし文字列が大きいと, リストが著しく高速になります. 私たちはこれを前のセクションで見ていてこちらは私たちの結論における私たちの自信を高めるもうひとつのベンチマークです. 今回私たちは 1000 文字長の文字列を使います:

use Benchmark;
use Symbol;
my $fh = gensym;
open $fh, ">/dev/null" or die;

my ($one, $two, $three, $four) = map { $_ x 1000 } ('a'..'b');

timethese(500_000,
{
interp => sub {
print $fh "$one$two$three$four";
},
list => sub {
print $fh $one, $two, $three, $four;
},
conc => sub {
print $fh $one.$two.$three.$four;
},
});

Benchmark: timing 500000 iterations of interp, list...
conc: 5 wallclock secs ( 4.47 usr + 0.27 sys = 4.74 CPU)
interp: 4 wallclock secs ( 4.25 usr + 0.26 sys = 4.51 CPU)
list: 4 wallclock secs ( 2.87 usr + 0.16 sys = 3.03 CPU)

In this case using a list is about 30% faster than interpolation. Concatenation is a little bit slower than interpolation.

このケースでは list の利用が interpolation よりも約 30% 高速です. concatenation は interpolation よりも少しだけ低速です.
Let's look at this code:

このコードを見てみましょう:

$title = 'My Web Page';
print "<h1>$title</h1>"; # Interpolation (slow)
print '<h1>' . $title . '</h1>'; # Concatenation (slow)
print '<h1>', $title, '</h1>'; # List (fast for long strings)

When you use "<h1>$title</h1>" Perl does interpolation (since "" is an operator in Perl), which must parse the contents of the string and replace any variables or expressions it finds with their respective values. This uses more memory and is slower than using a list. Of course if there are no variables to interpolate it makes no difference whether to use "string" or 'string'.

あなたが "<h1>$title</h1>" を使うと Perl は interpolaion (# 内挿, 展開) します (Perl では "" がオペレータだからです), これは文字列のコンテンツを解析して何らかの変数や式をそれぞれの値でリプレイスします. これはより多くのメモリを使い list を使うよりも低速です. もちろん interpolation する変数がなければ "string" と 'string' のどちらを使っても違いはなくなります.
Concatenation is also potentially slow since Perl might create a temporary string which it then prints.

concatenation も Perl が一時文字列を作成してから print するかもしれないので潜在的に遅くなります.
Lists are fast because Perl can simply deal with each element in turn. This is true if you don't run join() on the list at the end to create a single string from the elements of list. This operation might be slower than direct append to the string whenever a new string springs into existence.

Perl が各エレメントを順番でシンプルに処理できるので list は高速です. これはあなたがエレメントのリストから単一の文字列を作成するためにその list の末尾で join() を実行しない場合に 真 です. このオペレーションは新しい文字列が出現するたびに文字列へ直接追加するよりも遅くなるかもしれません.
[ReaderMETA]: Please send more mod_perl relevant Perl performance hints

[読者 META]: より多くの mod_perl 関連の Perl パフォーマンスヒントを送ってください


Perl stat() コールのキャッシュされた結果の使用 : Using Perl stat() Call's Cached Results



When you do a stat() (or its variations -M -- last modification time, -A -- last access time, -C -- last inode-change time, etc), the returned information is cached internally. If you need to make an additional check for the same file, use the _ magic variable and save the overhead of an unnecessary stat() call. For example when testing for existence and read permissions you might use:

あなたが stat() (またはそのバリエーション -M -- 最終修正時刻, -A -- 最終アクセス時刻, -C -- 最終 inode 変更時刻, etc,) を使うと, 内部にキャッシュされた情報をリターンします. もしあなたが同じファイルを追加でチェックする必要がある場合は, マジック変数 _ を使って不必要な stat() コールのオーバーヘッドを節約します. 例えば存在と読取りパーミッションをテストするときにあなたはこれを使えます:

my $filename = "./test";
# three stat() calls
print "OK\n" if -e $filename and -r $filename;
my $mod_time = (-M $filename) * 24 * 60 * 60;
print "$filename was modified $mod_time seconds before startup\n";

or the more efficient:

またはより効率的に:

my $filename = "./test";
# one stat() call
print "OK\n" if -e $filename and -r _;
my $mod_time = (-M _) * 24 * 60 * 60;
print "$filename was modified $mod_time secondes before startup\n";

Two stat() calls were saved!

ふたつの stat() コールが節約されました !


コードの最適化 : Optimizing Code



Here are some other resources that explain how to optimize your code, which are usually applied when you profile your code and need to optimize it but in many cases are useful to know when you develop the code.

こちらはあなたのコードをどのように最適化するかの説明をするその他のリソースで, これは通常あなたがあなたのコードをプロファイルしてその最適化を必要とするときに適用されますが, あなたがコードを開発するときに知っておくと多くのケースで便利です.

  • Interesting C code optimization notes, most applying to Perl code as well: http://www.utsc.utoronto.ca/~harper/cscb09/lecture11.html#code

    興味深い C コードの最適化ノートで, Perl コードにもほとんど適用できます: http://www.utsc.utoronto.ca/~harper/cscb09/lecture11.html#code (# 404 Not Found)


[ReaderMETA]: please send me similar resources if you know of such.

[読者META]: あなたがこのようなものを知っているなら私たちに同様のリソースを送ってください.


Apache::Registry とその派生に固有の注記 : Apache::Registry and Derivatives Specific Notes



These are the sections that deal solely with Apache::Registry and derived modules, like Apache::PerlRun and Apache::RegistryBB. No Perl handlers code is discussed here, so if you don't use these modules, feel free to skip this section.

これらは Apache::Registry と Apache::PerlRun や Apache::RegistryBB のような, 派生したモジュールをもっぱら取り扱うセクションです. ここで Perl ハンドラコードは論じていませんので, あなたがこれらのモジュールを使わないなら, 気楽にこのこのセクションをスキップしてください.




As you know Apache::Registry caches the scripts in the packages whose names are constructed by scripts' URI. If you have the same script that can be reached by different URIs, which is possible if you have used symbolic links, you will get the same script stored twice in the memory.

あなたが知っているように Apache::Registry はスクリプトの URI で名前が構成されているパッケージ内でスクリプトをキャッシュします. もしあなたが異なる URI に到達できる同じスクリプトをもっている場合, それはあなたがシンボリックリンクを使っている場合に可能です, あなたは 2 度メモリに格納された同じスクリプトをゲットすることになります.
For example:

例えば:

% ln -s /home/httpd/perl/news/news.pl /home/httpd/perl/news.pl

Now the script can be reached through the both URIs /news/news.pl and /news.pl. It doesn't really matter until you advertise the two URIs, and users reach the same script from both of them.

これでこのスクリプトは /news/news.pl と /news.pl の両方の URI を通じてリーチされます. 実際にはあなたがこの 2 つの URL をアドバタイズ (# 広告) して, ユーザがそれら両方からこのスクリプトに到達しない限りこれは重要ではありません.
So let's assume that you have issued the requests to the both URIs:

それではあなたがこの両方の URL にリクエストを発行したと仮定してみましょう.

http://localhost/perl/news/news.pl
http://localhost/perl/news.pl

To spot the duplication you should use the Apache::Status module. Amongst other things, it shows all the compiled Apache::Registry scripts (using their respective packages):

重複を見つけるためにあなたは Apache::Status モジュールを使います. とりわけ, それはすべてのコンパイルされた Apache::Registry スクリプト (それぞれのパッケージを使って) を表示します:
If you are using the default configuration directives you should either use this URI:

あなたがデフォルトの構成ディレクティブを使っているならあなたはそれぞれこの URI を使うでしょう:

http://localhost/perl-status?rgysubs

or just go to the main menu at:

あるいは単にメインメニューにいく:

http://localhost/perl-status

And click on Compiled Registry Script menu item.

そして Compiled Registry Script メニューアイテムをクリックします.
META: we need a screen snapshot here!!!

META: 私たちはここにスクリーンスナップショットが必要だ !!!
If you the script was accessed through the URI that was remapped to the real file and through the URI that was remapped to the symbolic link, you will see the following output:

もし実際のファイルに再マップされた URI を通じておよびシンボリックリンクに再マップされた URI を通じてそのスクリプトがあなたにアクセスされた場合、あなたは次の出力を見るはずです:

Apache::ROOT::perl::news::news_2epl
Apache::ROOT::perl::news_2epl

You should run the server in the single mode, to see it immediately. If you test it in the normal mode--it's possible that some child processes would show only one entry or none at all, since they might not serve the same requests as the others. For more hints see the section "Run the server in single mode".

サーバをすぐに見るために, あなたはシングルモードでそれを実行します. もしあなたがノーマルモードでテストをする場合 -- いくつかの child プロセスが 1 つだけまたはまったく示されない可能性があります, それらは他と同じでないリクエストをサーブするかもしれないからです. より多くのヒントはセクション "Run the server in single mode" (シングルモードでサーバを実行する) を参照してください.


予防によるパフォーマンスの改善 : Improving Performance by Prevention



There are two ways to improve performance: one is by tuning to squeeze the most out of your hardware and software; and the other is preventing certain bad things from happening, like impolite robots that crawl your site without pausing between requests, memory leakages, getting the memory unshared, making sure that some processes won't take up all the CPU etc.

パフォーマンスを改善するための 2 つの方法があります: ひとつはあなたのハードウェアとソフトウェアを最大限搾りきるようにチューニングします; そしてもう 1 つはリクエスト間で一時停止することなくあなたのサイトをクロールする無作法なロボット, メモリリーク, メモリの非共有化, 一部のプロセスが CPU のすべてを取らないようにする etc. のような, 特定の悪い出来事の発生を防止します.
In the following sections we are going to discuss about the tools and programming techniques that would help you to keep your service in order, even if you are not around.

続くセクションで私たちはたとえあなたが近くにいなくても, あなたのサービスの秩序を維持するためにあなたを助けることになるだろうツールとプログラミングテクニックについて論じます.


メモリの漏えい : Memory leakage



Scripts under mod_perl can very easily leak memory! Global variables stay around indefinitely, lexically scoped variables (declared with my ()) are destroyed when they go out of scope, provided there are no references to them from outside that scope.

mod_perl 下のスクリプトはとても簡単にメモリをリーク (# 漏えい) できます ! グローバル変数は無期限に存在し, レキシカルスコープの変数 (my () で宣言された) はそのスコープの外側からそれらへのリファレンスがないという条件で, それらがスコープ外になると破棄されます.
Perl doesn't return the memory it acquired from the kernel. It does reuse it though!

Perl はカーネルから獲得したメモリを返しません. けれども再利用します !


ファイル全体を読込む : Reading In A Whole File



open IN, $file of die $!;
local $/ = undef; # will read the whole file in
$content = <IN>;
close IN;

If your file is 5Mb, the child which served that script will grow by exactly that size. Now if you have 20 children, and all of them will serve this CGI, they will consume 20*5M = 100M of RAM in total! If that's the case, try to use other approaches to processing the file, if possible. Try to process a line at a time and print it back to the file. If you need to modify the file itself, use a temporary file. When finished, overwrite the source file. Make sure you use a locking mechanism!

もしあなたのファイルが 5Mb なら, スクリプトがサーブしたその child は正確にそのサイズだけ大きくなります. いまあなたが 20 の child たちをもっていて, それらすべてがこの CGI をサーブする場合, それらは合計で 20*5 = 100Mの RAM を消費します! もしそういうケースで, 可能であれば, そのファイルを処理するための別のアプローチにトライしてください. 1 度に 1 行処理しそれをファイルに出力することにトライしてください. もしあなたがそのファイル自体を修正する必要があるなら, テンポラリファイルを使ってください. 終了するときに, そのソースファイルを上書きしてください. あなたは確実にロックメカニズムを使ってください !


ファンクション間で変数をコピーする : Copying Variables Between Functions



Now let's talk about passing variables by value. Let's use the example above, assuming we have no choice but to read the whole file before any data processing takes place. Now you have some imaginary process() subroutine that processes the data and returns it. What happens if you pass the $content by value? You have just copied another 5M and the child has grown in size by another 5M. Watch your swap space! Now multiply it again by factor of 20 you have 200M of wasted RAM, which will apparently be reused, but it's a waste! Whenever you think the variable can grow bigger than a few Kb, pass it by reference!

では値によって変数を渡すことについて話しをしましょう. 上記の例を使います, 私たちはデータを処理する前の段階でファイル全体を読込むしかないと仮定します. 今あなたはデータを処理してそれをリターンするある架空のサブルーチン process() をもちます. もしあなたが値で $content を渡したら何が起こるでしょうか ? あなたはコピーされた別の 5M をもってその child は別に 5M のサイズで大きくなります. あなたのスワップスペースを監視してください ! そしてそれを再び 20倍で乗算するとあなたは無駄な 200M の RAM をもち, それは再利用されるように見えますが, 無駄です ! あなたがその変数が数 Kb よりも大きくなると思われる場合は, それをリファレンスで渡してください !
Once I wrote a script that passed the contents of a little flat file database to a function that processed it by value -- it worked and it was fast, but after a time the database became bigger, so passing it by value was expensive. I had to make the decision whether to buy more memory or to rewrite the code. It's obvious that adding more memory will be merely a temporary solution. So it's better to plan ahead and pass variables by reference, if a variable you are going to pass might eventually become bigger than you envisage at the time you code the program. There are a few approaches you can use to pass and use variables passed by reference. For example:

私は小さくフラットなファイルデータベースのコンテンツを値として処理するファンクションに渡すスクリプトを書いたことがあります -- それは機能して高速でした, しかししばらくしてそのデータベースはより大きくなり, 値でそれを渡すのは高価になりました. 私はより多くのメモリを購入するかそのコードをリライトするかを決断しなければなりませんでした。より多くのメモリの追加がただの一時しのぎであることは明白です. ですから事前に計画してリファレンスで変数を渡すことがベターです, もしあなたが変数を渡すならあなたがそのプログラムのコードを書くときのあなたの予測よりもきっと大きくなります. リファレンス渡しの変数をあなたが渡して使えるいくつかのアプローチがあります. 例えば:

my $content = qq{foobarfoobar};
process(\$content);
sub process{
my $r_var = shift;
$$r_var =~ s/foo/bar/gs;
# nothing returnd - the veriable $content outside has already
# been modified
#---
# 何もリターンしない - 外側の変数 $content は
# すでに修正されている
}

If you work with arrays or hashes it's:

もしあなたが配列やハッシュで作業しているならそれはこうなります:

@{$var_lr} dereference an array
%{$var_hr} dereference a hash

We can still access individual elements of arrays and hashes that we have a reference to without dereferencing them:

私たちは私たちがもっているリファレンスをデリファレンスしなくともそれらの配列やハッシュの個別のエレメントにアクセスできます:

$var_lr->[$index] get $index'th element of an array via a ref
$var_hr->{$key} get $key'th element of a hash via a ref

For more information see perldoc perlref.

より詳細な情報は perldoc perlref を参照してください.
Another approach would be to use the @_ array directly. This has the effect of passing by reference:

もう 1 つのアプローチは @_ 配列を直接使うようにすることです. これはリファレンス渡しの効果をもっています:

process($content);
sub process{
$_[0] =~ s/foo/bar/gs;
# nothing returned - the variable $content outside has been
# already modified
#---
# 何もリターンしない - 外側の変数 $content は
# すでに修正されている

From perldoc perlsub:

perldoc perlsub より:
The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not possible to update)...

この配列 @_ はローカル配列ですが, そのエレメントは実際のスカラパラメータのエイリアスです. とりわけ, もしエレメント $_[0] がアップデートされると, 対応する引数がアップデートされます (あるいはそれがアップデートできない場合はエラーが発生します)...

Be careful when you write this kind of subroutine, since it can confuse a potential user. It's not obvious that call like process($content); modifies the passed variable. Programmers (the users of your library in this case) are used to subroutines that either modify variables passed by reference or expressly return a result (e.g. $content=process($content);).

あなたがこの種のサブルーチンを書くときは注意してください, それは潜在的なユーザを混乱させるかもしれないからです. process($content); のようなコールがその渡された値を変更することは分かりやすいものではありません. プログラマ (このケースでのあなたのライブラリのユーザたち) はリファレンスによって渡された変数を変更したり結果を明示的にリターンするサブルーチンに慣れています (e.g. $content=process($content);).


データベースとの連携 : Work With Databases



If you do some DB processing, you will often encounter the need to read lots of records into your program, and then print them to the browser after they are formatted. I won't even mention the horrible case where programmers read in the whole DB and then use Perl to process it!!! Use a relational DB and let the SQL do the job, so you get only the records you need!

あなたが何か DB 処理を行う場合, あなたは多くのレコードをあなたのプログラムに読み込んで, それらをフォーマットした後でそれらをブラウザに出力する必要性によく遭遇します. 私はプログラマが DB 全体を読込んで Perl を使いそれを処理するという恐ろしいケースは言及しません !!! リレーショナル DB を使いそのジョブを SQL にやらせれば, あなたはあなたが必要なレコードだけをゲットできます !
We will use DBI for this (assume that we are already connected to the DB--refer to perldoc DBI for a complete reference to the DBI module):

私たちはこのために DBI を使います (私たちはすでに DB に接続していると仮定します -- DBI モジュールの完全なリファレンスは perldoc DBI を参照してください):

$sth->execute;
while(@row_ary = $sth->fetchrow_array) {
# do DB accumulation into some variable
# 何らかの変数に DB を蓄積する
}
# print the output using the data returned from the DB
# DB からリターンされたデータを使ってアウトプットを出力する

In the example above the httpd_process will grow by the size of the variables that have been allocated for the records that matched the query. Again remember to multiply it by the number of the children your server runs!

上記の例で httpd_process はそのクエリにマッチしたレコードが割当てられた変数のサイズだけ大きくなります. あなたのサーバが実行する child たちの数でそれを乗算することをあたらめて思い出してください.
A better approach is not to accumulate the records, but rather to print them as they are fetched from the DB. Moreover, we will use the bind_col() and $sth->fetchrow_arrayref() (aliased to $sth->fetch()) methods, to fetch the data in the fastest possible way. The example below prints an HTML table with matched data, the only memory that is being used is a @cols array to hold temporary row values. The table will be rendered by the client browser only when the whole table will be out though.

ベターなアプローチはレコードを蓄積せずに, むしろ DB からフェッチされたところでそれらを出力することです. さらに, 私たちはできるだけ速いやり方でデータをフェッチするために, bind_col() と $sth->fetchrow_arrayref() ($sth->fetch() にエイリアスされています) メソッドを使います. 以下の例はマッチしたデータで HTML テーブルを出力します, 使われるメモリは行の値を一時的にホールドするための @cols 配列だけです. しかしながらこのテーブルはテーブル全体が出力されるときのみクライアントブラウザによってレンダリングされます.

my @select_fields = qw(a b c);
# create a list of cols values
# カラムの値のリストを作成
my @cols = ();
@cols[0..$#select_fields] = ();
$sth = $dbh->prepare($do_sql);
$sth->execute;
# Bind perl variables to columns.
# perl 変数をカラムにバインド
$sth->bind_columns(undef,\(@cols));
print "<TABLE>";
while($sth->fetch) {
print "<TR>",
map("<TD>"$_</TD>", @cols),
"</TR>";
}
print "</TABLE>";

Note: the above method doesn't allow you to know how many records have been matched. The workaround is to run an identical query before the code above where you use SELECT count(*) ... instead of 'SELECT * ..., to get the number of matched records. It should be much faster, since you can remove any SORTBY and similar attributes.

ノート: 上記メソッドでいくつのメソッドがマッチしたかをあなたは知ることができません. 回避策はマッチしたレコードの数をゲットするために, 上記コードの前で ... SELECT * のかわりに ... あなたが SELECT count(*) を使い同じクエリを実行することです. これははるかに高速になるはずです, あなたは SORTBY や同様のアトリビュートを削除できるからです.
For those who think that $sth->rows will do the job, here is the quote from the DBI manpage:

$sth->rows が役目を果たすと考える人たちのための, DBI man ページからの引用がこちらです:
rows();

$rv = $sth->rows;
Returns the number of rows affected by the last database altering command, or -1 if not known or not available. Generally you can only rely on a row count after a do or non-select execute (for some specific operations like update and delete) or after fetching all the rows of a select statement.

最後のデータベース変更コマンドによる影響された行の数, またはわからなかったり利用不可の場合に -1 を返します. 通常あなたは do や 非 select の実行後 (update や delete のような特定の操作) か select ステートメントの全ての行がフェッチした後にのみ行カウントに頼ることができます.
For select statements it is generally not possible to know how many rows will be returned except by fetching them all. Some drivers will return the number of rows the application has fetched so far but others may return -1 until all rows have been fetched. So use of the rows method with select statements is not recommended.

select ステートメントでそれは通常それらすべてをフェッチする以外にいくつの行が返されるかを知ることはできません. 一部ドライバはこれまでアプリケーションがフェッチした行の数をリターンしますがそれ以外はすべての行がフェッチされない限り -1 を返すかもしれません. ですから select ステートメントでの rows メソッドの利用は推奨しません.

As a bonus, I wanted to write a single sub that flexibly processes any query. It would accept conditions, a call-back closure sub, select fields and restrictions.

ボーナスとして, 私は任意のクエリを柔軟に処理する単一の sub を書きたいと思いました. それは条件, コールバッククロージャ sub, select フィールドと制約を受け入れます.

# Usage:
# $o->dump(\%conditions,\&callback_closure,\@select_fields,@restrictions);
#
sub dump{
my $self = shift;
my %param = %{+shift}; # dereference hash
my $rsub = shift;
my @select_fields = @{+shift}; # dereference list
my @restrict = shift || '';

# create a list of cols values
# カラムの値のリストを作成
my @cols = ();
@cols[0..$#select_fields] = ();

my $do_sql = '';
my @where = ();

# make a @where list
map { push @where, "$_=\'$param{$_}\'" if $param{$_};} keys %param;

# prepare the sql statement
$do_sql = "SELECT ";
$do_sql .= join(" ", @restrict) if @restrict; # append restriction list
$do_sql .= " " .join(",", @select_fields) ; # append select list
$do_sql .= " FROM $DBConfig{TABLE} "; # from table

# we will not add the WHERE clause if @where is empty
# 私たちは @where が空の場合 WHERE 句を追加しない
$do_sql .= " WHERE " . join " AND ", @where if @where;

print "SQL: $do_sql \n"; if $debug;

$dbh->{RaiseError} = 1; # do this, or check every call for errors
$sth = $dbh->prepare($do_sql);
$sth->execute;
# Bind perl variables to columns.
# カラムに perl 変数をバインド
$sth->bind_columns(undef,\(@cols));
while($sth->fetch) {
&$rsub(@cols);
}
# print the tail or "no records found" message
# according to the previous calls
# 前のコールにしたがって tail または
# "レコードが見つからない" メッセージを出力
&$rsub();

} # end of sub dump

Now a callback closure sub can do lots of things. We need a closure to know what stage are we in: header, body or tail. For example, we want a callback closure for formatting the rows to print:

これでコールバッククロージャ sub は多くのことを行えます. 私たちは私たちがどのステージにいるかを知るためにクロージャが必要です; ヘッダ, ボディまたはテイル. 例えば, 私たちは出力する行をフォーマットするためにコールバッククロージャを欲しいと思います:

my $rsub = eval {
# make a copy of @fields list, since it might go
# out of scope when this closure is called
# @fields リストのコピーをする, このクロージャがコールされると
# それがスコープ外になるかもしれないため
my @fields = @fields;
my @query_fields = qw(user dir tool act); # no data field!!!
my $header = 0;
my $tail = 0;
my $counter = 0;
my %cols = (); # colums name=> value hash

# Clouser with the following behavior:
# 次の振る舞いでのクロージャ:
# ---
# 1. Header's code will be executed on the first call only and
# if @_ was set
# 2. Row's printing code will be executed on every call with @_ set
# 3. Tail's code will be executed only if Header's code was
# printed and @_ isn't set
# 4. "No record found"
# ---
# 1. ヘッダのコードは最初のコールのみまたは @_ がセットされている場合に実行される
# 2. 行を出力するコードは @_ がセットされているコールで常に実行される
# 3. テイルのコードはヘッダのコードが出力されて @_ がセットされていない場合のみ実行される
# 4. "レコードが見つからない"

sub {
# Header
if (@_ and !$header){
print "<TABLE>\n";
print $q->Tr(map{ $q->td($_) } @fields );
$header = 1;
}
# Body
if (@_) {
print $q->Tr(map{$q->td($_)} @_ );
$counter++;
return;
}

# Tail, will be printed only at the end
# テイル, 終了でのみ出力される
if ($header and !($tail or @_)){
print "</TABLE>\n $counter records found";
$tail = 1;
return;
}

# No record found
# レコードが見つからない
unless ($header){
print $q->p($q->center($q->b("No record was found!\n")));
}

} # end of sub {}
}; # end of my $rsub = eval {

You might also want to check the section Preventing Your Processes from Growing and Limiting Other Resources Used by Apache Child Processes.

あなたはセクション Preventing Your Processes from GrowingLimiting Other Resources Used by Apache Child Processes もチェックするとよいでしょう.


邪悪なトロイカを回避 : Avoid the Evil Troika



The perlre manpage says:

perlre man ページはこう言います:
WARNING: Once Perl sees that you need one of "$&", "$`", or "$'" anywhere in the program, it has to provide them for every pattern match. This may substantially slow your program.

警告: あなたがプログラムのどこかで "$&" (# 最後にパターンマッチした文字列), "$`" (# 最後のパターンマッチ文字列の前の部分), や "$'" (# 最後のパターンマッチ文字列の後ろの部分) のいずれかを必要としていると Perl が見立てると, パターンがマッチするごとにそれらを提供しなければなりません. これはあなたのプログラムを実質的に遅くします.

(# 一般的にこれらの定義済み変数 $&, $`, $' は非推奨とされていたが, Perl 5.20.0 で性能問題が修正されて安全に利用できるようになった ? - perlvar 性能問題)
The mere existence of these variables will trigger this behavior, regardless of whether or not the code that accesses them will be executed. Removing these variables should significantly improve the regex performance.

これらの変数がたんに存在するだけでこの振る舞いはトリガーされます, それらにアクセスするコードが実行されるかどうかは関係ありません.
How do you know whether some code loads them? You could grep(1), but it's hard to remember to do that as you include more modules from CPAN and write new code. Luckily Devel::SawAmpersand comes to help. (http://search.cpan.org/dist/Devel-SawAmpersand/lib/Devel/SawAmpersand.pm) This module will alert you if it detects any of the evil troika variables present.

あるコードがそれらをロードしているかどうかをあなたはどうやって知るのでしょうか ? あなたは grep(1) をできますが, あなたはより多くのモジュールをインクルードして新しいコードを書くとそれを覚えておくことが大変です. 幸運にも Devele::SawAmpersand が助けになります. (http://search.cpan.org/dist/Devel-SawAmpersand/lib/Devel/SawAmpersand.pm) このモジュールはいずれかの邪悪なトロイカ変数の存在を検知するとあなたに警告します.


あなたのプロセスの肥大化を防止する : Preventing Your Processes from Growing



If you have already worked with mod_perl, you have probably noticed that it can be difficult to keep your mod_perl processes from using a lot of memory. The less memory you have, the fewer processes you can run and the worse your server will perform, especially under a heavy load. This chapter presents several common situations which can lead to unnecessary consumption of RAM, together with preventive measures.

もしあなたがすでに mod_perl で作業しているなら、あなたはおそらくあなたの多くのメモリ使用しないように mod_perl プロセスを維持することが難しいことに気づかされたはずです. あなたもっているメモリがより少ないと, あなたが実行するプロセスはより少なくあなたのサーバのパフォーマンスはより悪くなるはずです, 特に高負荷のもとでは. このチャプタは不要な RAM の消費に導くいくつかの一般的な状況を, 予防策とともに提示します.
When you need to control the size of your httpd processes, use one of the two modules Apache::GTopLimit and Apache::SizeLimit which kill Apache httpd processes when the latter grow too large or lose a big chunk of their shared memory. The two modules differ in methods for finding out the memory usage. Apache::GTopLimit relies on the libgtop library to perform this task, therefore if this library can be built on your platform you can use this module. Apache::SizeLimit includes different methods for different platforms, you will have to check the modules' manpage to figure out which platforms are supported.

あなたがあなたの httpd プロセスのサイズのコントロールを必要とするとき, プロセスが大きくなり過ぎたり共有メモリの大きなチャンク (# 塊り) が失われたときに Apache httpd プロセスをキルする 2 つのモジュール Apache::GTopLimit と Apache::SizeLimit のうち 1 つを使います. この 2 つのモジュールはメモリ使用量を見つけだす方法が違います. Apache::GTopLimit は このタスクを実行するために libgtop ライブラリに頼っています, したがってこのライブラリをあなたのプラットフォーム上でビルドできるならあなたはこのモジュールを使えます. Apache::SizeLimit は異なるプラットフォームのために異なる方法を含んでいます, あなたはどのプラットフォームがサポートされているかを見つけるためにこのモジュールの man ページをチェックするべきでしょう.


最小共有メモリサイズのしきい値を定義する : Defining the Minimum Shared Memory Size Threshold



As we have already discussed, when it is first created an Apache child process usually has a large fraction of it memory shared with its parent. During the child process' life some of its data structures are modified and a part of its memory becomes unshared (pages become "dirty"), leading to an increase in memory consumption. You will remember that the MaxRequestsPerChild directive allows you to specify the number of requests a child process should serve before it is killed. One way to limit the memory consumption of a process is to kill it and let Apache replace it with a newly started process, which again will have all its memory shared with the Apache parent. The new child process serves requests and eventually the cycle is repeated.

私たちがすでに論じたように, 最初に作成された Apache child プロセスは最初は通常そのメモリの大部分をその parent と共有します. child プロセスのライフ間にデータ構造の一部が変更されてそのメモリの一部が非共有化されると (ページは "ダーティ" になります), メモリ消費が増加に導かれます. あなたは MaxRequestsPerChild ディレクティブがあなたに child プロセスがキルされる前にサーブすべきリクエストの数を指定できるようにすることを覚えておいてください. プロセスのメモリ消費を制限するひとつの方法はそれをキルして Apache がそれを新しくスタートさせたプロセスでリプレイスするようにさせることで, それは Apache parent とそのすべてのメモリを再び共有されるようにします. この新しい child プロセスはリクエストをサーブして結局のところはこのサイクルが繰り返されます.
This is a fairly crude means of limiting unshared memory and you will probably need to tune MaxRequestsPerChild, eventually finding an optimum value. If, as is likely, your service is undergoing constant changes then this is an inconvenient solution. You have to re-tune this number again and again to adapt to the ever changing code base.

これは非共有メモリを制限するためのわりと雑な手段であなたはおそらく MaxRequestPerChild を調整して, 最終的には最適な値を見つける必要があります. あなたのサービスがコンスタントに変更されている, もし, そうならこれは不便なソリューションです. あなたは再びこの数値を再調整し変化し続けるコードベースに再び適応しなければなりません.
You really want to set some guardian to watch the shared size and kill the process if it goes below some limit. This way, processes will not be killed unnecessarily.

あなたは共有サイズをウォッチしてもしプロセスがある制限を下回ればそれをキルする何らかのガーディアンを本当に欲しいと思うでしょう. これなら, プロセスは不必要にキルされません.
To set a shared memory lower limit of 4MB using Apache::GTopLimit add the following code into the startup.pl file:

Apache::GTopLimit を使って共有メモリの下限を 4MB にセットするには startup.pl に次のコードを追加します:

use Apache::GTopLimit;
$Apache::GTopLimit::MIN_PROCESS_SHARED_SIZE = 4096;

and in httpd.conf:

そして httpd.conf で:

PerlFixupHandler Apache::GTopLimit

don't forget to restart the server for the changes to take effect.

この変更の効果をえるためにサーバをリスタートすることを忘れないでください.
This has the effect that as soon as the child process shares less than 4MB, (the corollary being that it must therefore be occupying a lot of memory with its unique pages), it will be killed after completing to serve the last request, and, as a consequence, a new child will take its place.

これは child プロセスが 4MB 未満を共有するようになるとすぐに, (したがって当然その固有のページでは多くのメモリを占有しているはずです), その最後のリクエストのサーブが完了した後でキルされ, そして, 結果として, 新しい child がその場所をとります.
If you use Apache::SizeLimit you can accomplish the same with the adding to startup.pl:

もしあなたが Apache::SizeLimit を使うならあなたは startup.pl にこれを追加して同じことを実現できます:

use Apache::SizeLimit;
$Apache::SizeLimit::MIN_SHARE_SIZE = 4096;

and in httpd.conf:

そして httpd.conf で:

PerlFixupHandler Apache::SizeLimit

If you only want to set this limit for some requests (presumably the ones which you think are likely to cause memory to become unshared) then you can register a post-processing check using the set_min_shared_size() function. For example:

あなたが一部のリクエストにのみこれをセットしたい (おそらくあなたがメモリが非共有になるだろうと考えるもの) ならあなたは set_min_shared_size() を使ってポストプロセスチェック (# 処理後のチェック) を登録できます. 例えば:

use Apache::GTopLimit;
if ($need_to_limit) {
# make sure that at least 4MB are shared
# 少なくとも 4MB が共有されていることを確認する
Apache::GTopLimit->set_min_shared_size(4096);
}

or for Apache::SizeLimit:

または Apache::SizeLimit で:

use Apache::SizeLimit;
if ($need_to_limit) {
# make sure that at least 4MB are shared
Apache::SizeLimit->setmin(4096);

Since accessing the process information adds a little overhead, you may want to only check the process size every N times. In this case set the $Apache::GTopLimit::CHECK_EVERY_N_REQUESTS variable. For example to test the size every other time, put in your startup.pl:

プロセス情報にアクセスすると小さなオーバーヘッドが追加されるので, あなたは N 回ごとにのみそのプロセスをチェックするようにしたほうがよいでしょう. このケースでは $Apache::GTopLimit::CHECK_EVERY_N_REQUESTS 変数をセットします. 例えば 1 回おきにサイズをテストするには, あなたの startup.pl にこれをおきます:

$Apache::GTopLimit::CHECK_EVERY_N_REQUESTS = 2;

or for Apache::SizeLimit:

または Apache::SizeLimit では:

$Apache::SizeLimit::CHECK_EVERY_N_REQUESTS = 2;

You can run the Apache::GTopLimit module in the debug mode by setting:

あなたは httpd.conf でのこのセッティングによってデバッグモードで Apache::GTopLimit モジュールを実行できます:

PerlSetVar Apache::GTopLimit::DEBUG 1

in httpd.conf. It's important that this setting should happen before the Apache::GTopLimit module is loaded.

このセッティングは Apache::GTopLimit モジュールがロードされる前に行われていることが重要です.
When debug mode is turned on the module reports in the error_log file the memory usage of the current process and also when it detects that at least one of the thresholds was crosses and the process is going to be killed.

デバッグモードを on にしたときこのモジュールはファイルに現在のプロセスのメモリ使用量およびそれが少なくともしきい値のひとつを超えてそのプロセスがキルされたことを検出したときに error_log ファイルでレポートします.
Apache::SizeLimit controls the debug level via $Apache::SizeLimit::DEBUG variable:

Apache::SizeLimit は Apache::SizeLimit::DEBUG 変数を介してデバッグレベルをコントロールします:

$Apache::SizeLimit::DEBUG = 1;

which can be modified any time, even after the module was loaded.

それはいつでも変更できます, モジュールがロードされた後だとしても.


メモリ共有制限の潜在的な難点 : Potential Drawbacks of Memory Sharing Restriction



It's very important that the system won't be heavily engaged in swapping process. Some systems do swap in and out every so often even if they have plenty of real memory available and it's OK. The following applies to conditions when there is hardly any free memory available.

システムがスワッピングプロセスにひどく関与しないことはとても重要です. 一部のシステムはしばしば十分な実メモリをもちそれが OK であってもスワップインとアウトを行います. 次のものは利用可能なフリーのメモリがほとんどない条件に適用されます.
So if the system uses almost all of its real memory (including the cache), there is a danger of parent's process memory pages being swapped out (written to a swap device). If this happens the memory usage reporting tools will report all those swapped out pages as non-shared, even though in reality these pages are still shared on most OSs. When these pages are getting swapped in, the sharing will be reported back to normal after a certain amount of time. If a big chunk of the memory shared with child processes is swapped out, it's most likely that Apache::SizeLimit or Apache::GTopLimit will notice that the shared memory floor threshold was crossed and as a result kill those processes. If many of the parent process' pages are swapped out, and the newly created child process is already starting with shared memory below the limit, it'll be killed immediately after serving a single request (assuming that we the $CHECK_EVERY_N_REQUESTS is set to one). This is a very bad situation which will eventually lead to a state where the system won't respond at all, as it'll be heavily engaged in swapping process.

ですからシステムが実メモリ (キャッシュを含む) のほとんどすべてを使う場合, parent のプロセスメモリページがスワップアウト (スワップデバイスへの書込み) される危険があります. もしこれが発生するとメモリ使用量をレポートするツールはそれらスワップアウトされたページをすべて非共有としてレポートします, ほとんどの OS 上で実際にはそれらのページがまだ共有されていたとしてもです. それらのページがスワップインされると, その共有は一定時間経過後にノーマルに戻ったとレポートされます. もし child プロセスと共有されたメモリの大きなチャンクがスワップアウトした場合, Apache::SizeLimit や Apache::GTopLimit はほとんどの場合で共有メモリのフロアの敷居が横断されたことに気づき結果としてそれらのプロセスをキルするでしょう. もし parent プロセスページの多くがスワップアウトされて, 新しく作成された child プロセスがすでにリミット以下の共有メモリでスタートしている場合, それはシングルリクエストをサーブした直後にキルされます (私たちが $CHECK_EVERY_N_REQUESTS が 1 にセットされていると仮定しています). これはとてもバッドな状況で最終的にシステムがまったくレスポンスしない状態まだ導きます, それがスワッピングプロセスにかなり関与するからです.
This effect may be less or more severe depending on the memory manager's implementation and it certainly varies from OS to OS, and different kernel versions. Therefore you should be aware of this potential problem and simply try to avoid situations where the system needs to swap at all, by adding more memory, reducing the number of child servers or spreading the load across more machines, if reducing the number of child servers is not an options because of the request rate demands.

この影響は多かれ少なかれメモリマネージャの実装にシビアに依存し OS ごと, カーネルバージョンの違いで確実に異なります. 従ってあなたはこの潜在的に問題を認識してより多くのメモリを追加したり child サーバの数を削減する, もし child サーバの数の削減が要求するリクエストレートのためにオプションにならない場合は, 負荷をより多くのマシン間で分散して, システムがすべてをスワップする状況をシンプルに回避することにトライしなければなりません.


最大共有メモリサイズのしきい値を定義 : Defining the Maximum Memory Size Threshold



Not less important than maximizing shared memory is restricting the absolute size of the processes. If the processes grow after each request, and if nothing restricts them from growing, you can easily run out of memory.

共有メモリの最大化に劣らず重要なのはプロセスの絶対サイズを制限することです. もしプロセスが各リクエストの後で増加して, それらを増加から制限するものがない場合, あなたは簡単にメモリを不足させることができます.
Again you can set the MaxRequestPerChild directive to kill the processes after a few requests have been served. But as we have explained in the previous section this solution is not as good as one which monitors the process size and kills it only when some limit is reached.

あなたはいくつかのリクエストが処理されたあとでプロセスをキルするために再び MaxRequestsPerChild をセットできます. しかし私たちが前のセクションで説明したとおりこのソリューションはプロセスサイズをモニタしてあるリミットがリーチされたときのみそれをキルことよりもグッドではありません.
If you have Apache::GTopLimit (described in the previous section) you can limit process' memory usage by setting the $Apache::GTopLimit::MAX_PROCESS_SIZE directive. For example if you want the processes to be killed when they reach 10MB you should put the following in your startup.pl file:

もしあなたが Apache::GTopLmit (前のセクションで説明された) をもっているならあなたはプロセスのメモリ使用量を $Apache::GTopLimit::MAX_PROCESS_SIZE ディレクティブをセットすることで制限できます. 例えばプロセスが 10MB にリーチしたときにあなたがそれらをキルしたい場合あなたはあなたの startup.pl ファイルに次をおくようにします:

$Apache::GTopLimit::MAX_PROCESS_SIZE = 10240;

Just as when limiting shared memory, you can set a limit for the current process using the set_max_size() method in your code:

共有メモリを制限するときと同様に, あなたはあなたのコードで set_max_size() メソッドを使い現在のプロセスへの制限をセットできます:

use Apache::GTopLimit;
Apache::GTopLimit->set_max_size(10000);

For Apache::SizeLimit the equivalents are:

Apache::SizeLimit で等価なのはこれと:

use Apache::SizeLimit;
$Apache::SizeLimit::MAX_PROCESS_SIZE = 10240;

and:

これです:

use Apache::SizeLimit;
$Apache::SizeLimit->setmax(10240);



最大非共有メモリサイズのしきい値を定義 : Defining the Maximum Unshared Memory Size Threshold



Instead of setting the shared and total memory usage thresholds, you can set a single threshold which measures the amount of unshared memory, by subtracting the shared memory size from the total memory size.

共有と合計メモリ使用量のしきい値をセッティングするかわりに, あなたは合計メモリサイズから共有メモリサイズを引くことで, 非共有メモリの量を測定する単一のしきい値をセットできます.
Both modules allow you to set the thresholds in similar ways. With Apache::GTopLimit you can set the unshared memory threshold server-wide with:

どちらのモジュールもあなたに似た方法でしきい値をセットできるようにします. Apache::GTopLimit であなたはサーバ全体で非共有メモリのしきい値をこれでセットできて:

$Apache::GTopLmit::MAX_PROCESS_UNSHARED_SIZE = 6144;

and locally for a handler with:

ハンドラのためにローカルではこれです:

Apache::GTopLimit->set_max_unshared_size(6144);

If you are using Apache::SizeLimit the corresponding settings would be:

もしあなたが Apache::SizeLimit を使うなら対応するセッティングはこれと:

$Apache::SizeLimit::MAX_UNSHARED_SIZE = 6144;

and:

これです:

Apache::SizeLimit->setmax_unshared(6144);



Apache Child プロセスによって使われる他のプロセスの制限 : Limiting Other Resources Used by Apache Child Processes



In addition to the absolute and shared memory sizes limiting, you might need to prevent the processes from excessive consumption of the system resources. Like limiting the CPU usage, the number of files that can be opened, or memory segment usage and more.

絶対および共有メモリサイズ制限に加えて, あなたはプロセスがシステムリソースを過度に消費しないようにすることを必要とするかもしれません. CPU 使用量制限, ファイルがオープンされる数や, メモリセグメントの使用量などと同様にです.
The Apache::Resource module allows this all by deploying the BSD::Resource module, which in turn uses the C function setrlimit() to set limits on system resources.

Apache::Resource モジュールはシステムリソースで制限をセットするために C ファンクションを setrlimit() を使う, BSD::Resource モジュールをデプロイすることでこれをできるようにします.
A resource limit is specified as a soft limit and a hard limit. When a soft limit is exceeded a process may receive a signal (for example, if the CPU time or file size is exceeded), but it will be allowed to continue execution until it reaches the hard limit (or modifies its resource limit). The rlimit structure is used to specify the hard and soft limits on a resource. (See the manpage for setrlimit for your OS specific information.)

リソース制限はソフトリミットとハードリミットとして指定されます. ソフトリミットを上回るとプロセスはシグナルを受信します (例えば, CPU タイムやファイルサイズが上回る) が, それはハードリミットに達する (あるいはそのリソース制限を変更する) まで実行が継続されます. rlimit 構造体はリソースのハードとソフトリミットを指定するために使われます. (あなたの OS 固有の情報のために setrlimit の man ページを参照してください).
If the value of the variable is of the form S:H, S is treated as the soft limit, and H is the hard limit. If it is just a single number, it is used for both soft and hard limits. So if you set 10:20, the soft limit is 10 and the hard limit is 20. If you set just 10--both the soft and the hard limits are set to 20.

もし変数の値が S:H 形式なら, S はソフトリミットとして, H はハードリミットとして扱われます. もしそれがひとつの数値だけなら, それはソフトとハードリミット両方に使われます. ですからもしあなたが 10:20 をセットすると, ソフトリミットが 10 でハードリミットは 20 です. もしあなたが 10 だけをセットしたら -- ソフトとハードリミットの両方は 20 (# 10 の typo ?) にセットされます.
The mostly spread usage of this module is to limit the CPU usage. The environment variable PERL_RLIMIT_CPU defines the maximum amount of CPU time the process can use. If it runs for longer than this, it gets killed, no matter what it does, either processing a new request or just waiting. This is very useful when you have a code with a bug and the process starts to spin in an infinite loop or alike using a lot of CPU and never completing the request.

このモジュールのよく普及した使用方法は CPU 使用量を制限することです. 環境変数 PERL_RLIMIT_CPU はプロセスが使える CPU タイムの最大量を定義します. もしそれよりも長く実行されると, それはキルされることになります, 新しいリクエストを処理しているのかただに待っているのか, そのどちらであっても関係ありません. これはあなたがバグのあるコードをもっていてプロセスが無限ループ等でスピンを始めて大量の CPU を使いそのリクエストをいつまでも完了しないときにとても便利です.
The value is measured in seconds. The following example sets the soft limit of the CPU usage to 120 seconds (the default is 360).

この値は秒間で測定されます. 次の例は CPU 使用量のソフトリミットを 120 秒 (デフォルトは 360) にセットします.

PerlModule Apache::Resource
PerlSetEnv PERL_RLIMIT_CPU 120

Of course you should tell mod_perl to use this module, which is done by adding the following directive to httpd.conf:

もちろんこのモジュールを使うためにあなたは mod_perl に伝えなければなりません, それは httpd.conf に次のディレクティブを追加することで行われます:

PerlChildInitHandler Apache::Resource

There are other resources that you might want to limit. For example you can limit the memory data and stack segment sizes (PERL_RLIMIT_DATA and PERL_RLIMIT_STACK), the maximum process file size (PERL_RLIMIT_FSIZE), the core file size (PERL_RLIMIT_CORE), the address space (virtual memory) limit (PERL_RLIMIT_AS), etc. Refer to the setrlimit(2) man page on your OS for other possible resources. Remember to prepend PERL_ before the resource types you will see in the man page.

他にもあなたが制限したいだろうリソースがあります. 例えばあなたはメモリデータとスタックセグメントサイズ (PERL_RLIMIT_DATA と PERL_RLIMIT_STACK), 最大プロセスファイルサイズ (PERL_RLIMIT_FSIZE), コアファイルサイズ (PERL_RLIMIT_CORE), アドレス空間 (仮想メモリ) 制限 (PERL_RLIMIT_AS), etc を制限できます. 他の可能なリソースはあなたの OS の setrlimit(2) man ページを参照してください. あなたが man ページで見るだろうリソースタイプの前に PERL_ を前置することをおぼえておいてください.
If you configure Apache::Status, it will let you review the resources set in this way. Remember that Apache::Status must be loaded before Apache::Resource in order to enable the resources display menu.

もしあなたが Apache::Status を構成すると, それはこの方法でセットされたリソースをレビューさせてくれます. リソースディスプレイメニューを有効にするためには Apache::Resource の前に Apache::Status がロードされなければならないことをおぼえておいてください.
If you want to set the debug mode set the $Apache::Resource::Debug before loading the module, for example by using the Perl sections in httpd.conf.

もしあなたがデバッグモードをセットしたいならこのモジュールをロードする前に $Apache::Resource::Debug をセットします, 例えば httpd.conf の Perl セクションをつかってこうします.

<Perl>
$Apache::Resource::Debug = 1;
require Apache::Resource;
</Perl>
PerlChildInitHandler Apache::Resource

Now open in the error_log file using tell and watch the debug messages showing up, when the requests are served.

そして tell (# tail ?) を使って error_log ファイルを開いてリクエストがサーブされたときに, 表示されるデバッグメッセージをウォッチします.


OS 固有の注記 : OS Specific notes



Note that under Linux malloc() uses mmap() instead of brk(). This is done to conserve virtual memory - that is, when you malloc a large block of memory, it isn't actually given to your program until you initialize it. The old-style brk() system call obeyed resource limits on data segment size as set in setrlimit() - mmap() doesn't.

Linux のもとで malloc は brk() の代わりに mmap() を使うことに注意してください. これは仮想メモリを節約するために行われます - つまり, あなたがメモリの大きなブロックを malloc するとき, それはあなたがそれを初期化するまで実際には与えられません. 古いスタイルの brk() システムコールは setrlimit() でセットしたデータセグメントサイズに従っていました - mmap() はそうではありません.
Apache::Resource's defaults put caps on data size and stack size. Linux's current memory allocation scheme doesn't honor these limits, so if you just do

Apache::Resource のデフォルトはデータサイズとスタックサイズにキャップします (# 上限を設ける). Linux の現在のメモリアロケーションスキームはそれらの制限を尊重しないので, もしあなたが単にこれを行うと

PerlSetEnv PERL_RLIMIT_DEFAULTS On
PerlModule Apache::Resource
PerlChildInitHandler Apache::Resource

Your Apache processes are still free to use as much memory as they like.

あなたの Apache プロセスはまだ自由に好きなだけのメモリを使えます.
However, BSD::Resource also has a limit called RLIMIT_AS (Address Space) which limits the total number of bytes of virtual memory assigned to a process. Happily, Linux's memory manager does honor this limit.

しかしながら, BSD::Resource は RLIMIT_AS (アドレス空間) と呼ばれる制限ももっていてプロセスにアサインされる仮想メモリのバイトの合計数を制限します. 幸い, Linux のメモリマネージャはこの制限を尊重します.
Therefore, you can limit memory usage under Linux with Apache::Resource -- simply add a line to httpd.conf:

したがって, あなたは Apache::Resource で Linux でのメモリ使用量を制限できます -- httpd.conf にシンプルにこの行を追加します:

PerlSetEnv PERL_RLIMIT_AS 67108864

This example sets a hard and soft limit of 64MB of total address space.

この例は合計アドレス空間の 64MB のハードとソフトリミットをセットします.
Refer to the Apache::Resource and setrlimit(2) manpages for more information.

詳細は Apache::Resource と setrlimit(2) の man ページを参照してください.


同じリソースをサーブするプロセスの数の制限 : Limiting the Number of Processes Serving the Same Resource



If you want to limit number of Apache children that could simultaneously be serving the (nearly) same resource, you should take a look at the mod_throttle_access module.

もしあなたが (ほぼ) 同じリソースを同時に提供する Apache child たちの数を制限したいなら, あなたは mod_throttle_access モジュール (# mod_throttle_access の項目は削除済み?) を確認するとよいでしょう.
It solves the problem of too many concurrent request accessing the same URI, if for example the handler that serves this URI uses some resource that has a limitation on the maximum number of possible users or the handlers code is very CPU intensive and you cannot afford more than a certain number of concurrent requests to this specific URI.

それは多すぎる同時リクエストが同じ URI にアクセスする問題を解決します, 例えばこの URI をサーブするハンドラが利用可能なユーザの最大数の制限をもっている場合やそのハンドラのコードがかなり CPU インテンシブ (# 集中する) でこの特定の URI への特定数を超える同時リクエストの余裕がない場合です.
Imagine that your service provides the three following URIs:

あなたのサービスが次の 3 つの URI を提供しているとイメージしてください:

/perl/news/
/perl/webmail/
/perl/morphing/

The first two URIs are response critical as people want to read news and their email. The third URI is very CPU and RAM intensive image morphing service, provided as a bonus to your users. Since you don't want users to abuse this service, you have to set some limits on the number of concurrent requests for this resource, since if you don't--the other two critical resources can be hurt.

最初の 2 つの URI は人がニュースや彼らの email を読みたいのでレスポンスが重要です. 3 つめの URI は CPU と RAM に集中的 (# インテンシブ) なイメージモーフィングサービスで, あなたのユーザへのボーナスとして提供されます. あなたはユーザにこのサービスを悪用させたくないので, あなたはこのリソースへの同時リクエスト数を何らかの制限をセットしなければなりません, もしあなたがそうしないなら -- 他の 2 つの重要なレスポンスを損ないます.
When you compile in and enable the Apache mod_throttle_access module, the MaxConcurrentReqs directive becomes available. For example, the following setting:

あなたが Apache mod_throttle_access モジュールをコンパイルして有効にすると, MaxConcurrentReqs ディレクティブが利用可能になります. 例えば, 次のセッティングは:

<Location "/perl/morphing">
<Limit PUT GET POST>
MaxConcurrentReqs 10
</Limit>
</Location>

will allow only 10 concurrent PUT, GET or POST requests under the URI /perl/morphing to be processed at one time. The other two URIs remain unlimited.

1 度に URI /perl/morphing/ のもとで同時に処理される PUT, GET や POST リクエストは 10 のみになります. 他の 2 つの URI は無制限のままです.


リクエストレート速度の制限 (ロボットブロッキング) : Limiting the Request Rate Speed (Robot Blocking)



A limitation of using pattern matching to identify robots is that it only catches the robots that you know about, and then only those that identify themselves by name. A few devious robots masquerade as users by using user agent strings that identify themselves as conventional browsers. To catch such robots, you'll have to be more sophisticated.

ロボットを識別するためのパターンマッチングを使う限界はあなたが知っているロボットのみ, 名前によって自分自身を識別するもののみをキャッチすることです. いくつか邪なロボットは従来のブラウザとしてそれ自身を識別するユーザエージェント文字列を使うことでユーザになりすまします. そのようなロボットをキャッチするに, あなたはより洗練されなければなりません.
Apache::SpeedLimit comes to your aid, see:

Apache::SpeedLimit はあなたの補助になります, 参照してください:

http://www.modperl.com/chapters/ch6.html#Blocking_Greedy_Clients (# 404)


パフォーマンス改善のための Perl モジュール : Perl Modules for Performance Improvement



These sections are about Perl modules that improve performance without requiring changes to your code. Mostly you just need to tweak the configuration file to plug these modules in.

これらのセクションはあなたのコードの変更を要求することなくパフォーマンスを改善する Perl モジュールについてのものです. ほとんどの場合あなたはこれらのモジュールを差し込むために構成ファイルを微調整するだけです.


圧縮された出力としてプレーンな HTML を送信する : Sending Plain HTML as Compressed Output



See Apache::GzipChain - compress HTML (or anything) in the OutputChain

Apache::GzipChain - 出力チェーンで HTML (その他) を圧縮する を参照してください


HTML::Mason でのコンポーネントのキャッシュ : Caching Components with HTML::Mason



META: complete the full description

META: 完全な説明を完成する
HTML::Mason is a system that makes use of components to build HTML pages.

HTML::Mason は HTML ページをビルドするためにコンポーネントを使うシステムです.
If most of your output is generated dynamically, but each finished page can be separated into different components, HTML::Mason can cache those components. This can really improve the performance of your service and reduce the load on the system.

もしあなたの出力の大部分が動的に生成されているが, 完了したページが異なるコンポーネントに分割できるなら, HTML::Mason はそれらのコンポーネントをキャッシュできます. これはあなたのサービスのパフォーマンスを本当に改善しシステムの負荷を削減します.
Say for example that you have a page consisting of five components, each generated by a different SQL query, but for four of the five components it's the same four queries for each user so you don't have to rerun them again and again. Only one component is generated by a unique query and will not use the cache.

例えばあなたがそれぞれ異なる SQL クエリによって生成される, 5 つのコンポーネントで構成されるページをもっているとします, しかし 5 つのコンポーネントの 4 つは各ユーザごとの同じ 4 つのクエリなのであなたはそれらを何度も再実行するする必要はありません. 一意のクエリによってただひとつのコンポーネントが生成されキャッシュは使われないでしょう.
META: HTML::Mason docs (v 8.0) said Mason was 2-3 times slower than pure mod_perl, implying that the power & convenience made up for this.

META: HTML::Mason ドキュメント (v 8.0) は Mason はピュアな mod_perl よりも 2 - 3 倍遅いといっていて, そのパワーと利便性がこれを補っていることを意味します.
META: Should also mention Embperl (especially since its C + XS)

META: Embperl にも言及したほうがよい (それが C + XS なので)


mod_perl のもとでのデータベースの効果的な作業 : Efficient Work with Databases under mod_perl



Most of the mod_perl enabled servers work with database engines, so in this section we will learn about two things: how mod_perl makes working with databases faster and a few tips for a more efficient DBI coding in Perl. (DBI provides an identical Perl interface to many database implementations.)

ほとんどの mod_perl が有効化されたサーバはデータベースエンジンと共に動作します, ですからこのセクションで私たちは 2 つのことについて学びます: mod_perl がどのようにしてデータベースとの作業を高速にするかと Perl でより効果的な DBI コーディングをするためのいくつかのチップスです. (DBI は多くのデータベース実装への同一の Perl インターフェイスを提供します.)


永続的な DB 接続 : Persistent DB Connections



Another popular use of mod_perl is to take advantage of its ability to maintain persistent open database connections.

mod_perl のもうひとつのポピュラーな使用法はオープンしたデータベースコネクションを永続的に維持する能力のアドバンテージをとることです.
You want to have a persistent database connection because the most expensive part of a network transaction for most databases is the business of building and tearing down connections.

ほとんどのデータベースのネットワークトランザクションの最も高価なパートが接続の構築と破壊に従事することなのであなたは永続的なデータベースコネクションをもちたいはずです.
Of course the persistence doesn't help with the latency problems during the actual use of the database connections. Oracle is notoriously latency-sensitive which in most cases generates a network transaction per row returned which slows things down if the query execution matches many rows. You may want to read the Tim Bunce's Advanced DBI talk at http://dbi.perl.org/doc/conferences/tim_1999/index.html which covers a lot of techniques to reduce latency.

もちろん永続性はデータベースコネクションを実際に使用する間のレイテンシ問題では助けになりません. Oracle はレイテンシセンシティブ (# レイテンシの影響を受けやすい) で悪名高くほとんどのケースでリターンされる行ごとにネットワークトランザクションを生成するのでもしクエリの実行が多くの行にマッチするとものごとを遅くします. あなたはレイテンシを削減するための多くのテクニックをカバーする Tim Bunce の Advanced DBI talk http://dbi.perl.org/doc/conferences/tim_1999/index.html (# 404) を読むとよいでしょう.
So here is the basic approach of making the connection persistent:

ですからコネクションを永続化する基本的なアプローチはこうなります:

# Apache::Registry script
-------------------------
use strict;
use vars qw($dbh);

$dbh ||= SomeDbPackage->connect(...);

Since $dbh is a global variable for the child, once the child has opened the connection it will use it over and over again, unless you perform disconnect().

$dnh は child のためのグローバル変数なので, child がオープンされたコネクションをもつとそれを繰り返し使います, あなたが disconnect() を実行しない限り.
Be careful to use different names for handlers if you open connections to different databases!

もしあなたが異なるデータベースへのコネクションをオープンする場合はハンドラに異なる名前を使うように注意してください !
Apache::DBI allows you to make a persistent database connection. With this module enabled, every connect() request to the plain DBI module will be forwarded to the Apache::DBI module. This looks to see whether a database handle from a previous connect() request has already been opened, and if this handle is still valid using the ping method. If these two conditions are fulfilled it just returns the database handle. If there is no appropriate database handle or if the ping method fails, a new connection is established and the handle is stored for later re-use. There is no need to delete the disconnect() statements from your code. They will not do anything, the Apache::DBI module overloads the disconnect() method with a NOP. When a child exits there is no explicit disconnect, the child dies and so does the database connection. You may leave the use DBI; statement inside the scripts as well.

Apache::DBI はあなたが永続的なデータベースコネクションを作成できるようにします. このモジュールを有効化すると, プレーン DBI モジュールへのすべての connect() リクエストは Apache::DBI モジュールに転送されます. これは前の connect() リクエストからのデータベースハンドルがすでに開かれているかどうか* および ping メソッドでこのハンドルがまだ有効かを確認します. もしこれら 2 つの条件が満たされるとたんにデータベースハンドルをリターンします. 適切なデータベースハンドルがないか ping メソッドが失敗した場合, 新しいコネクションが確立されてそのハンドルが後で再利用するために格納されます. あなたのコードから disconnect() ステートメントを削除する必要はありません. それらは何もしないでしょう, Apache::DBI モジュールは disconnect() メソッドを NOP (# No Operation: 何もしない) でオーバーロードします. child が終了すると明示的な disconnect はなく, その child は die しますのでデータベースコネクションもそうなります. あなたはスクリプト内の use DBI; ステートメントも同様にそのままにしておけます.
The usage is simple -- add to httpd.conf:

使用方法はシンプルです -- httpd.conf にこれを追加します:

PerlModule Apache::DBI

It is important to load this module before any other DBI, DBD::* and ApacheDBI* modules!

他の DBI, DBD::* それから ApacheDBI* の前にこのモジュールをロードすることが重要です !

db.pl
-------------
use DBI ();
use strict;

my $dbh = DBI->connect( 'DBI:mysql:database', 'user', 'password',
{ autocommit => 0 }
) || die $DBI::errstr;

...rest of the program



Child プロセスフォーク時での接続のプレオープン : Preopening Connections at the Child Process'Fork Time



If you use DBI for DB connections, and you use Apache::DBI to make them persistent, it also allows you to preopen connections to the DB for each child with the connect_on_init() method, thus saving a connection overhead on the very first request of every child.

もしあなたが DB コネクションのために DBI を使い, それらを永続的にするためにあなたが Apache::DBI を使うなら, あなたは connect_on_init() メソッドで child ごとに DB へのコネクションをプレオープンすることもできるので, すべての child の最初のリクエストでコネクションのオーバーヘッドを節約します.

use Apache::DBI ();
Apache::DBI->connect_on_init("DBI:mysql:test",
"login",
"passwd",
{
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
}
);

This is a simple way to have Apache children establish connections on server startup. This call should be in a startup file require()d by PerlRequire or inside a <Perl> section. It will establish a connection when a child is started in that child process. See the Apache::DBI manpage for the requirements for this method.

これは Apache child たちがサーバのスタートアップでコネクションを確立するシンプルな方法です. このコールはPerlRequire により require() されたスタートアップファイルか <Perl> の内部である必要があります. これは child プロセスで child がスタートされたときにコネクションを確立します. このメソッドの要件は Apache::DBI man ページを参照してください.


prepare() ステートメントのキャッシュ : Caching prepare() Statements



You can also benefit from persistent connections by replacing prepare() with prepare_cached(). That way you will always be sure that you have a good statement handle and you will get some caching benefit. The downside is that you are going to pay for DBI to parse your SQL and do a cache lookup every time you call prepare_cached().

あなたは prepare() を prepare_cached() でリプレイスすることで永続的コネクションからの恩恵をうけることもできます. それであなたはあなたがグッドなステートメントハンドルをもっていることを常に確認できてあなたはキャッシュの恩恵をゲットします. そのマイナス面はあなたの SQL を解析してあなたが prepare_cached() をコールするたびにキャッシュ検索を行うためのツケをあなたが DBI に支払うことです.
Be warned that some databases (e.g PostgreSQL and Sybase) don't support caches of prepared plans. With Sybase you could open multiple connections to achieve the same result, although this is at the risk of getting deadlocks depending on what you are trying to do!

一部のデータベース (e.g PostgreSQL や Sybase) が準備されたプラン (# prepared plans) のキャッシュをサポートしていないことに注意してください. Sybase であなたは複数のコネクションをオープンして同じ結果を成すことができますが, これはあなたがトライすることによってはデッドロックが発生するリスクがあります !


mod_perl データベースパフォーマンスの改善 : mod_perl Databese Performance Improving




問題の分析 : Analysis of the Problem



A common web application architecture is one or more application servers which handle requests from client browsers by consulting one or more database servers and performing a transform on the data. When an application must consult the database on every request, the interaction with the database server becomes the central performance issue. Spending a bit of time optimizing your database access can result in significant application performance improvements. In this analysis, a system using Apache, mod_perl, DBI, and Oracle will be considered. The application server uses Apache and mod_perl to service client requests, and DBI to communicate with a remote Oracle database.

一般的な web アプリケーションアーキテクチャはひとつ以上のデータベースを参照してデータの変換を実行することでクライアントブラウザからのリクエストを処理するひとつ以上のアプリケーションサーバです. アプリケーションがすべてのリクエストでデータベースを参照しなければならないとき, そのデータベースとのインタラクション (# 相互作用) が中心的なパフォーマンスの課題になります. あなたのデータベースアクセスの最適化に少しの時間を費やすと大幅なアプリケーションパフォーマンスの改善につながるかもしれません. この分析では, Apache, mod_perl, DBI と Oracle を使うシステムが考慮されます. そのアプリケーションサーバはクライアントリクエストをサービスするために Apache と mod_perl を, リモートの Oracle データベースとの通信に DBI を使います.
In the course of servicing a typical client request, the application server must retrieve some data from the database and execute a stored procedure. There are several steps that need to be performed to complete the request:

典型的なクライアントリクエストをサービスする過程で, このアプリケーションサーバはデータベースからいくつかのデータを取得してストアドプロシージャ (# 格納された処理手順) を実効しなければなりません. リクエストの完了のために実効する必要があるいくかのステップがあります:

1: データベースサーバへ接続する (Connect to the database server)
2: SQL SELECT 文を準備する (Prepare a SQL SELECT statement)
3: その SELECT 文を実行する (Execute the SELECT statement)
4: その SELECT 文の結果を取得する (Retrieve the results of the SELECT statement)
5: その SELECT 文のハンドルをリリースする (Release the SELECT statement handle)
6: PL/SQL ストアドプロシージャのコールを準備する (Prepare a PL/SQL stored procedure call)
7: そのストアドプロシージャを実行する (Execute the stored procedure)
8: そのストアドプロシージャ文のハンドルをリリースする (Release the stored procedure statement handle)
9: コミットまたはロールバック (Commit or rollback)
10: そのデータベースサーバから切断する (Disconnect from the database server)

In this document, an application will be described which achieves maximum performance by eliminating some of the steps above and optimizing others.

このドキュメントでは, 上記ステップの一部ステップを除去して他を最適化することで最大パフォーマンスを実現するアプリケーションが説明されます.


データベースサーバのキャッシュの最適化 : Optimizing Database Server's Cache



A naive implementation would perform steps 1 through 10 from above on every request. A portion of the source code might look like this:

素朴な実装はすべてのリクエストで上記 1 から 10 のステップを実行します. ソースコードの一部はこのようになっているはずです:

# ...
my $dbh = DBI->connect('dbi:Oracle:host', 'user', 'pass')
|| die $DBI::errstr;

my $baz = $r->param('baz');

eval {
my $sth = $dbh->prepare(qq{
SELECT foo
From bar
WHERE baz = $baz
});
$sth->execute;

while (my @row = $sth->fetchrow_array) {
# do HTML stuff
}

$sth->finish;

my $sph = $dbh->prepare(qq{
BEGIN
my_procedure(
arg_in => $baz
);
END;
});
$sph->execute;
$sph->finish;

$dbh->commit;
};
if ($@) {
$dbh->rollback;
}

$dbh->disconnect;
# ...

In practice, such an implementation would have hideous performance problems. The majority of the execution time of this program would likely be spent connecting to the database. An examination shows that step 1 is comprised of many smaller steps:

実際には, このような実装はぞっとするパフォーマンスの問題をもちます. このプログラムの実行時間の大半はデータベースへの接続に費やされることになるでしょう. 調査はステップ 1 が多くの小さなステップで構成されていることを示します:

1: データベースサーバに接続する (Connect to the database server)
1a: Oracle 接続のためのクライアント側データ構造をビルド (Build client-side data structures for an Oracle connection)
1b: ファイル内でサーバのエイリアスを探す (Look up the server's alias in a file)
1c: サーバのホスト名を探す (Look up the server's hostname)
1d: サーバへのソケットをビルド (Build a socket to the server)
1e: この接続のためのサーバ側データ構造をビルド (Build server-side data structures for this connection)

The naive implementation waits for all of these steps to happen, and then throws away the database connection when it is done! This is obviously wasteful, and easily rectified. The best solution is to hoist the database connection step out of the per-request lifecycle so that more than one request can use the same database connection. This can be done by connecting to the database server once, and then not disconnecting until the Apache child process exits. The Apache::DBI module does this transparently and automatically with little effort on the part of the programmer.

この素朴な実装はこれらすべてのステップが発生するのを待ってから, それが完了するとそのデータベースへのコネクションを破棄します ! これは明らかに無駄で, 簡単に修正できます. ベストなソリューションはリクエストライフサイクルごとのこのデータベース接続ステップを巻き上げてひとつ以上のリクエストが同じデータベースコネクションを使えるようにすることです. これはデータベースサーバに一度接続して, Apache child プロセスが終了するまで切断しないことで行えます. Apache::DBI モジュールはプログラマ側の少しの努力でこれを透過的かつ自動的に行います.
Apache::DBI intercepts calls to DBI's connect and disconnect methods and replaces them with its own. Apache::DBI caches database connections when they are first opened, and it ignores disconnect commands. When an application tries to connect to the same database, Apache::DBI returns a cached connection, thus saving the significant time penalty of repeatedly connecting to the database. You will find a full treatment of Apache::DBI at Persistent DB Connections

Apache::DBI は DBI の接続と切断メソッドをインターセプトしてそれらを独自のものでリプレイスします. Apache::DBI はデータベース接続をそれが最初にオープンされたときにキャッシュして, 切断コマンドを無視します. アプリケーションが同じデータベースへの接続をトライすると, Apache::DBI はキャッシュされた接続をリターンするので, そのデータベースへ繰り返し接続する大幅な時間のペナルティを節約します. あなたは Persistent DB Connections で Apache::DBI の完全な取り扱いを見つけられます
When Apache::DBI is in use, none of the code in the example needs to change. The code is upgraded from naive to respectable with the use of a simple module! The first and biggest database performance problem is quickly dispensed with.

Apache::DBI を使う場合, 例のコードを変更する必要はありません. そのコードはシンプルなモジュールの使用で素朴から立派へアップグレードされます ! 最初かつ最大のデータベースパフォーマンスの問題はこれですぐに解消されます.


データベースサーバのキャッシュを活用する : Utilizing the Database Server's Cache



Most database servers, including Oracle, utilize a cache to improve the performance of recently seen queries. The cache is keyed on the SQL statement. If a statement is identical to a previously seen statement, the execution plan for the previous statement is reused. This can be a considerable improvement over building a new statement execution plan.

Oracle を含む, 多くのデータベースサーバは, 最近みたクエリのパフォーマンスを向上させるためにキャッシュを活用します. そのキャッシュは SQL ステートメントが key とされます. もしステートメントが前に見たステートメントと同一なら, 前のステートメントのための実行プランが再利用されます. これは新しいステートメント実行プランを構築するよりもかなりの改善になりえます.
Our respectable implementation from the last section is not making use of this caching ability. It is preparing the statement:

前のセクションの私たちの立派な実装はこのキャッシュ機能を使うようになっていません. それはステートメントを準備しています:

SELECT foo FROM bar WHERE baz = $baz

The problem is that $baz is being read from an HTML form, and is therefore likely to change on every request. When the database server sees this statement, it is going to look like:

問題は $baz が HTML フォームから読まれているため, リクエスト毎に変更になる可能性があることです. データベースサーバがこのステートメントをみると, それはこのようになり:

SELECT foo FROM bar WHERE baz = 1

and on the next request, the SQL will be:

そして次のリクエストで, この SQL はこうなります:

SELECT foo FROM bar WHERE baz = 42

Since the statements are different, the database server will not be able to reuse its execution plan, and will proceed to make another one. This defeats the purpose of the SQL statement cache.

ステートメントが違うので, データベースサーバはその実行プランの再利用ができず, 別のそれの作成に進みます. これは SQL ステートメントキャッシュの目的をダメにします.
The application server needs to make sure that SQL statements which are the same look the same. The way to achieve this is to use placeholders and bound parameters. The placeholder is a blank in the SQL statement, which tells the database server that the value will be filled in later. The bound parameter is the value which is inserted into the blank before the statement is executed.

アプリケーションは SQL ステートメントが同じようにみえることを確かめる必要があります. これを成すための方法はプレースホルダとバインドされたパラメータを使うことです. プレースホルダは SQL ステートメント内の空白で, その値が後で詰められることをデータベースサーバに伝えます. バインドされたパラメータはステートメントが実行される前に空白に挿入される値です.
With placeholders, the SQL statement looks like:

プレースホルダでは, SQL ステートメントはこのようにみえます:

SELECT foo FROM bar WHERE baz = :baz

Regardless of whether baz is 1 or 42, the SQL always looks the same, and the database server can reuse its cached execution plan for this statement. This technique has eliminated the execution plan generation penalty from the per-request runtime. The potential performance improvement from this optimization could range from modest to very significant.

baz が 1 か 42 かとは関係なく, SQL は常に同じにみえて, データベースサーバはこのステートメントのためにキャッシュされた実行プランを再利用できます. このテクニックでリクエストごとのランタイムの実行プランの生成ペナルティが排除されます. この最適化からの潜在的なパフォーマンス改善は控えめなものからとても重大なものにおよびます.
Here is the updated code fragment which employs this optimization:

こちらがこの最適化を採用したアップデートされたコードの断片です:

# ...
my $dbh = DBI->connect('dbi:Oracle:host', 'user', 'pass')
|| die $DBI::errstr;

my $baz = $r->param('baz');

eval {
my $sth = $dbh->prepare(qq{
SELECT foo
FROM bar
WHERE baz = :baz
});
$sth->bind_param(':baz', $baz);
$sth->execute;

while (my @row = $sth->fetchrow_array) {
# do HTML stuff
}

$sth->finish;

my $sph = $dbh->prepare(qq{
BEGIN
my_procedure(
arg_in => :baz
);
END;
});
$sph->bind_param(':baz', $baz);
$sph->execute;
$sph->finish;

$dbh->commit;
};
if ($@) {
$dbh->rollback;
}
# ...



SQL ステートメント解析の除去 : Eliminating SQL Statement Parsing



The example program has certainly come a long way and the performance is now probably much better than that of the first revision. However, there is still more speed that can be wrung out of this server architecture. The last bottleneck is in SQL statement parsing. Every time DBI's prepare() method is called, DBI parses the SQL command looking for placeholder strings, and does some housekeeping work. Worse, a context has to be built on the client and server sides of the connection which the database will use to refer to the statement. These things take time, and by eliminating these steps the time can be saved.

この例のプログラムは確実に長い道のりをへていてそのパフォーマンスは現在おそらくその最初のバージョンよりもはるかにベターです. しかしながら, このサーバアーキテクチャから絞りだせる速度がまださらにあります. 最後のボトルネックは SQL ステートメントの解析です. DBI の prepare() メソッドはコールされるたびに, DBI はその SQL コマンドを解析しプレースホルダ文字列を探して, いくつかハウスキーピング (# 準備) 作業を行います. さらに悪いことに, そのデータベースがステートメントを参照するために使うコンテキストをクライアントとサーバ側でビルドしなければなりません. これらは時間がかかります, そしてこれらのステップを除去することで時間は節約されます.
To get rid of the statement handle construction and statement parsing penalties, we could use DBI's prepare_cached() method. This method compares the SQL statement to others that have already been executed. If there is a match, the cached statement handle is returned. But the application server is still spending time calling an object method (very expensive in Perl), and doing a hash lookup. Both of these steps are unnecessary, since the SQL is very likely to be static and known at compile time. The smart programmer can take advantage of these two attributes to gain better database performance. In this example, the database statements will be prepared immediately after the connection to the database is made, and they will be cached in package scalars to eliminate the method call.

ステートメントハンドルの構築とステートメント解析のペナルティを取り除くために, 私たちは DBI の prepare_cached() メソッドを使えます. このメソッドは SQL ステートメントをすでに実行された他のものと比較します. もしマッチするものがあれば, そのキャッシュされたステートメントハンドルが返されます. しかしアプリケーションサーバはオブジェクトメソッドのコール (Perl ではとても高価です) と, ハッシュルックアップを行うことにまだ時間を費やします. これらのステップはいずれも不必要です, その SQL は静的でコンパイルタイムですでに知られている可能性が高いからです. スマートなプログラマはベターなデータベースパフォーマンスをえるためにこれらの 2 つの特性を利用できます. この例では, データベースステートメントはデータベースへのコネクションが確立した後すぐに準備され, それらはメソッドコールを除去するためにパッケージのスカラにキャッシュされます.
What is needed is a routine that will connect to the database and prepare the statements. Since the statements are dependent upon the connection, the integrity of the connection needs to be checked before using the statements, and a reconnection should be attempted if needed. Since the routine presented here does everything that Apache::DBI does, it does not use Apache::DBI and therefore has the added benefit of eliminating a cache lookup on the connection.

必要なのはデータベースにコネクトしてステートメントを準備するルーチンです. ステートメントはそのコネクションに依存しているため, そのステートメントを使う前にコネクションの整合性をチェックし, 必要に応じて再接続を試みる必要があります. ここに示したルーチンは Apache::DBI が行うすべてを行い, Apache::DBI を使用しませんのでコネクションでのキャッシュルックアップを除去する追加の利点をもちます.
Here is an example of such a package:

こちらがそのようなパッケージの例です:

package My::DB;

use strict;
use DBI ();

sub connect {
if (defined $My::DB::conn) {
eval {
$My::DB::conn->ping;
};
if (!$@) {
return $My::DB::conn;
}
}

$My::DB::conn = DBI->connect (
'dbi:Oracle:server', 'user', 'pass', {
PrintError => 1,
RaiseError => 1,
AutoCommit => 0
}
) || die $DBI::errstr; # アプリケーションがこれを処理すると仮定 (Assume application handles this)

$My::DB::select = $My::DB::conn->prepare(q{
SELECT foo
FROM bar
WHERE baz = :baz
});

$My::DB::procedure = $My::DB::conn->prepare(q{
BEGIN
my_procedure (
arg_in => :baz
);
END;
});

return $My::DB::conn;
}

1;

Now the example program needs to be modified to use this package.

そして例のプログラムをこのパッケージを使うように変更する必要があります.

# ...
my $dbh = My::DB->connect;

my $baz = $r->param('baz');

eval {
my $sth = $My::DB::select;
$sth->bind_param(':baz', $baz);
$sth->execute;

while (my @row = $sth->fetchrow_array) {
# do HTML stuff
}

my $sph = My::DB::procedure;
$sph->bind_param(':baz', $baz);
$sph->execute;

$dbh->commit;
};
if ($@) {
$dbh->rollback;
}
# ...

Notice that several improvements have been made. Since the statement handles have a longer life than the request, there is no need for each request to prepare the statement, and no need to call the statement handle's finish method. Since Apache::DBI and the prepare_cached() method are not used, no cache lookups are needed.

いくつか改善が行われたことに注目してください. ステートメントハンドルがリクエストよりも長いライフをもっているので, リクエストごとにそのステートメントを準備する必要がなく, ステートメントハンドルの finish メソッドをコールする必要がありません. Apache::DBI と parepare_cached() メソッドは使われていないので, キャッシュルックアップが必要とされません.


結論 : Conclusion



The number of steps needed to service the request in the example system has been reduced significantly. In addition, the hidden cost of building and tearing down statement handles and of creating query execution plans is removed. Compare the new sequence with the original:

例のシステムでリクエストをサービスするために必要とされたステップの数が大幅に削減されました. 加えて, ステートメントハンドルの構築と破棄およびクエリ実行プランの隠れたコストが削除されます. 新しいシーケンスをオリジナルと比較します:

1: データベースへの接続をチェック (Check connection to database)
2: SQL SELECT ステートメントにパラメータをバインド (Bind parameter to SQL SELECT statement)
3: SELECT ステートメントを実行 (Execute SELECT statement)
4: 行を取得 (Fetch rows)
5: PL/SQL ストアドプロシージャに パラメータをバインド (Bind parameters to PL/SQL stored procedure)
6: PL/SQL ストアドプロシージャを実行 (Execute PL/SQL stored procedure)
7: コミットまたはロールバック (Commit or rollback)

It is probably possible to optimize this example even further, but I have not tried. It is very likely that the time could be better spent improving your database indexing scheme or web server buffering and load balancing.

この例をさらにもっと最適化するのはおそらく可能ですが, 私はトライしていません. その時間をあなたのデータベースのインデクス作成スキームや web サーバのバッファリングとロードバランシングに費やすことがよりベターなはずです.


3rd パーティアプリケーションを使う : Using 3rd Party Applications



It's been said that no one can do everything well, but one can do something specific extremely well. This seems to be true for many software applications, when you don't try to do everything but instead concentrate on something specific you can do it really well.

すべてをうまく行える人はいないといわれますが, 特定の何かを非常にうまくやることはできます. 多くのソフトウェアアプリケーションでこれは真実のように見えます, あなたがすべてを行うことにトライせず代わりに何か特定のことに集中するときあなたはそれを本当にうまく行うことができます.
Based on the above introduction, while the mod_perl server can do many many things, there are other applications (or Apache server modules) that can do some specific operations faster or do a really great job for the mod_perl server by unloading it when doing some operations by themselves.

上記のイントロダクションに基づいて, mod_perl サーバはとても多くのことを行える一方で, それ自身が何らかのオペレーションを行うときに mod_perl サーバをアンロードすることで何か特定のオペレーションをより高速に行ったり本当にグレートな仕事をする他のアプリケーション (または Apache サーバモジュール) があります.
Let's take a look at a few of these.

それらのいくつかをみてみましょう.

mod_perl サーバをプロキシする : Proxying the mod_perl Server



Proxy gives you a great performance increase in most cases. It's discussed in the section Adding a Proxy Server in http Accelerator Mode.

プロキシはほとんどのケースでグレートなパフォーマンス向上をあなたに与えます. これは Adding a Proxy Server in http Accelerator Mode セクションで論じられています.


大きなファイルのアップロードとダウンロード : Upload and Download of Big Files



You don't want to tie up your precious mod_perl backend server children doing something as long and simple as transferring a file, especially a big one. The overhead saved by mod_perl is typically under one second, which is an enormous saving for the scripts whose run time is under one second. The user won't really see any important performance benefits from mod_perl, since the upload may take up to several minutes.

あなたはあなたの貴重な mod_perl サーババックエンドの child たちをファイル, 特に大きなそれの転送のような長くて単純な何かを行うことに縛ることを望まないはずです. mod_perl によって節約されるこのオーバーヘッドは典型的には 1 秒未満で, それはランタイムが 1 秒未満のスクリプトにとっては大幅な節約です. ユーザはその mod_perl からの重要なパフォーマンスの利点を実際に見ることはないはずです, アップロードは数分かかる場合があるためです.
If some particular script's main functionality is the uploading or downloading of big files, you probably want it to be executed on a plain apache server under mod_cgi (i.e. performing this operation on the front-end server, if you use a dual-server setup.

もし何か特定のスクリプトのメイン機能が大きなファイルのアップロードやダウンロードの場合, あなたはおそらくそれを mod_cgi の元にあるプレーンな apache サーバで実行するはずです (i.e. フロントエンドサーバでこの操作を実行する, もしあなたがデュアル構成のサーバを使っている場合.
This of course assumes that the script requires none of the functionality of the mod_perl server, such as custom authentication handlers.

もちろんこれはそのスクリプトがカスタム認証ハンドラのような, mod_perl サーバの機能を必要としないことを想定しています.


Apache/mod_perl のビルドオプション : Apache/mod_perl Build Options



It's important how you build mod_perl enabled Apache. It influences the size of the httpd executable, some irrelevant modules might slow the performance.

あなたが mod_perl を有効化した Apache をどのようにビルドするかは重要です. それは httpd 実行可能ファイルのサイズに影響し, 何らか無関係なモジュールがそのパフォーマンスを遅くするかもしれません.
[ReaderMETA: Any other building time things that influence performance?]

[読者 META: 他にビルディングタイムでパフォーマンスに影響するものはある ?]


C モジュールでコンパイルされたファンクションとしての mod_perl のプロセスサイズと mod_perl の特徴 : mod_perl Process Size as a Function of Compiled in C Modules and mod_perl Features



You might wonder whether it's better to compile in only the required modules and mod_perl hooks, or it doesn't really matter. To answer on this question lets first make a few compilation and compare the results.

必要とされるモジュールと mod_perl フックでのみコンパイルしたほうがベターなのかそれともそれは問題ではないのかをあなたは疑問に思うかもしれません. この疑問の答えのためにまずいくつかのコンパイルを行ってその結果を比較してみましょう.
So we are going to build mod_perl starting with:

さて私たちはこれで mod_perl のビルドを始めて:

% perl Makefile.PL APACHE_SRC=../apache_x.x.x/src \
DO_HTTPD=1 USE_APACI=1

and followed by one of these option groups:

それからこれらオプショングループのひとつが続きます:

  1. Default (デフォルト)
    no arguments

  2. Minimum (最小)

    APACI_ARGS='--disable-module=env, \
    --disable-module=negotiation, \
    --disable-module=status, \
    --disable-module=info, \
    --disable-module=include, \
    --disable-module=autoindex, \
    --disable-module=dir, \
    --disable-module=cgi, \
    --disable-module=asis, \
    --disable-module=imap, \
    --disable-module=userdir, \
    --disable-module=access, \
    --disable-module=auth'

  3. Everything (全て)

    EVERYTHING=1

  4. Everything + Debug (全て + デバッグ)

    EVERYTHING=1 PERL_DEBUG=1


After re-compiling with arguments of each of these groups, we can summarize the results:

これらのグループをそれぞれの引数で再コンパイルした後で, 私たちはこの結果を要約できます:

Build group httpd size (bytes) Difference
---------------------------------------------
Minimum 892928 + 0
Default 994316 +101388
Everything 1044432 +151504
Everything+Debug 1162100 +269172

Indeed when you strip most of the default things, the server size is slimmer. But the savings are insignificant since you don't multiply the added size by the number of child processes if your OS supports sharing memory. The parent processes is a little bigger, but it shares these memory pages with its child processes. Of course not everything will be shared, if some module you add does some process memory modification particular to the process, but the most will.

実際にはあなたがデフォルトのほとんどのものを取り除くと, そのサーバのサイズはよりスリムになります. しかしあなたの OS が共有メモリをサポートしている場合あなたは child プロセスの数によって追加されたサイズを乗算しないためこの節約は重要ではありません. その parent プロセスは少し大きいですが, それらのメモリページはその child プロセスと共有します. もちろんすべてがそうではありませんが, あなたが追加する何かのモジュールがそのプロセス固有の何かのプロセスメモリを変更するなら, ほとんどは共有されます.
And of course this was just an example to show the difference is size. It doesn't mean that you can everything away, since there will be Apache modules and mod_perl options that you won't be able to work without.

そしてもちろんこれはサイズが違うことを示すための単なる例です. これはあなたがすべてを取っ払えることを意味しません, あなたがそれなしでは作業できない Apache モジュールや mod_perl オプションがあるからです.
But as a good system administrator's rule says: "Run the absolute minimum of the applications. If you don't know or need something, disable it". Following this rule to decide on the required Apache components and disabling the unneeded default components, makes you a good Apache administrator.

しかしグッドなシステム管理者のルールはこう言います: "絶対的に最小のアプリケーションを実行する. もしあなたが知らないか必要でない場合は, それを無効化する". 必要な Apache コンポーネントと不必要なデフォルトコンポーネントを決めるためにこのルールに従うことで, あなたはグッドな Apache 管理者になります.


Perl ビルドオプション : Perl Build Options



The Perl interpreter lays in the brain of the mod_perl server and if we can optimize perl into doing things faster under mod_perl we make the whole server faster. Generally, optimizing the Perl interpreter means enabling or disabling some command line options. Let's see a few important ones.

Perl インタプリタは mod_perl サーバの頭脳にあってもし私たちが mod_perl のもとで高速にものごとを行うように perl を最適化できれば私たちはサーバ全体を高速にできます. 通常, Perl インタプリタの最適化は何らかのコマンドラインオプションを有効化したり無効化することを意味します. いくつか重要なものを見てみましょう.


-DTWO_POT_OPTIMIZE and -DPACK_MALLOC Perl Build Options



Newer Perl versions also have build time options to reduce runtime memory consumption. These options might shrink the size of your httpd by about 150k -- quite a big number if you remember to multiply it by the number of children you use.

新しい Perl バージョンはランタイムのメモリ消費を削減するビルド時のオプションももっています. それらのオプションはあなたの httpd のサイズを約 150k 縮小するかもしれません -- あなたが使う child たちの数にそれを乗算することをあなたが思い出せればかなり大きな数字だとわかります.
The -DTWO_POT_OPTIMIZE macro improves allocations of data with size close to a power of two; but this works for big allocations (starting with 16K by default). Such allocations are typical for big hashes and special-purpose scripts, especially image processing.

-DTWO_POT_OPTIMIZE マクロは 2 の累乗に近いサイズのデータ割り当てを改善します; しかしこれは大きな割り当て (デフォルトが 16k でスタートする) で機能します. そうした割当ては典型的には大きなハッシュや特殊な目的のスクリプト, 特にイメージ処理です.
Perl memory allocation is by bucket with sizes close to powers of two. Because of these the malloc() overhead may be big, especially for data of size exactly a power of two. If PACK_MALLOC is defined, perl uses a slightly different algorithm for small allocations (up to 64 bytes long), which makes it possible to have overhead down to 1 byte for allocations which are powers of two (and appear quite often).

Perl メモリアロケーション (# 割り当て) は 2 の累乗に近いサイズでのバケットです. そのため malloc() オーバーヘッドが大きくなるかもしれません, 特に正確に 2 の累乗のサイズのデータでは. もし PACK_MALLOC が定義されている場合は, perl は小さな割り当て (最大 64 バイト長) のためにわずかに異なるアルゴリズムを使います, それは 2 の累乗 (そして頻繁に発生する) の割り当てのためのオーバーヘッドを 1 バイトまで下げることが可能になります.
Expected memory savings (with 8-byte alignment in alignbytes) is about 20% for typical Perl usage. Expected slowdown due to additional malloc() overhead is in fractions of a percent and hard to measure, because of the effect of saved memory on speed.

予想されるメモリの節約 (alignbytes での 8 バイトアラインメントでの) は典型的な Perl の使用法で約 20% です. 予想される追加の malloc() オーバーヘッドに起因する速度低下はわずかなパーセントで測定が困難です, 保存されたメモリの影響が速度にあるからです.
You will find these and other memory improvement details in perl5004delta.pod.

あなたはこれらおよび他のメモリ改善の詳細を perl5004delta.pod で見つけられます.
Important: both options are On by default in perl versions 5.005 and higher.

重要: perl version 5.005 以降で両方のオプションはデフォルトで On です.


-Dusemymalloc Perl Build Option



You have a choice to use the native or Perl's own malloc() implementation. The choice depends on your Operating System. Unless you know which of the two is better on yours, you better try both and compare the benchmarks.

あなたはネイティブまたは Perl 独自の malloc() 実装の利用を選択できます. この選択はあなたのオペレーティングシステムに依存します. あなたが 2 つのどちらがベターなのかを知らないなら, あなたはベンチマークで両方の比較をトライするとベターです.
To build without Perl's malloc(), you can use the Configure command:

Perl の malloc() を除外してビルドするために, あなたはこの構成コマンドを使えます:

% sh Configure -Uusemymalloc

Note that:

これに留意してください:

-U == undefine usemymalloc (use system malloc)
-D == define usemymalloc (use Perl's malloc)

It seems that Linux still defaults to system malloc so you might want to configure Perl with -Dusemymalloc. Perl's malloc is not much of a win under linux, but makes a huge difference under Solaris.

Linux はまだシステムの malloc を使うデフォルトのように見えるのであなたは -Dusemymalloc で Perl を構成したほうがよいかもしれません. Perl の malloc は linux のもとではそれほどではありませんが, Solaris のもとでは大きな違いを作ります.


アーキテクチャ固有のコンパイルオプション : Architecture Specific Compile Options



When you build Apache and Perl you can optimize the compiled applications to take the benefits of your machine's architecture.

あなたが Apache と Perl をビルドするときあなたはあなたのマシンのアーキテクチャの利点をえるためにコンパイルされたアプリケーションの最適化ができます.
Everything depends on the kind of compiler that you use, the kind of CPU and

すべてはあなたが使うコンパイラの種類, CPU の種類に依存します
For example if you use gcc(1) you might want to use:

例えばもしあなたが gcc(1) を使っている場合あなたはこれを使えるはずです:

  • -march=pentium if you have a pentium CPU

    -march=pentium あなたが pentium CPU をもっている場合

  • -march=pentiumpro for pentiumpro and above (but the binary won't run on i386)

    pentiumpro 以上では -march=pentiumpro (しかし i386 上でバイナリは動作しない)

  • -fomit-frame-pointer makes extra register available but disables debugging

    -fomit-frame-pointer は追加のレジスタを利用可能にするがデバッギングを無効にする

  • you can try these options were reported to improve the performance: -ffast-math, -malign-double, -funroll-all-loops, -fno-rtti, -fno-exceptions.

    あなたはパフォーマンスを向上するとレポートされているこれらのオプションの利用をトライできます: -ffast-math, -malign-double, -funroll-all-loops, -fno-rtti, -fno-exceptions.
    see the gcc(1) manpage for the details about these

    これらの詳細は gcc(1) man ページを参照

  • and of course you may want to change the usually default -02 flag with a higher number like -O3. -OX (where X is a number between 1 and 6) defines a collection of various optimization flags, the higher the number the more flags are bundled. The gcc man page will tell you what flags are used for each number.

    もちろんあなたは通常のデフォルト -02 フラグを -03 のようなより高い番号で変更することができます. -0X (X の場所は 1 と 6 の間の番号) は様々な最適化フラグのコレクションを定義していて, より高い番号はより多くのフラグがバンドルされています. gcc man ページは各番号で何のフラグが使われているかをあなたに教えてくれます.


Test your applications thoroughly when you change the default optimization flags, especially when you go beyond -02. It's possible that the optimization will make the code work incorrectly and/or cause segmentation faults.

あなたがデフォルトの最適化フラグを変更するときはあなたのアプリケーションを徹底的にテストしてください, 特に -02 を超えるときです. その最適化はコードは正しく機能しない and/or セグメンテーション違反を起こすようにする可能性があります.
See your preferred compiler's man page for detailed information about optimization.

最適化についての詳細情報はあなたが好むコンパイラの man ページを参照してください


メンテナ : Maintainers



Maintainer is the person(s) you should contact with updates, corrections and patches.
  • Stas Bekman [http://stason.org/]



作者 : Authors



  • Stas Bekman [http://stason.org/]

Only the major authors are listed above. For contributors see the Changes file.


NEXT



次回は、「 Documentation / Tutorials 」を「 mod_perl 」公式サイト (https://perl.apache.org/index.html) より確認します。


参考情報は書籍「 続・初めての Perl 改訂版 」, 「 Effective Perl 第 2 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。

目次 - Perl Index


























同じカテゴリー(Perl)の記事
 Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228) (2023-10-11 23:49)
 Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227) (2023-05-26 15:41)
 Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226) (2023-05-19 17:05)
 Perl mp2 翻訳 テンプレートシステムの選択 (d225) (2022-08-15 22:23)
 Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224) (2022-06-15 20:43)
 Perl mp2 翻訳 チュートリアル (d223) (2022-06-15 20:42)
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

Llama
リャマ
TI-DA
てぃーだブログ
プロフィール
セラ (perlackline)
セラ (perlackline)
QRコード
QRCODE
オーナーへメッセージ

PAGE TOP ▲