Remove eval from ArtifactInfo#to_hash - #437
Open
tas50 wants to merge 1 commit into
Open
Conversation
`#to_hash` called `eval(attribute)` once per attribute, so reading 13 instance
variables compiled 13 fresh Ruby ASTs on every call. `#initialize` had a
smaller version of the same problem, building `"@#{attribute}"` and
`attribute.to_sym` per attribute per object.
Precompute the attribute name and instance variable name as Symbols once at
load time and look them up directly.
Measured on Ruby 4.0.6:
#to_hash 33194 ns -> 1814 ns 18.3x 67 -> 2 allocations
#clone_with 37218 ns -> 3251 ns 11.4x 84 -> 6 allocations
.new 2945 ns -> 1211 ns 2.4x 14 -> 1 allocation
The gain compounds through `Backend::Base#windows_artifact_fixup!`, which
calls `#clone_with` for every Windows artifact plus every supported desktop
version. Cloning a 20 artifact set: 744 us -> 64 us, 11.7x.
Removing the eval also drops an unnecessary dynamic code path from a method
that only ever needs to read instance variables.
Output is unchanged: 576 unit examples pass and all 176 golden SHA256 digests
match main.
Signed-off-by: Tim Smith <tsmith84@proton.me>
tas50
force-pushed
the
perf/artifact-info-no-eval
branch
from
August 28, 2026 03:58
b85f21c to
ba9b950
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ArtifactInfo#to_hashcalledeval(attribute)once per attribute:So reading 13 instance variables compiled 13 fresh Ruby ASTs on every call — 33 µs and 67 allocations to do the work of 13 ivar reads.
#initializehad a smaller version of the same problem, building"@#{attribute}"andattribute.to_symper attribute per object.This precomputes the attribute name and instance variable name as Symbols once at load time and looks them up directly.
Measurements
Ruby 4.0.6:
#to_hash#clone_with.newThe gain compounds through
Backend::Base#windows_artifact_fixup!, which calls#clone_withfor every Windows artifact plus every supported desktop version:Also
Removing the
evaldrops a dynamic code path from a method that only ever needs to read instance variables. It is not exploitable today sinceATTRIBUTESis a frozen literal, but it is a sharp edge worth not having in a method that is otherwise trivial.Verification
chefstyleclean.to_hashequals the input hash,clone_withoverrides only the given keys, the generated readers agree withto_hash, and absent keys still read backnil.main.