Skip to content

Commit d7a3a05

Browse files
committed
ActiveModel::Attribute: elide dup for immutable types
`Attribute` is mostly immutable, but the deserialized value is memoized, and that value can potentially be mutable, and mutated. When the value is immutable however, we can share instances. Benchmark: https://gist.github.com/casperisfine/ae56bec1e7eecbff3a696b367e2bafa2 Before: ``` ruby 3.4.0preview2 (2024-10-07 master 32c733f57b) +YJIT +PRISM [arm64-darwin23] Warming up -------------------------------------- assign_attributes 6.140k i/100ms Calculating ------------------------------------- assign_attributes 60.732k (± 1.9%) i/s (16.47 μs/i) - 307.000k in 5.056757s ``` After: ``` ruby 3.4.0preview2 (2024-10-07 master 32c733f57b) +YJIT +PRISM [arm64-darwin23] Warming up -------------------------------------- assign_attributes 8.245k i/100ms Calculating ------------------------------------- assign_attributes 82.410k (± 1.3%) i/s (12.13 μs/i) - 412.250k in 5.003220s ```
1 parent 5465c7d commit d7a3a05

File tree

3 files changed

+16
-0
lines changed

3 files changed

+16
-0
lines changed

activemodel/lib/active_model/attribute.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ def with_type(type)
9696
end
9797
end
9898

99+
def deep_dup # :nodoc:
100+
if @type.mutable?
101+
dup
102+
else
103+
self # If the underlying type is immutable we can get away with not duping
104+
end
105+
end
106+
99107
def type_cast(*)
100108
raise NotImplementedError
101109
end

activemodel/lib/active_model/attribute/user_provided_default.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ def with_type(type)
2626
self.class.new(name, user_provided_value, type, original_attribute)
2727
end
2828

29+
# Can't elide dup when a default is provided.
30+
# See Attribute#deep_dup
31+
alias_method :deep_dup, :dup
32+
2933
def marshal_dump
3034
result = [
3135
name,

activemodel/lib/active_model/type/string.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def to_immutable_string
2929
)
3030
end
3131

32+
def mutable? # :nodoc:
33+
true
34+
end
35+
3236
private
3337
def cast_value(value)
3438
case value

0 commit comments

Comments
 (0)