11require_relative '../lib/class_kit'
22require 'date'
33require 'benchmark'
4+ require 'json'
45
5- class Item
6+ # -------------------------------------------------------------------
7+ # Test entities
8+ # -------------------------------------------------------------------
9+
10+ class BenchContact
611 extend ClassKit
12+ attr_accessor_type :landline , type : String
13+ attr_accessor_type :mobile , type : String
14+ attr_accessor_type :email , type : String
15+ end
16+
17+ class BenchAddress
18+ extend ClassKit
19+ attr_accessor_type :line1 , type : String
20+ attr_accessor_type :line2 , type : String
21+ attr_accessor_type :postcode , type : String
22+ attr_accessor_type :country , type : String
23+ end
724
25+ class BenchEmployee
26+ extend ClassKit
27+ attr_accessor_type :name , type : String
28+ attr_accessor_type :age , type : Integer
29+ attr_accessor_type :salary , type : Float
30+ attr_accessor_type :dob , type : Date
31+ attr_accessor_type :active , type : :bool
32+ attr_accessor_type :address , type : BenchAddress
33+ attr_accessor_type :contacts , type : Array , collection_type : BenchContact
34+ end
35+
36+ class BenchFlatItem
37+ extend ClassKit
838 attr_accessor_type :text , type : String
939 attr_accessor_type :integer , type : Integer
1040 attr_accessor_type :float , type : Float
@@ -13,9 +43,19 @@ class Item
1343 attr_accessor_type :bool , type : :bool
1444end
1545
16- items = [ ]
17- 10_000 . times do
18- items << Item . new . tap do |e |
46+ class BenchDeeplyNested
47+ extend ClassKit
48+ attr_accessor_type :name , type : String
49+ attr_accessor_type :child , type : BenchEmployee
50+ attr_accessor_type :employees , type : Array , collection_type : BenchEmployee
51+ end
52+
53+ # -------------------------------------------------------------------
54+ # Data builders
55+ # -------------------------------------------------------------------
56+
57+ def build_flat_item
58+ BenchFlatItem . new . tap do |e |
1959 e . text = 'foo bar'
2060 e . integer = 50
2161 e . float = 25.2
@@ -25,12 +65,186 @@ class Item
2565 end
2666end
2767
68+ def build_address
69+ BenchAddress . new . tap do |a |
70+ a . line1 = '25 The Street'
71+ a . line2 = 'Home Town'
72+ a . postcode = 'NE3 5RT'
73+ a . country = 'United Kingdom'
74+ end
75+ end
76+
77+ def build_contact
78+ BenchContact . new . tap do |c |
79+ c . landline = '01234567890'
80+ c . mobile = '07891234567'
81+ c . email = 'test@example.com'
82+ end
83+ end
84+
85+ def build_employee
86+ BenchEmployee . new . tap do |e |
87+ e . name = 'Joe Bloggs'
88+ e . age = 42
89+ e . salary = 55_000.50
90+ e . dob = Date . parse ( '1980-06-03' )
91+ e . active = true
92+ e . address = build_address
93+ e . contacts = [ build_contact , build_contact ]
94+ end
95+ end
96+
97+ def build_deeply_nested
98+ BenchDeeplyNested . new . tap do |d |
99+ d . name = 'Root'
100+ d . child = build_employee
101+ d . employees = 5 . times . map { build_employee }
102+ end
103+ end
104+
105+ # -------------------------------------------------------------------
106+ # Benchmark runner
107+ # -------------------------------------------------------------------
108+
109+ ITERATIONS = 5
110+ SIZES = { small : 100 , medium : 1_000 , large : 10_000 }
111+
28112helper = ClassKit ::Helper . new
29113
30- json = ''
114+ def separator
115+ puts '-' * 70
116+ end
117+
118+ def run_benchmark ( label , iterations : ITERATIONS )
119+ times = iterations . times . map do
120+ t0 = Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
121+ yield
122+ Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) - t0
123+ end
124+ median = times . sort [ times . size / 2 ]
125+ best = times . min
126+ printf " %-50s median: %8.4fs best: %8.4fs\n " , label , median , best
127+ median
128+ end
129+
130+ puts '=' * 70
131+ puts 'ClassKit Benchmark Suite'
132+ puts "Ruby #{ RUBY_VERSION } | #{ RUBY_PLATFORM } "
133+ puts '=' * 70
134+
135+ results = { }
136+
137+ # -------------------------------------------------------------------
138+ # 1. Flat serialization (to_json / from_json)
139+ # -------------------------------------------------------------------
140+ puts "\n ### 1. Flat items (6 typed attributes, no nesting)"
141+ separator
31142
32- puts '***serialize items***'
33- puts Benchmark . measure { json = helper . to_json ( items ) }
143+ SIZES . each do |size_name , count |
144+ items = count . times . map { build_flat_item }
145+ json = helper . to_json ( items )
34146
35- puts '***deserialize items***'
36- puts Benchmark . measure { helper . from_json ( json : json , klass : Item ) }
147+ key_ser = "flat_#{ size_name } _serialize"
148+ key_de = "flat_#{ size_name } _deserialize"
149+
150+ results [ key_ser ] = run_benchmark ( "to_json #{ count } flat items" ) { helper . to_json ( items ) }
151+ results [ key_de ] = run_benchmark ( "from_json #{ count } flat items" ) do
152+ helper . from_json ( json : json , klass : BenchFlatItem )
153+ end
154+ end
155+
156+ # -------------------------------------------------------------------
157+ # 2. Nested serialization (Employee with Address + Contacts)
158+ # -------------------------------------------------------------------
159+ puts "\n ### 2. Nested items (Employee -> Address + 2x Contact)"
160+ separator
161+
162+ SIZES . each do |size_name , count |
163+ items = count . times . map { build_employee }
164+ json = helper . to_json ( items )
165+
166+ key_ser = "nested_#{ size_name } _serialize"
167+ key_de = "nested_#{ size_name } _deserialize"
168+
169+ results [ key_ser ] = run_benchmark ( "to_json #{ count } nested items" ) { helper . to_json ( items ) }
170+ results [ key_de ] = run_benchmark ( "from_json #{ count } nested items" ) do
171+ helper . from_json ( json : json , klass : BenchEmployee )
172+ end
173+ end
174+
175+ # -------------------------------------------------------------------
176+ # 3. Deeply nested (3 levels deep with arrays)
177+ # -------------------------------------------------------------------
178+ puts "\n ### 3. Deeply nested items (3 levels, arrays of nested)"
179+ separator
180+
181+ [ 10 , 100 , 500 ] . each do |count |
182+ items = count . times . map { build_deeply_nested }
183+ json = helper . to_json ( items )
184+
185+ key_ser = "deep_#{ count } _serialize"
186+ key_de = "deep_#{ count } _deserialize"
187+
188+ results [ key_ser ] = run_benchmark ( "to_json #{ count } deep items" ) { helper . to_json ( items ) }
189+ results [ key_de ] = run_benchmark ( "from_json #{ count } deep items" ) do
190+ helper . from_json ( json : json , klass : BenchDeeplyNested )
191+ end
192+ end
193+
194+ # -------------------------------------------------------------------
195+ # 4. Hash round-trip (no JSON overhead)
196+ # -------------------------------------------------------------------
197+ puts "\n ### 4. Hash round-trip (to_hash / from_hash, 1000 nested)"
198+ separator
199+
200+ items = 1_000 . times . map { build_employee }
201+ hashes = items . map { |i | helper . to_hash ( i ) }
202+
203+ results [ 'hash_serialize' ] = run_benchmark ( 'to_hash 1000 nested items' ) { items . each { |i | helper . to_hash ( i ) } }
204+ results [ 'hash_deserialize' ] = run_benchmark ( 'from_hash 1000 nested items' ) do
205+ hashes . each do |h |
206+ helper . from_hash ( hash : h , klass : BenchEmployee )
207+ end
208+ end
209+
210+ # -------------------------------------------------------------------
211+ # 5. Micro: single object round-trip
212+ # -------------------------------------------------------------------
213+ puts "\n ### 5. Micro: single object (10_000 iterations)"
214+ separator
215+
216+ employee = build_employee
217+ employee_json = helper . to_json ( employee )
218+ employee_hash = helper . to_hash ( employee )
219+
220+ results [ 'micro_to_json' ] = run_benchmark ( 'to_json single employee x10k' ) do
221+ 10_000 . times do
222+ helper . to_json ( employee )
223+ end
224+ end
225+ results [ 'micro_from_json' ] = run_benchmark ( 'from_json single employee x10k' ) do
226+ 10_000 . times do
227+ helper . from_json ( json : employee_json , klass : BenchEmployee )
228+ end
229+ end
230+ results [ 'micro_to_hash' ] = run_benchmark ( 'to_hash single employee x10k' ) do
231+ 10_000 . times do
232+ helper . to_hash ( employee )
233+ end
234+ end
235+ results [ 'micro_from_hash' ] = run_benchmark ( 'from_hash single employee x10k' ) do
236+ 10_000 . times do
237+ helper . from_hash ( hash : employee_hash , klass : BenchEmployee )
238+ end
239+ end
240+
241+ # -------------------------------------------------------------------
242+ # Summary
243+ # -------------------------------------------------------------------
244+ puts "\n "
245+ puts '=' * 70
246+ puts 'Summary (median times in seconds)'
247+ puts '=' * 70
248+ results . sort_by { |_ , v | -v } . each do |label , time |
249+ printf " %-45s %8.4fs\n " , label , time
250+ end
0 commit comments