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:
parent
28e44d0abd
commit
42827362b6
4 changed files with 25 additions and 18 deletions
2
Gemfile
2
Gemfile
|
@ -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'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
BIN
vendor/cache/parallel-0.5.17.gem
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue