Make full use of speed_gun

こんにちは、ロージーです。今回は手前味噌 gem である、 speed_gun の活用方法について解説します。

## SpeedGun とは

more better performance profiler for rails or rack として開発されたパフォーマンス測定 gem です。

もっと詳しい解説記事は[こちら](http://rosylilly.hatenablog.com/entry/2013/12/03/184748)。

今回、 speed_gun そのものの解説は割愛します。

## まずは使ってみる

サンプルアプリを作って、 speed_gun を導入します。まずは Rails アプリを作ります。

```shell-session
$ rails new speed_gun_example
...
```

これで、とりあえずひな形が出来上がりました。次は Gemfile に speed_gun を追加します。

```shell-session
$ echo "gem 'speed_gun'" >> Gemfile
$ bundle install
$ bundle list | grep speed_gun
  * speed_gun (0.0.1)
```

しっかり導入されていますね。じゃあ早速ですが Rails を起動しましょう！

```shell-session
$ bundle exec rails s
```

この状態で http://localhost:3000/ にアクセスすると、しっかり speed_gun が稼働しています。Rails の Welcome ページが表示され、右下に ***ms と小さな表示が出ていますね。

この表示をクリックすると、 speed_gun の詳細画面を見ることが出来ます。

Rack は 30ms ほどで返しているのに、ブラウザ側で DOM のパースや画像のロードに時間がかかり、結局全部終わって document の load イベントが発火しているのはアクセス開始から 300ms 後……などの情報がわかります。

このままでも ActiveRecord のクエリの実行時間や、 ActionView がレンダリングするビューの処理時間、 ActionController の処理時間など、様々な情報がわかりますが、せっかくなので他にもいろんなものを計測してみましょう。

## Rails の特定部分の処理を計測する

まずは適当に scaffold します。

```shell-session
$ bundle exec rails g scaffold Post title:string body:string
$ bundle exec rake db:migrate
```

この状態で http://localhost:3000/posts へアクセスすれば、それっぽい画面が出ているはずです。

さて、ではこの Post の表示時に、 body を markdown で表示できるようにしましょう。

markdown gem を導入して……

```shell-session
$ echo "gem 'markdown'" >> Gemfile
$ bundle install
```

`Post` モデルに `#body_with_markdown` を実装します。

```rb:app/models/post.rb
class Post < ActiveRecord::Base
  def body_with_markdown
    Markdown.new(body).to_html
  end
end
```

あとは `app/views/posts/show.html.erb` で `#body_with_markdown` を使うようにするだけですね。

```erb:app/views/posts/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<p>
  <strong>Body:</strong>
</p>

<div class="markdown">
<%== @post.body_with_markdown %>
</div>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
```

これで投稿内容が markdown を解釈して HTML で表示されるようになりました。

ここで、 speed_gun を使って `#body_with_markdown` のプロファイリングをしてみましょう。

先ほど実装した `Post#body_with_markdown` を以下のように書き換えます。

```rb:app/models/post.rb
  def body_with_markdown
    SpeedGun.profile('Post#body_with_markdown') do
      Markdown.new(body).to_html
    end
  end
```

この状態でもう一度実行すると、 `Manual: Post#body_with_markdown` という項目が speed_gun の詳細画面に追加されていると思います。

このように speed_gun なら簡単にプロファイリングを行うことが出来ます。

## カスタムプロファイラでメソッド監視

speed_gun ならカスタムプロファイラを使ったメソッド監視も簡単に実装できます。

`config/initializers/markdown_profile.rb` に以下のようなカスタムプロファイラを作ります。

```rb:config/initializers/markdown_profile.rb
require 'speed_gun/profiler/base'
require 'markdown'

class MarkdownProfiler < SpeedGun::Profiler::Base
  hook_method Markdown::Wrapper, :to_html
end
```

`#to_html` は `Markdown::Wrapper` に定義されているので、そこにフックしています。あとは Rails を再起動してもう一度 Post の個別ページをみると、 `MarkdownProfiler` という項目が増えていますね。

筆者の環境だと `Post#body_with_markdown` が 118ms で `#to_html` が 105ms なので、 `Markdown.new` にかかっている時間は大体 13ms ぐらいなのでしょうか？みたいなことを考えることが出来ます。

## Javascript のパフォーマンスだってちゃんと見たい

Rails のパフォーマンスと言うと、どうしても Ruby のコードそのものだけに注視しがちですが、リッチコンテンツを扱うこのご時世ですから、 Javascript のパフォーマンスだってちゃんと考えてあげなくてはいけません。

ということで、試しに Javascript で適当な処理を作りましょう。絵文字処理とかいいですね。やりましょう。

gemoji gem を導入します。

```shell-session
$ echo "gem 'gemoji'" >> Gemfile
$ bundle install
```

```rb:config/application.rb
module SpeedGunExample
  class Application < Rails::Application
    config.assets.paths << Emoji.images_path
  end
end
```

では、肝心の置換処理です。

```coffeescript:app/assets/javascripts/emoji.js.coffee
jQuery(($) ->
  $('.markdown p').each((index, paragraph) ->
    paragraph = $(paragraph)

    original = paragraph.html()

    paragraph.html(original.replace(/:(\w+?):/g, (matched) ->
      "<img class=\"emoji\" src=\"/assets/emoji/#{RegExp.$1}.png\"></img>"
    ))
  )
)
```

ずいぶん適当ですが、なにはともあれ、絵文字を表示することが出来ました。では、この JS 処理をプロファイリングしましょう。

```coffeescript:app/assets/javascripts/emoji.js.coffee
jQuery(($) ->
  speedGun.profile('Emoji', () ->
    $('.markdown p').each((index, paragraph) ->
      paragraph = $(paragraph)

      original = paragraph.html()

      paragraph.html(original.replace(/:(\w+?):/g, (matched) ->
        "<img class=\"emoji\" src=\"/assets/emoji/#{RegExp.$1}.png\"></img>"
      ))
    )
  )
)
```

単に処理部分を `speedGun.profile` でくくってあげただけです。

こうしてくくっておくことで、 JS のプロファイル結果をサーバー側に送信し、ためておくことが出来ます。もちろん、ここでとったプロファイルは speed_gun の詳細画面からも見ることが出来ます。

## まとめ

speed_gun には他にも `SpeedGun::Hook` などの便利な機能も存在していますが、今回は割愛します。

また、 speed_gun の詳細画面には Navigation Timing API を用いて、レンダリングやリダイレクトにかかった時間も表示されています。

他にも様々な活用法があると思うので、ぜひお試しください。