2x faster connection.type_cast

`nil`, `Numeric`, and `String` are most basic objects which are passed
to `type_cast`. But now each `when *types_which_need_no_typecasting`
evaluation allocates extra two arrays, it makes `type_cast` slower.

The `types_which_need_no_typecasting` was introduced at #15351, but the
method isn't useful (never used any adapters) since all adapters
(sqlite3, mysql2, postgresql, oracle-enhanced, sqlserver) still
overrides the `_type_cast`.

Just expanding the method would make the `type_cast` 2x faster.

```ruby
module ActiveRecord
  module TypeCastFast
    def type_cast_fast(value, column = nil)
      value = id_value_for_database(value) if value.is_a?(Base)

      if column
        value = type_cast_from_column(column, value)
      end

      _type_cast_fast(value)
    rescue TypeError
      to_type = column ? " to #{column.type}" : ""
      raise TypeError, "can't cast #{value.class}#{to_type}"
    end

    private
      def _type_cast_fast(value)
        case value
        when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
          value.to_s
        when true       then unquoted_true
        when false      then unquoted_false
        # BigDecimals need to be put in a non-normalized form and quoted.
        when BigDecimal then value.to_s("F")
        when nil, Numeric, String then value
        when Type::Time::Value then quoted_time(value)
        when Date, Time then quoted_date(value)
        else raise TypeError
        end
      end
  end
end

conn = ActiveRecord::Base.connection
conn.extend ActiveRecord::TypeCastFast

Benchmark.ips do |x|
  x.report("type_cast") { conn.type_cast("foo") }
  x.report("type_cast_fast") { conn.type_cast_fast("foo") }
  x.compare!
end
```

```
Warming up --------------------------------------
           type_cast    58.733k i/100ms
      type_cast_fast   101.364k i/100ms
Calculating -------------------------------------
           type_cast    708.066k (± 5.9%) i/s -      3.583M in   5.080866s
      type_cast_fast      1.424M (± 2.3%) i/s -      7.197M in   5.055860s

Comparison:
      type_cast_fast:  1424240.0 i/s
           type_cast:   708066.0 i/s - 2.01x  slower
```
This commit is contained in:
Ryuta Kamizono 2019-01-04 07:24:57 +09:00
parent c6ef670aee
commit e9aa0c5c72

@ -157,10 +157,6 @@ def id_value_for_database(value)
end
end
def types_which_need_no_typecasting
[nil, Numeric, String]
end
def _quote(value)
case value
when String, ActiveSupport::Multibyte::Chars
@ -188,10 +184,9 @@ def _type_cast(value)
when false then unquoted_false
# BigDecimals need to be put in a non-normalized form and quoted.
when BigDecimal then value.to_s("F")
when nil, Numeric, String then value
when Type::Time::Value then quoted_time(value)
when Date, Time then quoted_date(value)
when *types_which_need_no_typecasting
value
else raise TypeError
end
end