Rails 3 - 加速控制台加载时间

45

我想知道是否有相对容易的方法来加快我的控制台加载时间,因为它开始接近30秒。我有很多子类,它们的方法似乎不受reload!影响,所以我经常打开和关闭控制台。IRB加载非常快。

我是否安装了太多的gems?我该如何计时加载任务,以便我可以看到哪些任务占用了大部分时间?正如您所看到的,我已经尝试过dev-boost gem,但没有效果。应用程序在Passenger中运行良好,只是控制台加载让我感到非常烦恼。在MBP OSX 10.6.6上运行,配备2.4GHz和4GB RAM。未使用RVM。

版本:

Ovid$ rails -v
Rails 3.0.3
Ovid$ ruby -v
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10]

内存:

Ovid$ vm_stat
Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                         118818.
Pages active:                       341320.
Pages inactive:                      99490.
Pages speculative:                  310576.
Pages wired down:                   112527.
"Translation faults":             23097323.
Pages copy-on-write:               1270961.
Pages zero filled:                13836659.
Pages reactivated:                      36.
Pageins:                            165761.
Pageouts:                                0.
Object cache: 28 hits of 760846 lookups (0% hit rate)

Gemfile:

source 'http://rubygems.org'

gem 'rails', '3.0.3'
gem 'mysql2'
gem 'foreigner'
gem 'haml'
gem 'capistrano'
gem 'nokogiri'

#web services
gem 'yammer4r'
gem 'ruby-freshbooks'

#authentication gems from nifty generator
gem "bcrypt-ruby", :require => "bcrypt"
gem "mocha", :group => :test
gem 'authlogic'

#dev
group :development do
  gem 'rails-dev-boost', :git => 'git://github.com/thedarkone/rails-dev-boost.git', :require => 'rails_development_boost'
end

#testing
group :test do
  gem 'database_cleaner'
  gem 'cucumber-rails'
  gem 'cucumber'
  gem 'rspec-rails'
  gem 'spork'
  gem 'launchy'
  gem 'machinist'
  gem 'faker'
  gem 'capybara'
end

非常感谢!


好问题!让我们知道你发现了什么。 :) - TK-421
也发布到http://www.ruby-forum.com/topic/914379 - pendevere
8个回答

59

使用Benchmark,我终于找出了创业的瓶颈。特别是,导航到bundler gem,在lib/bundler/runtime.rb中找到执行Kernel.require的那一行,并进行包装,如下:

puts Benchmark.measure("require #{file}") {
  Kernel.require file
}.format("%n: %t %r")

您可能需要在应用程序中的某个地方添加 require 'benchmark',例如在 config/boot.rb 中。这将显示每个宝石所需的时间。我不能保证您的结果会与我的匹配,但我有一些宝石需要超过 1 秒才能加载,而大多数宝石只需要少于 1 毫秒。有几个是我在开发中不需要的宝石,但我在开发环境中需要执行一些任务,例如 capistrano、shoulda。我对启动的其他区域(初始化等)进行了基准测试,但找不到任何重要的瓶颈。
我还没有找出一种干净的方法来配置应用程序仅在确实需要时加载宝石。可能,我可以创建一个名为 :speedy 的环境,并在我知道不需要那些宝石时使用 RAILS_ENV=speedy rails s/c 进行启动。然后在 Gemfile 中,我可以使用 group: speedy 来在某些情况下排除那些宝石。
尽管如此,对我来说最大的启动恼人之处是必须加载整个环境才能运行 rake 任务。我可能可以为大多数宝石排除加载,但 Gemfile 将变得混乱,所以我不知道是否值得这样做。

7
这个黑客技巧提供了一些非常有趣的数据。我想知道有多少宝石作者意识到他们的工作对于漫长的启动时间做出了多大的贡献。 - Tobias Cohen
这个输出显示在哪里?我本以为是日志文件,但我没有看到任何东西。甚至尝试了一个通用的puts并验证我正在修改正确的runtime.rb文件。我错过了什么? - Bob Benedict
标准输出 - 通常是Rails/Ruby进程运行的终端/命令窗口。 - Brian Deterling
谢谢这个技巧。真的非常有帮助。@ Bob Benedict:创建一个新的Rails应用程序,进入新的应用程序文件夹,然后输入:rails g。当我想要查看输出时,这对我很有效。 - Nick_K
1
太棒了的提示。我们一直在看到一些疯狂的加载时间,这对于帮助隔离问题宝石等非常有用。这真的应该成为bundler的默认选项。此外,是否有一种方法可以将其扩展到消耗> 1秒的方法,以用于一般的ruby脚本? - ylluminate
我认为如果要扩展到任何方法,您需要使用分析器。 - Brian Deterling

