Skip to content

Optimize internal extract! calls to save on memory allocation #598

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

moberegger
Copy link

@moberegger moberegger commented May 30, 2025

Simply optimizes internal calls to extract! to avoid extra memory allocations to splat the *attributes. Both _extract_hash_values and _extract_method_values accept an array for attributes, so no need to re-splat it by calling extract!, which then splats it again.

Solution is to implement a private _extract that just accepts an array, which can be used internally.

A simple way to compare it is with the call DSL, which was just running extract! underneath.

# person = { first_name: 'John', last_name: 'Doe', age: 30, city: 'New York' }

json.(person, :first_name, :last_name, :age, :city)
Warming up --------------------------------------
              before   156.678k i/100ms
               after   176.882k i/100ms
Calculating -------------------------------------
              before      1.674M (± 1.6%) i/s  (597.37 ns/i) -      8.461M in   5.055454s
               after      1.802M (± 2.0%) i/s  (554.82 ns/i) -      9.021M in   5.007138s

Comparison:
               after:  1802399.6 i/s
              before:  1673994.1 i/s - 1.08x  slower
Calculating -------------------------------------
              before   320.000  memsize (     0.000  retained)
                         6.000  objects (     0.000  retained)
                         4.000  strings (     0.000  retained)
               after   240.000  memsize (     0.000  retained)
                         5.000  objects (     0.000  retained)
                         4.000  strings (     0.000  retained)

Comparison:
               after:        240 allocated
              before:        320 allocated - 1.33x more

This change has no impact in using json.extract! directly.

# person = { first_name: 'John', last_name: 'Doe', age: 30, city: 'New York' }

json.extract! person, :first_name, :last_name, :age, :city
Calculating -------------------------------------
              before   240.000  memsize (     0.000  retained)
                         5.000  objects (     0.000  retained)
                         4.000  strings (     0.000  retained)
               after   240.000  memsize (     0.000  retained)
                         5.000  objects (     0.000  retained)
                         4.000  strings (     0.000  retained)

Comparison:
              before:        240 allocated
               after:        240 allocated - same

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.

1 participant