@aldo, thanks for the info. I'll look into it. But it seems that I don't
find any problems atm.
Meanwhile, I've been playing a bit around to see what happens during the
precompilation task. (I used the task from rails head for testing, not
the buggy one included in rails 3.1).
I have noticed the 2 loops:
config.assets.precompile.each do |path|
env.each_logical_path do |logical_path|
........
end
end
if first starts to loop over all files that need to be precompiled. My
setting was a bit different. I had 4 extra files that needed to be
precompiled. So that meant, 4 extra loops.
In the inner loop, it starts to loop over all the assets available.
First I thought this was the error. But it seemed like it only matches
files that are ok by the result of the first loop. Not that big of a
deal, the penalty for looping multiple times over the same asset list is
pretty small.
I started to calculte the time it took to run the task localy:
real 0m31.001s
user 0m28.357s
sys 0m2.330s
So I started to tinker a bit with the config.assets.precompile. I don't
use the application.css/js so they can be removed. So came up with this:
config.assets.precompile -= [/application.(css|js)$/]
config.assets.precompile += [/^(juggler|frontend).(css|js)$/]
Using this config setting, I was able to almost remove a whole second of
the precompilation time
real 0m30.606s
user 0m28.409s
sys 0m2.324s
What I did noticed, was that precompiling my biggest js manifes took a
lot af time. So I created a custom js compressor based on the jsmin gem
and used that for precompilation:
config.assets.js_compressor = JsminTransformer.new
The result was tremendous:
real 0m22.895s
user 0m20.884s
sys 0m2.008s
It takes almost 8 seconds less to precompile using jsmin instead of
uglifier!
Since I was impressed, I retook a look at the 2 loops we encountered
earlier. It seems like env.each_logical_path always calls up the same
assets. But why do all the checks over again (see the sprockets base
class)? Either way you put it, it's a IO penalty, when you check for
files.
So I changed the rake task a bit. I call each_logical_path once and
store the result (an Enumerable object) in a variable that I'll loop:
asset_list = env.each_logical_path
config.assets.precompile.each do |path|
asset_list.each do |logical_path|
....
end
end
So I ran the altered precompile task again and got this result:
real 0m18.404s
user 0m17.211s
sys 0m1.158s
Again, almost 4 seconds off the clock!
As a final test, I wanted to see if limiting the asset list while the
script ran had some performance gain. So I tried to remove the assets
that where already precompiled:
asset_list = env.each_logical_path.to_a
remove_list =
config.assets.precompile.each do |path|
asset_list -= remove_list
remove_list =
asset_list.each do |logical_path|
....
if asset = env.find_asset(logical_path)
...
remove_list << logical_path
end
end
end
The end result was a bit slim, but still:
real 0m17.772s
user 0m16.548s
sys 0m1.189s
Now that I altered the deploy recipe to use my altered precompile task,
everything seems to be in order, except the high cpu usage during the
rake task. But this is due to I/O I guess while copying the files
around.
At this point, I think I'll use my custom recipe, and precompile the
localy like Jim suggested, so that I can avoid the the high load on the
production machine.
At this point, I'm still curious of other findings that improve the
precompilation of assets. So any info is welcome.