Erlang Process
出典: KLablabWiki
目次 |
概要
Erlang の軽量プロセスは非常に多くのプロセスを扱う事が出来るという特徴が あり、過去には 2000万プロセスが起動したとの情報がニュースグループに流れ 話題となりました。 しかし、ドキュメントにある情報を元に Erlang プロセスの起動数を計算して みても計算が合わなかったり、この 2000万という数字が半ば信じられなかった ので自分の目で確かめてみたいと思っていました。 そこで今回は、サン・マイクロシステムズ(株)様に 32G のメモリを搭載した Sun Fire T2000 をお貸しし頂くことが出来ましたので。このハードを使用して Erlangプロセス をどれくらい起動することが出来るのかを調べてみました。
検証環境
- ハードウェア
- Sun Fire T2000
- CPU
- UltraSPARC T1
- メモリ
- 32G
- OS
- Solaris 10 8/07
- Erlang 実行環境
- OTP 12B-0
ソースコード
今回の実験で使用したプログラムのソースコード全て公開しています。
Erlang プロセスのメモリ使用量
Erlang のドキュメントには Erlang のプロセス 1つにつき 309 ワード使用す る、と書かれています。32bit 環境で 1236 byte ということなのでずいぶんと 少ない印象を受けます。まずはこの点を前記したソースコードで確かめてみま す。
% erl -noshell -run proc start 1000 process count: 1021 memory total: 3593000 memory processes: 1552024 memory processes(user): 1267016 spawn time: 10177
このプログラムは、引数で与えた数だけプロセスを生成(spawn)し、メモリ使用 量とプロセス生成に掛かった時間を表示するだけのプログラムです。
出力についてですが、process count は起動した Erlang プロセス数です。プ ログラムで起動した process の数は 1000個なのに 1021 個起動しているのは Erlang の VM を起動した時点でシステムで使用するプロセスが 21個起動して いるためです。次にメモリ使用量を erlang:memory/1 を使用して表示していま す。memory total は VM が使用している全メモリ使用量です、次の memory processes は VM が1024個プロセスに使用したメモリ使用量です。次の memory processes(user) はシステムが勝手に起動した 21 個のプロセスを除き、 明示的に起動した 1000 個のプロセスが使用したメモリのみを表示しています。
この結果から memory processes(user) の 1267016 / 1000 = 1267 byte とい うことで、大体ドキュメント通りのメモリ使用量になっていることが解りまし た。
プロセスの起動数に関する調査
32768個の壁
まずは普通に 4万個のプロセスを起動してみると・・・
% erl -noshell -run proc start 40000 process count: 32768 =ERROR REPORT==== 1-Feb-2008::17:49:52 === Too many processes
この様なエラーが出てしまいました。 どうやら 32769個目のプロセスを起動する時にエラーが出てしまったようです。
これは erl のドキュメントにある通り、VM で起動出来るプロセスの量はデフォ ルトで 32768 に制限されているためです。この制限を超えるには erl コマン ドのオプションに +P を付けてやります。
% erl -noshell +P 40030 -run proc start 40000 process count: 40021 memory total: 54349269 memory processes: 52162844 memory processes(user): 51830356 spawn time: 121310
正常に 4万個のプロセスが起動しました。(オプションに +P 40030 と指定した のは前記した 21個のシステムプロセス分の余裕が必要な為)
4G byte の壁
まだまだ行けそうな感じなので次は 400万個に挑戦してみます。
% erl -noshell +P 4000030 -run proc start 4000000
いつまで経ってもプログラムが終了しません・・・。
ps コマンドで VM のメモリ使用量を確認してみると 4G byte を超えた辺りで 止まっているようでした。これは 32bit のアドレス空間がネックになっている 為で 64 bit 版の Erlang 実行環境を使用することで 4G byte 以上のメモリを 使用することが出来るようになりました。
SPARC/Solaris で 64bit版の Erlang 実行環境をビルドするのは少し厄介でし たのでこの記事の最後に SPARC/Solaris 10 で Erlang 実行環境をビルドする ための手順をまとめてあります。
1500 万プロセスの壁
前記した 64bit版 VM を使用することであっさり 400万個のプロセスを起動す ることが出来ましたのでこの 64 bit版 VM で何処までプロセスを起動出来るの か調べてみたところ、1000万プロセスの起動に成功しましたが 2000万プロセス は起動出来ませんでした。1000万プロセスの起動にメモリを 24G 使用している ことから 32G のメモリでは 1500万プロセスあたりが限度という事が解ります。 先のニュースグループに投稿された記事には 16G のメモリで 2000万プロセス 起動したと在りましたのでまだ工夫の余地がありそうです。 そこで、調べてみたところ Erlang には幾つかのメモリを節約するテクニック が在ることが解りましたので紹介したいと思います。
ヒープメモリを節約する
Erlang の VM の起動オプションには +h オプションというプロセスの初期ヒー プサイズを指定するオプションがあります。
たとえば 64bit 版 VM で
% erl -noshell +h 1 -run proc start 1000 process count: 1021 memory total: 5530543 memory processes: 1639248 memory processes(user): 906848 spawn time: 23658
という様に実行すると、プロセスの初期ヒープサイズを 1ワードにすることが 出来ます。ただし、実際に使用されるヒープサイズは初期ヒープサイズから幾 らか拡張される様です。erlang:process_info/2 で調べて見ると実際のヒープ サイズは 30 words になっていることが解りました。 それでも、プロセスのメモリ使用量はデフォルトの 309 words から 113 words に節約出来ていますので十分有効であることが解ります。 このオプションを使用した場合のプロセス起動数を調べてみると 3000万個起動 する事が出来ました。
hibernate して更にメモリを節約する
そろそろこの辺りが限界かなとも思ったのですが、なんと Erlang では プロセ スを冬眠(hibernate)させる事が出来るようです。 詳細はドキュメントに erlang:hibernate/3 について書かれてありますが、こ の関数を呼び出すと、プロセスは call スタックを捨てて、ヒープメモリを出 来るだけ節約して待ち状態に入ります。そしてメッセージを受け取った時に再 び活性化します。 この hibernate 機能は目覚めの時に若干のコストを要しますが、非常に大量の プロセスを起動しなければならないけれど、ほとんどのプロセスがメッセージ 待ち状態にある場合に効率良くメモリを使用することが出来そうです。 この hibernate 機能を使用してどれくらいプロセスが起動するか試してみると、 4000万個起動することが出来ました。
リングベンチマーク
リングベンチマークとは、Programming Erlang 8章11節に読者への課題として 掲載されている問題で、N 個のプロセスをリング状に生成し、最初のプロセス にメッセージを M個送信することで合計 N * M 回のメッセージ通信が行われる というベンチマークです。 参考までに大量プロセスでのリングベンチマーク結果を載せておきます。 (ソースコードは ring.erl です)
| プロセス数 N | メッセージ数 M | メッセージ処理時間(秒) |
| 1000万 | 1 | 428 |
| 1000万 | 10 | 1129 |
| 1000万(hibernate) | 1 | 947 |
| 1000万(hibernate) | 10 | 3502 |
Solaris 10 に OTP 12B-0 64bit版をインストールする
Solaris 10 で Erlang の実行環境 OTP 12B-0 64bit版をビルドする方法が若干 解りにくかったので手順についてまとめておきます。
- 準備
ホームディレクトリ下の ~/bin に以下に symlink を張ります
% mkdir ~/bin % ln -s /usr/sfw/bin/gar ~/bin/ar % ln -s /usr/sfw/bin/gld ~/bin/ld % ln -s /usr/sfw/bin/gmake ~/bin/make % ln -s /usr/sfw/bin/gm4 ~/bin/m4
gcc の CFLAGS に必ず-m64 を付ける為のラッパを用意します。 (単純に CFLAGS="-m64" ./configure とやりたいところですが、これだと上手 く行きませんでした。)
% cat > ~/bin/gcc64 #!/bin/sh exec gcc -m64 "$@" ^D % chmod +x ~/bin/gcc64
ld も同様ですが、LD=gcc64 だと上手くいかないため以下のような symlink を 張ります。
% ln -sf ~/bin/gcc64 ~/bin/ld
環境変数を以下の様に設定します。
% export PATH=~/bin:/usr/sfw/bin:/usr/bin
- ダウンロード
http://www.erlang.org/download.html より、R12B-0 の Source をダウンロー ドしてきます。
% gtar xzf otp_src_R12B-0.tar.gz % cd otp_src_R12B-0
common_test がエラーになるので rename してやります
% mv lib/common_test lib/.common_test
また、--without-odbc が効かないためこれも rename します。
% mv lib/odbc lib/.odbc
- ビルド
% CC=gcc64 ./configure --prefix=/opt/otp-R12B-0-m64 --disable-hipe --enable- kernel-poll --enable-smp-support
- 確認
% /opt/otp-R12B-0-m64/bin/erl Erlang (BEAM) emulator version 5.6 [source] [64-bit] [smp:32] [async-threads: 0] [kernel-poll:false]
この 64bit 化と同時に hipe を併用することが出来ないようなので hipe を無 効にしました。もし 32bit 版で hipe を有効にする場合、sun のリンカではな く GNU の gld を使用するしなければなりません。
参考URL
更新履歴
- 2008/02/11 公開しました。