Elixirでプロセス5000万くらい作ってみた

  • 96
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Elixirで、表題の通り5000万プロセスぐらい作ってみた。

Elixirで20万プロセスを作ったその後

Elixirはerlang VMをベースとした関数型の言語だ。Rubyっぽい文法も使えて便利である。erlangの特徴である、軽量のプロセスが使えるのが一つの売りである。

少し前に、Elixirでプロセス20万位作ってみた という投稿を行った。その時は、デフォルトのprosess_limitのサイズ約26万を超えて、プロセスを指定する方法がわからなかった。親切な方から、26万を超えるprocess_limitの指定方法を教えてもらったので、それならどれくらいまで行けるかと思い、5000万までプロセスを作ってみた。プロセスの単位がインフレしすぎて、実際何の単位なのかわからなくなった。

erlangのプロセス

この場合のプロセスは、erlangで使われる軽量プロセスのことだ。プロセスはerlangの並列処理を形作っており、それぞれ別のCPUでも動作する。プロセスにすることで複数のCPUを効率的に使える。
プログラムの並列度を高める方法として、(通常の)マルチプロセス、マルチスレッド、コルーチン、rubyのfiberなどがあるが、そのような中で、erlangの軽量プロセスがどの程度並列に動くか試してみた。

通常のOSのプロセスを複数立ち上げた場合、大体数百プロセス以上は辛くなりそう。また、スレッドの場合は、数千レベルで辛くなるだろう。一方、erlangの軽量プロセスは数万のオーダーで並列化出来ることが知られている。

実験環境

実験環境として、AWSのm4.10largeを利用した

インスタンスタイプ CPU メモリ OS erlang elxir
m4.10xlarge 40 160GiB Ubuntu14.04 Erlang/OTP 18 1.1.1

40CPUで160GiBだ。昔、erlangが途中で落ちてコアを吐いたことがあり、その時はメモリいっぱい使っていて、HDDのそのメモリーイメージそのまま吐いたことがあった(うろ覚え)。160GiBのコアを吐く可能性があったのでHDDDは250GB程度用意した。

erlangとElixirのバージョンは以下

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:40:40] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)

erlangの最大プロセス数の指定

こちらのブログにあるが、+P 1024などと指定すれば、最大プロセス数を設定できる。
elixirで使う場合には、

export ELIXIR_ERL_OPTIONS="+P 50000000"

と環境変数を設定すれば、最大プロセス数を指定できる。

5000万プロセスを作ってみる。

次のレポジトリに、今回使ったコード一式を用意した。簡単なkey-value storeを5000万個作ってそれぞれに値を入れ引き出す。簡単なプログラムだ。殆どの処理はlib/many_process.exに入っている。値の取得を一回しかやらないため、多くのプロセスは一回値の出し入れをした後、休眠状態になる。

https://github.com/shibacow/elixir_many_process_test

git cloneで手に入れた後、mk.shを実行

iexが立ち上がった後、次のようにした。

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(bar@localhost)1> :erlang.system_info(:process_limit)
67108864
iex(bar@localhost)2> iex(bar@localhost)2> ManyProcessFactory.main(["--pnum=50000000"])

のようにすれば良い。

そうすると、ひたすら5000万のプロセスを作る。
5000万プロセスが出来た段階で、大体メモリは130GB位使っていた。

top

free

実験結果

elixirを使えば、大体5000万プロセスくらい作れる。ただしメモリーは139GB程度使用する。

その他Tips

バイナリでの実行

当初は、mix escript.buildでプログラムを作って実行していたが、

export ELIXIR_ERL_OPTIONS="+P 50000000"
./many_process --pnum=10000

みたいにして、上手くプロセス数が変更できず、仕方なくiexで立ち上げる方式を採用した。

プロセス数とプロセスリミット

プロセス数は

psize = Enum.count(:erlang.processes)

で取れた。
プロセス数の上限は

plimit = :erlang.system_info(:process_limit)

で取れた。

afterを使った定時実行

afterを使えば、例えば10秒毎に、プロセス数を取得するみたいな構文がかけるが、このやり方がベストプラクティスかはわからない。

https://github.com/shibacow/elixir_many_process_test/blob/master/lib/many_process.ex#L50