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

improve performance of strawberry.lazy() with relative imports #2926

Merged
merged 5 commits into from
Jul 8, 2023

Conversation

karimsa
Copy link
Contributor

@karimsa karimsa commented Jul 6, 2023

Description

I've been profiling a few tests in my company's codebase and realized that some tests that actually only perform about 1s of business logic take ~12s to run. A large part of that time (~8-9s) is spent resolving forward refs in strawberry.lazy(). The largest culprit there was the use of inspect.stack() which seems to be incredibly slow.

This PR updates the method of retrieving the target stack frame. It seems that the stack frames are stored as a linkedlist anyways, so retrieving the active frame and then working backwards is significantly faster (likely because .stack() collects more information for every frame that is unused by strawberry). This one line change took my test down from 12s to 2.5s.

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Documentation

Issues Fixed or Closed by This PR

N/a

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

@@ -73,7 +73,7 @@ def __init__(self, module: str) -> None:
self.package = None

if module.startswith("."):
frame = inspect.stack()[2][0]
frame = inspect.currentframe().f_back.f_back
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't bother to check for nulls at every location, since this should only be accessed via .lazy() and also the previous code would've hit a bounds check in the same condition anyways

Copy link
Member

Choose a reason for hiding this comment

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

nice! we also use sys._getframe(x) in another place, would you mind trying with that and see if performs the same? 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yupp! It has the same effect - actually _getframe is in C, so looks like it is even faster. Whipped up a small benchmark to look at the difference:

Screenshot 2023-07-06 at 5 34 35 PM
import time
import inspect
import sys

def run_benchmark(name, fn):
	start_time = time.time()

	for i in range(1000000):
		fn()

	end_time = time.time()
	duration = end_time - start_time

	print(f"{name} completed in {duration}s")

def main():
	run_benchmark("inspect.stack()[2]", lambda: inspect.stack()[2])
	run_benchmark("inspect.currentframe().f_back.f_back", lambda: inspect.currentframe().f_back.f_back)
	run_benchmark("sys._getframe(2)", lambda: sys._getframe(2))

main()

Copy link
Member

Choose a reason for hiding this comment

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

awesome! thanks for that!

@botberry
Copy link
Member

botberry commented Jul 6, 2023

Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:


This release includes a performance improvement to strawberry.lazy() to allow relative module imports to be resolved faster.


Here's the preview release card for twitter:

Here's the tweet text:

🆕 Release (next) is out! Thanks to Karim Alibhai for the PR 👏

Get it here 👉 https://github.com/strawberry-graphql/strawberry/releases/tag/(next)

@patrick91
Copy link
Member

as a not for myself, I think we should add some benchmark for this :)

@codecov
Copy link

codecov bot commented Jul 6, 2023

Codecov Report

Merging #2926 (42a3679) into main (1de7477) will not change coverage.
The diff coverage is 100.00%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2926   +/-   ##
=======================================
  Coverage   96.17%   96.17%           
=======================================
  Files         211      211           
  Lines        9223     9223           
  Branches     1704     1704           
=======================================
  Hits         8870     8870           
  Misses        227      227           
  Partials      126      126           

Copy link
Member

@patrick91 patrick91 left a comment

Choose a reason for hiding this comment

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

Thank you so much! 🏃‍♂️💨

@patrick91 patrick91 merged commit 0b57c09 into strawberry-graphql:main Jul 8, 2023
37 checks passed
@botberry
Copy link
Member

botberry commented Jul 8, 2023

Thanks for contributing to Strawberry! 🎉 You've been invited to join
the Strawberry GraphQL organisation 😊

You can also request a free sticker by filling this form: https://forms.gle/dmnfQUPoY5gZbVT67

And don't forget to join our discord server: https://strawberry.rocks/discord 🔥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants