CoW 要点まとめ
共有メモリを上手につかってアプリのパフォーマンスを向上させる
CoW とは
CoW = Copy-On-Write
[ CoW - Wikipedia ]
プロセスのコピーを作成 (fork) するとき、書き換えることのないメモリページは、両方のプロセス(元のプロセスと生成されたプロセス)で共用し、書き換える可能性のあるメモリページは、新たなメモリページを割り当ててコピーを作成する
鉄則
fork するプロセスを生成する場合、親子間でメモリ共有した方が良い
- forkする前に確保したメモリ領域は子プロセスとの間で共有される
- 共有されたメモリ領域に対して書き込みを行うと共有は解除(コピー)される
- forkした後に確保したメモリ領域は子プロセスごとに確保される(共有されない)
どのプロセスでも同じように参照するだけの(書き換えない)データは、fork前に確保して共有した方が全体でのメモリ使用量を小さく保てる。つまり、fork 数を多くしたりできる。そして、処理数を上げることができる。
メモリ共有の確認
あるプロセスのメモリマッピング状態を見るには cat /proc/$PID/smaps | head
すれば良い
- Shared_Clean と Shared_Dirty の合計が共有メモリ量
- Private_* の項目がそのプロセスだけのメモリ
- Perl だと Linux::Smaps が便利
- シンプルに子プロセスのなかで
ref $foo
してみてそれぞれアドレスが同じか見る
Plack App の場合
- Plack アプリなら、master プロセスで全部 use したり require したりする
- 遅延ロードされるものも親で use するべし
遅延ロードされるモジュールを調べる
Plack アプリの場合
- Plack::Middleware::Debug::LazyLoadModules を使って遅延ロードされるモジュールを見つける
- たとえば、starman なら
--preload-app
オプションを活用する- 個人的には
-M
で全部根こそぎ読むようにしている
- 個人的には
mod_perl の場合
以下のようなモジュールを利用する
package INCdiff;
# 親で init しておいて、ケツで handler 呼ぶ
use strict;
use Apache::Constants qw(OK);
use vars qw(%init_module);
sub init { $init_module{$_}++ for keys %INC; }
sub handler {
my @required = grep { !$init_module{$_} } keys %INC;
warn "-------- module diff ---------";
warn join "\n", @required;
return OK;
}
1;
TIPS
starman の CoW っぷりを調べるスクリプト
実行結果はこんな感じ
$ ./check_starman_cow.pl
PID TYPE RSS SHARED
3418 master 17732 13692 (77%)
3419 worker 20736 12996 (62%)
3420 worker 20740 13000 (62%)
3421 worker 20736 12996 (62%)
- Perl モジュールの個別メモリ使用量は GTop で調べられる