optimize outfit image generation - 4x speed boost on my box

Use the ImageMagick flatten command to generate the output all at
once instead of compositing each layer individually, and download
the layers in parallel. On my box, saving roopal27 five times took
a total of 30 seconds before, whereas now it takes 7 seconds. I
expect it to be even better on the production box, where latency
is even lower.
This commit is contained in:
Emi Matchu 2012-07-27 23:07:20 -04:00
parent 28e44d0abd
commit 42827362b6
4 changed files with 25 additions and 18 deletions

View file

@ -46,6 +46,8 @@ gem "mini_magick", "~> 3.4"
gem "fog", "~> 1.1.2" gem "fog", "~> 1.1.2"
gem "carrierwave", "~> 0.5.8" gem "carrierwave", "~> 0.5.8"
gem "parallel", "~> 0.5.17"
group :development_async do group :development_async do
# async wrappers # async wrappers
gem 'eventmachine', :git => 'git://github.com/eventmachine/eventmachine.git' gem 'eventmachine', :git => 'git://github.com/eventmachine/eventmachine.git'

View file

@ -149,6 +149,7 @@ GEM
open4 (1.3.0) open4 (1.3.0)
openneo-auth-signatory (0.1.0) openneo-auth-signatory (0.1.0)
ruby-hmac ruby-hmac
parallel (0.5.17)
polyglot (0.3.3) polyglot (0.3.3)
rack (1.2.5) rack (1.2.5)
rack-fiber_pool (0.9.2) rack-fiber_pool (0.9.2)
@ -256,6 +257,7 @@ DEPENDENCIES
newrelic_rpm newrelic_rpm
nokogiri (~> 1.5.2) nokogiri (~> 1.5.2)
openneo-auth-signatory (~> 0.1.0) openneo-auth-signatory (~> 0.1.0)
parallel (~> 0.5.17)
rack-fiber_pool rack-fiber_pool
rails (= 3.0.5) rails (= 3.0.5)
rdiscount (~> 1.6.5) rdiscount (~> 1.6.5)

View file

@ -138,25 +138,28 @@ class Outfit < ActiveRecord::Base
# file. # file.
def create_image!(output) def create_image!(output)
unless image_layers.empty? unless image_layers.empty?
base_layer = image_layers.first temp_image_files = Parallel.map(image_layers, :in_threads => 8) do |swf_asset|
above_layers = image_layers[1..-1] image_file = Tempfile.open(['outfit_layer', '.png'])
write_temp_swf_asset_image!(base_layer, output) write_temp_swf_asset_image!(swf_asset, image_file)
output.close image_file.close
image_file
unless above_layers.empty?
Tempfile.open(['outfit_overlay', '.png']) do |overlay|
above_layers.each do |layer|
overlay.open
write_temp_swf_asset_image! layer, overlay
overlay.close
previous_image = MiniMagick::Image.open(output.path)
overlay_image = MiniMagick::Image.open(overlay.path)
output_image = previous_image.composite(overlay_image)
output_image.write output.path
end
end
end end
# Here we do some awkwardness to get the exact ImageMagick command we
# want, though it's still less awkward than handling the command
# ourselves. Give all of the temporary images as input, flatten them and
# write them to the output path.
command = MiniMagick::CommandBuilder.new('convert')
temp_image_files.each { |image_file| command.push image_file.path }
command.layers 'flatten'
command.push output.path
# Though the above command really is sufficient, we still need a dummy
# image to handle execution.
output_image = MiniMagick::Image.new(output.path)
output_image.run(command)
temp_image_files.each(&:unlink)
else else
output.close output.close
end end

BIN
vendor/cache/parallel-0.5.17.gem vendored Normal file

Binary file not shown.