From 613060d106f2d3bf7350bab540c952b1567ad66b Mon Sep 17 00:00:00 2001
From: Ryuta Kamizono <kamipo@gmail.com>
Date: Wed, 29 May 2019 06:45:34 +0900
Subject: Avoid making extra 5 arrays in each `save`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Each `save` calls `all_timestamp_attributes_in_model` to fill timestamp
columns. Allthough the `all_timestamp_attributes_in_model` returns the
same value every time, the `all_timestamp_attributes_in_model` makes
extra 5 arrays every time.

This avoids the making extra 5 arrays by memoizing the result, it makes
`save` economical and a bit faster.

https://gist.github.com/kamipo/1ddad2235073f508637bf9a72d64bb83

Before (2a015f6c0be0593a624b0c800e5335319ac4c660):

```
{["~/rails/activerecord/lib/active_record/timestamp.rb",
  76,
  :T_ARRAY]=>[1000, 0, 341, 0, 1, 13640],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  64,
  :T_ARRAY]=>[1000, 0, 341, 0, 1, 13640],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  80,
  :T_ARRAY]=>[1000, 0, 341, 0, 1, 13640],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  68,
  :T_ARRAY]=>[1000, 0, 341, 0, 1, 13640],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  73,
  :T_ARRAY]=>[1000, 0, 341, 0, 1, 13640]}
Warming up --------------------------------------
    User.create * 10    36.000  i/100ms
Calculating -------------------------------------
    User.create * 10    353.644  (± 7.4%) i/s -      1.764k in   5.021876s
```

After (this change):

```
{["~/rails/activerecord/lib/active_record/timestamp.rb",
  83,
  :T_ARRAY]=>[1, 0, 1, 1, 1, 40],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  87,
  :T_ARRAY]=>[1, 0, 1, 1, 1, 40],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  64,
  :T_ARRAY]=>[1, 1, 1, 1, 1, 0],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  69,
  :T_ARRAY]=>[1, 1, 1, 1, 1, 0],
 ["~/rails/activerecord/lib/active_record/timestamp.rb",
  74,
  :T_ARRAY]=>[1, 1, 1, 1, 1, 0]}
Warming up --------------------------------------
    User.create * 10    37.000  i/100ms
Calculating -------------------------------------
    User.create * 10    380.063  (± 7.1%) i/s -      1.924k in   5.097917s
```
---
 activerecord/lib/active_record/timestamp.rb | 42 ++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 04a1c03474..a5862ae06b 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -59,19 +59,26 @@ module ActiveRecord
         attribute_names.index_with(time || current_time_from_proper_timezone)
       end
 
-      private
-        def timestamp_attributes_for_create_in_model
-          timestamp_attributes_for_create.select { |c| column_names.include?(c) }
-        end
+      def timestamp_attributes_for_create_in_model
+        @timestamp_attributes_for_create_in_model ||=
+          (timestamp_attributes_for_create & column_names).freeze
+      end
 
-        def timestamp_attributes_for_update_in_model
-          timestamp_attributes_for_update.select { |c| column_names.include?(c) }
-        end
+      def timestamp_attributes_for_update_in_model
+        @timestamp_attributes_for_update_in_model ||=
+          (timestamp_attributes_for_update & column_names).freeze
+      end
 
-        def all_timestamp_attributes_in_model
-          timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
-        end
+      def all_timestamp_attributes_in_model
+        @all_timestamp_attributes_in_model ||=
+          (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
+      end
 
+      def current_time_from_proper_timezone
+        default_timezone == :utc ? Time.now.utc : Time.now
+      end
+
+      private
         def timestamp_attributes_for_create
           ["created_at", "created_on"]
         end
@@ -80,8 +87,11 @@ module ActiveRecord
           ["updated_at", "updated_on"]
         end
 
-        def current_time_from_proper_timezone
-          default_timezone == :utc ? Time.now.utc : Time.now
+        def reload_schema_from_cache
+          @timestamp_attributes_for_create_in_model = nil
+          @timestamp_attributes_for_update_in_model = nil
+          @all_timestamp_attributes_in_model = nil
+          super
         end
     end
 
@@ -124,19 +134,19 @@ module ActiveRecord
     end
 
     def timestamp_attributes_for_create_in_model
-      self.class.send(:timestamp_attributes_for_create_in_model)
+      self.class.timestamp_attributes_for_create_in_model
     end
 
     def timestamp_attributes_for_update_in_model
-      self.class.send(:timestamp_attributes_for_update_in_model)
+      self.class.timestamp_attributes_for_update_in_model
     end
 
     def all_timestamp_attributes_in_model
-      self.class.send(:all_timestamp_attributes_in_model)
+      self.class.all_timestamp_attributes_in_model
     end
 
     def current_time_from_proper_timezone
-      self.class.send(:current_time_from_proper_timezone)
+      self.class.current_time_from_proper_timezone
     end
 
     def max_updated_column_timestamp
-- 
cgit v1.2.3