Skip to content

Commit eea4496

Browse files
guilleiguaranshishirmk
authored andcommitted
Add benchmarks for jsonapi-serializers library
1 parent 33f08d9 commit eea4496

6 files changed

Lines changed: 272 additions & 15 deletions

File tree

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ GEM
5555
jsonapi-renderer (0.2.0)
5656
jsonapi-serializable (0.3.0)
5757
jsonapi-renderer (~> 0.2.0)
58+
jsonapi-serializers (1.0.0)
59+
activesupport
5860
loofah (2.1.1)
5961
crass (~> 1.0.2)
6062
nokogiri (>= 1.5.9)
@@ -104,6 +106,7 @@ DEPENDENCIES
104106
byebug
105107
fast_jsonapi!
106108
jsonapi-rb (~> 0.5.0)
109+
jsonapi-serializers (~> 1.0.0)
107110
oj (~> 3.3)
108111
rspec (~> 3.5.0)
109112
rspec-benchmark (~> 0.3.0)

fast_jsonapi.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ Gem::Specification.new do |gem|
3131
gem.add_development_dependency(%q<active_model_serializers>, ["~> 0.10.7"])
3232
gem.add_development_dependency(%q<sqlite3>, ["~> 1.3"])
3333
gem.add_development_dependency(%q<jsonapi-rb>, ["~> 0.5.0"])
34+
gem.add_development_dependency(%q<jsonapi-serializers>, ["~> 1.0.0"])
3435
end

spec/lib/object_serializer_performance_spec.rb

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
include_context 'movie class'
55
include_context 'ams movie class'
66
include_context 'jsonapi movie class'
7+
include_context 'jsonapi-serializers movie class'
78

89
include_context 'group class'
910
include_context 'ams group class'
1011
include_context 'jsonapi group class'
12+
include_context 'jsonapi-serializers group class'
1113

1214
before(:all) { GC.disable }
1315
after(:all) { GC.enable }
@@ -42,34 +44,38 @@
4244
end
4345
end
4446

45-
def print_stats(message, count, ams_time, jsonapi_time, our_time)
47+
def print_stats(message, count, ams_time, jsonapi_time, jsonapis_time, our_time)
4648
format = '%-15s %-10s %s'
4749
puts ''
4850
puts message
4951
puts format(format, 'Serializer', 'Records', 'Time')
5052
puts format(format, 'AMS serializer', count, ams_time.round(2).to_s + ' ms')
5153
puts format(format, 'jsonapi-rb serializer', count, jsonapi_time.round(2).to_s + ' ms')
54+
puts format(format, 'jsonapi-serializers', count, jsonapis_time.round(2).to_s + ' ms')
5255
puts format(format, 'Fast serializer', count, our_time.round(2).to_s + ' ms')
5356
end
5457

55-
def run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
56-
our_time = Benchmark.measure { our_hash = our_serializer.serializable_hash }.real * 1000
57-
ams_time = Benchmark.measure { ams_hash = ams_serializer.as_json }.real * 1000
58-
jsonapi_time = Benchmark.measure { ams_hash = jsonapi_serializer.to_hash }.real * 1000
58+
def run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
59+
our_time = Benchmark.measure { our_serializer.serializable_hash }.real * 1000
60+
ams_time = Benchmark.measure { ams_serializer.as_json }.real * 1000
61+
jsonapi_time = Benchmark.measure { jsonapi_serializer.to_hash }.real * 1000
62+
jsonapis_time = Benchmark.measure { jsonapis_serializer.to_hash }.real * 1000
5963

60-
print_stats(message, movie_count, ams_time, jsonapi_time, our_time)
64+
print_stats(message, movie_count, ams_time, jsonapi_time, jsonapis_time, our_time)
6165
end
6266

63-
def run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
67+
def run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
6468
our_json = nil
6569
ams_json = nil
6670
jsonapi_json = nil
71+
jsonapis_json = nil
6772
our_time = Benchmark.measure { our_json = our_serializer.serialized_json }.real * 1000
6873
ams_time = Benchmark.measure { ams_json = ams_serializer.to_json }.real * 1000
6974
jsonapi_time = Benchmark.measure { jsonapi_json = jsonapi_serializer.to_json }.real * 1000
75+
jsonapis_time = Benchmark.measure { jsonapis_json = jsonapis_serializer.to_json }.real * 1000
7076

71-
print_stats(message, movie_count, ams_time, jsonapi_time, our_time)
72-
return our_json, ams_json, jsonapi_json
77+
print_stats(message, movie_count, ams_time, jsonapi_time, jsonapis_time, our_time)
78+
return our_json, ams_json, jsonapi_json, jsonapis_json
7379
end
7480

7581
context 'when comparing with AMS 0.10.x' do
@@ -79,15 +85,17 @@ def run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jso
7985
ams_movies = build_ams_movies(movie_count)
8086
movies = build_movies(movie_count)
8187
jsonapi_movies = build_jsonapi_movies(movie_count)
88+
jsonapis_movies = build_js_movies(movie_count)
8289
our_serializer = MovieSerializer.new(movies)
8390
ams_serializer = ActiveModelSerializers::SerializableResource.new(ams_movies)
8491
jsonapi_serializer = JSONAPISerializer.new(jsonapi_movies)
92+
jsonapis_serializer = JSONAPISSerializer.new(jsonapis_movies)
8593

8694
message = "Serialize to JSON string #{movie_count} records"
87-
our_json, ams_json, jsonapi_json = run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
95+
our_json, ams_json, _, _ = run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
8896

8997
message = "Serialize to Ruby Hash #{movie_count} records"
90-
run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
98+
run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
9199

92100
expect(our_json.length).to eq ams_json.length
93101
expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
@@ -103,18 +111,20 @@ def run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jso
103111
ams_movies = build_ams_movies(movie_count)
104112
movies = build_movies(movie_count)
105113
jsonapi_movies = build_jsonapi_movies(movie_count)
114+
jsonapis_movies = build_js_movies(movie_count)
106115
options = {}
107116
options[:meta] = { total: movie_count }
108117
options[:include] = [:actors, :movie_type]
109118
our_serializer = MovieSerializer.new(movies, options)
110119
ams_serializer = ActiveModelSerializers::SerializableResource.new(ams_movies, include: options[:include], meta: options[:meta])
111120
jsonapi_serializer = JSONAPISerializer.new(jsonapi_movies, include: options[:include], meta: options[:meta])
121+
jsonapis_serializer = JSONAPISSerializer.new(jsonapis_movies, include: options[:include].map{|i| i.to_s.dasherize}, meta: options[:meta])
112122

113123
message = "Serialize to JSON string #{movie_count} with includes and meta"
114-
our_json, ams_json = run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
124+
our_json, ams_json = run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
115125

116126
message = "Serialize to Ruby Hash #{movie_count} with includes and meta"
117-
run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer)
127+
run_hash_benchmark(message, movie_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
118128

119129
expect(our_json.length).to eq ams_json.length
120130
expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
@@ -129,16 +139,19 @@ def run_json_benchmark(message, movie_count, our_serializer, ams_serializer, jso
129139
it "should serialize #{group_count} records at least #{speed_factor} times faster than AMS" do
130140
ams_groups = build_ams_groups(group_count)
131141
groups = build_groups(group_count)
142+
jsonapi_groups = build_jsonapi_groups(group_count)
143+
jsonapis_groups = build_jsonapis_groups(group_count)
132144
options = {}
133145
our_serializer = GroupSerializer.new(groups, options)
134146
ams_serializer = ActiveModelSerializers::SerializableResource.new(ams_groups)
135147
jsonapi_serializer = JSONAPISerializerB.new(jsonapi_groups)
148+
jsonapis_serializer = JSONAPISSerializerB.new(jsonapis_groups)
136149

137150
message = "Serialize to JSON string #{group_count} with polymorphic has_many"
138-
our_json, ams_json, jsonapi_json = run_json_benchmark(message, group_count, our_serializer, ams_serializer, jsonapi_serializer)
151+
our_json, ams_json, _, _ = run_json_benchmark(message, group_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
139152

140153
message = "Serialize to Ruby Hash #{group_count} with polymorphic has_many"
141-
run_hash_benchmark(message, group_count, our_serializer, ams_serializer, jsonapi_serializer)
154+
run_hash_benchmark(message, group_count, our_serializer, ams_serializer, jsonapi_serializer, jsonapis_serializer)
142155

143156
expect(our_json.length).to eq ams_json.length
144157
expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times

spec/shared/contexts/js_context.rb

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
RSpec.shared_context 'jsonapi-serializers movie class' do
2+
before(:context) do
3+
# models
4+
class JSMovie
5+
attr_accessor :id, :name, :release_year, :actors, :owner, :movie_type
6+
end
7+
8+
class JSActor
9+
attr_accessor :id, :name, :email
10+
end
11+
12+
class JSUser
13+
attr_accessor :id, :name
14+
end
15+
16+
class JSMovieType
17+
attr_accessor :id, :name
18+
end
19+
20+
# serializers
21+
class JSActorSerializer
22+
include JSONAPI::Serializer
23+
attributes :name, :email
24+
25+
def type
26+
'actor'
27+
end
28+
end
29+
class JSUserSerializer
30+
include JSONAPI::Serializer
31+
attributes :name
32+
33+
def type
34+
'user'
35+
end
36+
end
37+
class JSMovieTypeSerializer
38+
include JSONAPI::Serializer
39+
attributes :name
40+
41+
def type
42+
'movie_type'
43+
end
44+
end
45+
class JSMovieSerializer
46+
include JSONAPI::Serializer
47+
attributes :name, :release_year
48+
has_many :actors
49+
has_one :owner
50+
has_one :movie_type
51+
52+
def type
53+
'movie'
54+
end
55+
end
56+
57+
class JSONAPISSerializer
58+
def initialize(data, options = {})
59+
@options = options.merge(is_collection: true)
60+
@data = data
61+
end
62+
63+
def to_json
64+
JSONAPI::Serializer.serialize(@data, @options).to_json
65+
end
66+
67+
def to_hash
68+
JSONAPI::Serializer.serialize(@data, @options)
69+
end
70+
end
71+
end
72+
73+
after(:context) do
74+
classes_to_remove = %i[
75+
JSMovie
76+
JSActor
77+
JSUser
78+
JSMovieType
79+
JSONAPISSerializer
80+
JSActorSerializer
81+
JSUserSerializer
82+
JSMovieTypeSerializer
83+
JSMovieSerializer]
84+
classes_to_remove.each do |klass_name|
85+
Object.send(:remove_const, klass_name) if Object.constants.include?(klass_name)
86+
end
87+
end
88+
89+
let(:js_actors) do
90+
3.times.map do |i|
91+
a = JSActor.new
92+
a.id = i + 1
93+
a.name = "Test #{a.id}"
94+
a.email = "test#{a.id}@test.com"
95+
a
96+
end
97+
end
98+
99+
let(:js_user) do
100+
ams_user = JSUser.new
101+
ams_user.id = 3
102+
ams_user
103+
end
104+
105+
let(:js_movie_type) do
106+
ams_movie_type = JSMovieType.new
107+
ams_movie_type.id = 1
108+
ams_movie_type.name = 'episode'
109+
ams_movie_type
110+
end
111+
112+
def build_js_movies(count)
113+
count.times.map do |i|
114+
m = JSMovie.new
115+
m.id = i + 1
116+
m.name = 'test movie'
117+
m.actors = js_actors
118+
m.owner = js_user
119+
m.movie_type = js_movie_type
120+
m
121+
end
122+
end
123+
end
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
RSpec.shared_context 'jsonapi-serializers group class' do
2+
3+
# Person, Group Classes and serializers
4+
before(:context) do
5+
# models
6+
class JSPerson
7+
attr_accessor :id, :first_name, :last_name
8+
end
9+
10+
class JSGroup
11+
attr_accessor :id, :name, :groupees # Let's assume groupees can be Person or Group objects
12+
end
13+
14+
# serializers
15+
class JSPersonSerializer
16+
include JSONAPI::Serializer
17+
attributes :first_name, :last_name
18+
19+
def type
20+
'person'
21+
end
22+
end
23+
24+
class JSGroupSerializer
25+
include JSONAPI::Serializer
26+
attributes :name
27+
has_many :groupees
28+
29+
def type
30+
'group'
31+
end
32+
end
33+
34+
class JSONAPISSerializerB
35+
def initialize(data, options = {})
36+
@options = options.merge(is_collection: true)
37+
@data = data
38+
end
39+
40+
def to_json
41+
JSON.fast_generate(to_hash)
42+
end
43+
44+
def to_hash
45+
JSONAPI::Serializer.serialize(@data, @options)
46+
end
47+
end
48+
end
49+
50+
after :context do
51+
classes_to_remove = %i[
52+
JSPerson
53+
JSGroup
54+
JSPersonSerializer
55+
JSGroupSerializer]
56+
classes_to_remove.each do |klass_name|
57+
Object.send(:remove_const, klass_name) if Object.constants.include?(klass_name)
58+
end
59+
end
60+
61+
let(:jsonapi_groups) do
62+
group_count = 0
63+
person_count = 0
64+
3.times.map do |i|
65+
group = JSGroup.new
66+
group.id = group_count + 1
67+
group.name = "Test Group #{group.id}"
68+
group_count = group.id
69+
70+
person = JSPerson.new
71+
person.id = person_count + 1
72+
person.last_name = "Last Name #{person.id}"
73+
person.first_name = "First Name #{person.id}"
74+
person_count = person.id
75+
76+
child_group = JSGroup.new
77+
child_group.id = group_count + 1
78+
child_group.name = "Test Group #{child_group.id}"
79+
group_count = child_group.id
80+
81+
group.groupees = [person, child_group]
82+
group
83+
end
84+
end
85+
86+
let(:jsonapis_person) do
87+
person = JSPerson.new
88+
person.id = 3
89+
person
90+
end
91+
92+
def build_jsonapis_groups(count)
93+
group_count = 0
94+
person_count = 0
95+
count.times.map do |i|
96+
group = JSGroup.new
97+
group.id = group_count + 1
98+
group.name = "Test Group #{group.id}"
99+
group_count = group.id
100+
101+
person = JSPerson.new
102+
person.id = person_count + 1
103+
person.last_name = "Last Name #{person.id}"
104+
person.first_name = "First Name #{person.id}"
105+
person_count = person.id
106+
107+
child_group = JSGroup.new
108+
child_group.id = group_count + 1
109+
child_group.name = "Test Group #{child_group.id}"
110+
group_count = child_group.id
111+
112+
group.groupees = [person, child_group]
113+
group
114+
end
115+
end
116+
end

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'active_model_serializers'
55
require 'oj'
66
require 'jsonapi/serializable'
7+
require 'jsonapi-serializers'
78

89
Dir[File.dirname(__FILE__) + '/shared/contexts/*.rb'].each {|file| require file }
910

0 commit comments

Comments
 (0)