# Gemfile @@ -31,8 +31,6 @@ group :development, :test do end group :development do - # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' end @@ -43,6 +41,3 @@ group :test do # Easy installation and use of web drivers to run system tests with browsers gem 'webdrivers' end - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
A Very Rough Monkey-patch for Benchmarking `process_action` # config/initializers/perf.rb ActionController::Base.prepend( Module.new { def process_action(*) result = nil p Benchmark.realtime { result = super } result end } )
A Benchmarking Script that Iterates the `process_action` # config/initializers/perf.rb ActionController::Base.prepend( Module.new { def process_action(*) Benchmark.ips do |x| x.report('process_action') { super } end super end } )
Or We Could Include the Rack Middleware Stack and `before_action` and Such # config/initializers/perf.rb Rails::Engine.prepend( Module.new { def call(*) Benchmark.ips do |x| x.report('call') { super } end super end } )
Route to /posts/ok # config/routes.rb Rails.application.routes.draw do - resources :posts + resources :posts do + collection do + get :ok + end + end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
The Controller Action # app/controllers/posts_controller.rb class PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] + def ok + head :ok + end + # GET /posts # GET /posts.json def index
Number of Objects Allocated During a Rails Request # config/initializers/perf.rb Rails::Engine.prepend( Module.new { def call(*) c = GC.stat(:total_allocated_objects) result = super p GC.stat(:total_allocated_objects) - c result end } )
An Utility Method That Prints the Memory Usage module Prof def self.mem result = nil MemoryProfiler.report { result = yield }.pretty_print(retained_strings: 0, allocated_strings: 100, normalize_paths: true) result end end
Let's See Who Is Instantiating AC::Renderer # actionpack/lib/action_controller/renderer.rb @@ -63,6 +63,7 @@ module ActionController # It will be merged with the default Rack environment defined by # +ActionController::Renderer::DEFAULTS+. def initialize(controller, env, defaults) + puts caller @controller = controller @defaults = defaults @env = normalize_keys defaults.merge(env)
An Initializer in ActionText::Engine # actiontext-6.0.0/lib/action_text/engine.rb initializer "action_text.renderer" do |app| app.executor.to_run { ActionText::Content.renderer = ApplicationController.renderer } app.executor.to_complete { ActionText::Content.renderer = ApplicationController.renderer } ActiveSupport.on_load(:action_text_content) do self.renderer = ApplicationController.renderer end ActiveSupport.on_load(:action_controller_base) do before_action { ActionText::Content.renderer = ApplicationController.renderer.new(request.env) } end end
What If We Skip Requiring Some More Other Components That We Don't Use Here? # config/application.rb @@ -2,13 +2,10 @@ require_relative 'boot' %w( active_record/railtie - active_storage/engine action_controller/railtie action_view/railtie action_mailer/railtie active_job/railtie - action_cable/engine - action_mailbox/engine
So Instead, Let's Bundle Edge Rails and See How Things Are Improved in the Current master # Gemfile -gem 'rails', '~> 6.0.0' +gem 'rails', path: 'PATH/TO/RAILS'
Measuring Active Record Finder Alone # app/controllers/posts_controller.rb private # Use callbacks to share common setup or constraints between actions. def set_post - @post = Post.find(params[:id]) + Prof.mem do + @post = Post.find(params[:id]) + end end
Result Total allocated: 1336 bytes (9 objects) allocated memory by class ----------------------------------- 928 Hash 368 Array 40 String allocated objects by class ----------------------------------- 4 Array 4 Hash 1 String
Like This? module I18nCache def self.extended(obj) obj.instance_variable_set :@i18n_cache, {} end def translate(*args, **options) cache = @i18n_cache[I18n.locale] ||= {} cache_key = args << options cache[cache_key] || begin result = super cache[cache_key] = result if result result ennd alias t translate end I18n.extend I18nCache
Anyway, Now I18n.t Became Much Cheaper $ env RAILS_ENV=production rails r 'I18n.t(:hello); Prof.mem { I18n.t :hello }' Total allocated: 272 bytes (2 objects) allocated memory by class ----------------------------------- 232 Hash 40 Array allocated objects by class ----------------------------------- 1 Array 1 Hash
Allocated Objects by Class allocated objects by class ----------------------------------- 3235 String 1300 Array 1019 Hash 162 ActiveSupport::SafeBuffer 100 ActiveModel::Attribute::FromDatabase 50 ActiveModel::AttributeSet 50 ActiveModel::LazyAttributeHash 50 Post
Allocated Objects by Class: with lightweight_attributes allocated objects by class ----------------------------------- 3187 String 1293 Array 996 Hash 162 ActiveSupport::SafeBuffer 52 Class 50 LightweightAttributes::AttributeSet 50 Post
The Monkey-Patches for Inflector def camelize(term, uppercase_first_letter = true) return super unless uppercase_first_letter if (cached = InflectorCache.camelize_cache[term]) cached.dup else super ennd def underscore(camel_cased_word) if (cached = InflectorCache.underscore_cache[camel_cased_word]) cached.dup else super ennd
And Another Monkey-Patch That Creates the Cache from Table Name module SchemaStatementsExtension def columns(table_name) result = super unless InflectorCache.camelize_cache[table_name] InflectorCache.camelize_cache[table_name] = table_name.camelize end unless InflectorCache.underscore_cache[table_name] InflectorCache.underscore_cache[table_name] = table_name.underscor end result ennd ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend SchemaStatementsExtension
Only if Given `:layout` Is `nil` or String def _write_layout_method # :nodoc: super if _layout.nil? || (String === _layout) @_layout_cache = {} prepend CachedLayout ennd
And What Is This Binding Thing? allocated memory by class ----------------------------------- 14440 Hash 9987 String 9688 Array 2520 MatchData 1728 ActionDispatch::Request 1608 Thread::Backtrace 1160 ActiveSupport::HashWithIndifferentAccess 1040 Proc 944 Rack::Utils::HeaderHash 912 Class 512 Regexp 352 Binding
binding.local_variable_ get def do_something(if: nil, unless: nil) if binding.local_variable_get(:if) ... end if binding.local_variable_get(:unless) ... end end