Last active
September 24, 2022 03:32
-
-
Save barbolo/0a2bf9e78d9484fca1efe4ea4a9c0f31 to your computer and use it in GitHub Desktop.
Benchmark of JSON rendering in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# gem install multi_json json yajl-ruby oj benchmark-memory | |
require 'multi_json' | |
require 'json' | |
require 'yajl' | |
require 'oj' | |
require 'benchmark' | |
require 'benchmark-memory' | |
hash1 = { | |
'key1' => 'string', # String | |
'key2' => 153, # Integer | |
'key3' => 224.81, # Float | |
'key4' => true, # Boolean | |
'key5' => nil, # Null | |
'key6' => Date.today, # Date | |
'key7' => Time.now, # Datetime | |
} | |
hash2 = hash1.merge({ | |
'key8' => { # 3-level tree | |
'key8.1' => hash1.clone, | |
'key8.2' => { | |
'key8.2.1' => hash1.clone, | |
'key8.2.2' => hash1.clone | |
} | |
}, | |
'key9' => 10.times.map { hash1.clone }, # Array of Hashes | |
'key10' => 'a'*100_000, # 100KB file | |
}) | |
hash3 = Hash[1_000.times.map do |i| | |
key = "key#{i+1}" | |
value = if i % 2 == 0 | |
hash1.clone | |
else | |
[hash1.clone, {'1' => {'2' => {'3' => {'4' => {'5' => {'6' => {'7' => hash2.clone}}}}}}}] | |
end | |
[key, value] | |
end] | |
[hash1, hash2, hash3].each_with_index do |hash_i, i| | |
puts | |
puts '------------------' | |
puts "Benchmark of hash#{i+1}" | |
puts '------------------' | |
nrep = 100 | |
bench = Benchmark.bm(10) do |x| | |
MultiJson.use :json_gem | |
x.report('json_gem:') { nrep.times { MultiJson.dump(hash_i) } } | |
MultiJson.use :yajl | |
x.report('yajl:') { nrep.times { MultiJson.dump(hash_i) } } | |
MultiJson.use :oj | |
x.report('oj:') { nrep.times { MultiJson.dump(hash_i) } } | |
x.report('to_json:') { nrep.times { hash_i.to_json } } | |
end | |
puts | |
puts "Results in ms per execution:" | |
bench.each do |bm| | |
puts ">>>>> #{bm.label} - %.3f ms" % (bm.real*1000.0/nrep) | |
end | |
puts | |
# https://github.com/michaelherold/benchmark-memory | |
Benchmark.memory do |x| | |
MultiJson.use :json_gem | |
x.report('json_gem:') { MultiJson.dump(hash_i) } | |
MultiJson.use :yajl | |
x.report('yajl:') { MultiJson.dump(hash_i) } | |
MultiJson.use :oj | |
x.report('oj:') { MultiJson.dump(hash_i) } | |
x.report('to_json:') { hash_i.to_json } | |
x.compare! | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# gem install multi_json oj benchmark-memory rabl jbuilder | |
require 'multi_json' | |
require 'oj' | |
require 'rabl' | |
require 'jbuilder' | |
require 'benchmark' | |
require 'benchmark-memory' | |
class BaseModel | |
def initialize(attrs={}) | |
attrs.each { |k, v| self.send("#{k}=", v) } | |
end | |
end | |
class UserModel < BaseModel | |
attr_accessor :name, :age, :email | |
end | |
class CommentModel < BaseModel | |
attr_accessor :user, :message, :created_at, :attachment | |
end | |
class PostModel < BaseModel | |
attr_accessor :content, :author, :comments, :created_at, :published | |
end | |
def create_post(size=1_000, comments=10, attachment_size=0) | |
PostModel.new( | |
content: '-'*size, | |
author: UserModel.new(name: 'First Last', age: rand(100), email: '[email protected]'), | |
created_at: Time.now, | |
published: true, | |
comments: comments.times.map do | |
CommentModel.new( | |
user: UserModel.new(name: 'First Last', age: rand(100), email: '[email protected]'), | |
message: '-'*400, | |
created_at: Time.now, | |
attachment: '-'*attachment_size | |
) | |
end | |
) | |
end | |
def render_jbuilder(post) | |
Jbuilder.encode do |json| | |
json.content post.content | |
json.created_at post.created_at | |
json.published post.published | |
json.author do | |
json.name post.author.name | |
json.age post.author.age | |
json.email post.author.email | |
end | |
json.comments post.comments do |comment| | |
json.created_at comment.created_at | |
json.message comment.message | |
json.user do | |
json.name comment.user.name | |
json.age comment.user.age | |
json.email comment.user.email | |
end | |
json.attachment comment.attachment | |
end | |
end | |
end | |
def render_rabl(post) | |
# # Create a post.rabl file with the following content | |
# attribute :content | |
# attribute :created_at | |
# attribute :published | |
# child(:author) do | |
# attribute :name | |
# attribute :age | |
# attribute :email | |
# end | |
# child(:comments) do | |
# attribute :created_at | |
# attribute :message | |
# child(:user) do | |
# attribute :name | |
# attribute :age | |
# attribute :email | |
# end | |
# attribute :attachment | |
# end | |
Rabl.render(post, 'post', view_path: '.', format: :json) | |
end | |
def render_dump(post) | |
MultiJson.dump({ | |
'content' => post.content, | |
'created_at' => post.created_at, | |
'published' => post.published, | |
'author' => { | |
'name' => post.author.name, | |
'age' => post.author.age, | |
'email' => post.author.email, | |
}, | |
'comments' => post.comments.map do |comment| | |
{ | |
'created_at' => comment.created_at, | |
'message' => comment.message, | |
'user' => { | |
'name' => comment.user.name, | |
'age' => comment.user.age, | |
'email' => comment.user.email, | |
}, | |
'attachment' => comment.attachment, | |
} | |
end | |
}) | |
end | |
def render_string(post) | |
%({ | |
"content": #{MultiJson.encode(post.content)}, | |
"created_at": #{MultiJson.encode(post.created_at)}, | |
"published": #{MultiJson.encode(post.published)}, | |
"author":{ | |
"name": #{MultiJson.encode(post.author.name)}, | |
"age": #{MultiJson.encode(post.author.age)}, | |
"email": #{MultiJson.encode(post.author.email)} | |
}, | |
"comments":[ | |
#{ | |
post.comments.map do |comment| | |
%({ | |
"created_at": #{MultiJson.encode(comment.created_at)}, | |
"message": #{MultiJson.encode(comment.message)}, | |
"user":{ | |
"name": #{MultiJson.encode(comment.user.name)}, | |
"age": #{MultiJson.encode(comment.user.age)}, | |
"email": #{MultiJson.encode(comment.user.email)} | |
}, | |
"attachment": #{MultiJson.encode(comment.attachment)} | |
}) | |
end.join(",\n") | |
} | |
] | |
}) | |
end | |
# Confirm render_string is working fine | |
# MultiJson.load(render_string(post_i)) == MultiJson.load(render_dump(post_i)) | |
post1 = create_post(1_000, 10, 0) | |
post2 = create_post(1_000_000, 200, 1_000) | |
post3 = create_post(10_000_000, 5_000, 10_000) | |
MultiJson.use :oj | |
[post1, post2, post3].each_with_index do |post_i, i| | |
puts | |
puts '------------------' | |
puts "Benchmark of post#{i+1}" | |
puts '------------------' | |
nrep = 1000 | |
bench = Benchmark.bm(10) do |x| | |
x.report('rabl:') { nrep.times { render_rabl(post_i) } } | |
x.report('jbuilder:') { nrep.times { render_jbuilder(post_i) } } | |
x.report('dump:') { nrep.times { render_dump(post_i) } } | |
x.report('string:') { nrep.times { render_string(post_i) } } | |
end | |
puts | |
puts "Results in ms per execution:" | |
bench.each do |bm| | |
puts ">>>>> #{bm.label} - %.3f ms" % (bm.real*1000.0/nrep) | |
end | |
puts | |
# https://github.com/michaelherold/benchmark-memory | |
Benchmark.memory do |x| | |
x.report('rabl:') { render_rabl(post_i) } | |
x.report('jbuilder:') { render_jbuilder(post_i) } | |
x.report('dump:') { render_dump(post_i) } | |
x.report('string:') { render_string(post_i) } | |
x.compare! | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment