elixir が結局速いのかそうでもないのか検証してみたいから、誰か isucon4 qual のアプリを elixir で。
— そのっつ (SEO Naotoshi) (@sonots) 2015, 9月 5
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
とかてきとうなことをつぶやいていたら、なんと @ma2ge さんが実装してくれました!
@sonots まずは動くところまで作ってみました。手元のマシンですが ruby との比較結果だけ README に載せています。benchmarker は通ったのですがバグなどあるかもしれません。 https://t.co/6WaI4LAbME
— taka (@ma2ge) 2015, 9月 7
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
@ma2ge おお、すごい!!
— そのっつ (SEO Naotoshi) (@sonots) 2015, 9月 7
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
コードはこちらにあるようです > https://github.com/ma2gedev/isucon4-qual-phoenix
ということで、早速試しに ISUCON4 qual を題材にして、例によって kazeburo の術 を適用した状態で、どのぐらいのスコアが出るのか試してみました。
CentOS6 に Erlang / Elixir のインストール をして、
isucon $ cd webapp
isucon $ git clone https://github.com/ma2gedev/isucon4-qual-phoenix
あとは README 通りにやってみたら動きました🙏
他のアプリと同様に supervisord で動かすようにします。
/etc/supervisord.conf
+[program:isucon_elixir]
+directory=/home/isucon/webapp/elixir
+command=/home/isucon/env.sh mix phoenix.server
+user=isucon
+stdout_logfile=/tmp/isucon.elixir.log
+stderr_logfile=/tmp/isucon.elixir.log
+autostart=true
[program:isucon_ruby]
directory=/home/isucon/webapp/ruby
command=/home/isucon/env.sh foreman start
user=isucon
stdout_logfile=/tmp/isucon.ruby.log
stderr_logfile=/tmp/isucon.ruby.log
-autostart=true
+autostart=false
/home/isucon/env.sh
MIX_ENV=prod
で production モードにしないと benchmark で too many open files
が出て測定できなかったので、標準で production モードにしちゃっています。あと、何に使ってるのかは知りませんが HOME
がないとエラーが出たので定義しています。
export PATH=/opt/elixir/bin:$PATH
export HOME=/home/isucon
export PORT=8080
export MIX_ENV=prod
supervisord を再起動
sudo /usr/bin/supervisorctl reload
[isucon@ip-172-31-18-166 ~]$ ./benchmarker bench
13:20:37 type:info message:launch benchmarker
13:20:37 type:warning message:Result not sent to server because API key is not set
13:20:37 type:info message:init environment
13:20:45 type:info message:run benchmark workload: 1
13:21:45 type:info message:finish benchmark workload: 1
13:21:50 type:info message:check banned ips and locked users report
13:22:10 type:report count:banned ips value:3
13:22:10 type:report count:locked users value:2572
13:22:10 type:info message:Result not sent to server because API key is not set
13:22:10 type:score success:6850 fail:0 score:1480
1480点
ちなみに、公平に RACK_ENV=production にした ruby アプリで測ったところ、
[isucon@ip-172-31-18-166 ~]$ ./benchmarker bench
13:25:49 type:info message:launch benchmarker
13:25:49 type:warning message:Result not sent to server because API key is not set
13:25:49 type:info message:init environment
13:25:57 type:info message:run benchmark workload: 1
13:26:57 type:info message:finish benchmark workload: 1
13:27:02 type:info message:check banned ips and locked users report
13:27:20 type:report count:banned ips value:3
13:27:20 type:report count:locked users value:2569
13:27:20 type:info message:Result not sent to server because API key is not set
13:27:20 type:score success:6370 fail:0 score:1376
1376点 でした。
MySQL の index。init.sh に以下を追加
$ cat init.sh
cat <<'EOF' | mysql -h ${myhost} -P ${myport} -u ${myuser} ${mydb}
alter table login_log add index ip (ip), add index user_id (user_id);
EOF
/etc/sysctl.conf に以下を追加。
$ cat /etc/sysctl.conf
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.ip_local_port_range = 10000 65000
net.core.somaxconn = 32768
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 10
sudo /sbin/sysctl -p
で適用
/etc/nginx/nginx.conf の設定。erlang は unix domain socket 非対応っぽい?ので、8080 port 指定のままにして、代わりに keepalive 設定を有効にしました。
worker_processes 1;
events {
worker_connections 10000;
}
http {
include mime.types;
access_log off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
etag off;
upstream app {
server 127.0.0.1:8080;
keepalive 16;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://app;
}
location ~ ^/(stylesheets|images)/ {
open_file_cache max=100;
root /home/isucon/webapp/public;
}
}
}
/etc/my.conf の設定
$ cat /etc/my.cnf
innodb_buffer_pool_size = 1G
innodb_flush_log_at_trx_commit = 0
innodb_flush_method=O_DIRECT
再起動して適用
$ sudo service mysqld restart
$ sudo /usr/bin/supervisorctl reload
$ sudo service nginx restart
golang の GOMAXPROCS のようなものがあるか探しみたところ、「スケジューラスレッド数のデフォルトはCPUのコア数になります」http://www.mikage.to/erlang/#toc_5 という記述を見つけたので、デフォルトでよさそう、ということでそのままにしてみました (というか elixir でのいじり方わかってない ^^;
いよいよ benchmark
$ ./benchmarker bench --workload 8
13:38:53 type:info message:launch benchmarker
13:38:53 type:warning message:Result not sent to server because API key is not set
13:38:53 type:info message:init environment
13:39:02 type:info message:run benchmark workload: 8
13:40:02 type:info message:finish benchmark workload: 8
13:40:07 type:info message:check banned ips and locked users report
13:40:09 type:report count:banned ips value:546
13:40:09 type:report count:locked users value:4319
13:40:10 type:info message:Result not sent to server because API key is not set
13:40:10 type:score success:177690 fail:0 score:38385
kazeburo の術で 38385 点でした。他の言語でも 400000 点近く出ているので、同程度と言えそうです。
Elixir アプリがデフォルトで動いている kazeburo の術適用前状態の AMI イメージを作ってみたので試したい方はこれを使ってみると良いかも > ami-b28309b2
補足: もっと処理系のみに依存したベンチを取ると、golang > erlang > ruby みたいな構図になるのかもしれません。わかりません。Phoenix ウェブフレームワーク を使うようなウェブアプリというコンテキストにおいては、同程度出るとみなしていいのでは、という結論になります。
追記: コメントにあるように /home/isucon/env.sh elixir --detached -S mix phoenix.server
で起動して、40000 点超えました🙏
あー、supervisord だと空回りで出力をしないのかな? supervisord については知識不足なので申し訳ありません。
デモナイズしても --detached 付けないとメモリが溢れるという記事があったので、
http://qiita.com/maruware/items/7765837384795b1d9659
Erlang/Elixir は裏で何かやっているという印象があったんですが、ベンチマーカーが動く範囲では完動してるからいいのかも?