Skip to content

Instantly share code, notes, and snippets.

@okuramasafumi

okuramasafumi/Gemfile

Last active Sep 1, 2020
Embed
What would you like to do?
Ruby JSON serializers benchmark
require "bundler/setup"
require "active_record"
require "logger"
require "oj"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
# ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.string :body
end
create_table :comments, force: true do |t|
t.integer :post_id
t.string :body
t.integer :commenter_id
end
create_table :users, force: true do |t|
t.string :name
end
end
class Post < ActiveRecord::Base
has_many :comments
has_many :commenters, through: :comments, class_name: 'User', source: :commenter
def attributes
{id: nil, body: nil, commenter_names: commenter_names}
end
def commenter_names
commenters.pluck(:name)
end
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :commenter, class_name: 'User'
def attributes
{id: nil, body: nil}
end
end
class User < ActiveRecord::Base
has_many :comments
end
require "alba"
Alba.backend = :oj
class AlbaCommentResource
include ::Alba::Resource
attributes :id, :body
end
class AlbaPostResource
include ::Alba::Resource
attributes :id, :body
many :comments, resource: AlbaCommentResource
attribute :commenter_names do
commenters.pluck(:name)
end
end
require "jbuilder"
class Post
def to_builder
Jbuilder.new do |post|
post.call(self, :id, :body, :comments, :commenter_names)
end
end
def commenter_names
commenters.pluck(:name)
end
end
class Comment
def to_builder
Jbuilder.new do |comment|
comment.call(self, :id, :body)
end
end
end
require "fast_jsonapi"
class FastCommentSerializer
include FastJsonapi::ObjectSerializer
attributes :id, :body
end
class FastPostSerializer
include FastJsonapi::ObjectSerializer
attributes :id, :body
attribute :commenter_names do |object|
object.commenters.pluck(:name)
end
has_many :comments, serializer: FastCommentSerializer
end
require "active_model_serializers"
class AMSCommentSerializer < ActiveModel::Serializer
attributes :id, :body
end
class AMSPostSerializer < ActiveModel::Serializer
attributes :id, :body
has_many :comments, serializer: AMSCommentSerializer
attribute :commenter_names
def commenter_names
object.commenters.pluck(:name)
end
end
require "blueprinter"
class CommentBlueprint < Blueprinter::Base
fields :id, :body
end
class PostBlueprint < Blueprinter::Base
fields :id, :body, :commenter_names
association :comments, blueprint: CommentBlueprint
def commenter_names
commenters.pluck(:name)
end
end
require "jsonapi"
require "jsonapi/renderer"
require "jsonapi/serializable"
class SerializablePost < JSONAPI::Serializable::Resource
type 'posts'
attributes :id, :body
attribute :commenter_names do
@object.commenters.pluck(:name)
end
has_many :comments do
attributes :data, :body
end
end
# require "jsonapi-resources"
# class PostResource < JSONAPI::Resource
# attributes :id, :body
# attribute :commenter_names do
# @object.commenters.pluck(:name)
# end
# has_many :comments do
# attributes :data, :body
# end
# end
require "representable"
class CommentRepresenter < Representable::Decorator
include Representable::JSON
property :id
property :body
end
class PostRepresenter < Representable::Decorator
include Representable::JSON
property :id
property :body
property :commenter_names
collection :comments
def commenter_names
commenters.pluck(:name)
end
end
post = Post.create!(body: 'post')
user1 = User.create!(name: 'John')
user2 = User.create!(name: 'Jane')
post.comments.create!(commenter: user1, body: 'Comment1')
post.comments.create!(commenter: user2, body: 'Comment2')
post.reload
alba = Proc.new { AlbaPostResource.new(post).serialize }
jbuilder = Proc.new { post.to_builder.target! }
fast_jsonapi = Proc.new { ActiveSupport::JSON.encode(FastPostSerializer.new(post).serializable_hash) }
ams = Proc.new { AMSPostSerializer.new(post, {}).to_json }
rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
blueprinter = Proc.new { PostBlueprint.render(post) }
jsonapi = Proc.new do
# Doesn't work
# renderer = JSONAPI::Serializable::Renderer.new
# renderer.render(post, class: {Post: SerializablePost})
end
representable = Proc.new { PostRepresenter.new(post).to_json }
alba_inline = Proc.new do
Alba.serialize(post) do
attributes :id, :body
attribute :commenter_names do
commenters.pluck(:name)
end
many :comments do
attributes :id, :body
end
end
end
[alba, jbuilder, fast_jsonapi, ams, rails, blueprinter, jsonapi, representable, alba_inline].each {|x| puts x.call }
require 'benchmark'
time = 10000
Benchmark.bmbm do |x|
x.report(:alba) { time.times(&alba) }
x.report(:jbuilder) { time.times(&jbuilder) }
x.report(:fast_jsonapi) { time.times(&fast_jsonapi) }
x.report(:ams) { time.times(&ams) }
x.report(:rails) { time.times(&rails) }
x.report(:blueprinter) { time.times(&blueprinter) }
x.report(:representable) { time.times(&representable) }
x.report(:alba_inline) { time.times(&alba_inline) }
end
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "rails"
gem "sqlite3"
gem "jbuilder"
gem "active_model_serializers"
gem "fast_jsonapi"
gem "jsonapi"
gem "jsonapi-serializable"
gem "jsonapi-resources"
gem "blueprinter"
gem "representable"
gem "jsonapi-serializer"
gem "alba"
gem "ruby-prof"
gem "oj"
gem "multi_json"
GEM
remote: https://rubygems.org/
specs:
actioncable (6.0.3.2)
actionpack (= 6.0.3.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
mail (>= 2.7.1)
actionmailer (6.0.3.2)
actionpack (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.3.2)
actionview (= 6.0.3.2)
activesupport (= 6.0.3.2)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.3.2)
actionpack (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
nokogiri (>= 1.8.5)
actionview (6.0.3.2)
activesupport (= 6.0.3.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.10.10)
actionpack (>= 4.1, < 6.1)
activemodel (>= 4.1, < 6.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (6.0.3.2)
activesupport (= 6.0.3.2)
globalid (>= 0.3.6)
activemodel (6.0.3.2)
activesupport (= 6.0.3.2)
activerecord (6.0.3.2)
activemodel (= 6.0.3.2)
activesupport (= 6.0.3.2)
activestorage (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
marcel (~> 0.3.1)
activesupport (6.0.3.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
alba (0.5.0)
blueprinter (0.25.0)
builder (3.2.4)
case_transform (0.2)
activesupport
concurrent-ruby (1.1.6)
crass (1.0.6)
declarative (0.0.20)
declarative-option (0.1.0)
erubi (1.9.0)
fast_jsonapi (1.5)
activesupport (>= 4.2)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
jbuilder (2.10.0)
activesupport (>= 5.0.0)
jsonapi (0.1.1.beta6)
jsonapi-parser (= 0.1.1.beta3)
jsonapi-renderer (= 0.1.1.beta1)
jsonapi-parser (0.1.1.beta3)
jsonapi-renderer (0.1.1.beta1)
jsonapi-resources (0.10.2)
activerecord (>= 4.1)
concurrent-ruby
railties (>= 4.1)
jsonapi-serializable (0.1.3)
jsonapi-renderer (~> 0.1)
jsonapi-serializer (2.0.0)
activesupport (>= 4.2)
loofah (2.6.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
method_source (1.0.0)
mimemagic (0.3.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.1)
multi_json (1.15.0)
nio4r (2.5.2)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
oj (3.10.8)
rack (2.2.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3.2)
actioncable (= 6.0.3.2)
actionmailbox (= 6.0.3.2)
actionmailer (= 6.0.3.2)
actionpack (= 6.0.3.2)
actiontext (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
activemodel (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
bundler (>= 1.3.0)
railties (= 6.0.3.2)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
railties (6.0.3.2)
actionpack (= 6.0.3.2)
activesupport (= 6.0.3.2)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
rake (13.0.1)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
ruby-prof (1.4.1)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
thor (1.0.1)
thread_safe (0.3.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
uber (0.1.0)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.4.0)
PLATFORMS
ruby
DEPENDENCIES
active_model_serializers
alba
blueprinter
fast_jsonapi
jbuilder
jsonapi
jsonapi-resources
jsonapi-serializable
jsonapi-serializer
multi_json
oj
rails
representable
ruby-prof
sqlite3
BUNDLED WITH
2.1.4
user system total real
alba 2.820060 0.022638 2.842698 ( 2.895920)
jbuilder 3.771705 0.034647 3.806352 ( 3.901307)
fast_jsonapi 4.239389 0.041856 4.281245 ( 4.393856)
ams 4.844297 0.049414 4.893711 ( 5.092888)
rails 6.527143 0.065684 6.592827 ( 6.759237)
blueprinter 3.641437 0.036881 3.678318 ( 3.813814)
representable 4.165615 0.040480 4.206095 ( 4.318423)
alba_inline 4.100987 0.039418 4.140405 ( 4.300006)
@ThomasSevestre

This comment has been minimized.

Copy link

@ThomasSevestre ThomasSevestre commented Sep 1, 2020

Thanks for this good benchmark. Here are a few kind remarks :

  • some gems need Oj.optimize_rails for optimal performance. You can add the line after require "oj"
  • jsonapi-serializeris a bit faster than original fast_jsonpi. It is the winner of your benchmark with an up to date bundle on my machine (I'm using ruby 2.6.6)
  • it looks like there is a performance regression in alba since 0.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment