Don't round off subseconds unless necessary

Currently, if `:datetime` type has a precision, type casting always does
round off subseconds regardless of whether necessary or not, it is a bit
slow.

Since #34970, `t.timestamps` with `precision: 6` by default, so
`pluck(:created_at)` in newly created app will always be affected by the
round off.

This avoids the round off if possible, it makes `pluck(:created_at)`
about 25% faster.

https://gist.github.com/kamipo/e029539f2632aee9f5e711fe66fc8842

Before (0a87d7c9dd with postgresql adapter):

```
Warming up --------------------------------------
    users.pluck(:id)      344.000  i/100ms
  users.pluck(:name)      336.000  i/100ms
users.pluck(:created_at)  206.000  i/100ms
Calculating -------------------------------------
    users.pluck(:id)      3.620k (± 8.5%) i/s -     18.232k in   5.077316s
  users.pluck(:name)      3.579k (± 9.4%) i/s -     17.808k in   5.020230s
users.pluck(:created_at)  2.069k (± 8.0%) i/s -     10.300k in   5.019284s
```

Before (0a87d7c9dd with mysql2 adapter):

```
Warming up --------------------------------------
    users.pluck(:id)      245.000  i/100ms
  users.pluck(:name)      240.000  i/100ms
users.pluck(:created_at)  180.000  i/100ms
Calculating -------------------------------------
    users.pluck(:id)      2.548k (± 9.4%) i/s -     12.740k in   5.066574s
  users.pluck(:name)      2.513k (± 8.0%) i/s -     12.480k in   5.011260s
users.pluck(:created_at)  1.771k (±11.2%) i/s -      8.820k in   5.084473s
```

After (this change with postgresql adapter):

```
Warming up --------------------------------------
    users.pluck(:id)      348.000  i/100ms
  users.pluck(:name)      357.000  i/100ms
users.pluck(:created_at)  254.000  i/100ms
Calculating -------------------------------------
    users.pluck(:id)      3.628k (± 8.2%) i/s -     18.096k in   5.024748s
  users.pluck(:name)      3.624k (±12.4%) i/s -     17.850k in   5.020959s
users.pluck(:created_at)  2.567k (± 7.0%) i/s -     12.954k in   5.081153s
```

After (this change with mysql2 adapter):

```
Warming up --------------------------------------
    users.pluck(:id)      268.000  i/100ms
  users.pluck(:name)      265.000  i/100ms
users.pluck(:created_at)  207.000  i/100ms
Calculating -------------------------------------
    users.pluck(:id)      2.586k (±10.9%) i/s -     12.864k in   5.050546s
  users.pluck(:name)      2.543k (±10.2%) i/s -     12.720k in   5.067726s
users.pluck(:created_at)  2.263k (±14.5%) i/s -     10.971k in   5.004039s
```
This commit is contained in:
Ryuta Kamizono 2019-05-28 22:46:51 +09:00
parent 0a87d7c9dd
commit 497e52f8c2
1 changed files with 10 additions and 3 deletions

View File

@ -22,10 +22,17 @@ module ActiveModel
end
def apply_seconds_precision(value)
return value unless precision && value.respond_to?(:usec)
number_of_insignificant_digits = 6 - precision
return value unless precision && value.respond_to?(:nsec)
number_of_insignificant_digits = 9 - precision
round_power = 10**number_of_insignificant_digits
value.change(usec: value.usec - value.usec % round_power)
rounded_off_nsec = value.nsec % round_power
if rounded_off_nsec > 0
value.change(nsec: value.nsec - rounded_off_nsec)
else
value
end
end
def type_cast_for_schema(value)