スポンサーサイト 

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

vmstat研究 

Solarisのvmstat

システムの性能向上を狙って、vmstatによる情報を理解したい。vmstatはその名の通りVirtual Memoryの統計情報を出力する。

仮想メモリについて

そもそも近代的なOSにおけるメモリとは、物理メモリとスワップとからなる仮想メモリを指す。物理メモリはマザーボードに装着されたメモリチップによる一次記憶領域を指す。物理メモリは高価であるが非常に高速に動作する。スワップはHDDによる二次記憶領域を指す。スワップは物理メモリより安価であるが速度は物理メモリに劣る。

仮想メモリは、物理メモリとスワップをOSが合わせて活用することでユーザプログラムからは単なるメモリとして区別無く利用できる様にした仕組みのことを指す。つまりプログラムから見れば、自分が物理メモリ上にいるのかスワップ上にいるのか区別できず、区別する必要もないようOSが仲立ちをしているということだ。

OSは物理メモリ上にいるプログラムがアクセスされないままでいるのを見つけると、物理メモリからスワップへ移動(swap-out)させる。必要になると再び物理メモリへ移動(swap-in)させる。swap-inする時もswap-outする時もOSはそのプログラムには何も言わないし、プログラムも気が付くことはない。

物理メモリはまた、OSによってキャッシュやバッファとして使用される。キャッシュはHDDなどの二次記憶装置への書き込みや読み込みを一時的に物理メモリ上に保存しておくことで、低速な二次記憶装置へのアクセスによってシステムの処理速度低下を軽減する。

バッファは少し分かっていないのだが、ネットワークやシリアルポート、USBポートやSCSIなど各種I/Oを行う際のキャッシュだと思う。これらもやはり一次記憶に比較して低速であるため、データがI/Oされるまでの間物理メモリ上に保持する必要がある。

特にLinuxは可能な限りキャッシュにため込もうとする傾向が強い。Linuxでなくても一般的なOSでは物理メモリの空きはとても小さな値になるはずだ。

さて、簡単にswap-in/swap-outすると書いたが、低速な二次記憶装置への読み書きであるため物理メモリだけで動作するのに比較すると当然時間が掛かる。つまり動作が遅いわけだ。メモリの使用状況に余裕がある場合にはswap-inやswap-outによる速度低下は気にならない場合が多いが、必要なswap-in/swap-outが大量になると顕著な速度低下が発生する。こうした現象をスラッシング(thrashing)と呼ぶ。

vmstat [-pS] [INTERVAL [TIMES]]

-S swap-in/swap-out回数を出力。remfの代わりにsisoが出力される。 -p  ページング処理について出力する。
vmstat出力項目の説明
kthr カーネルスレッド数
r 実行可能キューのスレッド数
b ブロック中のスレッド数。入出力待ち、ページング等の理由
w スワップアウトされたLWP(軽量プロセス)
memory メモリ
swap 空きスワップ[KBytes]
free size of free list[KBytes] 要するに空き物理メモリと思うのだけど"free list"って何?
page ページ処理 (全て1秒当たりの値)
re ページ埋め立て。回収されたページ。オプション-S指定でsiと交換。
mf マイナーフォルト。オプション-S指定でsoと交換。
si swap-in。オプション-S指定で出力される。
so swap-out。オプション-S指定で出力される。
pi page-inメモリサイズ[KBytes]
po page-outメモリサイズ[KBytes]
fr 解放メモリサイズ[KBytes]
de 短期メモリ不足予想[KBytes]
sr clock algorithmでscanされたページ数
disk ディスク操作[回/秒]。表示は4台まで。英字はディスクの種類を示し、s = SCSI、i = IPIなど。数字はLUN。
faults 割込み、システムコール
in 割込み[回/秒]
sy システムコール[回/秒]
cs コンテキストスイッチ[回/秒]
cpu CPU使用時間。マルチプロセッサ/マルチコアプロセッサではその平均値を出力します。
us ユーザモード[%]
sy カーネルモード[%]
id アイドル[%] つまりCPUが遊んでいる時間の割合。
page ページング
epi Executable page-ins.
epo Executable page-outs.
epf Executable page-frees.
api Anonymous page-ins.
apo Anonymous page-outs.
apf Anonymous page-frees.
fpi File system page-ins.
fpo File system page-outs.
fpf File system page-frees.

