先日、Rails製のアプリケーションのパフォーマンス検証をする機会がありました。
計測したいのはメモリ使用量と実行時間の2つです。後者の実行時間に関してはRubyが標準ライブラリとして提供しているbenchmark
を使えば事足りますが、前者のメモリ使用量となるとRubyでは計測する方法が限られてきます。
どうしたものかな...と調べていたところ、以下の記事を発見しました。
Rubyでメモリ使用量を計測する2つの方法が紹介されています。
- ObjectSpace.memsize_of_all: Rubyオブジェクトが使用するメモリの総量
- rss: OSの1プロセス(Rubyを実行している)が使用するメモリの送料
この計測方法と実行時間の計測をまとめて便利に計測を行えるクラスを作成したので、共有したいと思います。
パフォーマンス計測のクラス
元々は単一の関数として定義しようかと思いましたが、最終的にクラスになりました。
このクラスを貼り付ければ、全ての準備が完了するようにしてあります。外部ライブラリは使用していないので、Rubyがインストールされていれば実行可能です。
class Messurement def initialize require 'benchmark' require 'objspace' @benchmark_caption = Benchmark::CAPTION @MESSURE_RESULT = Struct.new( 'MessurementResult', :label, :before_moa, :before_rss, :after_moa, :after_rss, :benchmark, ) end def exec(label, &block) raise 'Block not specified' unless block before_moa, before_rss = memory_values() benchmark_result = Benchmark.measure { yield } after_moa, after_rss = memory_values() @MESSURE_RESULT.new( label, before_moa, before_rss, after_moa, after_rss, benchmark_result, ) end def print_exec(label, &block) result = exec(label, &block) puts "Result of 「#{label}」" puts "[BEFORE] memsize_of_all: #{result.before_moa} MB, rss: #{result.before_rss} MB" puts "[AFTER] memsize_of_all: #{result.after_moa} MB, rss: #{result.after_rss} MB" puts '-Benchmark Result------' puts @benchmark_caption puts result.benchmark puts '-----------------------' end private def memory_values memsize_of_all = (ObjectSpace.memsize_of_all * 0.001 * 0.001).round(2) rss = (`ps -o rss= -p #{Process.pid}`.to_i * 0.001).round(2) [memsize_of_all, rss] end end
使い方としては、Messurementクラスのインスタンスを作成後、exec関数かexec_print関数を呼び出すのみです。
exec関数は計測結果を構造体として返しますが、exec_print関数は結果を標準出力するようにしました。
構造体には実行前のメモリ使用量(memsize_of_all, rss)と実行後のメモリ使用量。benchmark
で計測した実行結果が含まれています。
サンプル
exec
messure = Messurement.new messure.exec '1 + 1' do 1 + 1 end #<struct Struct::MessurementResult label="1 + 1", before_moa=4.12, before_rss=20.83, after_moa=4.13, after_rss=20.83, benchmark= #<Benchmark::Tms:0x00000001263ceb10 @cstime=0.0, @cutime=0.0, @label="", @real=4.999339580535889e-06, @stime=5.999999999950489e-06, @total=1.6999999999933735e-05, @utime=1.0999999999983245e-05>>
exec_print
messure = Messurement.new messure.print_exec '1 + 1' do 1 + 1 end Result of 「1 + 1」 [BEFORE] memsize_of_all: 3.6 MB, rss: 18.64 MB [AFTER] memsize_of_all: 3.6 MB, rss: 18.66 MB -Benchmark Result------ user system total real 0.000017 0.000006 0.000023 ( 0.000006) -----------------------
ブロックを指定して計測したい処理を受け渡すようにしたのは完全にRspecを意識しています。
ObjectSpaceたるモジュールについて全く知らなかったので勉強になりましたし、他にパフォーマンスを計測できる方法や取得可能な値がないか、読み込んでみようと思います。
少しでも「ええな〜」と思ったらはてなスター・はてなブックマーク・シェアを頂けると励みになります。