From 47e06c988e8377d04a6a6e28e798b06187326d87 Mon Sep 17 00:00:00 2001
From: Guo Xiang Tan <tgx_world@hotmail.com>
Date: Sun, 6 Sep 2015 08:49:35 +0800
Subject: Reduce calls to stringify_keys.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Stackprof output truncated.
```
TOTAL    (pct)     SAMPLES    (pct)     FRAME
  23   (4.7%)          12   (2.4%)     Hash#transform_keys
  11   (2.2%)          11   (2.2%)     block in Hash#transform_keys
  30   (6.1%)           7   (1.4%)     Hash#stringify_keys
```

Benchmark Script:
```
begin
  require 'bundler/inline'
rescue LoadError => e
  $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
  raise e
end

gemfile(true) do
  source 'https://rubygems.org'
  gem 'rails', path: '~/rails' # master against ref "f1f0a3f8d99aef8aacfa81ceac3880dcac03ca06"
  gem 'arel', github: 'rails/arel', branch: 'master'
  gem 'rack', github: 'rack/rack', branch: 'master'
  gem 'sass'
  gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
  gem 'sprockets', github: 'rails/sprockets', branch: 'master'
  gem 'pg'
  gem 'benchmark-ips'
end

require 'active_record'
require 'benchmark/ips'

ActiveRecord::Base.establish_connection('postgres://postgres@localhost:5432/rubybench')

ActiveRecord::Migration.verbose = false

ActiveRecord::Schema.define do
  create_table :users, force: true do |t|
    t.string :name, :email
    t.timestamps null: false
  end
end

class User < ActiveRecord::Base; end

attributes = {
  name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
  email: "foobar@email.com",
}

1000.times { User.create!(attributes) }

Benchmark.ips(5, 3) do |x|
  x.report('where with hash') { User.where(name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.") }
  x.report('where with string') { User.where("users.name = ?", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.") }
  x.compare!
end

key =
  if RUBY_VERSION < '2.2'
    :total_allocated_object
  else
    :total_allocated_objects
  end

before = GC.stat[key]
User.where(name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
after = GC.stat[key]
puts "Total Allocated Object: #{after - before}"
```

Before:
```
Calculating -------------------------------------
     where with hash     2.796k i/100ms
   where with string     4.338k i/100ms
-------------------------------------------------
     where with hash     29.177k (± 1.5%) i/s -    148.188k
   where with string     47.419k (± 2.8%) i/s -    238.590k

Comparison:
   where with string:    47419.0 i/s
     where with hash:    29176.6 i/s - 1.63x slower

Total Allocated Object: 85
```

After:
```
Calculating -------------------------------------
     where with hash     2.895k i/100ms
   where with string     4.416k i/100ms
-------------------------------------------------
     where with hash     30.758k (± 2.0%) i/s -    156.330k
   where with string     47.708k (± 2.6%) i/s -    238.464k

Comparison:
   where with string:    47707.9 i/s
     where with hash:    30757.7 i/s - 1.55x slower

Total Allocated Object: 84
```
---
 activerecord/lib/active_record/relation/predicate_builder.rb          | 4 ++--
 .../relation/predicate_builder/association_query_handler.rb           | 4 ++--
 activerecord/lib/active_record/relation/where_clause_factory.rb       | 1 +
 3 files changed, 5 insertions(+), 4 deletions(-)

(limited to 'activerecord/lib')

diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index d26db7d4cf..e232516b0c 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -24,12 +24,12 @@ module ActiveRecord
     end
 
     def build_from_hash(attributes)
-      attributes = convert_dot_notation_to_hash(attributes.stringify_keys)
+      attributes = convert_dot_notation_to_hash(attributes)
       expand_from_hash(attributes)
     end
 
     def create_binds(attributes)
-      attributes = convert_dot_notation_to_hash(attributes.stringify_keys)
+      attributes = convert_dot_notation_to_hash(attributes)
       create_binds_for_hash(attributes)
     end
 
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
index 159889d3b8..e81be63cd3 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -10,10 +10,10 @@ module ActiveRecord
 
         table = value.associated_table
         if value.base_class
-          queries[table.association_foreign_type] = value.base_class.name
+          queries[table.association_foreign_type.to_s] = value.base_class.name
         end
 
-        queries[table.association_foreign_key] = value.ids
+        queries[table.association_foreign_key.to_s] = value.ids
         predicate_builder.build_from_hash(queries)
       end
 
diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb
index 0430922be3..23eaab4699 100644
--- a/activerecord/lib/active_record/relation/where_clause_factory.rb
+++ b/activerecord/lib/active_record/relation/where_clause_factory.rb
@@ -15,6 +15,7 @@ module ActiveRecord
         when Hash
           attributes = predicate_builder.resolve_column_aliases(opts)
           attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
+          attributes.stringify_keys!
 
           attributes, binds = predicate_builder.create_binds(attributes)
 
-- 
cgit v1.2.3