Skip to content

Commit d36eb23

Browse files
committed
Specialize various #present? implementations
Because `#present?` always resolve to `Object#present?`, it's an extremely polymorphic method, and inline cache hits are low. In addition, it requires an extra call to `self.blank?` which is an overhead. By specializing `present?` on common types, we avoid both of these slow-downs: ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22] Warming up -------------------------------------- present? 198.028k i/100ms opt_present? 565.521k i/100ms Calculating ------------------------------------- present? 2.087M (± 8.8%) i/s - 10.297M in 5.028398s opt_present? 5.584M (± 8.6%) i/s - 27.711M in 5.023852s Comparison: present?: 2086621.6 i/s opt_present?: 5584373.5 i/s - 2.68x faster ``` ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22] Warming up -------------------------------------- present? 819.792k i/100ms opt_present? 1.047M i/100ms Calculating ------------------------------------- present? 12.192M (± 8.8%) i/s - 60.665M in 5.050622s opt_present? 16.540M (± 8.2%) i/s - 82.676M in 5.059029s Comparison: present?: 12192047.5 i/s opt_present?: 16539689.6 i/s - 1.36x faster ``` ```ruby require 'bundler/inline' gemfile do source 'https://rubygems.org' gem 'benchmark-ips' gem 'activesupport' end require 'active_support/all' class Object def opt_present? respond_to?(:empty?) ? !empty? : !!self end end class NilClass def opt_present? false end end class FalseClass def opt_present? false end end class TrueClass def opt_present? true end end class Array def opt_present? !empty? end end class Hash def opt_present? !empty? end end class Symbol def opt_present? !empty? end end class String def opt_present? !blank? end end class Numeric # :nodoc: def opt_present? true end end class Time # :nodoc: def opt_present? true end end array = [] hash = {} time = Time.now puts RUBY_DESCRIPTION Benchmark.ips do |x| x.report("present?") do true.present? false.present? 1.present? 1.0.present? array.present? hash.present? :foo.present? time.present? end x.report("opt_present?") do true.opt_present? false.opt_present? 1.opt_present? 1.0.opt_present? array.opt_present? hash.opt_present? :foo.opt_present? time.opt_present? end x.compare!(order: :baseline) end ```
1 parent 7567010 commit d36eb23

File tree

5 files changed

+53
-1
lines changed

5 files changed

+53
-1
lines changed

activesupport/lib/active_support/core_ext/date/blank.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ class Date # :nodoc:
1111
def blank?
1212
false
1313
end
14+
15+
def present?
16+
true
17+
end
1418
end

activesupport/lib/active_support/core_ext/date_time/blank.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ class DateTime # :nodoc:
1111
def blank?
1212
false
1313
end
14+
15+
def present?
16+
true
17+
end
1418
end

activesupport/lib/active_support/core_ext/object/blank.rb

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Object
1616
#
1717
# @return [true, false]
1818
def blank?
19-
respond_to?(:empty?) ? !!empty? : !self
19+
respond_to?(:empty?) ? !!empty? : false
2020
end
2121

2222
# An object is present if it's not blank.
@@ -56,6 +56,10 @@ class NilClass
5656
def blank?
5757
true
5858
end
59+
60+
def present? # :nodoc:
61+
false
62+
end
5963
end
6064

6165
class FalseClass
@@ -67,6 +71,10 @@ class FalseClass
6771
def blank?
6872
true
6973
end
74+
75+
def present? # :nodoc:
76+
false
77+
end
7078
end
7179

7280
class TrueClass
@@ -78,6 +86,10 @@ class TrueClass
7886
def blank?
7987
false
8088
end
89+
90+
def present? # :nodoc:
91+
true
92+
end
8193
end
8294

8395
class Array
@@ -88,6 +100,10 @@ class Array
88100
#
89101
# @return [true, false]
90102
alias_method :blank?, :empty?
103+
104+
def present? # :nodoc:
105+
!empty?
106+
end
91107
end
92108

93109
class Hash
@@ -98,6 +114,10 @@ class Hash
98114
#
99115
# @return [true, false]
100116
alias_method :blank?, :empty?
117+
118+
def present? # :nodoc:
119+
!empty?
120+
end
101121
end
102122

103123
class Symbol
@@ -106,6 +126,10 @@ class Symbol
106126
# :''.blank? # => true
107127
# :symbol.blank? # => false
108128
alias_method :blank?, :empty?
129+
130+
def present? # :nodoc:
131+
!empty?
132+
end
109133
end
110134

111135
class String
@@ -137,6 +161,10 @@ def blank?
137161
ENCODED_BLANKS[self.encoding].match?(self)
138162
end
139163
end
164+
165+
def present? # :nodoc:
166+
!blank?
167+
end
140168
end
141169

142170
class Numeric # :nodoc:
@@ -149,6 +177,10 @@ class Numeric # :nodoc:
149177
def blank?
150178
false
151179
end
180+
181+
def present?
182+
true
183+
end
152184
end
153185

154186
class Time # :nodoc:
@@ -160,4 +192,8 @@ class Time # :nodoc:
160192
def blank?
161193
false
162194
end
195+
196+
def present?
197+
true
198+
end
163199
end

activesupport/lib/active_support/core_ext/pathname/blank.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ class Pathname
1313
def blank?
1414
to_s.empty?
1515
end
16+
17+
def present? # :nodoc:
18+
!to_s.empty?
19+
end
1620
end

activesupport/lib/active_support/time_with_zone.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ def blank?
506506
false
507507
end
508508

509+
def present? # :nodoc:
510+
true
511+
end
512+
509513
def freeze
510514
# preload instance variables before freezing
511515
period; utc; time; to_datetime; to_time

0 commit comments

Comments
 (0)