ISUCONにRubyで挑戦するときに役立つStackProf Web navigatorの使い方

ISUCON9の予選が近づいてきましたね。

私自身、昨年のISUCON8にはGo言語で出場したのですが、今年はチームメンバーの都合上Rubyで出場することになりそうです。

ISUCONではきちんとプロファイリングして根拠のあるチューニングを行うようにしているのですが、Rubyでは何を使ってプロファイリングすべきかわからなかったので調べてみました。
(ちなみに、Go言語だとpprofを使っていればそれなりに解析ができていました)

結果、[stackprof](https://github.com/tmm1/stackprof)が使えそうということがわかりました。
加えて、[stackprof-webnav](https://github.com/alisnic/stackprof-webnav)を併用することでWebブラウザから解析結果を閲覧できるようになることもわかりました。
これはISUCONにおいてチームメンバーとプロファイリング結果を共有する際に便利です。

![Stackprof_navigator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60996/f04f2029-2f5b-2408-2bfb-1c181ccf5373.png)

![Stackprof_navigator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60996/749021cb-32c8-8d21-42ab-427a28c140f4.png)

![Stackprof_navigator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60996/9c6fe042-8d38-7dda-0fd0-ba0b71569af0.png)



例題として [Y!SUCON](https://github.com/yahoojapan/yisucon) のソースコードを使わせていただきました。
いつもありがとうございます。

## インストール

Gemを追加する。

```ruby:/var/www/webapp/ruby/Gemfile
gem 'stackprof'
gem 'stackprof-webnav'
```

```bash
$ bundle install
```

stackprofをmiddlewareとしてrackに組み込む。

```ruby:/var/www/webapp/ruby/isuwitter.rb
require 'digest/sha1'
require 'json'
require 'net/http'

require 'sinatra/base'
require 'sinatra/json'
require 'mysql2-cs-bind'
# ここから追記
require 'stackprof'
# ここまで追記

module Isuwitter
  class WebApp < Sinatra::Base
    # ここから追記
    use StackProf::Middleware, enabled: true,
                               mode: :cpu,
                               interval: 1000,
                               save_every: 5
    # ここまで追記
    use Rack::Session::Cookie, key: 'isu_session', secret: 'kioicho'
    set :public_folder, File.expand_path('../../public', __FILE__)
    // 中略
  end
end
```

stackprofのdumpファイルを格納するためtmpディレクトリを作成しておきます。

```bash
$ mkdir /var/www/webapp/ruby/tmp
$ chmod 777 /var/www/webapp/ruby/tmp # 他ユーザによる書き込みを許可
```

## プロファイリングする

上記のようにコードを仕込んだあと、アプリを再起動します。
[Y!SUCON](https://github.com/yahoojapan/yisucon)の場合はsystemdで再起動できるようになっています。

```
$ sudo systemctl restart isucon-ruby-isuwitter
```

この状態でベンチマークを実行します。(もしくは調査したいページを開きます)

<img width="1469" alt="Y_SUCON_Portal_と_Stackprof_navigator.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60996/9fc9f6d0-c174-7b15-1bdc-9b875bc971d3.png">

これにてプロファイリングは完了です。

## ブラウザ経由でレポートを閲覧する

stackprof-webnavを実行することで、ブラウザ経由でレポートを閲覧できます。

```bash
$ stackprof-webnav -f tmp/stackprof-cpu-*.dump
```

ただし、これだと `localhost:9292` でしかlistenしていないので、別ホストからページを開くことができません。
このため、nginxでリバースプロキシするよう設定します。

```/etc/nginx/nginx.conf
user nobody;
worker_processes 1;

events {
  worker_connections 1024;
}

http {
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  keepalive_timeout 65;

  server {
    listen 80;
    root /var/www/webapp/public;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://localhost:8080;
    }
  }
  # ここから追記
  server {
    listen 19292;
    root /var/www/webapp/public;

    location / {
      proxy_set_header Host $host;
      proxy_pass http://localhost:9292;
    }
  }
  # ここまで追記
}
```

confを修正したらnginxを再起動するのを忘れずに。

```bash
$ sudo systemctl restart nginx
$ stackprof-webnav -f tmp/stackprof-cpu-*.dump
```

ブラウザを開き、`http:(サーバのIPアドレス):19292/overview` にアクセスするとstackprofの結果を閲覧できます。

![Stackprof_navigator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60996/f04f2029-2f5b-2408-2bfb-1c181ccf5373.png)