- Webパフォーマンスについて - 「推測するな、計測せよ」のWebパフォーマンスにおける真の意味
- Web クライアントサイドのパフォーマンスメトリクス — Speed Index、Paint Timing、TTI etc...
- ソーシャルゲームのバックエンドの構成例
- Railsでパフォーマンスを低下させないために気をつけること
- Railsアプリケーションのパフォーマンス改善
- 横断的なチームを作ってパフォーマンス改善をやっている話
- マネーフォワード社内PRに見られるRubyの書き方について
- AWS クックパッドの運用事例
- Varnish入門と仕組み
- Varnish自前運用からFastly移行の際に調べたこと
- Synthetic Monitoring を活用したグローバルサービスのネットワークレイテンシの測定と改善
- 最初のバイトまでの時間を最適化して検索ランクを改善する
- Amazon Elasticsearch ServiceをつかったRDSのスロークエリの集計と監視
- ActiveRecord
- ActionView
https://developers.google.com/web/tools/chrome-devtools/rendering-tools?hl=ja
メモリを食う大きめのデータを取り回すと重くなる説ある。Ruby は「全てがオブジェクト」なためメモリ消費が多め、2.2 以前は GC の性能にも問題が合って顕著に遅かったみたい。
conditions = {
# ...
}
user_ids = []
User.where(conditions).each do |user|
#
# - each でクエリが実行される
# - SQL 実行のコスト
# - 仮にここで to_a すると User.where(conditions).count 分の
# ActiveRecord インスタンス生成 => 高コスト
# - user.id の参照により User インスタンスが都度生成され
# user.id を << で追加した後 user が GC で開放されない
#
user_ids << user.id
#
# user.save は User.where(conditions).count が増える毎に
# クエリが増えてしまういわゆる N + 1 アンチパターンになる
#
user.processed = true
user.save
end
#
# - pluck, update_all で User が増えても 2 回のクエリ
# - pluck なので User インスタンスが生成されない ( SQL 発行のみ )
# - update_all はモデルのコールバック/バリデーションが働かないことに注意
#
user_ids = User.where(conditions).pluck(:id)
User.where(id: user_ids).update_all(processed: true)
- ActiveRecordデータ処理アンチパターン
- メモリを意識したRubyプログラミング(翻訳)
- Rails: Active Record 5.2のメモリ肥大化を探る(翻訳)
- Railsパフォーマンスのヒント
- Rails.cacheについて
- Railsでクエリ結果をキャッシュしてDB負荷を軽減する
- ActiveRecord_Relation を保存したらそりゃ当然毎回クエリ走るわな
- to_a して保存したらデータ量は ActiveRecord_Relation より太るわな
- Rails: N+1クエリを「バッチング」で解決するBatchLoader gem(翻訳)
- バッチング ( 非同期でクエリを遅延評価 + キャッシュ ) 手法
- GraphQL のような「どんなフィールドの問い合わせがあるかわからない」API との相性よきらしい
- このバッチング + GraphQL の組み合わせなら graphql-batch というデファスタな gem もある
- EnumeratorとEnumerator::Lazyの違い
- 遅延評価を使うのが賢そう
- Rails向け高機能カウンタキャッシュ gem ‘counter_culture’ README(翻訳)
- flyerhzm/bullet - github.com
- N + 1 検証 gem
- 動的解析、アクション実行時にログで警告を出力
- ref
- joker1007/activerecord-cause - github.com
- SQL 発行時にログ出力させる gem
- どのタイミングで、どのファイル起因でクエリが走ったかわかるので SmartUI なんか見つけやすい
- brunofacca/active-record-query-trace
- 上と殆ど同じ
- kainosnoema/rack-lineprof - github.com
- Rack アプリ向けの行単位プロファイラ
- どの処理に時間がかかっているか見つけやすい
?lineprof=users_controller.rb
などで対象ファイルを指定すると標準出力にプロファイリング結果が出てくる- ref
- MiniProfiler/rack-mini-profiler - github.com
- ページごとのプロファイラ
- ページの左上に処理にかかった時間などプロファイリング結果を出してくれる
?pp=enable
?pp=disable
で表示切替も可能
- module Benchmark
- Ruby 組み込みクラス
Benchmark.benchmark do ... end
でブロック内のベンチマークが取れる- 修正前後の比較などに
さくっと確認するなら binding.pry
で止めて ActiveRecord::Associations::CollectionProxy
の #loaded?
で確認。
articles = Article.sample 3
articles.first.association(:author).loaded? # false
ログなんかで全体的に確認したいなら ActiveSupport::Notifications
の #subscribed
の第三引数にクエリ実行可能性のあるブロックを渡すような作りにするとよい。
require 'rails_helper'
RSpec.describe Order, type: :model do
specify "#average_line_gross_price_today eager loading" do
o = Order.new()
o.order_lines.build
o.order_lines.build
o.save!
count = count_queries { Order.average_line_gross_price_today }
expect(count).to eq(2)
# 2 を期待しつつ、実際の SQL トリガ回数は count_queries 内の
# ActiveSupport::Notifications.subscribed で購読した
# sql.active_record イベントのカウント総数が評価されるテスト
end
private
def count_queries &block
count = 0
counter_f = ->(name, started, finished, unique_id, payload) {
unless %w[ CACHE SCHEMA ].include?(payload[:name])
count += 1
end
}
ActiveSupport::Notifications.subscribed(
counter_f,
'sql.active_record',
&block
)
count
end
end
newrelic.com
New Relic ドキュメント - 日本代理店の非公式ドキュメント
WEB アプリケーションのパフォーマンス解析 SaaS 。Controller#action の以下のようなパフォーマンス指標を監視可能。詳細は別メモ参照。
- レスポンスタイム ( TTFB )
- ビューレンダリング
- DB スロークエリ
- Redis などのバックエンド通信
New Relic はバックエンドまでしか解析できないので ...
- アセットの取得速度
- Ajax など非同期処理速度
- ファーストビュー速度
- JavaScript 実行時間
- ページアイドルまでの時間
... などなどフロントエンド周りまで含めたパフォーマンスは Google PageSpeed Insights でチェックし、ローカルでは Chrome DevTools の Performance などでデバッグしていくようにする。
速度検査には Lighthouse を利用しており、こちらは Chrome 拡張もあるみたい。
https://chrome.google.com/webstore/detail/sonar/dibilcjfahbokhiodajibcajcabfjein/related