ロードアベレージに関する考察
ここ最近ロードアベレージについて調べています。本業の Oracle サーバのロードアベレージが最近高いのです。本日の夜はまだまだ安定した値。下のグラフは loadavg x 100 のグラフ。
Dual Core Xeon が2枚のサーバなので一般的なロードアベレージの解釈からすると4以下なら安全圏。ここ最近は6〜8という数値が多いわけですが、実際の体感的なパフォーマンスがそれほど悪いるわけではなくと言うか全然重く無くってイマイチ良く判らない。CPU とか他の数値は至って安全圏のものばかり。仕方がないので kernel 2.6 のソースを眺める日々がここ数日。とにかく kernel まわりの記事を手当たり次第読んでみました。
- マルチコア時代のロードアベレージの見方 - naoyaのはてなダイアリー
- Linux カーネルのコンテキストスイッチ処理を読み解く - naoyaのはてなダイアリー
- x86 Linux のメモリモデル、プロセス空間切り替え、カーネルスタック - naoyaのはてなダイアリー
- マルチスレッドのコンテキスト切り替えに伴うコスト - naoyaのはてなダイアリー
- Linux のスリープ処理、タイマ処理の詳細を見る - naoyaのはてなダイアリー
- Kernelメモ 時間管理編 - MOWiki
- 時刻と時間の管理
- Omicron Linux/CPU負荷
- 路地裏 ソース解読研究所: カーネルダンプ
- ITmedia エンタープライズ:プロセスディスパッチャの実装――プロセススケジューリング(その2)
- Linux Kernel 2.4 Internals
- ページフォルト - Linuxカーネルメモ
- メモリ管理
- Red Hat Enterprise Linux 4: システム管理入門ガイド
- Red Hat Enterprise Linux 4
- Red Hat Enterprise Linux 4: システム管理入門ガイド 4章. 物理メモリと仮想メモリ
- Red Hat Enterprise Linux 4: リファレンスガイド 5章. proc ファイルシステム
- @IT:/procによるLinuxチューニング [後編](2/4)
- procコマンドの説明 - Linux コマンド集 一覧表
だんだん理解できてきました。幾つか既読のものもあったわけですが、当初は流し読みしただけで全然理解が深まっていなかったわけで必要に迫られて kernel 2.6.9-34 のソースも読み進めていって初めて理解が深まって参りました。負荷値の代名詞として名高いロードアベレージは結局のところ、
それ以上でもそれ以下でもありません。誤解を恐れずに言えば、CPU が枯渇しているとか I/O wait が発生しているとかそんな情報は関係無しの数値です。プロセス数がどれだけいるか。そんだけなんです。でもってロードアベレージが高い場合は、何故にプロセスがそんなに存在しているかを調査する。I/O wait が多発していて待ち状態のプロセス数がたくさんあるのかもしれないし、CPU パワーが足りなくているのかもしれない。そんなわけで vmstat、iostat、mpstat、sar などなどでボトルネックの見極めをしていく感じです。
そんなわけでいろいろ解析していくことになったわけですが、全然 wait 対象となりそうな値が見つかりません。若干負荷が高いときに割り込みとマイナーフォルトが多いくらいか。
まぁそんなことはココではおいといて、プロセスの遷移図をちょっと書いてみました。赤い部分がロードアベレージとして計算されるプロセスの部分です。(多分こんな感じの遷移図であってると思う)
※Oracle の負荷についてはまた別途エントリにしよう・・・
さて、ココまで調べて自分の理解ももう一息です。kernel 2.3.9-34 のソースを元にロードアベレージの計算式を JavaScript に必要部分の概念のみを移植して仮想的に計算してみるものを作ってみました。毎秒存在するプロセス( task_running + task_uninterruptible )をランダムで決定して後はロードアベレージの計算式にぶち込んで仮想マシン時間を進めるって感じです。
なるほどサーバの挙動に似た数値がでました。
= 毎秒毎に生成する( task_running + task_uninterruptible )の最大値
マシンの実行時間(秒)
仮想的なロードアベレージは以下の通り(のはず・・・)
ソースコードはこんな感じ。
<html><body> <input id="TASK_RUNNING" value="10" size="4" maxlength="3"> = 毎秒毎に生成する( task_running + task_uninterruptible )の最大値 <br /> <input id="RUNSEC" value="300" size="4" maxlength="3"> マシンの実行時間(秒)<input type="button" value=" ロードアベレージを計算する " onclick="run()"> 仮想的なロードアベレージは以下の通り(のはず・・・)<br> <p><textarea id="logtx" cols="40" rows="5"></textarea></p> <script type="text/javascript"> var FSHIFT = 11; var FIXED_1 = (1 << FSHIFT); var HZ = 250; // 秒あたりのタイマ割り込みのおおよその回数. = CONFIG_HZ=250 = 1秒間に250回タイマ割り込みをする = 4ms に一回タイマ割り込みを発生させる var LOAD_FREQ = (5 * HZ); var EXP_1 = 1884; var EXP_5 = 2014; var EXP_15 = 2037; var avenrun = [ 0, 0, 0 ]; var count = LOAD_FREQ; var INITIAL_JIFFIES = (-300*HZ); var jiffies = 0; var wall_jiffies = INITIAL_JIFFIES; var nrrunning = 0; var nruninterruptible = 0; function $(id){ return document.getElementById(id) } function pad02d(x){ return (x<10) ? "0" + x : x; } function pad03d(x){ return (x<10) ? "00" + x : (x<100) ? "0" + x : x; } function LOAD_INT(x){ return ((x) >> FSHIFT) } function LOAD_FRAC(x){ return LOAD_INT(((x) & (FIXED_1-1)) * 100) } function CALC_LOAD(load,exp,n){ load *= exp; load += n*(FIXED_1-exp); load >>= FSHIFT; return load; } function do_timer(ticks){ update_times(ticks); } // ticks = jiffiesとシステム時間xtimeとの誤差 = kernel2.6の場合は常に1 function update_times(ticks){ ticks = 1; calc_load(ticks); } function calc_load(ticks){ var active_tasks; count -= ticks; if (count < 0) { count += LOAD_FREQ; active_tasks = count_active_tasks(); avenrun[0] = CALC_LOAD(avenrun[0], EXP_1, active_tasks); avenrun[1] = CALC_LOAD(avenrun[1], EXP_5, active_tasks); avenrun[2] = CALC_LOAD(avenrun[2], EXP_15, active_tasks); } } function count_active_tasks(){ return (nr_running() + nr_uninterruptible()) * FIXED_1; } function nr_running(){ return nrrunning; } function nr_uninterruptible(){ return nruninterruptible; } function loadavg_read_proc(){ var a = avenrun[0] + (FIXED_1/200); var b = avenrun[1] + (FIXED_1/200); var c = avenrun[2] + (FIXED_1/200); len = LOAD_INT(a) + "." + pad02d(LOAD_FRAC(a)) + " " + LOAD_INT(b) + "." + pad02d(LOAD_FRAC(b)) + " " + LOAD_INT(c) + "." + pad02d(LOAD_FRAC(c)) + "\n"; return len; } function run(){ $('logtx').value = ''; avenrun = [ 0, 0, 0 ]; var buff = []; // 5分間計測。秒アタリのタイマ割り込みはHZ for ( var sec=0; sec<$('RUNSEC').value; sec++ ) { var len; // 毎秒毎に指定のタスクが生成されるとする nrrunning = Math.floor( Math.random() * Number($('TASK_RUNNING').value)/2 ); nruninterruptible = Math.floor( Math.random() * Number($('TASK_RUNNING').value)/2 ); buff.push("[" + pad03d(sec) +"] " + "[" + pad02d(nrrunning) +"] " + "[" + pad02d(nruninterruptible) +"] " + len); for ( var tick=0; tick<HZ; tick++ ) { do_timer(1); len = loadavg_read_proc(); // すぐにタスク消化 nrrunning = 0; nruninterruptible = 0; } } buff.push("-------------------------------\n"); buff.push("[sec] [rq] [wq] avg1 avg5 avg15\n"); $('logtx').value = buff.reverse().join(""); } </script> </body></html>
さて、今回の解析を進めるにあたり Linuxカーネル2.6解読室を結構読んだけどほんの少ししか役に立ちませんでした。やはり kernel はソースを読んで理解すべきだと感じましたが、一応アフィっときます。
ソフトバンククリエイティブ (2006/11/18)
売り上げランキング: 19711
コメントやシェアをお願いします!
k.daiba
役に立つ本が見つからなかったのなら,自分で書くのじゃ!