How can you speed up the Rails Asset Pipeline precompile process?
1. Capistrano deployment speedup
(1) use capistrano built-in task 'deploy/assets' to deploy.
Capistrano has its own built-in task 'deploy/assets'. It will automatically do task for you.
The difference between your own handcraft task is it only load assets
group to precompile assets, not whole environment.
cd /home/apps/APP_NAME/releases/20120708184757 && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile
(2) skip precompile process when assets aren't changed.
https://gist.github.com/3072362
If
- app/assets
- lib/assets
- vendor/assets
- Gemfile.lock
- confir/routes.rb
are changed, it will recompile assets. Otherwise, it will skip the pecompile process, save a lot of time.
2. Use @import carefully.
(1) avoid using @import "compass";
directly.
It will both work when you
@import "compass";
or @import "compass/typography/links/link-colors";
in SCSS.
But @import "compass/typography/links/link-colors";
is 9 times faster than @import "compass";
when you compile assets.
That is because when @import "compass";
, it compile whole compass assets. not only just link-colors
part.
(2) avoid using partials
In SCSS, we like to use partial
to organize our assets.
But only if you need to share variables, or there are necessary dependencies, otherwise
//= require "reset"
//= require "base"
//= require "product"
is faster than
@import "reset";
@import "base";
@import "product";
3. don’t require .scss & .coffee for no reason
(1) avoid using require_tree
When we use Rails generator to generate controllers. Rails will also generate assets likes this
- product.css.scss
- product.js.coffee
and mount assets in application.js using this techniques:
//= require_tree
But the empty assets (output nothing) which only contain this lines:
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
It will cost you about 250ms to compile each of them. If you have 10 empty assets, it will be 2.5 seconds .
Remove them from your project, or mount them individually in application.js like this:
//= require prodcuts
//= require users
//= require albums
(2) Don't use css.scss
or js.coffee
if unnecessary.
- Compiled jquery-ui-1.8.16.custom.css (0ms) (pid 19108)
- Compiled jquery.ui.1.8.16.ie.css (0ms) (pid 19108)
- Compiled jquery.js (5ms) (pid 19108)
- Compiled jquery_ujs.js (0ms) (pid 19108)
- Compiled custom.css (14ms) (pid 19108)
custom.css
is custom.css.scss
Compile pure CSS and pure JS is fast ( cost almost 0 ms). But compile .scss and .coffee still cost some time.
Summarize
- replace deploy.rb assets task.
check logs/production.log
- find slow assets
- remove @import "compass"; use alternative solution.
- use require instead @import; ( use @import when it is really necessary )
- remove require_tree, mount assets individually
- remove empty .scss and .coffeescript
- use .css when assets are pure CSS.
How do I speed up asset precompiling in Rails 3+?
I am assuming that you are running Rails >3.1.
Don't run the rake tasks at all. Rails development mode doesn't need the assets to be precompiled. Rails will automagically compile the assets when there is a change else it will not.
If you are looking for performance while serving assets in development you can look the gems such as https://github.com/wavii/rails-dev-tweaks
Speed up assets:precompile with Rails 3.1/3.2 Capistrano deployment
The idea is that if you don't change your assets you don't need to recompile them each time:
This is the solution that Ben Curtis propose for a deployment with git:
namespace :deploy do
namespace :assets do
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
else
logger.info "Skipping asset pre-compilation because there were no asset changes"
end
end
end
end
Here is another approach based on asset age (https://gist.github.com/2784462) :
set :max_asset_age, 2 ## Set asset age in minutes to test modified date against.
after "deploy:finalize_update", "deploy:assets:determine_modified_assets", "deploy:assets:conditionally_precompile"
namespace :deploy do
namespace :assets do
desc "Figure out modified assets."
task :determine_modified_assets, :roles => assets_role, :except => { :no_release => true } do
set :updated_assets, capture("find #{latest_release}/app/assets -type d -name .git -prune -o -mmin -#{max_asset_age} -type f -print", :except => { :no_release => true }).split
end
desc "Remove callback for asset precompiling unless assets were updated in most recent git commit."
task :conditionally_precompile, :roles => assets_role, :except => { :no_release => true } do
if(updated_assets.empty?)
callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
callbacks[:after].delete(callback)
logger.info("Skipping asset precompiling, no updated assets.")
else
logger.info("#{updated_assets.length} updated assets. Will precompile.")
end
end
end
end
If you prefer to precompile your assets locally you can use this task:
namespace :deploy do
namespace :assets do
desc 'Run the precompile task locally and rsync with shared'
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if releases.length <= 1 || capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
%x{bundle exec rake assets:precompile}
%x{rsync --recursive --times --rsh=ssh --compress --human-readable --progress public/assets #{user}@#{host}:#{shared_path}}
%x{bundle exec rake assets:clean}
else
logger.info 'Skipping asset pre-compilation because there were no asset changes'
end
end
end
end
Another interesting approach can be that of using a git hook.
For example you can add this code to .git/hooks/pre-commit
which checks if there are any differences in the assets files and eventually precompiles them and add them to the current commit.
#!/bin/bash
# source rvm and .rvmrc if present
[ -s "$HOME/.rvm/scripts/rvm" ] && . "$HOME/.rvm/scripts/rvm"
[ -s "$PWD/.rvmrc" ] && . "$PWD/.rvmrc"
# precompile assets if any have been updated
if git diff-index --name-only HEAD | egrep '^app/assets' >/dev/null ; then
echo 'Precompiling assets...'
rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
git add public/assets/*
fi
If you decide to use this approach you would probably need to change your config/environments/development.rb
adding:
config.assets.prefix = '/assets_dev'
So that while in development you won't serve the precompiled assets.
rake assets:precompile is slow
If you don't need to load the Rails environment, you should disable that with:
config.assets.initialize_on_precompile = false
EDIT: I've just written a gem to solve this problem, called turbo-sprockets-rails3. It speeds up your assets:precompile
by only recompiling changed files, and only compiling once to generate all assets.
It would be awesome if you could help me test out the turbo-sprockets-rails3 gem, and let me know if you have any problems.
rake assets:precompile is slooooow. Any way to speed it up?
You can try to load the assets only on the changed files which would speed up compiling process by a huge margin. You can easily do so using turbo-sprockets-gem.
https://github.com/ndbroadbent/turbo-sprockets-rails3
The documentation is pretty straight forward. Hope this helps.
rake assets:precompile is slow in production
Try to skip compiling ckeditor assets
config/environments/production.rb
require_relative '../../lib/assets/selective_assets_compressor'
config.assets.js_compressor = SelectiveAssetsCompressor.new
lib/assets/selective_assets_compressor.rb
class SelectiveAssetsCompressor < Uglifier
def initialize(options = {})
super(options)
end
def compress(string)
if string =~ /CKSource/
string
else
super(string)
end
end
end
rake assets:precompile taking extremely long to complete
The hackety hack solution seems to be to monkey patch the standard sass compression engine out of the way. I added this to the top of my application.rb
module Sass
module Rails
class CssCompressor
def compress(css)
css
end
end
end
end
The difference in file size was 124k before the monkey patch and 125k after and an order of magnitude speed improvement.
Rails Assets precompile is too slow ~12hrs
I solved this problem:
The problem was the use of @extend
sentence in my .scss files.
This problem appear in this sass version.
I wanted more semantic html files(READ HERE for more information about it).
I found this problem making introspection into Sprockets precompile. With this great tutorial
The solution is take off all @extend
sentences from my scss files and use plain bootstrap in my html.
Example: I had to change this.
# main.html
<div class='user-information'> ... </div>
# main.scss
.user-information {
@extend .col-md-12
}
by this:
# main.html
<div class='col-md-12'> ... </div>
Related Topics
What Alternatives to Irb Are There
Differencebetween Methods and Attributes in Ruby
How to Reflect in the Database a New Belongs_To and Has_Many Relationship in Ruby on Rails
Code to Generate Gaussian (Normally Distributed) Random Numbers in Ruby
Rails Convert String to Number
What's the Difference Between a Class and the Singleton of That Class in Ruby
Ruby Merging Two Arrays into One
Best Way to Monitor for Completion of a Sidekiq Job
Context Aware Authorization Using Cancan
Profile a Rails Controller Action
Building a Windows Executable from My Ruby App
Can't Convert Fixnum to String During Rake Db:Create
Ruby on Rails: Provide VS Content_For
Possible to Alias a Belongs_To Association in Rails
Carrierwave: Create the Same, Unique Filename for All Versioned Files