Erlangで1プロセス作成するのに必要な時間を計ってみた

ラクス Advent Calendar 2016の11日目です。
昨日は[@rok-okamoto-rokさん](http://qiita.com/rok-okamoto-rok)の「[Haxe（へっくす）を使ってみる](http://qiita.com/rok-okamoto-rok/items/10b1aa18023e71a1c300)」でした。
<hr>

# はじめに
今日の担当は[@AkiraTamai](http://qiita.com/AkiraTamai)です。
直近では広告配信系システムのサーバーサイドを担当したりしています。
よろしくお願いします。

# 背景
以前とある本か記事にErlangサーバー1台で数千万プロセスをこなすことができるような事が書いてあり、
プロセスを生成する時間はどのくらいなんだろうと思ったことから記事にしてみました。

# 検証方法
・検証に用いたコードは[飛行機本](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0Erlang-Joe-Armstrong/dp/4274067149/)のサンプル。
https://github.com/AkiraTamai/erlang-sample/blob/master/server/simple/measure_processes.erl
-> X個のプロセスを作成、その1プロセス生成に要した時間を計測する。

```erlang:実行/結果書式
1> measure_processes:limit(プロセス生成数).
Limit allowed processes:システム上の最大許容プロセス数
Process spawn time=1プロセスあたりの実行(CPU)時間 (1プロセスあたりの実際の経過時間) μs
ok
```

・Erlangのバージョンは、Erlang/OTP 19.1
・検証マシンはConoHa64GBプラン

| OS | cpu | メモリ |SSD|
|:-----------:|:------------:|:------------:|:------------:|
|CentOS 7.2.1511|24コア|64GB|50GB

# 検証結果


## 10,000プロセス以下の場合

```erlang:1〜1万プロセス生成
[root@node001 ~]# erl
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(1).
Limit allowed processes:262144
Process spawn time=0.0 (0.0) μs
ok
2> measure_processes:limit(10).
Limit allowed processes:262144
Process spawn time=0.0 (0.0) μs
ok
3> measure_processes:limit(100).
Limit allowed processes:262144
Process spawn time=0.0 (0.0) μs
ok
4> measure_processes:limit(1000).
Limit allowed processes:262144
Process spawn time=0.0 (7.0) μs
ok
5> measure_processes:limit(10000).
Limit allowed processes:262144
Process spawn time=2.0 (5.1) μs
ok
```
10,000個のプロセスを生成した場合、1プロセスあたり2.0マイクロ秒（μs)の実行時間、
5.1μsの実際の経過時間がかかっていることが判りました。


## 1,000,000プロセス以下の場合

```erlang:50万プロセス生成
[root@node001 ~]# erl +P 500000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(500000).
Limit allowed processes:524288
Process spawn time=3.52 (3.998) μs
ok
```

```erlang:100万プロセス生成
[root@node001 ~]# erl +P 1000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(1000000).
Limit allowed processes:1048576
Process spawn time=3.86 (4.094) μs
ok
```

1,000,000個のプロセスを生成した場合、1プロセスあたり3.86マイクロ秒（μs)の実行時間、
4.094μsの実際の経過時間がかかっており、1秒あたり約259067プロセスが生成されています。

## 5,000,000プロセスの場合

```erlang:500万プロセス生成
[root@node001 ~]# erl +P 5000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(5000000).
Limit allowed processes:8388608
Process spawn time=5.262 (5.487) μs
ok
```

5,000,000個のプロセスを生成した場合、1秒あたり約190042プロセスが生成されています。

## 10,000,000プロセス以上の場合

```erlang:1000万プロセス生成
[root@node001 ~]# erl +P 10000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(10000000).
Limit allowed processes:16777216
Process spawn time=5.782 (5.8914) μs
```

```erlang:2000万プロセス生成
[root@node001 ~]# erl +P 20000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(20000000).
Limit allowed processes:33554432
Process spawn time=7.5785 (8.6104) μs
```

```erlang:2500万プロセス生成
[root@node001 ~]# erl +P 25000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(25000000).
Limit allowed processes:33554432
Process spawn time=5.9732 (7.52112) μs
ok
```

25,000,000個のプロセスを生成した場合、1プロセスあたり5.9732マイクロ秒（μs)の実行時間、
7.52112μsの実際の経過時間がかかっており、1秒あたり約167414プロセスが生成されています。
![image](https://qiita-image-store.s3.amazonaws.com/0/150252/2f951242-a468-d3a8-47c4-6d8a046a1c1c.png)
※今回の検証マシンでは、25,000,000個のプロセス生成が限界近く。


30,000,000個のプロセス生成では、

```erlang:3000万プロセス生成
[root@node001 ~]# erl +P 30000000
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:24:24] [async-threads:10] [kernel-poll:false]

Eshell V8.1  (abort with ^G)
1> measure_processes:limit(30000000).
Limit allowed processes:33554432
強制終了
[root@node001 ~]#
```
```tex:/var/log/messages
Dec 10 18:44:32 localhost kernel: [ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name
...中略
Dec 10 18:44:32 localhost kernel: [  383]     0   383 17690371 16208884   32960   475338             0 beam.smp
...中略
Dec 10 18:44:32 localhost kernel: Out of memory: Kill process 383 (beam.smp) score 955 or sacrifice child
Dec 10 18:44:32 localhost kernel: Killed process 383 (beam.smp) total-vm:70761484kB, anon-rss:64835536kB, file-rss:0kB
```
OOMキラーによるプロセス落ちとなっています。
なおこれは今回の検証環境リソース及び検証方法の問題であり、Erlangについてではありません。

# まとめと雑感
今回単純にErlangの軽量プロセスを大量に生成してその1プロセス生成あたりの時間を計測してみました。
※チューニング等によりさらに高速に処理できるようです。
他にも色々工夫ができそうですが、とりあえず考えてから手を動かしてみると色々わかりました。

週2回、職場のお昼休憩時間に、[『すごいErlangゆかいに学ぼう!』](https://www.amazon.co.jp/すごいErlangゆかいに学ぼう-Fred-Hebert/dp/4274069125/)の輪読会をしたりしています。
今後もErlangの面白いところ便利なところを色々発見していきたいです。
※軽量プロセスの説明については[この記事](https://ubiteku.oinker.me/2016/08/09/how-do-erlang-microprocesses-work-internally/)が面白かった。

それでは明日もよろしくお願いします！