This repo aims to demonstrate the performance of ruby-vips, the Ruby binding
for the libvips library, when used with the CarrierWave
file uploader
plugin for Ruby on Rails framework.
There is similar repository, vips-benchmarks, which focuses on the advantages ruby-vips has over other image processing libraries. This repo is intended to show, how these advantages spread to Rails territory.
This benchmark uses the carrierwave-vips gem written by Jeremy Nicoll, other participants are modules which are currently used in carrierwave master branch.
Last update: May 24, 2013
$ bundle exec ./runner
removing previous files uploaded by carrierwave...
Linux mm-jcupitt2 3.5.0-21-generic #32-Ubuntu SMP Tue Dec 11 18:51:59 UTC 2012
x86_64 x86_64 x86_64 GNU/Linux
This is RMagick 2.13.1 ($Date: 2009/12/20 02:33:33 $) Copyright (C) 2009 by
Timothy P. Hunter
Built with ImageMagick 6.7.7-10 2012-08-17 Q16 http://www.imagemagick.org
Built for ruby 1.9.3
Web page: http://rmagick.rubyforge.org
Email: [email protected]
MiniMagick 3.4
Ruby-vips 0.3.5 built against libvips 7.30.7-Tue Jan 15 11:40:02 GMT 2013
Timing (fastest wall-clock time of 3 runs):
ruby-vips, jpeg image: 56ms
rmagick, jpeg image: 166ms
mini_magick, jpeg image: 348ms
ruby-vips, png image: 1849ms
rmagick, png image: 13105ms
mini_magick, png image: 13445ms
Peak memuse (max of sum of mmap and brk, excluding sub-processes):
vips ... 60 MB
mini-magick ... 62 MB
rmagick ... 195 MB
Memory use is measured by watching strace output for brk and mmap calls, see Tim Starling's blog post.
We've timed for a 1024 x 768 JPEG image and a 5120 x 3840 PNG image.
JPEG images can be shrunk very quickly by subsampling during load, so we wanted to include a PNG as well to stress the memory systems on these libraries a little more.
MiniMagick does all processing in a forked
mogrify
command, so its direct memory use for image processing is zero.
Looking at top
, mogrify is is using about 150mb on this machine.
Darwin Stanislaws-MacBook-Air.local 11.4.2 Darwin Kernel Version 11.4.2:
Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64
This is RMagick 2.13.2 ($Date: 2009/12/20 02:33:33 $) Copyright (C) 2009
by Timothy P. Hunter
Built with ImageMagick 6.8.0-10 2013-03-03 Q16
http://www.imagemagick.org
Built for ruby 1.9.3
Web page: http://rmagick.rubyforge.org
Email: [email protected]
MiniMagick 3.4
Ruby-vips 0.3.5 built against libvips 7.32.1-Fri May 24 01:10:57 EEST
2013
Timing (fastest wall-clock time of 3 runs):
ruby-vips, jpeg image: 109ms
ruby-vips, png image: 1595ms
rmagick, jpeg image: 179ms
rmagick, png image: 6290ms
mini_magick, jpeg image: 540ms
mini_magick, png image: 6189ms
This machine has a faster CPU and has an SSD rather than a mechanical hard disc, but has a much slower jpeg decoder.
A similar procedure is run using each uploader. Each uploader is being run in its own file.
require 'benchmark'
def image src
File.open src
end
module Procedure
NUMBER = 1
class << self
def run processor, img, best_of
result = nil
capture_stdout do
result = Benchmark.bmbm do |b|
(1 .. best_of).each do |number|
b.report number.to_s do
NUMBER.times do
u = User.new :name => 'first'
u.send :"#{processor}_avatar=", img
u.save!
end
end
end
end
end
output result
end
def output result
result = (result.map(&:to_a).map{|el| el[5]}.min * 1000).to_i
puts "#{result}ms"
end
end
end
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => ':memory:'
)
ActiveRecord::Schema.define do
create_table :users, :force => true do |t|
t.integer :name
t.string :rmagick_avatar
t.string :mini_magick_avatar
t.string :vips_avatar
end
end
Besides uploading original file, each uploader has 3 versions to generate. Let's take one of them for example:
# encoding: utf-8
class RMagickUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# include CarrierWave::Uploader::Processing
# include CarrierWave::Vips
version :rtlimit do
process :resize_to_limit => [100, 100]
end
version :rtfit do
process :resize_to_fit => [100, 100]
end
version :rtfill do
process :resize_to_fill => [100, 100]
end
# Other stuff
end
require 'app/uploaders/rmagick_uploader'
require 'app/uploaders/mini_magick_uploader'
require 'app/uploaders/vips_uploader'
class User < ActiveRecord::Base
mount_uploader :rmagick_avatar, RMagickUploader
mount_uploader :mini_magick_avatar, MiniMagickUploader
mount_uploader :vips_avatar, VipsUploader
end
Run bundle
Runner running all sub-runners:
$ ./runner
# or just
$ rake
Runners for each of libraries:
$ ./runner-vips
$ ./runner-rmagick
$ ./runner-mini-magick
Feedback is appreciated!
Copyright (c) 2012 Stanislaw Pankevich, John Cupitt