Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize initializer #56

Merged
merged 2 commits into from
Mar 22, 2017

Conversation

michaeldiscala
Copy link
Contributor

We use the Values gem extensively in our application and in some pipelines allocate hundreds of thousands of these objects. As a result we're particularly sensitive to the performance of the #initialize method.

By switching from define_method to a class_eval, we roughly 2x the number of allocations that can be performed in a given timespan:

Warming up --------------------------------------
               value    16.766k i/100ms
              value2    31.020k i/100ms
Calculating -------------------------------------
               value    182.303k (± 3.3%) i/s -    922.130k in   5.063815s
              value2    337.187k (± 3.7%) i/s -      1.706M in   5.066975s
true

value is the original implementaion and value2 is the code provided in this patch. See https://gist.github.com/michaeldiscala/cee9dbefa06e0d73e15d5bb095ba11bd for the benchmark script.

@codecov-io
Copy link

codecov-io commented Mar 22, 2017

Codecov Report

Merging #56 into master will decrease coverage by 2.36%.
The diff coverage is 80%.

@@            Coverage Diff             @@
##           master      #56      +/-   ##
==========================================
- Coverage   98.79%   96.42%   -2.37%     
==========================================
  Files           1        1              
  Lines          83       84       +1     
==========================================
- Hits           82       81       -1     
- Misses          1        3       +2
Impacted Files Coverage Δ
lib/values.rb 96.42% <80%> (-2.37%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8b78d97...56f393b. Read the comment docs.

@ms-ati
Copy link
Contributor

ms-ati commented Mar 22, 2017

👍 from me. @tcrayford can you take a look please?

"@#{fields[idx]} = values[#{idx}]"
end.join("\n")

class_eval <<-RUBY
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks great, thanks! Mind adding a comment here mentioning this optimization? Whilst this library doesn't change much, I like to prevent opportunities for bitrot where possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review -- I added a comment above this section to explain a bit more of what's happening and to point back to the PR. Does that cover what you were hoping for?

ms-ati added a commit to ms-ati/Values that referenced this pull request Mar 22, 2017
This PR is inspired by tcrayford#56, and
assumes that code will be merged, so uses it in the benchmarks here:
https://gist.github.com/ms-ati/fa8002ef8a0ce00716e9aa6510d3d4d9

It is common in our code, as in any idiomatic code using value objects
in loops or pipelines, to call `#with` many times, returning a new immutable
object each time with 1 or more fields replaced with new values.

The optimizations in this PR eliminate a number of extra Hash and Array
instantiations that were occurring each time, in favor of iterating only over
the constant `VALUE_ATTRS` array and doing key lookups in the given
Hash parameter in the hot paths.

Per the gist above, this increases ips (iterations per second) 2.29x, from
335.9 to 769.6 on my machine.
@tcrayford
Copy link
Owner

nice, thanks! I'll cut a new release over the weekend if I get time.

@tcrayford tcrayford merged commit 121e18d into tcrayford:master Mar 22, 2017
@ms-ati ms-ati mentioned this pull request Mar 22, 2017
ms-ati added a commit to ms-ati/Values that referenced this pull request Mar 23, 2017
This PR is inspired by tcrayford#56, and
assumes that code will be merged, so uses it in the benchmarks here:
https://gist.github.com/ms-ati/fa8002ef8a0ce00716e9aa6510d3d4d9

It is common in our code, as in any idiomatic code using value objects
in loops or pipelines, to call `#with` many times, returning a new immutable
object each time with 1 or more fields replaced with new values.

The optimizations in this PR eliminate a number of extra Hash and Array
instantiations that were occurring each time, in favor of iterating only over
the constant `VALUE_ATTRS` array and doing key lookups in the given
Hash parameter in the hot paths.

Per the gist above, this increases ips (iterations per second) 2.29x, from
335.9 to 769.6 on my machine.
tomeon pushed a commit to ruby-clubhouse/Values that referenced this pull request Apr 21, 2024
This PR is inspired by tcrayford#56, and
assumes that code will be merged, so uses it in the benchmarks here:
https://gist.github.com/ms-ati/fa8002ef8a0ce00716e9aa6510d3d4d9

It is common in our code, as in any idiomatic code using value objects
in loops or pipelines, to call `#with` many times, returning a new immutable
object each time with 1 or more fields replaced with new values.

The optimizations in this PR eliminate a number of extra Hash and Array
instantiations that were occurring each time, in favor of iterating only over
the constant `VALUE_ATTRS` array and doing key lookups in the given
Hash parameter in the hot paths.

Per the gist above, this increases ips (iterations per second) 2.29x, from
335.9 to 769.6 on my machine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants