Skip to content

Instantly share code, notes, and snippets.

@sammyhenningsson
Forked from tjwallace/results.md
Created January 30, 2019 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sammyhenningsson/890f7e4d6967883666851eb6aab92adb to your computer and use it in GitHub Desktop.
Save sammyhenningsson/890f7e4d6967883666851eb6aab92adb to your computer and use it in GitHub Desktop.

Output on Ruby 2.6 (Dell Latitude E5470, 3.6 GHz Intel Core i7)

Note: I manually removed some BigDecimal deprecation warnings

Rehearsal -------------------------------------------------
as_json         0.133909   0.000000   0.133909 (  0.133921)
hal_presenter   0.231052   0.000037   0.231089 (  0.231093)
fast_jsonapi    0.171646   0.000000   0.171646 (  0.171650)
grape_entity    0.752034   0.000000   0.752034 (  0.753542)
blueprinter     0.236064   0.000000   0.236064 (  0.236073)
ams             0.903373   0.000000   0.903373 (  0.904142)
roar            0.796884   0.000000   0.796884 (  0.797658)
panko           0.132083   0.000000   0.132083 (  0.132869)
---------------------------------------- total: 3.357082sec

                    user     system      total        real
as_json         0.128401   0.000000   0.128401 (  0.128401)
hal_presenter   0.232206   0.000000   0.232206 (  0.233029)
fast_jsonapi    0.188536   0.000000   0.188536 (  0.188538)
grape_entity    0.772651   0.000000   0.772651 (  0.772654)
blueprinter     0.176390   0.000000   0.176390 (  0.176390)
ams             0.927703   0.000000   0.927703 (  0.929251)
roar            0.736453   0.000000   0.736453 (  0.736457)
panko           0.139502   0.000000   0.139502 (  0.140238)
Warming up --------------------------------------
             as_json     1.000  i/100ms
       hal_presenter     1.000  i/100ms
        fast_jsonapi     1.000  i/100ms
        grape_entity     1.000  i/100ms
         blueprinter     1.000  i/100ms
                 ams     1.000  i/100ms
                roar     1.000  i/100ms
               panko     1.000  i/100ms
Calculating -------------------------------------
             as_json      6.520  (± 2.6%) i/s -     65.000  in  10.090427s
       hal_presenter      3.419  (± 0.9%) i/s -     35.000  in  10.240197s
        fast_jsonapi      4.099  (± 2.6%) i/s -     41.000  in  10.054100s
        grape_entity      1.293  (± 3.9%) i/s -     13.000  in  10.100216s
         blueprinter      4.763  (± 2.5%) i/s -     48.000  in  10.163818s
                 ams      1.024  (± 1.1%) i/s -     11.000  in  10.742024s
                roar      1.223  (± 1.7%) i/s -     13.000  in  10.639683s
               panko      6.371  (± 3.3%) i/s -     63.000  in  10.064088s
                   with 95.0% confidence

Comparison:
             as_json:        6.5 i/s
               panko:        6.4 i/s - same-ish: difference falls within error
         blueprinter:        4.8 i/s - 1.37x  (± 0.05) slower
        fast_jsonapi:        4.1 i/s - 1.59x  (± 0.06) slower
       hal_presenter:        3.4 i/s - 1.91x  (± 0.05) slower
        grape_entity:        1.3 i/s - 5.04x  (± 0.24) slower
                roar:        1.2 i/s - 5.33x  (± 0.17) slower
                 ams:        1.0 i/s - 6.36x  (± 0.18) slower
                   with 95.0% confidence

Calculating -------------------------------------
             as_json    55.424M memsize (    12.195M retained)
                       840.577k objects (    60.037k retained)
                         8.000  strings (     0.000  retained)
       hal_presenter    72.240M memsize (     0.000  retained)
                       360.263k objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
        fast_jsonapi    37.208M memsize (     0.000  retained)
                       490.259k objects (     0.000  retained)
                        50.000  strings (     0.000  retained)
        grape_entity   113.797M memsize (    40.000  retained)
                       940.511k objects (     1.000  retained)
                         4.000  strings (     1.000  retained)
         blueprinter    36.768M memsize (     0.000  retained)
                       320.182k objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
                 ams   167.576M memsize (     0.000  retained)
                         1.651M objects (     0.000  retained)
                         6.000  strings (     0.000  retained)
                roar   130.757M memsize (     0.000  retained)
                       860.632k objects (     0.000  retained)
                         1.000  strings (     0.000  retained)
               panko    55.431M memsize (     0.000  retained)
                       840.650k objects (     0.000  retained)
                        13.000  strings (     0.000  retained)

Comparison:
         blueprinter:   36767984 allocated
        fast_jsonapi:   37208120 allocated - 1.01x more
             as_json:   55423592 allocated - 1.51x more
               panko:   55430736 allocated - 1.51x more
       hal_presenter:   72240408 allocated - 1.96x more
        grape_entity:  113796920 allocated - 3.10x more
                roar:  130756664 allocated - 3.56x more
                 ams:  167575856 allocated - 4.56x more
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'oj'
gem 'benchmark-ips', require: 'benchmark/ips'
gem 'kalibera'
gem 'benchmark-memory', require: 'benchmark/memory'
gem 'activesupport'
# https://github.com/Netflix/fast_jsonapi
gem 'fast_jsonapi'
# https://github.com/ruby-grape/grape-entity
gem 'grape-entity'
# https://github.com/procore/blueprinter
gem 'blueprinter'
# https://github.com/rails-api/active_model_serializers/tree/0-10-stable
gem 'active_model_serializers', '~> 0.10.0'
# https://github.com/trailblazer/roar
# https://github.com/trailblazer/representable
gem 'roar'
gem 'multi_json'
# https://github.com/yosiat/panko_serializer
gem 'panko_serializer'
# https://github.com/sammyhenningsson/hal_presenter
gem 'hal_presenter'
end
require 'active_support'
require 'active_support/core_ext/object' # For Hash#deep_dup
# Define models
Issue = Struct.new(:id, :number, :title, :user, :labels) do
alias_method :read_attribute_for_serialization, :send
def label_ids
labels.map(&:id)
end
def user_id
user.id
end
end
User = Struct.new(:id, :login) do
alias_method :read_attribute_for_serialization, :send
end
Label = Struct.new(:id, :name, :color) do
alias_method :read_attribute_for_serialization, :send
end
# Define serializers
module HALPresenter
class LabelSerializer
extend HALPresenter
attribute :id
attribute :name
attribute :color
end
class UserSerializer
extend HALPresenter
attribute :id
attribute :login
end
class IssueSerializer
extend HALPresenter
attribute :id
attribute :number
attribute :title
embed :labels, presenter_class: LabelSerializer
embed :user, presenter_class: UserSerializer
collection of: :issues
end
end
module FastJsonApi
class IssueSerializer
include FastJsonapi::ObjectSerializer
attributes :number, :title
has_many :labels
belongs_to :user
end
class LabelSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :color
end
class UserSerializer
include FastJsonapi::ObjectSerializer
attributes :login
end
end
module GrapeEntity
class Label < Grape::Entity
expose :id
expose :name
expose :color
end
class User < Grape::Entity
expose :id
expose :login
end
class Issue < Grape::Entity
expose :id
expose :number
expose :title
expose :labels, using: Label
expose :user, using: User
end
end
Blueprinter.configure do |config|
config.generator = Oj
config.sort_fields_by = :definition
end
module BluePrint
class Label < Blueprinter::Base
identifier :id
fields :name, :color
end
class User < Blueprinter::Base
identifier :id
field :login
end
class Issue < Blueprinter::Base
identifier :id
fields :number, :title
association :labels, blueprint: Label
association :user, blueprint: User
end
end
ActiveModelSerializers.logger = nil
module Ams
class Label < ActiveModel::Serializer
attributes :id, :name, :color
end
class User < ActiveModel::Serializer
attributes :id, :login
end
class Issue < ActiveModel::Serializer
attributes :id, :number, :title
has_many :labels, serializer: Label
belongs_to :user, serializer: User
end
end
require 'roar/decorator'
require 'roar/json'
module ROAR
class IssueRepresenter < Roar::Decorator
include Roar::JSON
property :id
property :number
property :title
collection :labels do
property :id
property :name
property :color
end
property :user do
property :id
property :login
end
end
end
module PANKO
class LabelSerializer < Panko::Serializer
attributes :id, :name, :color
end
class UserSerializer < Panko::Serializer
attributes :id, :login
end
class IssueSerializer < Panko::Serializer
attributes :id, :number, :title
has_many :labels, serializer: LabelSerializer
has_one :user, serializer: UserSerializer
end
end
# Generate data
users = Array.new(10) { |i| User.new(i, "User #{i}") }
labels = Array.new(4) { |i| Label.new(i, "Label #{i}", 'ffffff') }
issues = Array.new(10_000) { |i| Issue.new(i, i, "Issue #{i}", users.sample, labels.sample(rand(2..4))) }
serializers = [
{
name: :as_json,
serializer: -> { issues.as_json },
output_inspector: ->(output) { output.first }
},
{
name: :hal_presenter,
serializer: -> { HALPresenter::IssueSerializer.send(:to_collection_hash, issues, {_depth: 0}) },
output_inspector: ->(output) { output[:_embedded][:issues].first }
},
{
name: :fast_jsonapi,
serializer: -> { FastJsonApi::IssueSerializer.new(issues, include: [:labels, :user]).serializable_hash },
output_inspector: ->(output) { output[:data].first }
},
{
name: :grape_entity,
serializer: -> { GrapeEntity::Issue.represent(issues).as_json },
output_inspector: ->(output) { output.first }
},
{
name: :blueprinter,
serializer: -> { BluePrint::Issue.render_as_hash(issues) },
output_inspector: ->(output) { output.first }
},
{
name: :ams,
serializer: -> { ActiveModelSerializers::SerializableResource.new(issues, each_serializer: Ams::Issue).as_json },
output_inspector: ->(output) { output.first }
},
{
name: :roar,
serializer: -> { ROAR::IssueRepresenter.for_collection.new(issues).as_json },
output_inspector: ->(output) { output.first }
},
{
name: :panko,
serializer: -> { Panko::ArraySerializer.new(issues, each_serializer: PANKO::IssueSerializer).as_json },
output_inspector: ->(output) { output['subjects'].first }
}
]
# Display output
serializers.each do |name:, serializer:, output_inspector:|
puts "\n#{name}:\n"
puts output_inspector.call(serializer.call).inspect
puts
end
# Run benchmarks
require 'benchmark'
Benchmark.bmbm do |b|
serializers.each do |name:, serializer:, **_other|
b.report(name, &serializer)
end
end
%i[ips memory].each do |bench|
Benchmark.send(bench) do |b|
b.config(time: 10, warmup: 5, stats: :bootstrap, confidence: 95) if b.respond_to?(:config)
serializers.each do |name:, serializer:, **_other|
b.report(name, &serializer)
end
b.compare!
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment