ロードアベレージに関する考察

ここ最近ロードアベレージについて調べています。本業の Oracle サーバのロードアベレージが最近高いのです。本日の夜はまだまだ安定した値。下のグラフは loadavg x 100 のグラフ。

load01_.jpg

Dual Core Xeon が2枚のサーバなので一般的なロードアベレージの解釈からすると4以下なら安全圏。ここ最近は6〜8という数値が多いわけですが、実際の体感的なパフォーマンスがそれほど悪いるわけではなくと言うか全然重く無くってイマイチ良く判らない。CPU とか他の数値は至って安全圏のものばかり。仕方がないので kernel 2.6 のソースを眺める日々がここ数日。とにかく kernel まわりの記事を手当たり次第読んでみました。

- スポンサーリンク -

だんだん理解できてきました。幾つか既読のものもあったわけですが、当初は流し読みしただけで全然理解が深まっていなかったわけで必要に迫られて kernel 2.6.9-34 のソースも読み進めていって初めて理解が深まって参りました。負荷値の代名詞として名高いロードアベレージは結局のところ、

移動平均法で求めた1分、5分、15分あたりの実行中および実行待ちのプロセス平均数

それ以上でもそれ以下でもありません。誤解を恐れずに言えば、CPU が枯渇しているとか I/O wait が発生しているとかそんな情報は関係無しの数値です。プロセス数がどれだけいるか。そんだけなんです。でもってロードアベレージが高い場合は、何故にプロセスがそんなに存在しているかを調査する。I/O wait が多発していて待ち状態のプロセス数がたくさんあるのかもしれないし、CPU パワーが足りなくているのかもしれない。そんなわけで vmstat、iostat、mpstat、sar などなどでボトルネックの見極めをしていく感じです。

そんなわけでいろいろ解析していくことになったわけですが、全然 wait 対象となりそうな値が見つかりません。若干負荷が高いときに割り込みとマイナーフォルトが多いくらいか。

まぁそんなことはココではおいといて、プロセスの遷移図をちょっと書いてみました。赤い部分がロードアベレージとして計算されるプロセスの部分です。(多分こんな感じの遷移図であってると思う)
※Oracle の負荷についてはまた別途エントリにしよう・・・

img01.jpg


さて、ココまで調べて自分の理解ももう一息です。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 はソースを読んで理解すべきだと感じましたが、一応アフィっときます。

Linuxカーネル2.6解読室
Linuxカーネル2.6解読室
posted with amazlet on 08.02.09
高橋浩和 小田逸郎 山幡為佐久
ソフトバンククリエイティブ (2006/11/18)
売り上げランキング: 19711
- スポンサーリンク -