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

pass options to delegators when they accept it #336

Merged
merged 1 commit into from
Jul 15, 2020
Merged

pass options to delegators when they accept it #336

merged 1 commit into from
Jul 15, 2020

Conversation

dnesteryuk
Copy link
Member

@dnesteryuk dnesteryuk commented Jul 15, 2020

This change reduces hash allocations.

Here is a simplified example what was happening.

def do_not_accept_keywords(**)
end

100.times { do_not_accept_keywords(hash: true) }

After measuring via memory profiler, we got this:

Total allocated: 69600 bytes (300 objects)

allocated memory by location
-----------------------------------
     69600  profile/sample.rb:15

It points to a line where the method gets called. So, every call created a separate hash. It was happening for Grape::Entity::Delegator::PlainObject.

If options get extracted, the picture is better.

def do_not_accept_keywords(**)
end

opts = { hash: true }

100.times { do_not_accept_keywords(opts) }

Allocation:

Total allocated: 46632 bytes (201 objects)

allocated memory by location
-----------------------------------
     46400  profile/sample.rb:15
       232  profile/sample.rb:13

However, there is no object allocation if nothing is passed to the method.

Total allocated: 0 bytes (0 objects)

Btw, if a method "swallows" arguments, but nothing is passed, there is still allocation.

def do_not_accept_keywords(**)
end

100.times { do_not_accept_keywords }

Result:

Total allocated: 23200 bytes (100 objects)

allocated memory by location
-----------------------------------
     23200  profile/sample.rb:15

The splat operator brings its cost.

So, now Grape Entity checks whether the delegetor accepts options before passing them.

I measured this change against our production:

Before:

Total allocated: 1512362 bytes (12328 objects)

allocated memory by location
-----------------------------------
    605520  /usr/local/bundle/gems/grape-entity-0.8.0/lib/grape_entity/entity.rb:537 

After:

Total allocated: 908402 bytes (9733 objects)

allocated memory by location
-----------------------------------
     18000  /app/grape-entity/lib/grape_entity/entity.rb:553

0.57 MB of RAM were saved while serving the identical request.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 95.792% when pulling c7c18c9 on dnesteryuk:attr_delegation into ab3238d on ruby-grape:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 95.792% when pulling c7c18c9 on dnesteryuk:attr_delegation into ab3238d on ruby-grape:master.

@coveralls
Copy link

coveralls commented Jul 15, 2020

Coverage Status

Coverage increased (+0.1%) to 95.898% when pulling 2023ea8 on dnesteryuk:attr_delegation into ab3238d on ruby-grape:master.

This change reduces hash allocations.

Here is a simplified example what was happening.

    def do_not_accept_keywords(**)
    end

    100.times { do_not_accept_keywords(hash: true) }

After measuring via memory profiler, we got this:

    Total allocated: 69600 bytes (300 objects)

    allocated memory by location
    -----------------------------------
         69600  profile/sample.rb:15

It points to a line where the method gets called. So, every call created a separate hash. It was
happening for `Grape::Entity::Delegator::PlainObject`.

If options get extracted, the picture is better.

    def do_not_accept_keywords(**)
    end

    opts = { hash: true }

    100.times { do_not_accept_keywords(opts) }

Allocation:

    Total allocated: 46632 bytes (201 objects)

    allocated memory by location
    -----------------------------------
         46400  profile/sample.rb:15
           232  profile/sample.rb:13

However, there is no object allocation if nothing is passed to the method.
options.

    Total allocated: 0 bytes (0 objects)

Btw, if a method "swallows" arguments, but nothing is passed, there is still allocation.

    def do_not_accept_keywords(**)
    end

    100.times { do_not_accept_keywords }

Result:

    Total allocated: 23200 bytes (100 objects)

    allocated memory by location
    -----------------------------------
         23200  profile/sample.rb:15

The splat operator brings its cost.

So, now Grape Entity checks whether the delegetor accepts options before passing them.

I measured this change against our production:

**Before:**

    Total allocated: 1512362 bytes (12328 objects)

    allocated memory by location
    -----------------------------------
         605520  /usr/local/bundle/gems/grape-entity-0.8.0/lib/grape_entity/entity.rb:537

**After:**

    Total allocated: 908402 bytes (9733 objects)

    allocated memory by location
    -----------------------------------
         18000  /app/grape-entity/lib/grape_entity/entity.rb:553

0.57 MB of RAM were saved while serving the identical request.
@grape-bot
Copy link

1 Warning
⚠️ There’re library changes, but not tests. That’s OK as long as you’re refactoring existing code.

Generated by 🚫 danger

1 similar comment
@grape-bot
Copy link

grape-bot commented Jul 15, 2020

1 Warning
⚠️ There’re library changes, but not tests. That’s OK as long as you’re refactoring existing code.

Generated by 🚫 danger

@LeFnord
Copy link
Member

LeFnord commented Jul 15, 2020

thanks @dnesteryuk … especially for the explanation

but … can it have implications on upgrade?

@dnesteryuk
Copy link
Member Author

@LeFnord I don't see how it might impact existing code in projects which use grape entity 🤔

@LeFnord LeFnord merged commit 5d6b712 into ruby-grape:master Jul 15, 2020
@dnesteryuk dnesteryuk deleted the attr_delegation branch July 16, 2020 11:34
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