Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sonots/62179703d429d36b1a57 to your computer and use it in GitHub Desktop.
Save sonots/62179703d429d36b1a57 to your computer and use it in GitHub Desktop.

前置き

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点 でした。

kazeburo の術を適用

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 点超えました🙏

@babie
Copy link

babie commented Sep 10, 2015

command のとこ、mix phoenix.server だとコンソールと接続されたままで遅いというか、elixir --detached -S mix phoenix.server にするのが正式なのでこちらで試してもらえると嬉しいです:
http://www.phoenixframework.org/docs/deployment

@sonots
Copy link
Author

sonots commented Sep 10, 2015

ほう!ありがとうございます!と思ったんですが、遅い、というような記述はないですよね?supervisord で daemonize してるんで、foreground で動かしてて問題なさそうに見えます。

@babie
Copy link

babie commented Sep 11, 2015

あー、supervisord だと空回りで出力をしないのかな? supervisord については知識不足なので申し訳ありません。

デモナイズしても --detached 付けないとメモリが溢れるという記事があったので、
http://qiita.com/maruware/items/7765837384795b1d9659
Erlang/Elixir は裏で何かやっているという印象があったんですが、ベンチマーカーが動く範囲では完動してるからいいのかも?

@sonots
Copy link
Author

sonots commented Sep 11, 2015

なるほど、公式のドキュメントには書いてないですが、メモリが溢れる現象にあたった人がいるようだ、と。ちょっと試してみますね。

@sonots
Copy link
Author

sonots commented Sep 11, 2015

$ sudo supervisorctl stop isucon_elixir
$ cd webapp/elixir
$ /home/isucon/env.sh elixir --detached -S mix phoenix.server
[isucon@ip-172-31-24-197 ~]$ ./benchmarker bench --workload 8
05:17:54 type:info      message:launch benchmarker
05:17:54 type:warning   message:Result not sent to server because API key is not set
05:17:54 type:info      message:init environment
05:18:03 type:info      message:run benchmark workload: 8
05:19:03 type:info      message:finish benchmark workload: 8
05:19:08 type:info      message:check banned ips and locked users report
05:19:10 type:report    count:banned ips        value:579
05:19:10 type:report    count:locked users      value:4357
05:19:10 type:info      message:Result not sent to server because API key is not set
05:19:10 type:score     success:187650  fail:0  score:40535

40000 点超えました!メモリ使用量に関しては特に大きな差異はなさそうでした。

FYI: あと、念のため /home/isucon/env.sh elixir --detached -S mix do compile, phoenix.server もやってみましたが、特に差異なさそうです。

@babie
Copy link

babie commented Sep 11, 2015

お手を煩わせてすみません。おかげでElixirもなかなかだと分かりましたし私もすっきりしました。感謝です:pray:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment