ryanb / cancan

Authorization Gem for Ruby on Rails.
MIT License
6.27k stars 783 forks source link

Slow gem load time #610

Open wulftone opened 12 years ago

wulftone commented 12 years ago

I noticed that when starting rails, cancan takes about 4 times as long as the next slowest library (devise). This isn't critical, but it's noticeable, especially when doing testing and restarting rails a lot. I put a benchmark in my Kernel code, and this is what I found out:

$ Using RSpec
Preloading Rails environment
require rails: 0.000000 (0.000291)
require execjs: 0.090000 (0.085533)
require pg: 0.010000 (0.013008)
require devise: 0.570000 (0.580526)
require cancan: 2.310000 (2.317783) # notice the extreme time here compared to other libraries
require easy_roles: 0.000000 (0.002532)
require eco: 0.040000 (0.034419)
require paperclip: 0.350000 (0.358614)
require aws-sdk: 0.190000 (0.187651)
require chronic: 0.170000 (0.166717)
require validates_timeliness: 0.110000 (0.117373)
require rabl: 0.070000 (0.066649)
require jquery-rails: 0.100000 (0.100356)
require miniskirt: 0.040000 (0.042907)
require faker: 0.100000 (0.100896)
require thin-rails: 0.120000 (0.125145)
require awesome_print: 0.110000 (0.105269)
require sass-rails: 0.050000 (0.052730)
require coffee-rails: 0.110000 (0.099732)
require uglifier: 0.000000 (0.002297)
require rspec-rails: 0.000000 (0.001316)
require ruby-debug: 0.400000 (0.410388)
require spork: 0.000000 (0.000296)
require rspec-steps: 0.250000 (0.255104)
require jasmine-headless-webkit: 0.000000 (0.008218)
require jasmine-spec-extras: 0.010000 (0.001902)
require valid_attribute: 0.130000 (0.128073)
require shoulda: 0.260000 (0.266923)
require capybara: 0.350000 (0.361627)
require database_cleaner: 0.000000 (0.005957)
require capybara-webkit: 0.500000 (0.498786)
require capybara-firebug: 0.000000 (0.001135)
Loading Spork.prefork block...
Spork is ready and listening on 8989!

Is there any way to reduce this?

ryanb commented 12 years ago

This is just a guess, but I think the slow load times are due to CanCan's extensions to ActionController::Base and ActiveRecord::Base. Due to Rails' auto-loading behavior, this will trigger the loading of ActionController and ActiveRecord and make it appear that CanCan is slow since it is loading a good chunk of Rails at that time.

The result is that CanCan doesn't actually slow down startup time by that much, it just triggers the loading of Rails earlier on.

The solution would be to use a Railtie with ActiveSupport on_load hooks. I have been planning to do this but haven't gotten around to it.

Note: This is just a theory and is untested.

derekprior commented 12 years ago

@wulftone, The bundle benchmarking gist has been updated by the author to include a call to eager load rails. If you re-run that you should see the cost of loading those gems in a world where they aren't responsibile for autoloading required parts of rails. You may see your reported load time go down by quite a bit. In my case, CanCan never accounted for much of the reported load time as there was a gem loaded earlier on that paid the autoload penalty.

It seems that @ryanb's hypothesis is mostly correct. That said, it still may be worthwhile to use a railtie for 2.0.

glongman commented 12 years ago

I concur. I'm seeing cancan forcing the ActiveRecord to load really early,before most other gems register thier ActiveSupport on_load hooks.

In my case the hooks for other gems never run. I am in a peculiar situation though as we are depending on ActiveRecord being fully initialized before the middleware stack is built.

In all cases except mine everything just works. That said, adding a Raltie w/ on_load hooks into ActiveRecord would fix my problem and probably fix for anyone else who is forced to take a trip outside the conventions.