LoginSignup
19
16

More than 5 years have passed since last update.

TCPデータ受信時のCPU使用率が高すぎると思ったら、スケジューラ使用率も見てみると良いかもしれない

Last updated at Posted at 2015-07-21

概要

メモレベルの備忘録です。

  • ErlangでTCPデータ受信系のベンチマークを流してみると、サーバのCPU使用率がやたら高いのが気になっていた
  • スケジューラ使用率を見てみると、CPU使用率に比べて数分の一くらいの値だった
    • CPU使用率が高いだけで、スケジューラ的には余裕がある
    • 何か実際の仕事をしていた訳ではなく、スケジューラがsleepに入る前のビジーウェイトでCPUが消費されていただけの模様
      • 一度スケジューラ(スレッド)がsleepに入ってしまうと、復帰までに時間が掛かり急激な負荷(タスク)の増加への反応が遅くなるので、sleepに入る前に、まずビジーウェイトで新規タスクがないかをしばらくチェックしている (注: 予想を交えて書いている)
    • つまりスケジューラ使用率で見た方が、実際のErlangVMの負荷状況が正確に把握できそう
  • CPU使用率だけでより正確に測定したい(ex. sar等の一般的なツールを使いたい)なら+sbwt=noneをerlコマンドに指定するのが良さそう
    • スケジューラのsleep前のビジーウェイトの期間を指定できる (noneなら完全になくなる)

スケジューラ使用率の取得方法

『Erlang in Anger』で紹介されているreconというライブラリを使うならrecon:scheduler_usage/1で取得可能。

> recon:scheduler_usage(1000). % 引数は測定期間(ミリ秒)
[{1,5.1613409200070315e-6}, % スケジューラ毎の使用率(max=1.0)が返される
 {2,8.161533659627463e-4},
 {3,1.1358267166699054e-5},
 {4,1.0461382968977206e-5}]

標準関数だけを使う場合。
一回で取得可能な方法は提供されていない(おそらく)ので、若干面倒。

%% デフォルトでは情報が取得できないので、フラグをONにする
> erlang:system_flag(scheduler_wall_time, true).

%% 使用率を直接取得することはできないので、二点でスケジューラの使用状況を取得して、
%% その区間での使用率を計算する。
%% (http://www.erlang.org/doc/man/erlang.html#statistics-1 も参照)
> Start = statistics(scheduler_wall_time).
[{1,18752,156079030051}, % {SchedulerId, ActiveTime, TotalTime}
 {4,4197,156079017157},
 {3,11426,156079015196},
 {2,4806254,156079013592}]

> End = statistics(scheduler_wall_time).
[{1,24052,171623980032},
 {4,10831,171623967002},
 {3,21103,171623963945},
 {2,5441358,171623960966}]

> lists:map(fun({{I, A0, T0}, {I, A1, T1}}) -> {I, (A1 - A0)/(T1 - T0)} end, 
            lists:zip(lists:sort(End), lists:sort(Start))).
[{1,3.409467387465375e-7},
 {2,4.085597620370561e-5},
 {3,6.225173306295087e-7},
 {4,4.267623933269757e-7}]

ちなみに以降で記載されているCPU使用率およびスケジューラ使用率は拙作のbench_utilというモジュールを使って取得されている。

TCPデータ受信での例

TCPデータ受信時に、実際にCPU使用率とスケジューラ使用率がどのような値になるのかの一例。

測定方法

TCPデータの送受信にはrecvbenchという(随分以前に書き殴りで作成した)モジュールを使用した。

サーバ側:

%% TCPサーバ側(データ受信側) - CPU: Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz (x8)
%%
%% `+sbwt`の値を変えて計測を行う: none|very_short|short|medium(デフォルト)|long|very_long
$ erl +sbwt medium -pz ebin deps/*/ebin +K true -eval 'application:start(recvbench).'

%% 3000番ポートでサーバを起動する.
%% - 受信方法アクティブモード: `{active, 32}`
%% - バッファサイズ: 1MB (デフォルト値でも良いが、大きめ(recbuf以上)の方が若干性能が向上することが多いのでついでに指定)
> recv_active_n:start_accept(3000, 32, [{sockopts, [{buffer, 1024*1024}]}]).

%% 計測: クライアント実行後に30秒間以下の関数を実行して、CPU使用率とスケジューラ使用率の平均値を採用する
> bench_util:sar([{count, 6}]).
loop    time    reductions      context_switches        gc_count        gc_bytes        inputs  outputs run_queue       cpu_util        scheduler_util  mem_total       mem_procs       mem_sys mem_atom        mem_bin mem_ets ports   processes
0       2015-07-22T02:48:06     675570  332677  3596    28820152        457557326       1       6       0.190736        0.058807        93221272        16828104        76393168        215345  66138280        278624  708     852
1       2015-07-22T02:48:11     685347  333213  3304    28604376        458907404       532     0       0.318427        0.078794        113394872       19021952        94372920        215345  84125640        279440  708     852
2       2015-07-22T02:48:16     683528  331951  3487    28290992        458152534       145     1       0.294529        0.072784        134849080       21448032        113401048       215345  103155456       279440  708     852

クライアント側:

%% TCPクライアント側 (データ送信側) - サーバとは別マシンで実行
%%
$ erl +sbwt medium -pz ebin deps/*/ebin +K true -eval 'application:start(recvbench).'

%% 1Mbps x 700プロセス = 700Mbpsのデータを(0.1秒毎に)送信する
> Processes = 700.  % 700プロセス
> ByteRate = round(1 * 1024 * 1024 / 8). % 1Mbps (何故かバイト単位で指定する)
> RequestsPerSecond = 10. % 0.1秒毎に送信
> simple_load:start_link(ServerHost, 3000, Processes, ByteRate, RequestsPerSecond). % 起動

%% 負荷開始
> simple_load:start_load().

計測結果: sbwtオプションの値毎のCPU使用率とスケジュール使用率

+sbwt CPU使用率 スケジューラ使用率
very_long 45.48% 4.63%
long 40.09% 4.64%
medium 35.57% 5.35%
short 29.15% 5.77%
very_short 22.65% 6.06%
none 14.25% 7.32%

ビジーウェイトの時間を短くするに従い、CPU使用率が下がって、スケジュール使用率が(若干)上がる、という傾向がはっきり表れていて面白い。

最後に

  • Erlangで正確な性能測定を行いたい場合は(特にI/Oが絡む場合は?)CPU使用率だけではなく、スケジューラ使用率にも気を払った方が良さそう
  • この辺りの話は『Erlang in Anger』の五章でも触れられているので、興味のある人はそちらを参照すると良いと思う
19
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
16