仕事でパフォーマンス改善のタスクを遂行していました。count
メソッドの使用を避けることでパフォーマンスを向上できることがわかったため、備忘録としてまとめます。
バージョン
- Ruby 3.0.3
- Rails 6.1.7.7
記事の信頼性
- ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
- 実務ではVue.jsとRailsを毎日書いています。
- 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
問題
パフォーマンス改善のタスクで、先輩エンジニアから「count
が使われているからsize
にした方がいい」とアドバイスをいただきました。
修正対象のコードは次の2行です。
def index
# 他の記述は省略
@withdrawals_count = withdrawals.count
@withdrawals_total_amount = withdrawals.sum(&:amount)
# 他の記述は省略
end
withdrawals
はActiveRecordから取得した、出金記録に関するオブジェクトです。
下記のように修正することになりました。
def index
# 他の記述は省略
@withdrawals_total_amount = withdrawals.sum(&:amount)
@withdrawals_count = withdrawals.size
# 他の記述は省略
end
1行目と2行目を入れ替えた上で、count
メソッドをsize
メソッドに変更しただけです。
ひとまず修正したものの、なぜこれがパフォーマンス改善につながるのか理解できませんでした、、。
解決方法
結論として、count
メソッドとsize
メソッドの内部挙動の違いによるものでした。
count
:使うたびに毎回SQLを発行するsize
:すでにメモリに読み込まれている場合はSQLを発行しない
もう一度、修正前のコードを見てみましょう。
def index
# 他の記述は省略
@withdrawals_count = withdrawals.count # 1回目のSQL発行
@withdrawals_total_amount = withdrawals.sum(&:amount) # 2回目のSQL発行
# 他の記述は省略
end
この書き方だと、count
とsum
で合計2回SQLが発行されることになります。
次に修正後のコードを再確認します。
def index
# 他の記述は省略
@withdrawals_total_amount = withdrawals.sum(&:amount) # 1回目のSQL発行
@withdrawals_count = withdrawals.size # 2回目のSQLは発行されない!
# 他の記述は省略
end
withdrawals.sum)
を実行した時点で、withdrawals
がメモリに読み込まれるため、withdrawals.size
の実行時にSQLは発行されません。
つまり、SQLの発行回数を減らせるため、パフォーマンス改善につながります!
おわりに
ActiveRecordの裏で発行されるSQLの回数まで意識したことがなかったため、とても勉強になりました。
これまでパフォーマンス改善のタスクではN+1の解消くらいしかできていませんでしたが、これを機により深くRailsのパフォーマンス改善と向き合っていきたいです。
コメント