Skip to content

Instantly share code, notes, and snippets.

@okuramasafumi
Last active June 1, 2021 07:48
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829 to your computer and use it in GitHub Desktop.
Save okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829 to your computer and use it in GitHub Desktop.
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
Copy link

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

@rainerborene
Copy link

alba is using oj. so, it's fair to enable it for blueprinter as well.

Blueprinter.configure do |config|
  config.generator = Oj
end

@okuramasafumi
Copy link
Author

@rainerborene That's right. Since we now have a benchmark within Alba's repository, I'll add that config to it. Thanks!

@mathieujobin
Copy link

is there instructions on how to run the benchmark from the repo ? the link above is broken.

I would really like to see Blueprinter and Alba next to each other with both Oj as a backend.

sounds like we should add jsonapi-serializer as well if that wasn't done already

@mathieujobin
Copy link

from repo unmodified.

alba :-) (main) $ ruby benchmark/collection.rb
...
Warming up --------------------------------------
                alba     2.000  i/100ms
         alba_inline     2.000  i/100ms
                 ams     1.000  i/100ms
         blueprinter     1.000  i/100ms
            jbuilder     2.000  i/100ms
             jsonapi     1.000  i/100ms
 jsonapi_same_format     1.000  i/100ms
           primalize     2.000  i/100ms
               rails     1.000  i/100ms
       representable     1.000  i/100ms
          simple_ams     1.000  i/100ms
Calculating -------------------------------------
                alba     22.195  (±13.5%) i/s -    110.000  in   5.045800s
         alba_inline     21.541  (± 9.3%) i/s -    108.000  in   5.077406s
                 ams     14.762  (±13.5%) i/s -     73.000  in   5.047462s
         blueprinter     17.154  (±11.7%) i/s -     84.000  in   5.003230s
            jbuilder     21.432  (±14.0%) i/s -    106.000  in   5.029018s
             jsonapi     17.922  (±11.2%) i/s -     88.000  in   5.005450s
 jsonapi_same_format     18.060  (±11.1%) i/s -     89.000  in   5.029778s
           primalize     20.413  (± 9.8%) i/s -    102.000  in   5.078846s
               rails     12.973  (±15.4%) i/s -     64.000  in   5.023921s
       representable     14.273  (±14.0%) i/s -     69.000  in   5.005792s
          simple_ams     11.659  (±17.2%) i/s -     57.000  in   5.082957s

Comparison:
                alba:       22.2 i/s
         alba_inline:       21.5 i/s - same-ish: difference falls within error
            jbuilder:       21.4 i/s - same-ish: difference falls within error
           primalize:       20.4 i/s - same-ish: difference falls within error
 jsonapi_same_format:       18.1 i/s - same-ish: difference falls within error
             jsonapi:       17.9 i/s - same-ish: difference falls within error
         blueprinter:       17.2 i/s - 1.29x  (± 0.00) slower
                 ams:       14.8 i/s - 1.50x  (± 0.00) slower
       representable:       14.3 i/s - 1.56x  (± 0.00) slower
               rails:       13.0 i/s - 1.71x  (± 0.00) slower
          simple_ams:       11.7 i/s - 1.90x  (± 0.00) slower

Calculating -------------------------------------
                alba     3.046M memsize (    48.800k retained)
                        40.621k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
         alba_inline     3.053M memsize (    48.800k retained)
                        40.681k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
                 ams     3.668M memsize (    48.800k retained)
                        48.644k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
         blueprinter     4.090M memsize (    48.800k retained)
                        47.504k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
            jbuilder     3.829M memsize (    48.800k retained)
                        41.909k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
             jsonapi     6.033M memsize (    48.800k retained)
                        57.414k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
 jsonapi_same_format     5.888M memsize (    48.800k retained)
                        55.713k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
           primalize     3.555M memsize (    48.800k retained)
                        45.415k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
               rails     5.002M memsize (    48.800k retained)
                        59.603k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
       representable     5.257M memsize (    48.800k retained)
                        52.124k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)
          simple_ams    13.502M memsize (    48.800k retained)
                        94.522k objects (   600.000  retained)
                        50.000  strings (     0.000  retained)

Comparison:
                alba:    3046113 allocated
         alba_inline:    3053073 allocated - 1.00x more
           primalize:    3555443 allocated - 1.17x more
                 ams:    3668055 allocated - 1.20x more
            jbuilder:    3829477 allocated - 1.26x more
         blueprinter:    4089665 allocated - 1.34x more
               rails:    5002097 allocated - 1.64x more
       representable:    5256862 allocated - 1.73x more
 jsonapi_same_format:    5888481 allocated - 1.93x more
             jsonapi:    6032814 allocated - 1.98x more
          simple_ams:   13501897 allocated - 4.43x more

@mjobin-mdsol
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment