panko_serializer 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.clang-format +102 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +22 -0
- data/.travis.yml +8 -0
- data/Gemfile +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +8 -0
- data/Rakefile +62 -0
- data/benchmarks/BENCHMARKS.md +48 -0
- data/benchmarks/allocs.rb +23 -0
- data/benchmarks/app.rb +13 -0
- data/benchmarks/benchmarking_support.rb +43 -0
- data/benchmarks/bm_active_model_serializers.rb +45 -0
- data/benchmarks/bm_controller.rb +81 -0
- data/benchmarks/bm_panko_json.rb +60 -0
- data/benchmarks/bm_panko_object.rb +69 -0
- data/benchmarks/profile.rb +88 -0
- data/benchmarks/sanity.rb +67 -0
- data/benchmarks/setup.rb +62 -0
- data/benchmarks/type_casts/bm_active_record.rb +57 -0
- data/benchmarks/type_casts/bm_panko.rb +67 -0
- data/benchmarks/type_casts/bm_pg.rb +35 -0
- data/benchmarks/type_casts/support.rb +16 -0
- data/ext/panko_serializer/attributes_iterator.c +62 -0
- data/ext/panko_serializer/attributes_iterator.h +17 -0
- data/ext/panko_serializer/extconf.rb +8 -0
- data/ext/panko_serializer/panko_serializer.c +189 -0
- data/ext/panko_serializer/panko_serializer.h +17 -0
- data/ext/panko_serializer/serialization_descriptor.c +166 -0
- data/ext/panko_serializer/serialization_descriptor.h +30 -0
- data/ext/panko_serializer/time_conversion.c +94 -0
- data/ext/panko_serializer/time_conversion.h +6 -0
- data/ext/panko_serializer/type_cast.c +271 -0
- data/ext/panko_serializer/type_cast.h +74 -0
- data/lib/panko/array_serializer.rb +40 -0
- data/lib/panko/cache.rb +35 -0
- data/lib/panko/serialization_descriptor.rb +119 -0
- data/lib/panko/serializer.rb +57 -0
- data/lib/panko/version.rb +4 -0
- data/lib/panko.rb +8 -0
- data/panko_serializer.gemspec +31 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30e3e534552c9c7592b512b679460e53de9eae33
|
4
|
+
data.tar.gz: a0a70502582161816691ba9aaa327ed17695bf8c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 26d0cf119bad48ec684ce49fc8c6413d3a35e976ca135c96d37d7d6e738f19f00f23225195873289bfa5aff3f6059c27bb8bf54fd3f881d75c3e57b4b3da6847
|
7
|
+
data.tar.gz: d55cb3c5e156c2f4d64ac67ac9429328d3476944876cdfe4a41ef0b149bd78a636b229061d9648d5a0a9eb3eca79ea68000ab0a6f7ca9d0b472be9163b546903
|
data/.clang-format
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
---
|
2
|
+
Language: Cpp
|
3
|
+
# BasedOnStyle: Chromium
|
4
|
+
AccessModifierOffset: -1
|
5
|
+
AlignAfterOpenBracket: Align
|
6
|
+
AlignConsecutiveAssignments: false
|
7
|
+
AlignConsecutiveDeclarations: false
|
8
|
+
AlignEscapedNewlines: Left
|
9
|
+
AlignOperands: true
|
10
|
+
AlignTrailingComments: true
|
11
|
+
AllowAllParametersOfDeclarationOnNextLine: false
|
12
|
+
AllowShortBlocksOnASingleLine: false
|
13
|
+
AllowShortCaseLabelsOnASingleLine: false
|
14
|
+
AllowShortFunctionsOnASingleLine: Inline
|
15
|
+
AllowShortIfStatementsOnASingleLine: false
|
16
|
+
AllowShortLoopsOnASingleLine: false
|
17
|
+
AlwaysBreakAfterDefinitionReturnType: None
|
18
|
+
AlwaysBreakAfterReturnType: None
|
19
|
+
AlwaysBreakBeforeMultilineStrings: true
|
20
|
+
AlwaysBreakTemplateDeclarations: true
|
21
|
+
BinPackArguments: true
|
22
|
+
BinPackParameters: false
|
23
|
+
BraceWrapping:
|
24
|
+
AfterClass: false
|
25
|
+
AfterControlStatement: false
|
26
|
+
AfterEnum: false
|
27
|
+
AfterFunction: false
|
28
|
+
AfterNamespace: false
|
29
|
+
AfterObjCDeclaration: false
|
30
|
+
AfterStruct: false
|
31
|
+
AfterUnion: false
|
32
|
+
BeforeCatch: false
|
33
|
+
BeforeElse: false
|
34
|
+
IndentBraces: false
|
35
|
+
SplitEmptyFunctionBody: true
|
36
|
+
BreakBeforeBinaryOperators: None
|
37
|
+
BreakBeforeBraces: Attach
|
38
|
+
BreakBeforeInheritanceComma: false
|
39
|
+
BreakBeforeTernaryOperators: true
|
40
|
+
BreakConstructorInitializersBeforeComma: false
|
41
|
+
BreakConstructorInitializers: BeforeColon
|
42
|
+
BreakAfterJavaFieldAnnotations: false
|
43
|
+
BreakStringLiterals: true
|
44
|
+
ColumnLimit: 80
|
45
|
+
CommentPragmas: '^ IWYU pragma:'
|
46
|
+
CompactNamespaces: false
|
47
|
+
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
48
|
+
ConstructorInitializerIndentWidth: 4
|
49
|
+
ContinuationIndentWidth: 4
|
50
|
+
Cpp11BracedListStyle: true
|
51
|
+
DerivePointerAlignment: false
|
52
|
+
DisableFormat: false
|
53
|
+
ExperimentalAutoDetectBinPacking: false
|
54
|
+
FixNamespaceComments: true
|
55
|
+
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
56
|
+
IncludeCategories:
|
57
|
+
- Regex: '^<.*\.h>'
|
58
|
+
Priority: 1
|
59
|
+
- Regex: '^<.*'
|
60
|
+
Priority: 2
|
61
|
+
- Regex: '.*'
|
62
|
+
Priority: 3
|
63
|
+
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
64
|
+
IndentCaseLabels: true
|
65
|
+
IndentWidth: 2
|
66
|
+
IndentWrappedFunctionNames: false
|
67
|
+
JavaScriptQuotes: Leave
|
68
|
+
JavaScriptWrapImports: true
|
69
|
+
KeepEmptyLinesAtTheStartOfBlocks: false
|
70
|
+
MacroBlockBegin: ''
|
71
|
+
MacroBlockEnd: ''
|
72
|
+
MaxEmptyLinesToKeep: 1
|
73
|
+
NamespaceIndentation: None
|
74
|
+
ObjCBlockIndentWidth: 2
|
75
|
+
ObjCSpaceAfterProperty: false
|
76
|
+
ObjCSpaceBeforeProtocolList: false
|
77
|
+
PenaltyBreakAssignment: 2
|
78
|
+
PenaltyBreakBeforeFirstCallParameter: 1
|
79
|
+
PenaltyBreakComment: 300
|
80
|
+
PenaltyBreakFirstLessLess: 120
|
81
|
+
PenaltyBreakString: 1000
|
82
|
+
PenaltyExcessCharacter: 1000000
|
83
|
+
PenaltyReturnTypeOnItsOwnLine: 200
|
84
|
+
PointerAlignment: Left
|
85
|
+
ReflowComments: true
|
86
|
+
SortIncludes: true
|
87
|
+
SpaceAfterCStyleCast: false
|
88
|
+
SpaceAfterTemplateKeyword: true
|
89
|
+
SpaceBeforeAssignmentOperators: true
|
90
|
+
SpaceBeforeParens: ControlStatements
|
91
|
+
SpaceInEmptyParentheses: false
|
92
|
+
SpacesBeforeTrailingComments: 2
|
93
|
+
SpacesInAngles: false
|
94
|
+
SpacesInContainerLiterals: true
|
95
|
+
SpacesInCStyleCastParentheses: false
|
96
|
+
SpacesInParentheses: false
|
97
|
+
SpacesInSquareBrackets: false
|
98
|
+
Standard: Auto
|
99
|
+
TabWidth: 8
|
100
|
+
UseTab: Never
|
101
|
+
...
|
102
|
+
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisabledByDefault: true
|
3
|
+
TargetRubyVersion: 2.4.1
|
4
|
+
DisplayCopNames: true
|
5
|
+
StyleGuideCopsOnly: false
|
6
|
+
|
7
|
+
Metrics/LineLength:
|
8
|
+
Max: 120
|
9
|
+
|
10
|
+
Style/StringLiterals:
|
11
|
+
EnforcedStyle: double_quotes
|
12
|
+
|
13
|
+
Style/FrozenStringLiteralComment:
|
14
|
+
Enabled: true
|
15
|
+
EnforcedStyle: always
|
16
|
+
|
17
|
+
Layout/TrailingBlankLines:
|
18
|
+
Enabled: true
|
19
|
+
|
20
|
+
Layout/TrailingWhitespace:
|
21
|
+
Enabled: true
|
22
|
+
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
source "https://rubygems.org"
|
3
|
+
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
version = "4.2"
|
7
|
+
gem_version = "~> #{version}.9"
|
8
|
+
|
9
|
+
gem "rails", gem_version
|
10
|
+
gem "railties", gem_version
|
11
|
+
gem "activesupport", gem_version
|
12
|
+
gem "activemodel", gem_version
|
13
|
+
gem "actionpack", gem_version
|
14
|
+
gem "activerecord", gem_version, group: :test
|
15
|
+
|
16
|
+
group :benchmarks do
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "pg"
|
19
|
+
|
20
|
+
gem "memory_profiler"
|
21
|
+
gem "ruby-prof"
|
22
|
+
gem "ruby-prof-flamegraph"
|
23
|
+
|
24
|
+
gem "benchmark-ips"
|
25
|
+
gem "active_model_serializers", "0.9.7"
|
26
|
+
|
27
|
+
gem "terminal-table"
|
28
|
+
end
|
29
|
+
|
30
|
+
group :test do
|
31
|
+
gem "faker"
|
32
|
+
end
|
33
|
+
|
34
|
+
group :development do
|
35
|
+
gem "byebug"
|
36
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Yosi Attias
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require "json"
|
5
|
+
require "terminal-table"
|
6
|
+
require "rake/extensiontask"
|
7
|
+
|
8
|
+
gem = Gem::Specification.load( File.dirname(__FILE__) + "/panko_serializer.gemspec" )
|
9
|
+
|
10
|
+
|
11
|
+
Rake::ExtensionTask.new("panko_serializer", gem) do |ext|
|
12
|
+
ext.lib_dir = "lib/panko"
|
13
|
+
end
|
14
|
+
|
15
|
+
Gem::PackageTask.new(gem) do |pkg|
|
16
|
+
pkg.need_zip = pkg.need_tar = false
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec::Core::RakeTask.new(:spec)
|
20
|
+
Rake::Task[:spec].prerequisites << :compile
|
21
|
+
|
22
|
+
task default: :spec
|
23
|
+
|
24
|
+
|
25
|
+
def run_benchmarks(files, items_count: 14_000)
|
26
|
+
headings = ["Benchmark", "ip/s", "allocs/retained"]
|
27
|
+
files.each do |benchmark_file|
|
28
|
+
output = `ITEMS_COUNT=#{items_count} RAILS_ENV=production ruby #{benchmark_file}`
|
29
|
+
|
30
|
+
rows = output.each_line.map do |line|
|
31
|
+
result = JSON.parse(line)
|
32
|
+
result.values
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "\n\n"
|
36
|
+
title = File.basename(benchmark_file, ".rb")
|
37
|
+
table = Terminal::Table.new title: title, headings: headings, rows: rows
|
38
|
+
puts table
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Run all benchmarks"
|
43
|
+
task :benchmarks do
|
44
|
+
run_benchmarks Dir[File.join(__dir__, "benchmarks", "**", "bm_*")]
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Type Casts - Benchmarks"
|
48
|
+
task :bm_type_casts do
|
49
|
+
run_benchmarks Dir[File.join(__dir__, "benchmarks", "type_casts", "bm_*")]
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Sanity Benchmarks"
|
53
|
+
task :sanity do
|
54
|
+
puts Time.now.strftime("%d/%m %H:%M:%S")
|
55
|
+
puts "=========================="
|
56
|
+
|
57
|
+
run_benchmarks [
|
58
|
+
File.join(__dir__, "benchmarks", "sanity.rb")
|
59
|
+
], items_count: 2300
|
60
|
+
|
61
|
+
puts "\n\n"
|
62
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
## Initial state
|
2
|
+
```
|
3
|
+
Native_Posts_2300 57 ip/s 13802 allocs/op
|
4
|
+
Native_Posts_50 2956 ip/s 302 allocs/op
|
5
|
+
|
6
|
+
AMS_Simple_Posts_2300 21 ip/s 94309 allocs/op
|
7
|
+
AMS_Simple_Posts_50 1008 ip/s 2059 allocs/op
|
8
|
+
AMS_HasOne_Posts_2300 9 ip/s 147209 allocs/op
|
9
|
+
AMS_HasOne_Posts_50 451 ip/s 3209 allocs/op
|
10
|
+
|
11
|
+
Panko_HasOne_Posts_2300 164 ip/s 2372 allocs/op
|
12
|
+
Panko_HasOne_Posts_50 6118 ip/s 122 allocs/op
|
13
|
+
Panko_Reused_HasOne_Posts_2300 178 ip/s 2303 allocs/op
|
14
|
+
Panko_Reused_HasOne_Posts_50 8203 ip/s 53 allocs/op
|
15
|
+
|
16
|
+
Panko_Simple_Posts_2300 150 ip/s 2372 allocs/op
|
17
|
+
Panko_Simple_Posts_50 5639 ip/s 122 allocs/op
|
18
|
+
Panko_Reused_Simple_Posts_2300 180 ip/s 2303 allocs/op
|
19
|
+
Panko_Reused_Simple_Posts_50 8388 ip/s 53 allocs/op
|
20
|
+
```
|
21
|
+
|
22
|
+
## Refactorings, method call support, combining
|
23
|
+
|
24
|
+
### class eval
|
25
|
+
```
|
26
|
+
Panko_HasOne_Posts_2300 64 ip/s 9477 allocs/op
|
27
|
+
Panko_HasOne_Posts_50 2397 ip/s 477 allocs/op
|
28
|
+
Panko_Reused_HasOne_Posts_2300 70 ip/s 9423 allocs/op
|
29
|
+
Panko_Reused_HasOne_Posts_50 2596 ip/s 423 allocs/op
|
30
|
+
|
31
|
+
Panko_Simple_Posts_2300 191 ip/s 2472 allocs/op
|
32
|
+
Panko_Simple_Posts_50 5128 ip/s 222 allocs/op
|
33
|
+
Panko_Reused_Simple_Posts_2300 180 ip/s 2418 allocs/op
|
34
|
+
Panko_Reused_Simple_Posts_50 5534 ip/s 168 allocs/op
|
35
|
+
```
|
36
|
+
|
37
|
+
### instance eval
|
38
|
+
```
|
39
|
+
Panko_HasOne_Posts_2300 60 ip/s 9473 allocs/op
|
40
|
+
Panko_HasOne_Posts_50 2399 ip/s 473 allocs/op
|
41
|
+
Panko_Reused_HasOne_Posts_2300 66 ip/s 9419 allocs/op
|
42
|
+
Panko_Reused_HasOne_Posts_50 2582 ip/s 419 allocs/op
|
43
|
+
|
44
|
+
Panko_Simple_Posts_2300 195 ip/s 2470 allocs/op
|
45
|
+
Panko_Simple_Posts_50 4838 ip/s 220 allocs/op
|
46
|
+
Panko_Reused_Simple_Posts_2300 196 ip/s 2416 allocs/op
|
47
|
+
Panko_Reused_Simple_Posts_50 6241 ip/s 166 allocs/op
|
48
|
+
```
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require_relative "./benchmarking_support"
|
4
|
+
require_relative "./app"
|
5
|
+
require_relative "./setup"
|
6
|
+
|
7
|
+
require "memory_profiler"
|
8
|
+
|
9
|
+
class PostFastSerializer < Panko::Serializer
|
10
|
+
attributes :id, :body, :title, :author_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def count_allocs(&block)
|
14
|
+
memory_report = MemoryProfiler.report(&block)
|
15
|
+
puts memory_report.pretty_print
|
16
|
+
end
|
17
|
+
|
18
|
+
posts = Post.all.to_a
|
19
|
+
merged_options = {}.merge(each_serializer: PostFastSerializer)
|
20
|
+
posts_array_serializer = Panko::ArraySerializer.new([], merged_options)
|
21
|
+
|
22
|
+
# prints out 18402
|
23
|
+
count_allocs { posts_array_serializer.serialize_to_json posts }
|
data/benchmarks/app.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
|
5
|
+
require "rails"
|
6
|
+
require "active_model"
|
7
|
+
require "active_support"
|
8
|
+
require "active_support/json"
|
9
|
+
require "action_controller"
|
10
|
+
require "action_controller/railtie"
|
11
|
+
|
12
|
+
require "active_model_serializers"
|
13
|
+
require "panko"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "benchmark/ips"
|
3
|
+
require "json"
|
4
|
+
require "memory_profiler"
|
5
|
+
|
6
|
+
module Benchmark
|
7
|
+
module ActiveModelSerializers
|
8
|
+
def data
|
9
|
+
posts = Post.all.includes(:author).to_a
|
10
|
+
posts_50 = posts.first(50).to_a
|
11
|
+
{ all: posts, small: posts_50 }
|
12
|
+
end
|
13
|
+
|
14
|
+
def ams(label = nil, time: 10, disable_gc: true, warmup: 3, &block)
|
15
|
+
fail ArgumentError.new, "block should be passed" unless block_given?
|
16
|
+
|
17
|
+
GC.start
|
18
|
+
|
19
|
+
if disable_gc
|
20
|
+
GC.disable
|
21
|
+
else
|
22
|
+
GC.enable
|
23
|
+
end
|
24
|
+
|
25
|
+
memory_report = MemoryProfiler.report(&block)
|
26
|
+
|
27
|
+
report = Benchmark.ips(time, warmup, true) do |x|
|
28
|
+
x.report(label) { yield }
|
29
|
+
end
|
30
|
+
|
31
|
+
results = {
|
32
|
+
label: label,
|
33
|
+
ips: ActiveSupport::NumberHelper.number_to_delimited(report.entries.first.ips.round(2)),
|
34
|
+
allocs: "#{memory_report.total_allocated}/#{memory_report.total_retained}"
|
35
|
+
}.to_json
|
36
|
+
|
37
|
+
puts results
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
extend Benchmark::ActiveModelSerializers
|
43
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class AmsAuthorFastSerializer < ActiveModel::Serializer
|
7
|
+
attributes :id, :name
|
8
|
+
end
|
9
|
+
|
10
|
+
class AmsPostFastSerializer < ActiveModel::Serializer
|
11
|
+
attributes :id, :body, :title, :author_id
|
12
|
+
end
|
13
|
+
|
14
|
+
class AmsPostWithHasOneFastSerializer < ActiveModel::Serializer
|
15
|
+
attributes :id, :body, :title, :author_id
|
16
|
+
|
17
|
+
has_one :author, serializer: AmsAuthorFastSerializer
|
18
|
+
end
|
19
|
+
|
20
|
+
def benchmark_ams(prefix, serializer, options = {})
|
21
|
+
merged_options = options.merge(each_serializer: serializer)
|
22
|
+
|
23
|
+
data = Benchmark.data
|
24
|
+
posts = data[:all]
|
25
|
+
posts_50 = data[:small]
|
26
|
+
|
27
|
+
|
28
|
+
Benchmark.ams("AMS_#{prefix}_Posts_#{posts.count}") do
|
29
|
+
ActiveModel::ArraySerializer.new(posts, merged_options).serializable_object
|
30
|
+
end
|
31
|
+
|
32
|
+
data = Benchmark.data
|
33
|
+
posts = data[:all]
|
34
|
+
posts_50 = data[:small]
|
35
|
+
|
36
|
+
Benchmark.ams("AMS_#{prefix}_Posts_50") do
|
37
|
+
ActiveModel::ArraySerializer.new(posts_50, merged_options).serializable_object
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
benchmark_ams "HasOne", AmsPostWithHasOneFastSerializer
|
43
|
+
benchmark_ams "Simple", AmsPostFastSerializer
|
44
|
+
benchmark_ams "Except", AmsPostWithHasOneFastSerializer, except: [:title]
|
45
|
+
benchmark_ams "Include", AmsPostWithHasOneFastSerializer, include: [:id, :body, :author_id, :author]
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class NullLogger < Logger
|
7
|
+
def initialize(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(*args, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class BenchmarkApp < Rails::Application
|
15
|
+
routes.append do
|
16
|
+
get "/simple" => "main#simple"
|
17
|
+
get "/text" => "main#text"
|
18
|
+
|
19
|
+
get "/serialize_to_string" => "main#serialize_to_string"
|
20
|
+
end
|
21
|
+
|
22
|
+
config.secret_token = "s"*30
|
23
|
+
config.secret_key_base = "foo"
|
24
|
+
config.consider_all_requests_local = false
|
25
|
+
|
26
|
+
# simulate production
|
27
|
+
config.cache_classes = true
|
28
|
+
config.eager_load = true
|
29
|
+
config.action_controller.perform_caching = true
|
30
|
+
|
31
|
+
# otherwise deadlock occured
|
32
|
+
config.middleware.delete "Rack::Lock"
|
33
|
+
|
34
|
+
# to disable log files
|
35
|
+
config.logger = NullLogger.new
|
36
|
+
config.active_support.deprecation = :log
|
37
|
+
end
|
38
|
+
|
39
|
+
BenchmarkApp.initialize!
|
40
|
+
|
41
|
+
class AuthorFastSerializer < Panko::Serializer
|
42
|
+
attributes :id, :name
|
43
|
+
end
|
44
|
+
|
45
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
46
|
+
attributes :id, :body, :title, :author_id
|
47
|
+
|
48
|
+
has_one :author, serializer: AuthorFastSerializer
|
49
|
+
end
|
50
|
+
|
51
|
+
class MainController < ActionController::Base
|
52
|
+
def text
|
53
|
+
render text: '{"ok":true}'.freeze, content_type: "application/json".freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
def simple
|
57
|
+
render json: { ok: true }
|
58
|
+
end
|
59
|
+
|
60
|
+
def serialize_to_string
|
61
|
+
data = Benchmark.data[:all]
|
62
|
+
render text: Panko::ArraySerializer.new(data, each_serializer: PostWithHasOneFastSerializer).to_json, content_type: "application/json".freeze
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class RouteNotFoundError < StandardError;end
|
67
|
+
|
68
|
+
|
69
|
+
def request(method, path)
|
70
|
+
response = Rack::MockRequest.new(BenchmarkApp).send(method, path)
|
71
|
+
if response.status.in?([404, 500])
|
72
|
+
raise RouteNotFoundError.new, 'not found #{method.to_s.upcase} #{path}'
|
73
|
+
end
|
74
|
+
response
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
Benchmark.ams("text") { request(:get, "/text") }
|
79
|
+
Benchmark.ams("simple") { request(:get, "/simple") }
|
80
|
+
|
81
|
+
Benchmark.ams("serialize_to_string") { request(:get, "/serialize_to_string") }
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class AuthorFastSerializer < Panko::Serializer
|
7
|
+
attributes :id, :name
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
class PostFastSerializer < Panko::Serializer
|
12
|
+
attributes :id, :body, :title, :author_id
|
13
|
+
end
|
14
|
+
|
15
|
+
class PostFastWithMethodCallSerializer < Panko::Serializer
|
16
|
+
attributes :id, :body, :title, :author_id, :method_call
|
17
|
+
|
18
|
+
def method_call
|
19
|
+
object.id * 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
24
|
+
attributes :id, :body, :title, :author_id
|
25
|
+
|
26
|
+
has_one :author, serializer: AuthorFastSerializer
|
27
|
+
end
|
28
|
+
|
29
|
+
class AuthorWithHasManyFastSerializer < Panko::Serializer
|
30
|
+
attributes :id, :name
|
31
|
+
|
32
|
+
has_many :posts, serializer: PostFastSerializer
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def benchmark(prefix, serializer, options = {})
|
37
|
+
data = Benchmark.data
|
38
|
+
posts = data[:all]
|
39
|
+
posts_50 = data[:small]
|
40
|
+
|
41
|
+
merged_options = options.merge(each_serializer: serializer)
|
42
|
+
|
43
|
+
Benchmark.ams("Panko_#{prefix}_Posts_#{posts.count}") do
|
44
|
+
Panko::ArraySerializer.new(posts, merged_options).to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
data = Benchmark.data
|
48
|
+
posts = data[:all]
|
49
|
+
posts_50 = data[:small]
|
50
|
+
|
51
|
+
Benchmark.ams("Panko_#{prefix}_Posts_50") do
|
52
|
+
Panko::ArraySerializer.new(posts_50, merged_options).to_json
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
benchmark "HasOne", PostWithHasOneFastSerializer
|
57
|
+
benchmark "Simple", PostFastSerializer
|
58
|
+
benchmark "SimpleWithMethodCall", PostFastWithMethodCallSerializer
|
59
|
+
benchmark "Except", PostWithHasOneFastSerializer, except: [:title]
|
60
|
+
benchmark "Include", PostWithHasOneFastSerializer, include: [:id, :body, :author_id, :author]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class AuthorFastSerializer < Panko::Serializer
|
7
|
+
attributes :id, :name
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
class PostFastSerializer < Panko::Serializer
|
12
|
+
attributes :id, :body, :title, :author_id
|
13
|
+
end
|
14
|
+
|
15
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
16
|
+
attributes :id, :body, :title, :author_id
|
17
|
+
|
18
|
+
has_one :author, serializer: AuthorFastSerializer
|
19
|
+
end
|
20
|
+
|
21
|
+
class AuthorWithHasManyFastSerializer < Panko::Serializer
|
22
|
+
attributes :id, :name
|
23
|
+
|
24
|
+
has_many :posts, serializer: PostFastSerializer
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def benchmark(prefix, serializer, options = {})
|
29
|
+
data = Benchmark.data
|
30
|
+
posts = data[:all]
|
31
|
+
posts_50 = data[:small]
|
32
|
+
|
33
|
+
merged_options = options.merge(each_serializer: serializer)
|
34
|
+
|
35
|
+
Benchmark.ams("Panko_#{prefix}_Posts_#{posts.count}") do
|
36
|
+
Panko::ArraySerializer.new(posts, merged_options).to_a
|
37
|
+
end
|
38
|
+
|
39
|
+
data = Benchmark.data
|
40
|
+
posts = data[:all]
|
41
|
+
posts_50 = data[:small]
|
42
|
+
|
43
|
+
Benchmark.ams("Panko_#{prefix}_Posts_50") do
|
44
|
+
Panko::ArraySerializer.new(posts_50, merged_options).to_a
|
45
|
+
end
|
46
|
+
|
47
|
+
posts_array_serializer = Panko::ArraySerializer.new([], merged_options)
|
48
|
+
|
49
|
+
data = Benchmark.data
|
50
|
+
posts = data[:all]
|
51
|
+
posts_50 = data[:small]
|
52
|
+
|
53
|
+
Benchmark.ams("Panko_Reused_#{prefix}_Posts_#{posts.count}") do
|
54
|
+
posts_array_serializer.serialize posts
|
55
|
+
end
|
56
|
+
|
57
|
+
data = Benchmark.data
|
58
|
+
posts = data[:all]
|
59
|
+
posts_50 = data[:small]
|
60
|
+
|
61
|
+
Benchmark.ams("Panko_Reused_#{prefix}_Posts_50") do
|
62
|
+
posts_array_serializer.serialize posts_50
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
benchmark "HasOne", PostWithHasOneFastSerializer
|
67
|
+
benchmark "Simple", PostFastSerializer
|
68
|
+
benchmark "Except", PostWithHasOneFastSerializer, except: [:title]
|
69
|
+
benchmark "Include", PostWithHasOneFastSerializer, include: [:id, :body, :author_id, :author]
|