From 4810a841a0256d8e31039279f8543c50a584716c Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 9 Jan 2019 12:49:40 +1100 Subject: [PATCH] PERF: reduce workload when optimizing images Previously, we would initialize an ImageOptim object each time we resize. This object init is mega expensive (170ms on a VERY fast machine): ``` [1] pry(main)> Benchmark.measure { FileHelper.image_optim } => # ``` This happens cause during init it hunts for all the right binaries and sets up internals. We now memoize this object to avoid a huge amount of pointless work. --- lib/file_helper.rb | 50 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/file_helper.rb b/lib/file_helper.rb index 21c0cd79bc..702f302d46 100644 --- a/lib/file_helper.rb +++ b/lib/file_helper.rb @@ -82,24 +82,44 @@ class FileHelper tmp end - def self.optimize_image!(filename) - ImageOptim.new( - # GLOBAL - timeout: 15, - skip_missing_workers: true, - # PNG - optipng: { level: 2, strip: SiteSetting.strip_image_metadata }, - advpng: false, - pngcrush: false, - pngout: false, - pngquant: false, - # JPG - jpegoptim: { strip: SiteSetting.strip_image_metadata ? "all" : "none" }, - jpegtran: false, - jpegrecompress: false, + def self.optimize_image!(filename, allow_pngquant: false) + image_optim( + allow_pngquant: allow_pngquant, + strip_image_metadata: SiteSetting.strip_image_metadata ).optimize_image!(filename) end + def self.image_optim(allow_pngquant: false, strip_image_metadata: true) + # memoization is critical, initializing an ImageOptim object is very expensive + # sometimes up to 200ms searching for binaries and looking at versions + memoize("image_optim", allow_pngquant, strip_image_metadata) do + pngquant_options = false + if allow_pngquant + pngquant_options = { allow_lossy: true } + end + + ImageOptim.new( + # GLOBAL + timeout: 15, + skip_missing_workers: true, + # PNG + optipng: { level: 2, strip: strip_image_metadata }, + advpng: false, + pngcrush: false, + pngout: false, + pngquant: pngquant_options, + # JPG + jpegoptim: { strip: strip_image_metadata ? "all" : "none" }, + jpegtran: false, + jpegrecompress: false, + ) + end + end + + def self.memoize(*args) + (@memoized ||= {})[args] ||= yield + end + private def self.images