Rubyの実装で利用されるメモリを計測する方法には主に以下の2つがあります。
- psコマンドで実行プロセスのrss(物理メモリ使用量)を計測する
- ObjectSpaceモジュールでRubyオブジェクトのメモリ使用量を計測する
今回は上記2つの具体的な利用方法について紹介します。
なおObjectSpaceモジュールには『任意のオブジェクトのメモリを使用量を計測するmemsize_of』や『ブロック内に囲まれたオブジェクトをトレースするtrace_object_allocations』がありますが、今回は『Rubyオブジェクト全体のメモリ使用量を計測するmemsize_of_all』を利用した方法について紹介します。
メモリ使用量の計測方法
『メモリ使用量を計測 → 計測対象のロジックを実行 → メモリ使用量を計測』とすることで、ロジック内でどれだけメモリが利用されたかわかります。
たとえば、以下のようにすることでBook.all.each
というブロックで利用されるメモリが計測できます。
lib/tasks/batch.rake
namespace :batch do
desc "example"
task example: :environment do
require 'objspace'
# ObjectSpaceはデフォルトがバイト表示
# psのrssはデフォルトがキロバイト表示
# Process.pidはカレントプロセスのプロセスID
puts "[BEFORE] memsize_of_all: #{(ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2)} MB, rss: #{(`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2)} MB"
Book.all.each do |book|
book.price += 50
book.save!
end
puts "[AFTER] memsize_of_all: #{(ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2)} MB, rss: #{(`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2)} MB"
end
end
実行結果
> rails batch:example
[BEFORE] memsize_of_all: 20.31 MB, rss: 75.57 MB
[AFTER] memsize_of_all: 30.5 MB, rss: 89.55 MB
以下のようにロジックの途中でメモリ使用量を出力することで、メモリ使用量の変化が確認できます。
lib/tasks/batch.rake
namespace :batch do
desc "example"
task example: :environment do
require 'objspace'
puts "[BEFORE] memsize_of_all: #{(ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2)} MB, rss: #{(`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2)} MB"
Book.all.each_with_index do |book, n|
book.price += 50
book.save!
# 100回ごとに出力する
if n % 100 == 0
puts "[Book #{n}] memsize_of_all: #{(ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2)} MB, rss: #{(`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2)} MB"
end
end
puts "[AFTER] memsize_of_all: #{(ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2)} MB, rss: #{(`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2)} MB"
end
end
実行結果
> rails batch:example
[BEFORE] memsize_of_all: 20.33 MB, rss: 75.47 MB
[Book 0] memsize_of_all: 22.6 MB, rss: 81.27 MB
[Book 100] memsize_of_all: 25.99 MB, rss: 88.91 MB
[Book 200] memsize_of_all: 23.75 MB, rss: 89.16 MB
[Book 300] memsize_of_all: 32.49 MB, rss: 89.23 MB
[Book 400] memsize_of_all: 24.38 MB, rss: 89.24 MB
[Book 500] memsize_of_all: 27.09 MB, rss: 89.32 MB
[Book 600] memsize_of_all: 31.13 MB, rss: 89.34 MB
[Book 700] memsize_of_all: 29.55 MB, rss: 89.5 MB
[Book 800] memsize_of_all: 30.12 MB, rss: 89.52 MB
[Book 900] memsize_of_all: 26.84 MB, rss: 89.54 MB
[AFTER] memsize_of_all: 31.66 MB, rss: 89.58 MB
参考: メモリ使用量の計測をメソッド化する
Processing large CSV files with Rubyではyieldを利用してメモリ使用量の計測をメソッド化しています。
参考としてメモリ使用量をメソッド化したソースコードを紹介します。
lib/tasks/batch.rake
namespace :batch do
desc "example"
task example: :environment do
print_memory_usage do
Book.all.each do |book|
book.price += 50
book.save!
end
end
end
def print_memory_usage
require 'objspace'
memsize_before = ObjectSpace.memsize_of_all * 0.001 * 0.001
rss_before = `ps -o rss= -p #{Process.pid}`.to_i * 0.001
yield
memsize_after = ObjectSpace.memsize_of_all * 0.001 * 0.001
rss_after = `ps -o rss= -p #{Process.pid}`.to_i * 0.001
puts "memsize_of_all: #{(memsize_after - memsize_before).round(2)} MB, rss: #{(rss_after - rss_before).round(2)} MB"
end
end
実行結果
> rails batch:example
memsize_of_all: 11.31 MB, rss: 14.02 MB
さらにBenchmark.realtimeを利用すれば実行時間も計測できます。
namespace :batch do
desc "example"
task example: :environment do
print_memory_usage do
print_time_spent do
Book.all.each do |book|
book.price += 50
book.save!
end
end
end
end
def print_memory_usage
require 'objspace'
memsize_before = ObjectSpace.memsize_of_all * 0.001 * 0.001
rss_before = `ps -o rss= -p #{Process.pid}`.to_i * 0.001
yield
memsize_after = ObjectSpace.memsize_of_all * 0.001 * 0.001
rss_after = `ps -o rss= -p #{Process.pid}`.to_i * 0.001
puts "memsize_of_all: #{(memsize_after - memsize_before).round(2)} MB, rss: #{(rss_after - rss_before).round(2)} MB"
end
def print_time_spent
time = Benchmark.realtime do
yield
end
puts "Time: #{time.round(2)} secs"
end
end
実行結果
> rails batch:example
Time: 10.83 secs
memsize_of_all: 10.96 MB, rss: 14.21 MB
まとめ
- 『ObjectSpace.memsize_of_all』でRubyオブジェクトのメモリ使用量を確認できる
- 『ps -o rss= -p #{Process.pid}』でカレントプロセス(Rubyアプリケーション)の物理メモリ使用量を確認できる
- ロジック前後のObjectSpaceやrssを比較することで、ロジックで利用されたメモリを計測できる
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!