これの続きです。
1日百万回ぐらい実行されるアプリでパフォーマンスが出ず、問題はメモリ消費量では、ということで調査しました。
前回と同様、よくある適当な 1080 x 236 px の JPEG 画像を ImageMagick を使って適当な位置を切り取ってくるという処理をして速度を比較しています。
比較したのは以下の3つ。
- MiniMagick
- ファイル渡しで ImageMagick を直接叩く
- パイプ渡しで ImageMagick を直接叩く
環境
% ruby --version
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
比較方法
ps を叩いて ruby プロセスがどれだけメモリを消費しているかを出力します。対象は ruby だけなので ImageMagick の使用分は含まれてない、はず。(子プロセスは毎回終了されるのでここでは考慮しないでも平気だろう、という感じ)
適宜 minimagick
のところを入れ替えたり、GC.start
をコメントアウトしたりしながら、1つ1つ全てのパターンを調査してます。
require 'benchmark'
require 'mini_magick'
require 'tempfile'
require 'open3'
TARGET = 'common_case_1.jpg'
N = 10000
IMAGE = File.open(TARGET).read
def minimagick
piece_image = MiniMagick::Image.read(IMAGE)
piece_image.crop('450x556+0+0')
image = piece_image.to_blob
end
def imagemagick_file
Tempfile.open('tmp_piece_input', './') do |input_image|
Tempfile.open('tmp_piece_output', './') do |output_image|
input_image.write IMAGE
`convert -crop 450x556+0+0 #{input_image.path} #{output_image.path}`
output_image.read
end
end
end
def imagemagick_pipe
image,_ = Open3.capture2("convert -crop 450x556+0+0 jpeg:- jpeg:-", stdin_data: IMAGE, binmode: true)
end
def put_memory_size
puts `ps -o rss= -p #{Process.pid}`.to_i
end
counter = 0
N.times {
minimagick
put_memory_size
if (counter += 1) > 100
#GC.start
counter = 0
end
}
結果
縦軸はメモリ使用量、横軸はループ回数。
MiniMagick のメモリ使用量すごい。でもその前に自分で GC するの大切、という結果です。
さらに GC のタイミングを変えて比較した結果
軸は先程と同様、10, 100, 1000 は GC.start
するループの回数。10000 は自然に実行されるのに任せたもの。
考察
- 適当なタイミングで自前で GC 実行しないとヤバイ、GC 実行されるタイミングでってあまり詳しくないですが画像のように大量にメモリが必要な物を大量に回すにはなんか考えないと