While we were working on speeding the run time of our specs, it came to our attention that the Paperclip gem contributes a fair amount to the slowness of the specs, mainly because of its dependency on external software like ImageMagick’s convert and identify; and Unix’s file. Shelling-out may not be this slow, but when done a lot, it starts to have a visible effect. After digging into Paperclip source code, we came up with some fixes that worked well for us. Let’s get started.
Using posix-spawn gem
This is an easy one, yet it wasn’t this straightforward to find. Paperclip depends on cocaine gem to run the aforementioned commands. cocaine supports multiple strategies for how to run commands, from which it chooses the best strategy to use on the running platform. Our gemset didn’t have posix-spawn installed, so cocaine uses the next best strategy, which is to use Process.spawn. posix-spawn gem provides a spawn method as well, so in order to know the difference between each we quote posix-spawn‘s README:
fork(2) calls slow down as the parent process uses more memory due to the need to copy page tables. In many common uses of fork(), where it is followed by one of the exec family of functions to spawn child processes (Kernel#system, IO::popen, Process::spawn, etc.), it’s possible to remove this overhead by using special process spawning interfaces (posix_spawn(), vfork(), etc.)
Identify animated files cheaply
As part of creating thumbnails out of the original attached file, Paperclip checks if the original file is an animated file. Currently, the only format recognized as animated is GIF, but other formats can be added in the future. But this future hasn’t arrived since mid-2011, so for the time being, we can assume it will only be GIF files that are checked. Checking is done using ImageMagick’s
identify command, and we already established that we want to minimize our shelling-out. It turns out that checking if a file is GIF or not can be done easily by simply reading the first six bytes of the file and comparing it to either “GIF87a” or “GIF89a” (the magic numbers of GIF files).
So we monkey-patch Paperclip::Thumbnail class like this (e.g. in an initializer):
%w(GIF87a GIF89a).include? File.read(@file.path, 6)
<h3 style="text-align: center;">Disabling post-processing in test environment</h3>
This one is a bit subjective. In the case of our specs, we don't care for the generation of the thumbnails of the attached images, so why bother running commands like <em>convert</em> or <em>identify</em> if we're gonna delete the resulting thumbnails by the end of the suite run?
So we add this snippet to our <em>spec_helper.rb</em> or to a support file that's <em>required</em> by <em>spec_helper.rb</em>:
Applying these methods saved us about five minutes in test running time, which is definitely an improvement.
NOW IT’S YOUR TURN: How have you optimized Paperclip Gem? Let us know in the comments below.