ページとページサイズについて

また具体的にswap-in/swap-outがどんな単位で行われているのかを見てみよう。プログラム全体でpage-in/page-outするのではなく、page(ページ)と呼ばれる単位でメモリを操作する。OSによってpageサイズは異なるが、良くあるOSでは大抵は8KBytes程度だと思う。SolarisはSolaris9でMPSSと呼ぶ機能により8KBytes、64KBytes、512KBytes、4MBytesという複数の異なるページサイズを混在使用できる様になった。(どうも対象はヒープ、スタック、mmap()による割り当てに限定されるらしい)

page-in/page-outがページサイズ単位で行われるということは、どのようにパフォーマンスに影響するのだろうか? ページサイズ単位ではなくプロセス単位でpage-in/page-outされる場合を考えてみると、より小さな単位で操作されるということが分かる。巨大なプログラムではなくても5MBytes、10MBytes程度のメモリを使用するケースが多い。プロセス単位でのpage-in/page-outではこうした単位でpage-in/page-outを行わなければならない。ページ単位でのpage-in/page-outは必要な部分だけを物理メモリに戻し、実行することができる。

ではプロセスを分割する単位としてページというのは適当なものであるのだろうか? それを判断するためには実行中プログラムがどのようなメモリ領域を持っているか理解する必要があるだろう。まず実行前のプログラムバイナリには大きく分けてプログラムコードとデータ(テキスト)領域のふたつが存在する。プログラム実行のためにはこれがメモリ上に展開され、スタックが用意され、実際にプログラムが実行されるに従って更に動的取得(ヒープ)メモリが用意されます。

プログラムコードは文字通りコード自体で特定のアドレスからCPUが順に読んでいきます。処理に従ってCPUはコード領域をあちらこちらとジャンプして実行を続けますが、現代のユーザプログラムではCPUはプログラムコード領域から出ることはないのではないかと思います。

テキスト領域は文字列や数値などプログラムバイナリに含まれるデータが入っています。最近のコンピュータアーキテクチャでは上記のコード領域とテキスト領域はリードオンリとして変更/書き込みを禁止するケースが多いのではないかと思います。

スタックはCのautoな変数の保存領域ですね。関数の呼び出し時に戻り先アドレスをpushしておくというのもスタックだったと思います。スタックはプログラム開始時に確保してプログラム終了まで解放も追加取得もせず一定サイズです。一定サイズであるため、無駄が出ないよう小さなサイズにしておく場合が多い。

動的取得(ヒープ)メモリはmaloc()やC++のnew()やらでシステムコールで明示的にプログラムから取得するメモリ。一般に大きなメモリ領域を取得する際には動的取得でヒープメモリを使用する。

さて、このようなメモリ領域がある時、それぞれのメモリ領域はどのような頻度でアクセスされるだろうか? スタックはプログラムが動作する時に必ずアクセスされるだろうと予想される。プログラムコードは場合によっては一部が全くアクセスされないままプログラムが終了することがあると予想される。テキスト領域などもそうでしょう。スタックは更にその傾向が強そうで、データの大きさがより大きいためアクセスされるところ/されないところが出てきそう。

こうした使用頻度の高いメモリ領域が物理メモリ上にロードされ、そうでない領域をswap-outしておくことができるようページ単位でswap-in/swap-outするようになっている。

ページサイズを調べる/変更する

Solaris9以降ではシステムのデフォルトページサイズ及び対応可能なページサイズを出力するためには以下のようにする。(このあたりのことはここが参考になります)

デフォルトページサイズの出力

$ /bin/pagesize
8192
$

対応可能なページサイズの出力

$ /bin/pagesize -a
8192
65536
524288
4194304
$

デフォルトページサイズの変更はどうやるんだろう?

さて、デフォルトページサイズの変更もやりたいけども、手軽にパフォーマンス測定を行ってみたいのも事実。起動コマンドに適用するページサイズを指定するにはコマンドppgszを使用すればよいらしい。

/usr/sbin/ppgsz -o {heap|stack|anon}=SIZE COMMAND

"-o anon"はSolaris10から指定できるようになったらしい。また、CMDの代わりに-p PIDとすることで実行中のプロセスのページサイズを変更することも可能らしい。 当然自分のプロセスかrootによる実行が必要。ということで実験をしてみたいが、今の所自由に実行できるマシンが手元にないので現在未試験。

環境変数LD_PRELOADを用いて変更することもできるため、こちらの方が使いやすいかも。LD_PRELOADを用いる場合には以下のようにする。

$ MPSSHEAP=64K; export MPSSHEAP
$ MPSSSTACK=512K; export MPSSSTACK
$ LD_PRELOAD=mpss.so.1; export LD_PRELOAD
$ ./foobar

または設定ファイルに記述して、設定ファイルを指定する方法もあります。

$ cat > mpsscfg
foo*:512k:64k
^C
$ MPSSCFGFILE=mpsscfg; export MPSSCFGFILE
$ ./foobar

mpsscfgの1カラム目でプログラム名を限定しています。

プロセスの使用メモリページを調べることもできる。

pmap PID

とすればよい。実行例は次の通り。

% pmap 25437
25437: -csh
00010000 136K r-x-- /usr/bin/csh
00042000 16K rwx-- /usr/bin/csh
00046000 40K rwx-- [ heap ]
FEF80000 1664K r--s- dev:85,3 ino:151594
FF1A0000 24K r-x-- /usr/lib/locale/ja_JP.eucJP/methods_ja_JP.eucJP.so.3
FF1B4000 8K rwx-- /usr/lib/locale/ja_JP.eucJP/methods_ja_JP.eucJP.so.3
FF1C0000 144K r-x-- /usr/lib/locale/ja/ja.so.3
FF1F2000 16K rwx-- /usr/lib/locale/ja/ja.so.3
FF200000 864K r-x-- /lib/libc.so.1
FF2E8000 32K rwx-- /lib/libc.so.1
FF2F0000 8K rwx-- /lib/libc.so.1
FF31A000 8K rwxs- [ anon ]
FF320000 8K r-x-- /platform/sun4u-us3/lib/libc_psr.so.1
FF330000 8K rwx-- [ anon ]
FF340000 168K r-x-- /lib/libcurses.so.1
FF37A000 32K rwx-- /lib/libcurses.so.1
FF382000 8K rwx-- /lib/libcurses.so.1
FF390000 24K rwx-- [ anon ]
FF3A0000 8K r--s- dev:85,4 ino:4628
FF3B0000 184K r-x-- /lib/ld.so.1
FF3EE000 8K rwx-- /lib/ld.so.1
FF3F0000 8K rwx-- /lib/ld.so.1
FFBEE000 72K rw--- [ stack ]
total 3488K
%

ログインシェルcshに対して実行してみたが、ヒープ[heap]が40KBytes、libc.so.1が約900KBytes、その他libcurses.so.1が200KBytes程度、デバイスファイル85.3と85.4とが1.6MBytesと8KBytes、最後にスタックが72KBytesになっています。ヒープについては所々にある[ anon ]もスタックを指していると思われます。*.so.1については実際は共有メモリとして他のプロセスと共有している場合もありそうです。特にlibc.so.1は。よく見るとアクセス権も示されていてリードオンリのものは共有しやすいだろうと想像できます。

適当なページサイズとは?

ではページサイズはどの程度が適当なのか、どうやって判断するのだろうか。page-in/page-out回数は少ない方が良く、転送データ量は少ない方がよい。これをvmstatで測ればよいのではないか。 更に詳細にページサイズを検討するには

仮想メモリの中のどのページが、現在物理メモリのどこに存在しているのかという対応表が存在している。SPARC CPUには仮想アドレスと物理アドレスとの対応を扱うためのTLB(Translation Lookside Buffer)というバッファが存在する。仮想アドレスと物理アドレスとの対応をここにキャッシュする。TLBでキャッシュミスした場合には、TTL(Translate Table Entry)と言うより大きな変換テーブルから探し出す。TLBでキャッシュヒットした場合の方がメモリアクセスは高速となり、アクセスするページが物理メモリ上に存在する場合にはその速度の違いは速度性能の違いとして効いてくるものとなる。ページサイズを小さくするとページ数が増加するためTLBでキャッシュヒットするメモリサイズが小さくなり、ページサイズを大きくするとpage-in/page-outの単位転送メモリサイズが大きくなってしまう。

SPARCアーキテクチャのSolaris9以降では、trapstatコマンドが存在する。この-tオプションを用いることでTLBのキャッシュヒット率を参照できる。

ページング、スワップの違い/区別は何か?

vmstatではpi/po、si/soといった項目があるがそれぞれ何が異なるのだろうか。pi/poはページング、si/soはスワップである。スワップはプロセス単位でのスワップイン/スワップアウト、ページングはプロセスよりも(大抵は)小さなページ単位でのスワップイン/スワップアウトを指す。

一般にスワップで扱うプロセスサイズよりもページサイズの方が小さいため、ページングよりもスワップの方がパフォーマンスに対する悪影響が大きいと考えられる。

短期メモリ不足予想とは

vmstatの出力deは短期メモリ不足予想を示している。これはどんなものだろうか。物理メモリの空きが少なくなった時などに短期メモリ不足予想が発生するはず。

ページフォルトとは何か?

ページフォルトとは、プログラムがアクセスしようとした仮想メモリ空間が物理メモリ上に存在しなかったことを指す。つまりpage-outされていたということである。ページフォルトが発生するとOSが該当ページをpage-inさせなければならない。ここまでの説明で明らかなようにページフォルトは正常な運用の中で当然発生する現象であり、エラーでも問題でもない。

ただし、プログラムがアクセスしようとした仮想メモリが、そのプログラムにはアクセスする権限がないものであった場合にもページフォルトは発生する。プログラムのバグなどによって不正なアドレスにアクセスしようとした場合にこうしたページフォルトが発生する。この場合にはOSは当然page-inさせずにプログラムの異常動作であるとして終了させるなどの処理を行う。UNIX系であればシグナルSIGSEGVを送るなどする。要するに「セグメンテーションフォルト」や「バスエラー」だ。

性能を向上させる観点からは、ページフォルトは可能な限り少ない方が好ましい。ページフォルトの発生はすなわち低速な二次記憶装置とのアクセスの発生を意味するからだ。vmstatではmfをそうした意味合いで参照すればよいだろう。

タイトルと中身が異なっていますが、ここまで。

スポンサーサイト

use CGI 

use CGIする時の覚え書き

use CGI;
use CGI::Carp qw(fatalsToBrowser);
$msg = $cgi->header(-charset => 'UTF-8'); # キャラクタ・セットを指定
$msg .= $cgi->start_html(
   -title       => $title,
   -charset   => 'UTF-8', # キャラクタ・セットを指定
   -encoding => 'UTF-8', # キャラクタ・セットを指定
   -lang       => 'ja-JP' # 言語を指定
);
$msg .= $cgi->escapeHTML($body); #記号文字をエスケープ
$msg .= $cgi->end_html;

print($msg);

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。