Railsでのserialize Hashとjsonカラムでの速度比較

## jsonカラムってどうなのよ
MySQLでもPostgreSQLでもjson型をサポートし、
それぞれの方法でindexもはれるようになった。
正直便利な事この上ないが、じゃー使うかーって気軽に使ってしまっていいものだろうか。

Railsの`serialize`が遅いのはもはや有名な話だが、
json型での読み書きはどの程度のものなのか、気になったので調べてみた。

## 結論
先に結果だけ見たい方用に。
結果的には大差無いです。以下計測。

## 実行環境
- MacBook Air (13-inch, Mid 2013)
- OS: macOS High Sierra 10.13.1
- CPU: Intel Core i7 1.7GHｚ
- メモリ： 8GB 1600MHｚ DDR３
- Rails: 5.1.4
- MySQL: 5.7.20

## 前提
- 全ての比較検証は`rails console`から行っています
- 比較実行前にテーブル内は空にしています(db:rollback -> db:migrate)
- indexをはった場合の速度は見ません
- メモリ効率に関しては見ません

## 環境準備
```shell-session
$ rails g model serialize_sample name:string content:text
$ rails g model json_sample name:string content:json
$ rails db:migrate
```

```ruby:app/models/serialize_sample.rb
class SerializeSample < ApplicationRecord
  serialize :content, Hash
end
```

```ruby:app/models/json_sample.rb
class JsonSample < ApplicationRecord
end
```

## 比較１：Insert
```ruby
require 'open-uri'
require 'benchmark/ips'
Rails.logger.level = Logger::INFO
test_hash = JSON.parse(open("https://gist.githubusercontent.com/gnip/764239/raw/be630cd72a14d0f6033030381dbf45b97ecf3eb7/Twitter%2520(json%2520format).js").read).with_indifferent_access;

Benchmark.ips do |x|
  x.report("serialize") { SerializeSample.create(name: 'insert', content: test_hash) }
  x.report("json") { JsonSample.create(name: 'insert', content: test_hash) }
  x.compare!
end;
```

### 結果
```
Warming up --------------------------------------
           serialize    12.000  i/100ms
                json    28.000  i/100ms
Calculating -------------------------------------
           serialize    129.386  (± 2.3%) i/s -    648.000  in   5.011266s
                json    286.639  (± 5.6%) i/s -      1.428k in   5.000155s

Comparison:
                json:      286.6 i/s
           serialize:      129.4 i/s - 2.22x  slower
```

## 比較２：Read
```ruby
require 'open-uri'
require 'benchmark/ips'
Rails.logger.level = Logger::INFO
test_hash = JSON.parse(open("https://gist.githubusercontent.com/gnip/764239/raw/be630cd72a14d0f6033030381dbf45b97ecf3eb7/Twitter%2520(json%2520format).js").read).with_indifferent_access;

100.times do
  SerializeSample.create(name: 'insert', content: test_hash)
  JsonSample.create(name: 'insert', content: test_hash)
end;

Benchmark.ips do |x|
  x.report("serialize") { SerializeSample.find(1).content }
  x.report("json") { JsonSample.find(1).content }
  x.compare!
end;
```

### 結果
```
Warming up --------------------------------------
           serialize    75.000  i/100ms
                json   208.000  i/100ms
Calculating -------------------------------------
           serialize    760.620  (± 2.5%) i/s -      3.825k in   5.031852s
                json      2.077k (± 1.7%) i/s -     10.400k in   5.008244s

Comparison:
                json:     2077.2 i/s
           serialize:      760.6 i/s - 2.73x  slower
```

## 追加比較
あれ、`SerializeSample`のクラスがHashだからJSONとの比較だと変わるのか・・・と気付き再検証。
`serialize :content, JSON`に変更する以外検証内容は変更なし。

```ruby:app/models/serialize_sample.rb
class SerializeSample < ApplicationRecord
  serialize :content, JSON
end
```

## 追加比較１：Insert
### 結果
```
Warming up --------------------------------------
           serialize    28.000  i/100ms
                json    27.000  i/100ms
Calculating -------------------------------------
           serialize    290.488  (± 4.5%) i/s -      1.456k in   5.023126s
                json    282.069  (± 5.7%) i/s -      1.431k in   5.092156s

Comparison:
           serialize:      290.5 i/s
                json:      282.1 i/s - same-ish: difference falls within error
```

## 追加比較２：Read
### 結果
```
Warming up --------------------------------------
           serialize   217.000  i/100ms
                json   201.000  i/100ms
Calculating -------------------------------------
           serialize      2.266k (± 1.9%) i/s -     11.501k in   5.076196s
                json      2.054k (± 1.8%) i/s -     10.452k in   5.091438s

Comparison:
           serialize:     2266.4 i/s
                json:     2053.5 i/s - 1.10x  slower
```

## 総括
散々結果はったものの、結局速度差はたいしてありませんでした、という結果に終わった。
速度比較するまでもなく、コードを見ればやってることは同じなので大差ない事はわかるレベルだったので辛い。

- [serialize](https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/coders/json.rb)
- [jsonカラム](https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/type/internal/abstract_json.rb)

jsonカラムの方が若干処理が多いので、その分速度に出ているのかもしれない。