Skip to content

Commit dac855d

Browse files
committed
Benchmark abstract class instantiation
1 parent c4cee24 commit dac855d

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

benchmark/abstract_class_new.rb

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# typed: ignore
2+
# frozen_string_literal: true
3+
4+
# Benchmark the time it takes to instantiate a subclass of an abstract class
5+
6+
############################################# Results #############################################
7+
#
8+
# ruby 3.4.3 (2025-04-14 revision d0b7e5b6a0) +PRISM [arm64-darwin23]
9+
#
10+
# Time to instantiate a subclass of an abstract class
11+
# | | Interpreter | YJIT |
12+
# |-------------------|-------------------------:|-------------------------:|
13+
# | sorbet-runtime | (2.29x slower) 109.28 ns | (2.49x slower) 90.09 ns |
14+
# | type_toolkit | 47.80 ns | 36.23 ns |
15+
#
16+
# Time to instantiate a subclass of an abstract class with a custom implementation of `new`
17+
# | | Interpreter | YJIT |
18+
# |-------------------|-------------------------:|-------------------------:|
19+
# | sorbet-runtime | (1.10x slower) 132.95 ns | (1.32x slower) 104.45 ns |
20+
# | type_toolkit | 121.12 ns | 79.33 ns |
21+
#
22+
####################################################################################################
23+
24+
require "bundler"
25+
Bundler.require(:default, :benchmark)
26+
27+
require "type_toolkit"
28+
29+
# This benchmark has pretty high variance (it depends on the GC's allocation patterns),
30+
# so we run it for a longer time to get a more stable result.
31+
warmup = 10
32+
time = 30
33+
34+
width = ["type_toolkit", "sorbet-runtime", "manual delegation"].max_by(&:length).length
35+
36+
# module PreventConflictingAbstractPatch
37+
# def abstract!
38+
# # Prevent Sorbet's definition of `abstract!` from calling the TypeToolkit implementation of `abstract!`
39+
# return if singleton_class.include?(T::Helpers)
40+
41+
# super
42+
# end
43+
# end
44+
45+
# Class.prepend(PreventConflictingAbstractPatch)
46+
47+
module TypeKitDemo
48+
class Parent
49+
TypeToolkit.make_abstract!(self)
50+
end
51+
52+
class Child < Parent; end
53+
54+
class Child_OverridesNew < Parent
55+
def self.new(...) = super
56+
end
57+
end
58+
59+
module SorbetRuntimeDemo
60+
class Parent
61+
extend T::Helpers
62+
63+
# binding.irb
64+
abstract!
65+
end
66+
67+
class Child < Parent; end
68+
69+
class Child_OverridesNew < Parent
70+
def self.new(...) = super
71+
end
72+
end
73+
74+
# Run GC before each job run.
75+
#
76+
# Inspired by https://www.omniref.com/ruby/2.2.1/symbols/Benchmark/bm?#annotation=4095926&line=182
77+
class GCSuite
78+
def warming(*)
79+
GC.start
80+
end
81+
82+
def running(*)
83+
GC.start
84+
end
85+
86+
def warmup_stats(*)
87+
end
88+
89+
def add_report(*)
90+
end
91+
end
92+
93+
suite = GCSuite.new
94+
95+
[:interpreter, :yjit].each do |mode|
96+
if mode == :yjit
97+
puts <<~MSG
98+
99+
100+
================================================================================
101+
Enabling YJIT...
102+
================================================================================
103+
104+
105+
MSG
106+
RubyVM::YJIT.enable
107+
end
108+
109+
puts "\nBenchmark the time to instantiate a subclass of an abstract class..."
110+
Benchmark.ips do |x|
111+
x.config(warmup:, time:)
112+
113+
x.report("type_toolkit".rjust(width)) do |times|
114+
i = 0
115+
while (i += 1) < times
116+
TypeKitDemo::Child.new
117+
end
118+
end
119+
120+
x.report("sorbet-runtime".rjust(width)) do |times|
121+
i = 0
122+
while (i += 1) < times
123+
SorbetRuntimeDemo::Child.new
124+
end
125+
end
126+
127+
x.compare!
128+
end
129+
130+
puts "\nBenchmark the time to instantiate a subclass of an abstract class with a custom implementation of `new`..."
131+
Benchmark.ips do |x|
132+
x.config(warmup:, time:, suite:)
133+
134+
x.report("type_toolkit".rjust(width)) do |times|
135+
i = 0
136+
while (i += 1) < times
137+
TypeKitDemo::Child_OverridesNew.new
138+
end
139+
end
140+
141+
x.report("sorbet-runtime".rjust(width)) do |times|
142+
i = 0
143+
while (i += 1) < times
144+
SorbetRuntimeDemo::Child_OverridesNew.new
145+
end
146+
end
147+
148+
x.compare!
149+
end
150+
end

0 commit comments

Comments
 (0)