lazy_init 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require_relative '../lib/lazy_init'
6
+
7
+ class LazyInitBenchmark
8
+ VERSION = '1.0.0'
9
+
10
+ def initialize
11
+ @results = {}
12
+ configure_lazy_init
13
+ puts header
14
+ end
15
+
16
+ def run_all
17
+ run_basic_patterns
18
+ run_computational_complexity
19
+ run_dependency_injection
20
+ # run_conditional_loading
21
+ run_class_level_shared
22
+ run_method_memoization
23
+ run_timeout_overhead
24
+ run_exception_handling
25
+ run_real_world_scenarios
26
+
27
+ print_summary
28
+ end
29
+
30
+ private
31
+
32
+ def configure_lazy_init
33
+ LazyInit.configure do |config|
34
+ config.track_performance = false
35
+ config.debug = false
36
+ end
37
+ end
38
+
39
+ def header
40
+ <<~HEADER
41
+ ===================================================================
42
+ LazyInit Performance Benchmark v#{VERSION}
43
+ ===================================================================
44
+ Ruby: #{RUBY_VERSION} (#{RUBY_ENGINE})
45
+ Platform: #{RUBY_PLATFORM}
46
+ Time: #{Time.now}
47
+ ===================================================================
48
+ HEADER
49
+ end
50
+
51
+ def benchmark_comparison(category, test_name, manual_impl, lazy_impl, warmup: true)
52
+ puts "\n--- #{test_name} ---"
53
+
54
+ # Warmup if requested
55
+ if warmup
56
+ manual_impl.call
57
+ lazy_impl.call
58
+ end
59
+
60
+ # Run benchmark
61
+ suite = Benchmark.ips do |x|
62
+ x.report('Manual', &manual_impl)
63
+ x.report('LazyInit', &lazy_impl)
64
+ x.compare!
65
+ end
66
+
67
+ # Extract results
68
+ manual_ips = suite.entries[0].ips
69
+ lazy_ips = suite.entries[1].ips
70
+
71
+ # Store results
72
+ store_result(category, test_name, manual_ips, lazy_ips)
73
+
74
+ # Print formatted results
75
+ puts "Manual: #{format_ips(manual_ips)}"
76
+ puts "LazyInit: #{format_ips(lazy_ips)}"
77
+ puts "Ratio: #{(manual_ips / lazy_ips).round(2)}x"
78
+
79
+ [manual_ips, lazy_ips]
80
+ end
81
+
82
+ def store_result(category, test_name, manual_ips, lazy_ips)
83
+ @results[category] ||= {}
84
+ @results[category][test_name] = {
85
+ manual: manual_ips,
86
+ lazy_init: lazy_ips,
87
+ ratio: (manual_ips / lazy_ips).round(2),
88
+ overhead_percent: ((lazy_ips / manual_ips - 1) * 100).round(1)
89
+ }
90
+ end
91
+
92
+ def format_ips(ips)
93
+ case ips
94
+ when 0...1_000
95
+ "#{ips.round(0)} i/s"
96
+ when 1_000...1_000_000
97
+ "#{(ips / 1_000.0).round(1)}K i/s"
98
+ else
99
+ "#{(ips / 1_000_000.0).round(2)}M i/s"
100
+ end
101
+ end
102
+
103
+ # ========================================
104
+ # BENCHMARK SCENARIOS
105
+ # ========================================
106
+
107
+ def run_basic_patterns
108
+ puts "\n" + '=' * 60
109
+ puts '1. BASIC LAZY INITIALIZATION PATTERNS'
110
+ puts '=' * 60
111
+
112
+ # Hot path performance
113
+ manual_basic = create_manual_basic
114
+ lazy_basic = create_lazy_basic
115
+
116
+ benchmark_comparison(
117
+ 'Basic Patterns',
118
+ 'Hot Path (after initialization)',
119
+ -> { manual_basic.expensive_value },
120
+ -> { lazy_basic.expensive_value }
121
+ )
122
+
123
+ # Cold start performance
124
+ benchmark_comparison(
125
+ 'Basic Patterns',
126
+ 'Cold Start (new instances)',
127
+ -> { create_manual_basic.expensive_value },
128
+ -> { create_lazy_basic.expensive_value },
129
+ warmup: false
130
+ )
131
+ end
132
+
133
+ def run_computational_complexity
134
+ puts "\n" + '=' * 60
135
+ puts '2. COMPUTATIONAL COMPLEXITY SCENARIOS'
136
+ puts '=' * 60
137
+
138
+ # Light computation
139
+ manual_light = create_manual_light
140
+ lazy_light = create_lazy_light
141
+
142
+ benchmark_comparison(
143
+ 'Computational Complexity',
144
+ 'Lightweight (sum 1..10)',
145
+ -> { manual_light.light_computation },
146
+ -> { lazy_light.light_computation }
147
+ )
148
+
149
+ # Medium computation
150
+ manual_medium = create_manual_medium
151
+ lazy_medium = create_lazy_medium
152
+
153
+ benchmark_comparison(
154
+ 'Computational Complexity',
155
+ 'Medium (map+sum 1..1000)',
156
+ -> { manual_medium.medium_computation },
157
+ -> { lazy_medium.medium_computation }
158
+ )
159
+
160
+ # Heavy computation
161
+ manual_heavy = create_manual_heavy
162
+ lazy_heavy = create_lazy_heavy
163
+
164
+ benchmark_comparison(
165
+ 'Computational Complexity',
166
+ 'Heavy (filter+sqrt 1..10000)',
167
+ -> { manual_heavy.heavy_computation },
168
+ -> { lazy_heavy.heavy_computation }
169
+ )
170
+ end
171
+
172
+ def run_dependency_injection
173
+ puts "\n" + '=' * 60
174
+ puts '3. DEPENDENCY INJECTION PERFORMANCE'
175
+ puts '=' * 60
176
+
177
+ # Simple dependencies
178
+ manual_deps = create_manual_deps
179
+ lazy_deps = create_lazy_deps
180
+
181
+ benchmark_comparison(
182
+ 'Dependency Injection',
183
+ 'Simple Dependencies',
184
+ -> { manual_deps.database },
185
+ -> { lazy_deps.database }
186
+ )
187
+
188
+ # Complex dependencies
189
+ manual_complex = create_manual_complex_deps
190
+ lazy_complex = create_lazy_complex_deps
191
+
192
+ benchmark_comparison(
193
+ 'Dependency Injection',
194
+ 'Complex Dependencies',
195
+ -> { manual_complex.service },
196
+ -> { lazy_complex.service }
197
+ )
198
+ end
199
+
200
+ # def run_conditional_loading
201
+ # puts "\n" + "="*60
202
+ # puts "4. CONDITIONAL LOADING PERFORMANCE"
203
+ # puts "="*60
204
+
205
+ # # Condition true
206
+ # manual_true = create_manual_conditional(true)
207
+ # lazy_true = create_lazy_conditional(true)
208
+
209
+ # benchmark_comparison(
210
+ # "Conditional Loading",
211
+ # "Condition True",
212
+ # -> { manual_true.feature },
213
+ # -> { lazy_true.feature }
214
+ # )
215
+
216
+ # # Condition false
217
+ # manual_false = create_manual_conditional(false)
218
+ # lazy_false = create_lazy_conditional(false)
219
+
220
+ # benchmark_comparison(
221
+ # "Conditional Loading",
222
+ # "Condition False",
223
+ # -> { manual_false.feature },
224
+ # -> { lazy_false.feature }
225
+ # )
226
+ # end
227
+
228
+ def run_class_level_shared
229
+ puts "\n" + '=' * 60
230
+ puts '5. CLASS-LEVEL SHARED RESOURCES'
231
+ puts '=' * 60
232
+
233
+ manual_class = create_manual_class_var
234
+ lazy_class = create_lazy_class_var
235
+
236
+ benchmark_comparison(
237
+ 'Class-Level Resources',
238
+ 'Shared Resources',
239
+ -> { manual_class.shared_resource },
240
+ -> { lazy_class.shared_resource }
241
+ )
242
+ end
243
+
244
+ def run_method_memoization
245
+ puts "\n" + '=' * 60
246
+ puts '6. METHOD-LOCAL MEMOIZATION'
247
+ puts '=' * 60
248
+
249
+ manual_memo = create_manual_memo
250
+ lazy_memo = create_lazy_memo
251
+
252
+ benchmark_comparison(
253
+ 'Method Memoization',
254
+ 'Hot Path Memoization',
255
+ -> { manual_memo.expensive_calc(100) },
256
+ -> { lazy_memo.expensive_calc(100) }
257
+ )
258
+ end
259
+
260
+ def run_timeout_overhead
261
+ puts "\n" + '=' * 60
262
+ puts '7. TIMEOUT OVERHEAD'
263
+ puts '=' * 60
264
+
265
+ no_timeout = create_no_timeout
266
+ with_timeout = create_with_timeout
267
+
268
+ benchmark_comparison(
269
+ 'Timeout Support',
270
+ 'Timeout vs No Timeout',
271
+ -> { no_timeout.quick_operation },
272
+ -> { with_timeout.quick_operation }
273
+ )
274
+ end
275
+
276
+ def run_exception_handling
277
+ puts "\n" + '=' * 60
278
+ puts '8. EXCEPTION HANDLING OVERHEAD'
279
+ puts '=' * 60
280
+
281
+ manual_exception = create_manual_exception
282
+ lazy_exception = create_lazy_exception
283
+
284
+ benchmark_comparison(
285
+ 'Exception Handling',
286
+ 'Exception Recovery',
287
+ -> { manual_exception.failing_method },
288
+ -> { lazy_exception.failing_method }
289
+ )
290
+ end
291
+
292
+ def run_real_world_scenarios
293
+ puts "\n" + '=' * 60
294
+ puts '9. REAL-WORLD SCENARIOS'
295
+ puts '=' * 60
296
+
297
+ manual_webapp = create_manual_webapp
298
+ lazy_webapp = create_lazy_webapp
299
+
300
+ benchmark_comparison(
301
+ 'Real-World',
302
+ 'Web Application Stack',
303
+ -> { manual_webapp.application },
304
+ -> { lazy_webapp.application }
305
+ )
306
+ end
307
+
308
+ # ========================================
309
+ # FACTORY METHODS
310
+ # ========================================
311
+
312
+ def create_manual_basic
313
+ Class.new do
314
+ def expensive_value
315
+ @expensive_value ||= "computed_value_#{rand(1000)}"
316
+ end
317
+ end.new
318
+ end
319
+
320
+ def create_lazy_basic
321
+ Class.new do
322
+ extend LazyInit
323
+ lazy_attr_reader :expensive_value do
324
+ "computed_value_#{rand(1000)}"
325
+ end
326
+ end.new
327
+ end
328
+
329
+ def create_manual_light
330
+ Class.new do
331
+ def light_computation
332
+ @light_computation ||= (1..10).sum
333
+ end
334
+ end.new
335
+ end
336
+
337
+ def create_lazy_light
338
+ Class.new do
339
+ extend LazyInit
340
+ lazy_attr_reader :light_computation do
341
+ (1..10).sum
342
+ end
343
+ end.new
344
+ end
345
+
346
+ def create_manual_medium
347
+ Class.new do
348
+ def medium_computation
349
+ @medium_computation ||= (1..1000).map { |i| i * 2 }.sum
350
+ end
351
+ end.new
352
+ end
353
+
354
+ def create_lazy_medium
355
+ Class.new do
356
+ extend LazyInit
357
+ lazy_attr_reader :medium_computation do
358
+ (1..1000).map { |i| i * 2 }.sum
359
+ end
360
+ end.new
361
+ end
362
+
363
+ def create_manual_heavy
364
+ Class.new do
365
+ def heavy_computation
366
+ @heavy_computation ||= (1..10_000).select(&:even?).map { |i| Math.sqrt(i) }.sum
367
+ end
368
+ end.new
369
+ end
370
+
371
+ def create_lazy_heavy
372
+ Class.new do
373
+ extend LazyInit
374
+ lazy_attr_reader :heavy_computation do
375
+ (1..10_000).select(&:even?).map { |i| Math.sqrt(i) }.sum
376
+ end
377
+ end.new
378
+ end
379
+
380
+ def create_manual_deps
381
+ Class.new do
382
+ def config
383
+ @config ||= { database_url: 'postgresql://localhost/test' }
384
+ end
385
+
386
+ def database
387
+ @database ||= begin
388
+ config
389
+ "Connected to: #{config[:database_url]}"
390
+ end
391
+ end
392
+ end.new
393
+ end
394
+
395
+ def create_lazy_deps
396
+ Class.new do
397
+ extend LazyInit
398
+
399
+ lazy_attr_reader :config do
400
+ { database_url: 'postgresql://localhost/test' }
401
+ end
402
+
403
+ lazy_attr_reader :database, depends_on: [:config] do
404
+ "Connected to: #{config[:database_url]}"
405
+ end
406
+ end.new
407
+ end
408
+
409
+ def create_manual_complex_deps
410
+ Class.new do
411
+ def config
412
+ @config ||= { db_url: 'postgresql://localhost', api_key: 'test123', debug: false }
413
+ end
414
+
415
+ def database
416
+ @database ||= begin
417
+ config
418
+ "DB: #{config[:db_url]}"
419
+ end
420
+ end
421
+
422
+ def api_client
423
+ @api_client ||= begin
424
+ config
425
+ database
426
+ "API: #{config[:api_key]} using #{database}"
427
+ end
428
+ end
429
+
430
+ def logger
431
+ @logger ||= begin
432
+ config
433
+ "Logger: debug=#{config[:debug]}"
434
+ end
435
+ end
436
+
437
+ def service
438
+ @service ||= begin
439
+ api_client
440
+ logger
441
+ "Service: #{api_client} with #{logger}"
442
+ end
443
+ end
444
+ end.new
445
+ end
446
+
447
+ def create_lazy_complex_deps
448
+ Class.new do
449
+ extend LazyInit
450
+
451
+ lazy_attr_reader :config do
452
+ { db_url: 'postgresql://localhost', api_key: 'test123', debug: false }
453
+ end
454
+
455
+ lazy_attr_reader :database, depends_on: [:config] do
456
+ "DB: #{config[:db_url]}"
457
+ end
458
+
459
+ lazy_attr_reader :api_client, depends_on: %i[config database] do
460
+ "API: #{config[:api_key]} using #{database}"
461
+ end
462
+
463
+ lazy_attr_reader :logger, depends_on: [:config] do
464
+ "Logger: debug=#{config[:debug]}"
465
+ end
466
+
467
+ lazy_attr_reader :service, depends_on: %i[api_client logger] do
468
+ "Service: #{api_client} with #{logger}"
469
+ end
470
+ end.new
471
+ end
472
+
473
+ def create_manual_conditional(enabled)
474
+ Class.new do
475
+ attr_accessor :feature_enabled
476
+
477
+ def initialize(enabled)
478
+ @feature_enabled = enabled
479
+ end
480
+
481
+ def feature
482
+ return nil unless feature_enabled
483
+
484
+ @feature ||= 'Feature loaded'
485
+ end
486
+ end.new(enabled)
487
+ end
488
+
489
+ # def create_lazy_conditional(enabled)
490
+ # Class.new do
491
+ # extend LazyInit
492
+ # attr_accessor :feature_enabled
493
+
494
+ # def initialize(enabled)
495
+ # @feature_enabled = enabled
496
+ # end
497
+
498
+ # lazy_attr_reader :feature, if_condition: -> { feature_enabled } do
499
+ # "Feature loaded"
500
+ # end
501
+ # end.new(enabled)
502
+ # end
503
+
504
+ def create_manual_class_var
505
+ Class.new do
506
+ def self.shared_resource
507
+ @@shared_resource ||= "Shared resource #{rand(1000)}"
508
+ end
509
+
510
+ def shared_resource
511
+ self.class.shared_resource
512
+ end
513
+ end.new
514
+ end
515
+
516
+ def create_lazy_class_var
517
+ klass = Class.new do
518
+ extend LazyInit
519
+
520
+ lazy_class_variable :shared_resource do
521
+ "Shared resource #{rand(1000)}"
522
+ end
523
+ end
524
+
525
+ # Initialize the class variable
526
+ klass.shared_resource
527
+ klass.new
528
+ end
529
+
530
+ def create_manual_memo
531
+ Class.new do
532
+ def expensive_calc(key)
533
+ @memo ||= {}
534
+ @memo[key] ||= "computed_#{key}_#{rand(1000)}"
535
+ end
536
+ end.new
537
+ end
538
+
539
+ def create_lazy_memo
540
+ Class.new do
541
+ include LazyInit
542
+
543
+ def expensive_calc(key)
544
+ lazy_once { "computed_#{key}_#{rand(1000)}" }
545
+ end
546
+ end.new
547
+ end
548
+
549
+ def create_no_timeout
550
+ Class.new do
551
+ extend LazyInit
552
+
553
+ lazy_attr_reader :quick_operation do
554
+ (1..10).sum
555
+ end
556
+ end.new
557
+ end
558
+
559
+ def create_with_timeout
560
+ Class.new do
561
+ extend LazyInit
562
+
563
+ lazy_attr_reader :quick_operation, timeout: 5 do
564
+ (1..10).sum
565
+ end
566
+ end.new
567
+ end
568
+
569
+ def create_manual_exception
570
+ Class.new do
571
+ def failing_method
572
+ @failing_method ||= begin
573
+ raise StandardError, 'Always fails'
574
+ rescue StandardError
575
+ 'recovered'
576
+ end
577
+ end
578
+ end.new
579
+ end
580
+
581
+ def create_lazy_exception
582
+ Class.new do
583
+ extend LazyInit
584
+
585
+ lazy_attr_reader :failing_method do
586
+ raise StandardError, 'Always fails'
587
+ rescue StandardError
588
+ 'recovered'
589
+ end
590
+ end.new
591
+ end
592
+
593
+ def create_manual_webapp
594
+ Class.new do
595
+ def config
596
+ @config ||= {
597
+ database_url: ENV['DATABASE_URL'] || 'postgresql://localhost/app',
598
+ redis_url: ENV['REDIS_URL'] || 'redis://localhost:6379',
599
+ api_key: ENV['API_KEY'] || 'test_key',
600
+ debug: ENV['DEBUG'] == 'true'
601
+ }
602
+ end
603
+
604
+ def database
605
+ @database ||= begin
606
+ config
607
+ "Database connection: #{config[:database_url]}"
608
+ end
609
+ end
610
+
611
+ def cache
612
+ @cache ||= begin
613
+ config
614
+ "Redis connection: #{config[:redis_url]}"
615
+ end
616
+ end
617
+
618
+ def api_client
619
+ @api_client ||= begin
620
+ config
621
+ "API client with key: #{config[:api_key]}"
622
+ end
623
+ end
624
+
625
+ def logger
626
+ @logger ||= begin
627
+ config
628
+ "Logger (debug: #{config[:debug]})"
629
+ end
630
+ end
631
+
632
+ def application
633
+ @application ||= begin
634
+ database
635
+ cache
636
+ api_client
637
+ logger
638
+ 'Application initialized with all services'
639
+ end
640
+ end
641
+ end.new
642
+ end
643
+
644
+ def create_lazy_webapp
645
+ Class.new do
646
+ extend LazyInit
647
+
648
+ lazy_attr_reader :config do
649
+ {
650
+ database_url: ENV['DATABASE_URL'] || 'postgresql://localhost/app',
651
+ redis_url: ENV['REDIS_URL'] || 'redis://localhost:6379',
652
+ api_key: ENV['API_KEY'] || 'test_key',
653
+ debug: ENV['DEBUG'] == 'true'
654
+ }
655
+ end
656
+
657
+ lazy_attr_reader :database, depends_on: [:config] do
658
+ "Database connection: #{config[:database_url]}"
659
+ end
660
+
661
+ lazy_attr_reader :cache, depends_on: [:config] do
662
+ "Redis connection: #{config[:redis_url]}"
663
+ end
664
+
665
+ lazy_attr_reader :api_client, depends_on: [:config] do
666
+ "API client with key: #{config[:api_key]}"
667
+ end
668
+
669
+ lazy_attr_reader :logger, depends_on: [:config] do
670
+ "Logger (debug: #{config[:debug]})"
671
+ end
672
+
673
+ lazy_attr_reader :application, depends_on: %i[database cache api_client logger] do
674
+ 'Application initialized with all services'
675
+ end
676
+ end.new
677
+ end
678
+
679
+ # ========================================
680
+ # RESULTS & SUMMARY
681
+ # ========================================
682
+
683
+ def print_summary
684
+ puts "\n" + '=' * 80
685
+ puts 'BENCHMARK SUMMARY'
686
+ puts '=' * 80
687
+
688
+ print_detailed_results
689
+ print_performance_analysis
690
+ print_recommendations
691
+ end
692
+
693
+ def print_detailed_results
694
+ puts "\nDetailed Results:"
695
+ puts '-' * 50
696
+
697
+ @results.each do |category, tests|
698
+ puts "\n#{category}:"
699
+
700
+ tests.each do |test_name, data|
701
+ overhead_str = if data[:overhead_percent] < -50
702
+ "#{(-data[:overhead_percent]).round(1)}% faster"
703
+ elsif data[:overhead_percent] > 0
704
+ "#{data[:ratio]}x slower"
705
+ else
706
+ 'similar performance'
707
+ end
708
+
709
+ puts " #{test_name}:"
710
+ puts " Manual: #{format_ips(data[:manual])}"
711
+ puts " LazyInit: #{format_ips(data[:lazy_init])}"
712
+ puts " Result: #{overhead_str}"
713
+ end
714
+ end
715
+ end
716
+
717
+ def print_performance_analysis
718
+ puts "\n" + '=' * 50
719
+ puts 'PERFORMANCE ANALYSIS'
720
+ puts '=' * 50
721
+
722
+ all_ratios = @results.values.flat_map(&:values).map { |data| data[:ratio] }
723
+ avg_ratio = (all_ratios.sum / all_ratios.size).round(2)
724
+ min_ratio = all_ratios.min.round(2)
725
+ max_ratio = all_ratios.max.round(2)
726
+
727
+ puts "\nOverall Performance Impact:"
728
+ puts " Average slowdown: #{avg_ratio}x"
729
+ puts " Best case: #{min_ratio}x slower"
730
+ puts " Worst case: #{max_ratio}x slower"
731
+
732
+ # Analyze patterns
733
+ computational = @results['Computational Complexity']
734
+ if computational
735
+ light_ratio = computational['Lightweight (sum 1..10)'][:ratio]
736
+ heavy_ratio = computational['Heavy (filter+sqrt 1..10000)'][:ratio]
737
+
738
+ if light_ratio > heavy_ratio * 1.5
739
+ puts "\n• LazyInit overhead decreases with computation complexity"
740
+ puts '• Better suited for expensive operations'
741
+ else
742
+ puts "\n• LazyInit overhead is consistent across complexity levels"
743
+ end
744
+ end
745
+
746
+ conditional = @results['Conditional Loading']
747
+ return unless conditional
748
+
749
+ true_ratio = conditional['Condition True'][:ratio]
750
+ false_ratio = conditional['Condition False'][:ratio]
751
+
752
+ return unless false_ratio < true_ratio * 0.7
753
+
754
+ puts '• Conditional loading is efficient when conditions are false'
755
+ end
756
+
757
+ def print_recommendations
758
+ puts "\n" + '=' * 50
759
+ puts 'RECOMMENDATIONS'
760
+ puts '=' * 50
761
+
762
+ all_ratios = @results.values.flat_map(&:values).map { |data| data[:ratio] }
763
+ avg_ratio = all_ratios.sum / all_ratios.size
764
+
765
+ puts "\nWhen to use LazyInit:"
766
+ if avg_ratio > 10
767
+ puts '• Only for very expensive initialization (>10ms)'
768
+ puts '• When thread safety is critical'
769
+ puts '• Complex dependency chains'
770
+ elsif avg_ratio > 5
771
+ puts '• Expensive initialization (>1ms)'
772
+ puts '• Multi-threaded applications'
773
+ puts '• When manual synchronization is error-prone'
774
+ else
775
+ puts '• Most lazy initialization scenarios'
776
+ puts '• Thread-safe applications'
777
+ puts '• Clean dependency management needed'
778
+ end
779
+
780
+ puts "\n" + '=' * 80
781
+ puts 'BENCHMARK COMPLETED'
782
+ puts '=' * 80
783
+ end
784
+ end
785
+
786
+ # Run the benchmark
787
+ if __FILE__ == $0
788
+ begin
789
+ benchmark = LazyInitBenchmark.new
790
+ benchmark.run_all
791
+ rescue StandardError => e
792
+ puts "Benchmark failed: #{e.message}"
793
+ puts e.backtrace.first(5)
794
+ exit 1
795
+ end
796
+ end