> ## Content Index
> Fetch the complete content index at: https://serpapi.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Optimizing JSON performance in Rails
- URL: https://serpapi.com/blog/benchmarking-oj-vs-json/
- Published: 2022-12-12T09:16:49.000Z
- Updated: 2022-12-12T09:16:49.000Z
- Author: Yicheng Zhou

Scraping search results at SerpApi often involves JSON parsing. SerpApi is also generating large JSON responses, where JSON serialization takes place. It is worthwhile to investigate if we can speed up JSON processing.

A quick search of the Ruby ecosystem shows [Oj](https://github.com/ohler55/oj) as the best fit for Rails. It has support for being a drop-in replacement with super easy configuration. We benchmarked `Oj` vs. the standard `json` library that ships with Ruby.

Since `json` performance may differ across Ruby versions, we ran the same benchmark code under Ruby 2.7.2, Ruby 3.1.2, and Ruby 3.1.2 +YJIT.

We compared four methods that are mainly used: `JSON.parse`, `obj.to_json`, `JSON.dump`, `JSON.pretty_generate`. We ran each method for 10K iterations and compared the time taken.

Here is the benchmark code:

```ruby
def run(result)
  json = File.read('test.json')
  result[:parse] << Benchmark.ms {
    10000.times do
      JSON.parse(json)
    end
  }
  puts "parse #{result[:parse].last}"

  hash = JSON.parse(json)
  result[:to_json] << Benchmark.ms {
    10000.times do
      hash.to_json
    end
  }
  puts "to_json #{result[:to_json].last}"

  hash = JSON.parse(json)
  result[:dump] << Benchmark.ms {
    10000.times do
      JSON.dump(hash)
    end
  }
  puts "dump #{result[:dump].last}"

  hash = JSON.parse(json)
  result[:pretty_generate] << Benchmark.ms {
    10000.times do
      JSON.pretty_generate(hash)
    end
  }
  puts "pretty_generate #{result[:pretty_generate].last}"
end

json_result = {
  parse: [],
  to_json: [],
  dump: [],
  pretty_generate: []
}
10.times { run(json_result) }

Oj.optimize_rails()

oj_result = {
  parse: [],
  to_json: [],
  dump: [],
  pretty_generate: []
}
10.times { run(oj_result) }

[json_result, oj_result].each do |result|
  result.each do |k, v|
    result[k] = v.sum / v.size
  end
end

pp json_result
pp oj_result
```

We ran the tests on `DigitalOcean CPU-Optimized, 2 vCPUs, 4 GB`.

Here are the results:

![json vs Oj under ruby 2 7 2 (lower is better)](https://user-images.githubusercontent.com/3034310/202351478-108c298c-f1ef-49ac-b123-4b7eebe48d28.svg)

![json vs Oj under ruby 3 1 2 (lower is better)](https://user-images.githubusercontent.com/3034310/202351489-5cfa7680-f81f-4166-b0e3-f75118b33f16.svg)

![json vs Oj under ruby 3 1 2 +YJIT (lower is better)](https://user-images.githubusercontent.com/3034310/202352460-719df584-1a41-49dd-b967-84ae90f21588.svg)

And here is the raw data:

Ruby 2.7.2

|                       | json        | Oj          |
| --------------------- | ----------- | ----------- |
| JSON.parse            | 7994.661393 | 4043.134571 |
| obj.to\_json          | 64983.64482 | 3405.177611 |
| JSON.dump             | 2713.536324 | 1633.386881 |
| JSON.pretty\_generate | 2964.274016 | 1938.549645 |

Ruby 3.1.2

|                       | json        | Oj          |
| --------------------- | ----------- | ----------- |
| JSON.parse            | 6005.806509 | 4317.333413 |
| obj.to\_json          | 72626.32646 | 2760.772417 |
| JSON.dump             | 2901.954273 | 1708.129035 |
| JSON.pretty\_generate | 3214.653918 | 2136.254046 |

Ruby 3.1.2 +YJIT

|                       | json        | Oj          |
| --------------------- | ----------- | ----------- |
| JSON.parse            | 5963.452377 | 4363.962555 |
| obj.to\_json          | 69703.34656 | 2790.86789  |
| JSON.dump             | 2873.292326 | 1712.871591 |
| JSON.pretty\_generate | 3158.897077 | 2179.701062 |

We have also checked if the `Oj` and `json` outputs are identical.

`Oj` outperforms `json` on all tested ruby versions.

On Ruby 2.7.2, `Oj` has 1.97x `JSON.parse`, 19x `obj.to_json`, 1.69x `JSON.dump`, 1.5x `JSON.pretty_generate`. We can easily improve JSON performance by at least 50% with a few lines of code!

Ruby 3.1.2 has improved `JSON.parse`, but other methods have become slower. Ruby 3.1.2 with YJIT enabled does not have a significant impact on performance.

`obj.to_json` is unexpectedly slow by default. According to the [documentation](https://github.com/ohler55/oj/blob/develop/pages/Rails.md), `Oj` has optimized the behavior. We have yet to check if the output will differ on model objects, but we can certainly use it on pure `Hash` without a problem.

That's all for the JSON benchmark. Thank you for reading! See you at the following benchmark post.