23
稍作修改后,可以复制粘贴,包装所有需要的内容,并提供可排序输出:
# Add this to the top of boot.rb
require 'benchmark'
def require(file)
  puts Benchmark.measure("") {
    super
  }.format("%t require #{file}")
end

然后你可以执行空操作来查看它们:
rails runner 1

或者将它们排序并显示前50个:

rails runner 1 | sort -nr | head -n 50

2
对于Rails 2.3/Ruby 1.8.7,请使用script/runner 1 - professormeowingtons
2
你救了我们两天的时间 :) - Jayesh
我遇到了这个错误:C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/bundler-2.1.4/lib/bundler.rb:85:in `ui': uninitialized constant #Class:Bundler::UI (NameError)。该错误出现在运行Bundler 2.1.4的Ruby 2.6.6和Rails 5.1.7上。 - NemyaNation

7
你可以通过在缓慢的Gemfile条目中添加:require => nil并手动引用它们来加快速度。 例如:
gem 'jammit', :require => nil

我在一个聚会上也谈到了这个问题。

这似乎是ruby 1.9.2的一个bug(请参见此补丁的评论:https://gist.github.com/1008945

您可以通过对刚才链接中的补丁进行修补,或者升级到1.9.2-head1.9.3-head来解决它。


3
听起来是个好主意。我们发现像activeadmin和omniauth这样的宝石需要花费一定时间来加载,所以想知道在不影响任何功能的情况下,最好的加载策略是什么。在Rails完全加载后,手动要求它们的最佳位置在哪里? - gingerlime

2

这是一个非常古老的问题和答案,但仍然很有用...

在rails 5.2...中,jqr答案似乎与自动加载有关,并出现了错误。

-e:1:in `<main>'                                                                                                                                                                                           
0.002514 require i18n/config                                                                                                                                                                               
.../vendor/bundle-dev/ruby/2.7.0/gems/i18n-1.8.7/lib/i18n.rb:19:in `<module:I18n>': uninitialized constant I18n::Config                                           
Did you mean?  RbConfig (NameError)                                                 

固定了这个问题:
# Add this to the top of boot.rb
require 'benchmark'
def require(file)
  mod = nil
  puts Benchmark.measure("") {
    mod = super
  }.format("%t require #{file}")
  mod
end

2

这肯定与清理代码和识别瓶颈有关,但一旦你节省了这些,值得考虑使用类似于Zeus的工具来加快开发时间。

gem install zeus

https://github.com/burke/zeus (文档)

尽管存在一些bug,有时需要重启,但我仍然看到在小的代码更改后,通过快速服务器和控制台重新启动,总体开发时间有所增加。


1

我只能建议你穿上实验室外套,将问题分成两半来解决。尝试注释掉所有的 gem 要求,看看是否会加快速度(这可能也涉及到依赖于这些 gem 的代码片段的注释)。如果是这样,请一次注释一半,以此类推。

很抱歉这不是一个真正的答案... 你可以尝试使用 ruby-prof,例如通过使用 rails runner 和一个无操作脚本来调用它。

我在我的 Mac 上尝试了 ruby-prof script/rails runner 'nil',但它似乎崩溃了 :-)

编辑

如果你正在使用 git 来管理你的应用程序,你也可以尝试使用它的 bisect 命令,看看是否有一个特定的时间点导致了速度变慢,而不仅仅是普遍的膨胀。


1

Bumbler gem 是一个非常好的工具,用于确定 gem 的加载速度并识别加载缓慢的 gems。


0

重新加载!这已经成为一个问题一段时间了。看看this。有一些补丁可以使用,以及一些提示,可能可以解决您的问题。

重新加载方法本身如下所示。

# reloads the environment
def reload!(print=true)
  puts "Reloading..." if print
  ActionDispatch::Callbacks.new(lambda {}, false).call({})
  true
end

你可以将你的环境添加到这个方法中,以覆盖它的功能并强制重新加载所需内容。

这对我很有效,如果对你也有效,请告诉我们。祝一切顺利。


谢谢您的建议 - 我尝试了您提供的补丁和该线程中的另一个更长的补丁。但是我没有成功。您的意思是这一行应该说ActionDispatch :: Callbacks.new(Proc.new {},false)。call(RAILS_ENV),对吗? - pendevere

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接